Swift3 closure crash - ios

Crashing while creating an instance of URLSessionTask with the completion handlers
func sessionTaskPostRequest (_ urlRequest : URLRequest , responseHandler: #escaping ResponseHandler) -> URLSessionTask {
// 5
let sesstionTask : URLSessionTask = networkSession.dataTask(with: urlRequest, completionHandler: { (data : Data? , urlResponse : URLResponse? , error : NSError? ) in
var json: NSDictionary!
do {
json = try JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions()) as? NSDictionary
} catch {
print(error)
}
// Did the JSONObjectWithData constructor return an error? If so, log the error to the console
if(error != nil) {
responseHandler (false , nil , error , nil)
}
else {
// The JSONObjectWithData constructor didn't return an error. But, we should still
// check and make sure that json has a value using optional binding.
if let parseJSON = json {
let errorJSON = parseJSON ["Err"] as! String
if !errorJSON.isEmpty {
responseHandler (false , nil , nil , errorJSON)
}else {
responseHandler (true , parseJSON , nil , nil)
}
print("Succes: \(parseJSON)")
}
else {
// Woa, okay the json object was nil, something went worng. Maybe the server isn't running?
let jsonStr = NSString(data: data!, encoding: String.Encoding.utf8.rawValue)
responseHandler (false , nil , error , "Error could not parse JSON")
print("Error could not parse JSON: \(jsonStr)")
}
}
} as! (Data?, URLResponse?, Error?) -> Void)
return sesstionTask
}
And created a type alias for response handler that returns the response JSON Object . Type alias as follows
typealias ResponseHandler = (_ successFlag :Bool , _ data : NSDictionary? , _ errorObject : NSError? , _ errorString : String?) -> Void

Seems like Response handler is getting the Error Object as NSError which will get crashed if Error object is nil and failed to cast it to NSError
Error = nil
and when you receive it as NSError Typecasting fails because of nil object , but the same thing is handled wisely if you don't do any typecasting

Related

How to get the value of the variable that is assigned in a closure (Swift)

