I'm new with xcode programming, I'm trying to implement an App in Swift 2 that makes an HTTP Get request. After upgrading xcode 7 its showing error of:
Cannot convert value of type
'(NSData!, response: NSURLResponse!, err: NSError!) -> ()'
to expected argument type
'(NSData?, NSURLResponse?, NSError?) -> Void'
(This code snippet uses the old error handling of swift 1.2.) Can anyone help me please how to implement this in Swift 2.0.
request.HTTPMethod = "GET"
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(request, completionHandler:loadedData)
task.resume()
}
func loadedData(data:NSData!, response:NSURLResponse!, err:NSError!){
if(err != nil)
{
print(err?.description)
}
else
{
var jsonResult: NSDictionary = NSDictionary()
let httpResponse = response as! NSHTTPURLResponse
print("\(httpResponse.statusCode)")
if (httpResponse.statusCode == 200)
{
jsonResult = (try! NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers)) as! NSDictionary
print(jsonResult)
self.performSegueWithIdentifier("SuccessSignin", sender: self)
}
else if (httpResponse.statusCode == 422){
print("422 Error Occured...")
}
}
}
The method signature has changed (parameters are now optionals). Also, you have to use try enclosed in a do catch block. And avoid using forced try (with !) but prefer catching possible errors, and use if let to safely unwrap optionals. Example:
let task = session.dataTaskWithRequest(request) { (data, response, error) -> Void in
if error != nil {
print(error!.description)
} else {
if let httpResponse = response as? NSHTTPURLResponse {
if httpResponse.statusCode == 200 {
do {
if let data = data, let jsonResult = try NSJSONSerialization.JSONObjectWithData(data, options: []) as? NSDictionary {
print(jsonResult)
self.performSegueWithIdentifier("SuccessSignin", sender: self)
}
} catch let JSONError as NSError {
print(JSONError)
}
} else if (httpResponse.statusCode == 422) {
print("422 Error Occured...")
}
} else {
print("Can't cast response to NSHTTPURLResponse")
}
}
}
task.resume()
Here's the error message that you are receiving:
Cannot convert value of type
'(NSData!, response: NSURLResponse!, err: NSError!) -> ()'
to expected argument type
'(NSData?, NSURLResponse?, NSError?) -> Void'
As is shown in the message, the parameters for the dataTaskWithRequest's completionHandler changed from being forced unwrapped (!) to being just optionals (?).
Notice the ! versus the ?:
// old
(NSData!, response: NSURLResponse!, err: NSError!)
// new
(NSData?, NSURLResponse?, NSError?)
As a result, you need to adjust your code accordingly.
For example, your method declaration will look like this:
func loadedData(data:NSData?, response:NSURLResponse?, err:NSError?)
In addition, evaluate the method body and make sure that you are now properly unwrapping the optional parameters data, response and err.
See the NSURLSession class reference for more information.
Related
I am following an e-class tutorial for SWIFT // XCODE 11.4 and I have to fetch data from Open Weather API and display it on the interface where people can type in a city and the view controller will display temperature, cloud icon, and description.
Clima App Tutorial
I am using the MVC pattern design and the delegate design to accomplish this tutorial. My swift files are as followed:
Swift Files in MVC Design Pattern
Here are the codes in each of the important files:
I. Model folder
WeatherManager.swift
protocol WeatherManagerDelegate {
func didUpdateWeather(weather: WeatherModel)
}
struct WeatherManager {
let weatherURL = "https://api.openweathermap.org/d.5/weather?appid=c8b50079338280b47a65dd6082551e3b&units=imperial"
let delegate: WeatherManagerDelegate?
func fetchWeather(cityName: String) {
let urlString = "\(weatherURL)&q=\(cityName)"
performRequest(urlString: urlString)
}
func performRequest(urlString: String) {
//create a URL
if let url = URL(string: urlString) {
//create a URLSession
let session = URLSession(configuration: .default)
//give session a task
let task = session.dataTask(with: url) { (data, response, error) in
if error != nil {
print(error!)
return //exit out of the func if there is an error
}
if let safeData = data {
if let weather = self.parseJSON(weatherData: safeData) {
self.delegate?.didUpdateWeather(weather: weather)
}
}
}
//start the tast
task.resume()
}
}
func parseJSON (weatherData: Data) -> WeatherModel? {
let decoder = JSONDecoder()
do {
let decodedData = try decoder.decode(WeatherData.self, from: weatherData)
let id = decodedData.weather[0].id
let temp = decodedData.main.temp
let name = decodedData.name
let weather = WeatherModel(conditionId: id, cityName: name, temperature: temp)
return weather
} catch {
print(error)
return nil
}
}
}
WeatherData.swift
struct WeatherData: Codable {
let name: String
let main: Main
let weather: [Weather]
}
struct Main: Codable {
let temp: Double
}
struct Weather: Codable {
let id: Int
}
WeatherModel.swift
struct WeatherModel {
let conditionId: Int
let cityName: String
let temperature: Double
var temperatureString: String {
return String(format: "%.1f", temperature)
}
var conditionName: String {
switch conditionId {
case 200...232:
return "cloud.bolt"
case 300...321:
return "cloud.drizzle"
case 500...531:
return "cloud.rain"
case 600...622:
return "cloud.snow"
case 701...781:
return "cloud.fog"
case 800:
return "sun.max"
case 801...804:
return "cloud.bolt"
default:
return "cloud"
}
}
}
II. Controller
WeatherViewController.swift (place where the error is)
class WeatherViewController: UIViewController, UITextFieldDelegate, WeatherManagerDelegate {
#IBOutlet weak var conditionImageView: UIImageView!
#IBOutlet weak var temperatureLabel: UILabel!
#IBOutlet weak var cityLabel: UILabel!
#IBOutlet weak var searchTextField: UITextField!
var weatherManager = WeatherManager()
override func viewDidLoad() {
super.viewDidLoad()
weatherManager.delegate = self
searchTextField.delegate = self
}
#IBAction func searchPressed(_ sender: UIButton) {
searchTextField.endEditing(true)
}
func textFieldShouldReturn(_ textField: UITextField) -> Bool {
searchTextField.endEditing(true)
print(searchTextField.text!)
return true
}
func textFieldShouldEndEditing(_ textField: UITextField) -> Bool {
if textField.text != "" {
return true
} else {
textField.placeholder = "Type something..."
return false
}
}
func textFieldDidEndEditing(_ textField: UITextField) {
if let city = searchTextField.text {
weatherManager.fetchWeather(cityname: city)
}
searchTextField.text = ""
}
func didUpdateWeather(weather: WeatherModel) {
print(weather.temperature)
}
}
Here is the error message: Missing argument for parameter 'delegate' in call
Error message in WeatherViewControl.swift
And when I hit the run button, I also got this error in the debug console:
dataCorrupted(Swift.DecodingError.Context(codingPath: [], debugDescription: "The given data was not valid JSON.", underlyingError: Optional(Error Domain=NSCocoaErrorDomain Code=3840 "Invalid value around character 0." UserInfo={NSDebugDescription=Invalid value around character 0.})))
Error in the debug console
What should I do to get rid of these errors?
You need to change the keyword "let" to "var" in let delegate: WeatherManagerDelegate? in struct WeatherManager.
Missing argument for parameter 'delegate' in call
When a struct is create a value for each property is required.
If each property is specified with a default value and there is no user-defined initializer then Swift will create a default initializer for the struct.
If there is at least one property without a default value and there is no user-defined initializer then Swift will create a memberwise initializer which has one parameter for each property without a default value.
For example your type:
struct WeatherModel {
let conditionId: Int
let cityName: String
let temperature: Double
...
has three properties without default values. If you start typing:
let myWeatherModel = WeatherModel(
and then take the completion offered you will get (something like):
let wm = WeatherModel(conditionId: <Int>, cityName: <String>, temperature: <Double>)
The completion is showing the memberwise initializer.
Your type which produces the error is:
struct WeatherManager {
let weatherURL = "https://api.openweathermap.org/d.5/weather?appid=c8b50079338280b47a65dd6082551e3b&units=imperial"
let delegate: WeatherManagerDelegate?
which has two properties only one of which has a default value, and it has no initializers, so Swift will create a member wise initialiser automatically.
There is nothing wrong so far.
The line producing the error is:
var weatherManager = WeatherManager()
Here you are attempting to create a WeatherManager without invoking the member wise initalizer, so Swift gives you the error message.
If you click on the error message itself you will see a Fix is offered, click that and Swift will change your code to:
var weatherManager = WeatherManager(delegate: <WeatherManagerDelegate?>)
Select the <WeatherManagerDelegate?> and type the value you wish to pass.
HTH
NSXMLParser Failed to parse data on watch but working fine on watch simulator.How to resolve this issue?
Kindly help
Here is Code:
let url:String="http://www.someurl.com/data"
let urlToSend: NSURL = NSURL(string: url)!
// Parse the XML
parser = NSXMLParser(contentsOfURL: urlToSend)!
parser.delegate = self
let success:Bool = parser.parse()
if success {
print("parse success!")
print(strXMLData)
} else {
print("parse failure!")
}
I've experienced the same issue, and I couldn't debug the app when running on the watch. My solution was to parse the XML file in the iPhone App side and communicate the data to the watch through a WCSession.
I had exactly the same problem. I found an answer on one of Apple's own forums.
You need to read in the XML from URL into NSData, then invoke XML parser with NSData object, not the NSURL.
Here is some code
var nsXMLData = NSData()
var parser = NSXMLParser()
func xmlFileRequest(urlweb:NSURL){
print("in xmlFileRequest")
let requestURL: NSURL = urlweb
let urlRequest: NSMutableURLRequest =
NSMutableURLRequest(URL: requestURL)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(urlRequest) {
(data, response, error) -> Void in
if error == nil {
print(">>>>>>xmlFileRequest success!")
self.nsXMLData = data!
self.beginParsing()
} else {
print(">>>>>>>xmlFileRequest fail")
}
}
task.resume()
}
func beginParsing()
{
parser = NSXMLParser.init(data:nsXMLData)
parser.delegate = self
let success:Bool = parser.parse()
if success {
print("***parse success***")
} else {
print("***parse failure!***")
let errorMsg:String =
(parser.parserError?.localizedDescription)!
print("Error = " + errorMsg)
}
}
I just tested this code on our Apple watch.
I am trying to use Swift to implement the Microsoft Band SDK. I keep getting this error when trying to set up my code.
class ViewController: UIViewController, UITableViewDelegate, UIImagePickerControllerDelegate, UINavigationControllerDelegate, MSBClientManagerDelegate, UIScrollViewDelegate {
I have never seen this before, but I have also never tried to convert an Objective C sample to Swift.
Any help would be appreciated!
EDIT: Here is the protocol from Objective C
#protocol MSBClientManagerDelegate<NSObject>
- (void)clientManager:(MSBClientManager *)clientManager clientDidConnect:(MSBClient *)client;
- (void)clientManager:(MSBClientManager *)clientManager clientDidDisconnect:(MSBClient *)client;
- (void)clientManager:(MSBClientManager *)clientManager client:(MSBClient *)client didFailToConnectWithError:(NSError *)error;
#end
EDIT 2: After using suggested Swift Helper class
This is how I am trying to set up the connection.
var clients:NSArray = bandHelper.attachedClients()!
var firstClient: MSBClient = clients[0] as MSBClient
if (clients.count == 0){
println("The band is not detected")
return
}
I have no clue how this should be set up
bandHelper.connectClient(firstClient, {completion: (connected:true -> void in)})
println("Please wait...connecting to band")
Then, when trying to send a photo to the band, this function does not work
bandHelper.client?.personalizationManager.updateMeTileImage(bandScaledImage, { (completionHandler: NSError!) -> Void in
NSLog("%#", NSError())})
I am getting thrown off by using the helper class. Any help would be appreciated!
Sample Project
I linked a sample Swift project for Microsoft Band Kit iOS that can send a haptic to the band. Find the link here: http://droolfactory.blogspot.com/2015/03/ios-swift-example-of-connecting-with.html
Microsoft Band Bridging Header
First to convert the Objective-C classes to be used with Swift, create a Bridging Header. Mine look like this for just the MicrosoftBandKit-iOS framework:
#ifndef ModuleName_Bridging_Header_h
#define ModuleName_Bridging_Header_h
#import <MicrosoftBandKit_iOS/MicrosoftBandKit_iOS.h>
#endif
Make sure to replace the ModuleName with the name of your apps Module. Find more on Bridging Header files at: https://developer.apple.com/library/ios/documentation/Swift/Conceptual/BuildingCocoaApps/MixandMatch.html
Band Helper Class
Next I wrapped the MSBClientManagerDelegate in a helper class (BandManager) which uses a singleton to manage the Band. I have a gist for it here (https://gist.github.com/mthistle/8f6eb30c68a918fc6240)
The code for this the gist is:
import Foundation
let kConnectionChangedNotification = "kConnectionChangedNotification"
let kConnectionFailedNotification = "kConnectionFailedNotification"
private let _SharedBandManagerInstance = BandManager()
class BandManager : NSObject, MSBClientManagerDelegate {
private(set) var client: MSBClient?
private var connectionBlock: ((Bool) -> ())?
private var discoveredClients = [MSBClient]()
private var clientManager = MSBClientManager.sharedManager()
class var sharedInstance: BandManager {
return _SharedBandManagerInstance
}
override init() {
super.init()
self.clientManager.delegate = self
}
func attachedClients() -> [MSBClient]? {
if let manager = self.clientManager {
self.discoveredClients = [MSBClient]()
for client in manager.attachedClients() {
self.discoveredClients.append(client as! MSBClient)
}
}
return self.discoveredClients
}
func disconnectClient(client: MSBClient) {
if (!client.isDeviceConnected) {
return;
}
if let manager = self.clientManager {
manager.cancelClientConnection(client)
self.client = nil
}
}
func connectClient(client: MSBClient, completion: (connected: Bool) -> Void) {
if (client.isDeviceConnected && self.client == client) {
if (self.connectionBlock != nil)
{
self.connectionBlock!(true)
}
return;
}
if let connectedClient = self.client {
self.disconnectClient(client)
}
self.connectionBlock = completion;
self.clientManager.connectClient(client)
}
func clientManager(clientManager: MSBClientManager!, clientDidConnect client: MSBClient!) {
if (self.connectionBlock != nil) {
self.client = client
self.connectionBlock!(true)
self.connectionBlock = nil
}
self.fireClientChangeNotification(client)
}
func clientManager(clientManager: MSBClientManager!, clientDidDisconnect client: MSBClient!) {
self.fireClientChangeNotification(client)
}
func clientManager(clientManager: MSBClientManager!, client: MSBClient!, didFailToConnectWithError error: NSError!) {
if error != nil {
println(error)
}
NSNotificationCenter.defaultCenter().postNotificationName(kConnectionFailedNotification, object: self, userInfo: ["client": client])
}
func fireClientChangeNotification(client: MSBClient) {
NSNotificationCenter.defaultCenter().postNotificationName(kConnectionChangedNotification, object: self, userInfo: ["client": client])
}
}
I am new to the swift. Just couldn't get the below code working on the http request. Other than printing "start...", it doesn't print anything else. All the connection methods seem not called at all? Any suggestions?
class Network {
var data: NSMutableData = NSMutableData()
func getAcctSummary() {
let urlAcctSummary = "http://www.google.com"
var url: NSURL = NSURL(string: urlAcctSummary)
var request: NSURLRequest = NSURLRequest(URL: url)
var connection: NSURLConnection = NSURLConnection(request: request, delegate: self, startImmediately: false)
connection.start()
println("started...")
}//getAcctSummary
func connection(connection: NSURLConnection!, didFailWithError error: NSError!) {
println("Failed with error:\(error.localizedDescription)")
}
func connection(didReceiveResponse: NSURLConnection!, didReceiveResponse response: NSURLResponse!) {
println("didReceiveResponse")
}
func connection(connection: NSURLConnection!, didReceiveData conData: NSData!) {
self.data.appendData(conData)
println("here 1")
}
func connectionDidFinishLoading(connection: NSURLConnection!) {
println(self.data)
println("here")
}
}
In order to determine which optional protocol methods are supported, the NSURLConnection delegate is required to be a subclass of NSObject. Change the first line to:
class Network : NSObject {
And you should be good-to-go
Try using swift NSURLSession. Worked best for me here is some ex code but had to pull out my custom code.
func someFunc() ->()
{
var session = NSURLSession.sharedSession()
var personURL = "URL"
let url = NSURL(string:personURL)
var task:NSURLSessionDataTask = session.dataTaskWithURL(url, completionHandler:apiHandler)
task.resume()
}
func apiHandler(data:NSData!, response:NSURLResponse!, error:NSError!)
{
if error
{
println("API error: \(error), \(error.userInfo)")
}
var jsonError:NSError?
var json:JMSMediaDictionary = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error: &jsonError) as Dictionary
println(json)
//do stuff with data
}
I was trying to cast an unknown given object to the required class using kotlin primitives conversion methods like this:
private fun callSelfParser(origObj: Any, destClazz: KClass<*>): Any {
val methodName = when (destClazz) {
Int::class -> "toInt"
Char::class -> "toChar"
Long::class -> "toLong"
Byte::class -> "toByte"
Float::class -> "toFloat"
Short::class -> "toShort"
Double::class -> "toDouble"
Boolean::class -> "toBoolean"
else -> ""
}
val parsingMethod: KFunction<*>
try {
parsingMethod = origObj::class.functions.toList().first { it ->
it.name == methodName
}
} catch (e: NoSuchElementException) {
throw ObjectMapperEx("Element $origObj doesn't have a method for parsing to $destClazz")
}
return parsingMethod.call(origObj)!!
}
But I got the following exception when executing the .call():
Exception in thread "main" kotlin.reflect.jvm.internal.KotlinReflectionInternalError: Reflection on built-in Kotlin types is not yet fully supported. No metadata found for public open val length: kotlin.Int defined in kotlin.String[DeserializedPropertyDescriptor#1cbb87f3]
Can someone say whats wrong or, if there's another way to achieve my goal?