I'm using the Twitter REST API in Swift, and I am trying to get the value of a variable that is assigned inside of a Twitter Request closure, so that I can use that value outside of the closure.
I acquired this code from the Twitter REST API tutorial for Swift, located at: https://dev.twitter.com/twitterkit/ios/access-rest-api
func jsonAvailable() -> Bool {
// Swift
let client = TWTRAPIClient()
let statusesShowEndpoint = "https://api.twitter.com/1.1/statuses/show.json"
let params = ["id": "20"]
var clientError : NSError?
var jsonAvailable: Bool = false
let request = client.urlRequest(withMethod: "GET", url:
statusesShowEndpoint, parameters: params, error: &clientError)
client.sendTwitterRequest(request) { (response, data, connectionError)-> Void in
if connectionError != nil {
print("Error: \(connectionError)")
}
do {
let json = try JSONSerialization.jsonObject(with: data!, options: [])
print("json: \(json)")
jsonAvailable = true
} catch let jsonError as NSError {
print("json error: \(jsonError.localizedDescription)")
}
print("Value of jsonAvailable: \(jsonAvailable)")
return jsonAvailable
//always returns false, even if it is changed to true inside of the closure
}
In the last line, jsonAvailable is always false, even when it is changed to true inside of the closure. How can I get the value of jsonAvailable at the end of the function, even as it is modified inside of the sendTwitterRequest closure?
I have tried writing this closure in a separate function and then calling the function to get the value, but because it is a custom closure that requires the client to be called by "sendTwitterRequest" I have found it difficult to pass all these required parameters to fit the API.
Thanks for the help!
Your closure is async. What happens is that you go through all the function body before sendTwitterRequest assigns true to jsonAvailable, resulting in jsonAvailable being false. What you need to do is having a callback instead, providing the json status if you'd like (or the json itself as a nillable object).
EDIT: You could have something like this
func jsonAvailable(callback: ((_ isJsonAvailable: Bool) -> Void)) {
client.sendTwitterRequest(request) { (response, data, connectionError)-> Void in {
do {
let json = try JSONSerialization.jsonObject(with: data!, options: [])
print("json: \(json)")
callback(true)
} catch let jsonError as NSError {
print("json error: \(jsonError.localizedDescription)")
callback(false)
}
}
}
jsonAvailable(callback: { (_ isJsonAvailable: Bool) in
print(isJsonAvailable)
})

Use Type T as parameter in completion handler

I have written a function for a URL request. This contains a completion handler that returns a dictionary of [String: AnyObject] that is fetched from the URL.
The code for this is:
func getDataAsyncFromURLRequest(url: NSURL, completion: ([String : AnyObject]) -> ()) {
let request = NSMutableURLRequest(URL: url)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(request) { (data, response, error) -> Void in
if error != nil {
print("error=\(error)")
return
}
else {
let datastring = NSString(data: data!, encoding: NSUTF8StringEncoding)
if let data = datastring!.dataUsingEncoding(NSUTF8StringEncoding) {
do {
let json = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions()) as! [String : AnyObject]
completion(json)
} catch {
print("json error: \(error)")
}
}
}
}
task.resume()
}
In some cases, however, I will receive an array of [String : AnyObject] and not the dictionary. So instead of making a duplicate function that takes the array of dictionaries as parameter for the completion handler, I though it was possible to do like this
func getDataAsyncFromURLRequest<T>(url: NSURL, completion: (T) -> ()) {
// code here
}
... and then do like this let json = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions()) as! T, but that gives me this error: Cannot invoke 'getDataAsyncFromURLRequest' with an argument list of type '(NSURL, completion: (_) -> ())'
What would be the best way to make the completion handler accept a parameter with a type decided at runtime, if possible at all?
It's very easy why don't you use AnyObject
func getDataAsyncFromURLRequest(url: NSURL, completion: (AnyObject) -> ()) {
let request = NSMutableURLRequest(URL: url)
let session = NSURLSession.sharedSession()
let task = session.dataTaskWithRequest(request) { (data, response, error) -> Void in
if error != nil {
print("error=\(error)")
return
}
else {
let datastring = NSString(data: data!, encoding: NSUTF8StringEncoding)
if let data = datastring!.dataUsingEncoding(NSUTF8StringEncoding) {
do {
let json = try NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions())
completion(json)
} catch {
print("json error: \(error)")
}
}
}
}
task.resume()
}
And result of JSONObjectWithData can be [AnyObject] (Array) or [String:AnyObject] and tree of those items.
So after got result, you can also check type of result in completion block
Like this
if result is [String:AnyObject]
...
else if result is [AnyObject]
...
else
//throw error : because then it is not JSON

Alamofire Completion Handler returning response + data

I have a function that includes a responseObject in it's completion handler. At the moment this returns the json body when I call it, along with any errors.
Some parts of the API I am using however, don't return any data in the response body, they just return a response (200, 404, etc...)
I was thinking about appending the response inside the empty json object that is getting returned, then realised that would be silly and it would probably be better if I returned the NSHTTPURLResponse as well, but everything I have found just explains how to return the responseObject along with the error...
This is the function that returns the completion handler:
func makePostRequest(url : String, params : AnyObject, completionHandler: (responseObject: NSHTTPURLResponse, JSON?, error: NSError?) -> ()) -> Request? {
println("params = \(params)")
return Alamofire.request(.POST, url, parameters: params as? [String : AnyObject], encoding: .JSON)
.responseJSON { (request, response, data, error) in completionHandler(
//This is wrong
response: response as? NSHTTPURLResponse,
responseObject:
{
println("Request is \(request)")
println("Response is \(response)")
println("Data is \(data)")
println("Error is \(error)")
//Append the response to this JSON object?
//
var json:JSON = [:]
if let anError = error
{
println(error)
}
else if let data: AnyObject = data
{
json = JSON(data)
}
//probably better to return the two...
//
return (response, json)
}(),
error: error
)
}
}
And this is how its used:
networking.makePostRequest(documentUrl, params: docParams) { response, json, error in
println("response is: \(response)")
println("document json: \(json)")
println("document error: \(error)")
}
I've added in the 'response' bits to all the bits of code, i'm sure this is possible? just not sure how to achieve it..
For anyone stuck trying to figure out how to return stuff this way, I solved it like this:
func makePostRequest(url : String, params : AnyObject, completionHandler: (httpResponse: NSHTTPURLResponse, responseObject:JSON?, error: NSError?) -> ()) -> Request? {
println("params = \(params)")
return Alamofire.request(.POST, url, parameters: params as? [String : AnyObject], encoding: .JSON)
.responseJSON { (request, response, data, error) in completionHandler(
//This is wrong
httpResponse: response!,
responseObject:
{
println("Request is \(request)")
println("Response is \(response)")
println("Data is \(data)")
println("Error is \(error)")
//Append the response to this JSON object?
//
var json:JSON = [:]
if let anError = error
{
println(error)
}
else if let data: AnyObject = data
{
json = JSON(data)
}
return json
}(),
error: error
)
}
}
and then calling it like this:
networking.makePostRequest(workDocumentUrl, params: params) { response, json, error in
if response.statusCode == 200{
//do something
}
println("json: \(json)")
println("error: \(error)")
}

Function Return Error in iOS

I have the following function: prepare() which returns NSMUtableArray. When I try, to return a json which is NSMutableArray object, I get the following error:
'NSMutableArray' is not convertible to 'Void'
Function Source Code:
func prepare() -> NSMutableArray {
let statusesShowEndpoint = "https://api.twitter.com/1.1/statuses/user_timeline.json"
let params = ["screen_name": "tikaDotMe"]
var clientError : NSError?
let request = Twitter.sharedInstance().APIClient.URLRequestWithMethod(
"GET", URL: statusesShowEndpoint, parameters: params,
error: &clientError)
if request != nil {
Twitter.sharedInstance().APIClient.sendTwitterRequest(request) {
(response, data, connectionError) -> Void in
if (connectionError == nil) {
var jsonError : NSError?
let json = NSJSONSerialization.JSONObjectWithData(data,
options: nil,
error: &jsonError) as NSMutableArray
//Error: 'NSMutableArray' is not convertible to 'Void'
return json
}
else {
println("Error: \(connectionError)")
}
}
}
else {
println("Error: \(clientError)")
}
return [""]
}
The problem is that you are trying to return json from a closure which is defined as returning a Void:
(response, data, connectionError) -> Void
EDIT: As #Paulw11 mentions, you need to handle the data in your closure, you can't return it from your prepare function.

Alamofire 'fatal error: unexpectedly found nil while unwrapping an Optional value'

When I am trying to server request in my swift code block, some errors occured after server response JSON like the code below;
Alamofire.request(.POST, Utility.WebServiceAddress+"/ApproveOrder", parameters: parameters)
.responseJSON
{(request, response, JSON, error) in
if let jsonResult: NSDictionary = JSON as? NSDictionary
{
if let wsResult = jsonResult[WSConstants.WSRESULT] as? NSDictionary
{
let resultCode = wsResult.valueForKey(WSConstants.RESULT_CODE) as Int
if(resultCode == Utility.WS_RESULT_SUCCESS)
{
self.PrepareMainItemsInBagToSetUpLocalNotifications()
Utility.ActivityIndicatorStopAnimating(self.activityIndicator, indicatorLabel: self.lblIndicatorMessage, viewController: self)
Utility.ReleaseBagAndPopToInitialViewController(self)
}
else
{
PromptDialogs.getPromptDialogWebServiceError(self.activityIndicator, lblIndicatorMessage: self.lblIndicatorMessage, viewController: self, message: "Sipariş tablebiniz gönderilirken hata oluştu. Lütfen tekrar deneyiniz!")
}
}
}
else
{
PromptDialogs.getPromptDialogWebServiceError(self.activityIndicator, lblIndicatorMessage: self.lblIndicatorMessage, viewController: self, message: "Sunucudan yanıt alınamadı. Lütfen internet bağlantınızı kontrol edip yeniden deneyiniz!")
}
} // gives 'fatal error: unexpectedly found nil while unwrapping an Optional value' at this line, as refering Alamofire code below ->
public func responseJSON(options: NSJSONReadingOptions = .AllowFragments, completionHandler: (NSURLRequest, NSHTTPURLResponse?, AnyObject?, NSError?) -> Void) -> Self {
return response(serializer: Request.JSONResponseSerializer(options: options), completionHandler: { (request, response, JSON, error) in
completionHandler(request, response, JSON, error)
})
}
I used this request way for some other UIViewControllers. But I did not understand that why am I getting this error only here? If it is necessary for you, I can share the more detailed code block.
Could you help me ?
Best regards

Resources