Asynchronous block call to self crashes with EXC_BAD_ACCESS in Swift - ios

I have an API call (using AFNetworking) that when fails calls a failure block. This block simply stop the table view refresh controller via 'self.refreshController.stopRefreshing();
However at run-time this causes a EXC_BAD_ACCESS error.
failure: { (error: NSError!, reason: String!) -> Void in
self.refreshController.endRefreshing()
}
I've tried putting the call in a 'dispatch_async' main queue but the call is already on the Main queue and the same error arises.
failure: { (error: NSError!, reason: String!) -> Void in
dispatch_async(dispatch_get_main_queue())
{
self.refreshController.endRefreshing()
}
}
This leads me to believe the issue is to do with a pointer to 'self' at the time the failure block is called... I've tried 'weak' and 'unowned' self but these don't resolve it.
failure: { [weak self] (error: NSError!, reason: String!) -> Void in
self?.refreshController.endRefreshing()
}
Any thoughts would be welcome.
UPDATE: Initialisaton
class ResultsViewController: UIViewController, UITableViewControllerDelegate, UITableViewControllerDataSource
{
var refreshController = UIRefreshControl()
override func viewDidLoad()
{
super.viewDidLoad()
// pull-to-refresh setup
self.refreshController.addTarget(self, action: "refreshTable:", forControlEvents: UIControlEvents.ValueChanged)
self.tableView.addSubview(self.refreshController)
}
}

Eventually found the root cause.
I was using AFNetworking for API calls and had created a custom Failure block which included the reason if the API call failed. This was up the chain in the call stack but this error seemed to mask it. None of the above methods were required, but using the weak self method did help the compiler surface the issue in the right file.
The issue could be traced back to the here:
Two block type aliases
typealias SuccessBlock = (responseArray: AnyObject!) -> Void
typealias FailureBlock = (error: NSError, reason: String) -> Void
AFNetworking POST call
private func post(url: String, inout parameters: [String : AnyObject], success: SuccessBlock, failure: FailureBlock)
{
self.sessionManager.POST(url,
parameters: parameters,
success: { (operation: NSURLSessionDataTask!, responseObject: AnyObject!) -> Void in
println("DEBUG: API POST Request to \(url) successful.")
var responses = responseObject as? [AnyObject]
success(responseArray:responses)
},
failure: { (operation: NSURLSessionDataTask!, error: NSError!) -> Void in
let reason = self.getResponseReasonFromError(error)
println("DEBUG: API GET Request to \(url) \nFailed: \(error). \nAPI Response: \(reason)")
failure(error: error, reason: reason)
}
)
}
Error was here in the FailureBlock type alias
typealias FailureBlock = (error: NSError, reason: String) -> Void
typealias FailureBlock = (error: NSError, reason: String!) -> Void
Missing a ! after String.

Related

Completion handler in function

I have a function that looks like this, and I have tried to add a completionHandler in the code below:
func getValueFromAPI(completionHandler: (_ result: Bool) -> Void){
apii.getVehicle(id!).done {
(vehicle: Vehicle) -> Void in
print("ggg.state: \(vehicle.state!)")
print("ggg.state: \(vehicle.displayName!)")
apii.getAllData(vehicle).done { (extendedVehicle: VehicleExtended) in
let entryBattery = (extendedVehicle.chargeState?.batteryLevel)!
let entryCarState = (extendedVehicle.state)!
print("entryBattery: \(entryBattery)")
print("entryCarState: \(entryCarState)")
completionHandler(true)
}.catch { (error) in
print("ERROOOOR: \(error)")
}
}.catch { error in
print("errorr: \(error)")
}
}
I have already tried to add a complete handler, but I get the following error on these lines:
Line: apii.getVehicle(id!).done {
Error: Escaping closure captures non-escaping parameter 'completionHandler'
Line: apii.getAllData(vehicle).done { (extendedVehicle: VehicleExtended) in
Error: Escaping closure captures non-escaping parameter 'completionHandler'
What am I doing wrong here, and how can I fix this?
I am using Swift 5.
You need to declare your completionHandler to be an escaping closure. E.g.:
func getValueFromAPI(completionHandler: #escaping (Bool) -> Void) {
...
}
Note the #escaping qualifier.

Understanding swift Alamofire completionHandler

I have these two methods in my API class to get data from an API:
func authenticateUser(completionHandler: (responseObject: String?, error: NSError?) -> ()) {
makeAuthenticateUserCall(completionHandler)
}
func makeAuthenticateUserCall(completionHandler: (responseObject: String?, error: NSError?) -> ()) {
Alamofire.request(.GET, loginUrlString)
.authenticate(user: "a", password: "b")
.responseString { request, response, responseString, responseError in
completionHandler(responseObject: responseString as String!, error: responseError)
}
}
Then in another class i use the following code to access the data:
API().authenticateUser{ (responseObject, error) in
println(responseObject)
}
The code is working but i don't understand it completely.
func authenticateUser has the parameter completionHandler: (responseObject: String?, error: NSError?) -> (), is this a reference to the completionHandler method? or is it an object? whats the purpose of -> ()?
When i call the authenticateUser func, how do i actually access the response? There is no return in any of my api funcs, the funcname{(parameter, parameter) in .. } syntax seems really strange.
completionHandler is a closure parameter. As Swift documentation says:
Closures are self-contained blocks of functionality that can be passed around and used in your code. Closures in Swift are similar to blocks in C and Objective-C and to lambdas in other programming languages.
So, what a closure is used for is to add some functionality of your own that you want to add to the execution of your function.
In your case, you call authenticateUser and you pass a closure that receives (responseObject, error) and executes println(responseObject). authenticateUser() receives your closure under the completionHandler parameter and it then calls makeAuthenticateUserCall() passing your completionHandler closure to it.
Then again, looking at the definition you can see func makeAuthenticateUserCall(completionHandler: (responseObject: String?, error: NSError?) -> ()) that means that like authenticateUser() makeAuthenticateUserCall() is a function that receives a closure as a parameter, under the name of completionHandler. makeAuthenticateUserCall() makes a network request using AlamoFire and you capture the response under a closure again that you pass as parameter of the responseString() method. So you have:
//here you call authenticateUser with a closure that prints responseObject
API().authenticateUser{ (responseObject, error) in
println(responseObject)
}
Then:
//authenticateUser receives your closure as a parameter
func authenticateUser(completionHandler: (responseObject: String?, error: NSError?) -> ()) {
//it passes your closure to makeAuthenticateUserCall
makeAuthenticateUserCall(completionHandler)
}
//makeAuthenticateUserCall receives your closure
func makeAuthenticateUserCall(completionHandler: (responseObject: String?,
error: NSError?) -> ()) {
Alamofire.request(.GET, loginUrlString)
.authenticate(user: "a", password: "b")
//here you pass a new closure to the responseString method
.responseString { request, response, responseString, responseError in
//in this closure body you call your completionHandler closure with the
//parameters passed by responseString and your code gets executed
//(that in your case just prints the responseObject)
completionHandler(responseObject: responseString as String!, error: responseError)
}
}
For more information read the documentation: Swift Closures

Alamofire return string value

i'm trying to write a method to return a json response from an API as a string. This is my code:
func authenticateUser(completionHandler: (responseObject: String?, error: NSError?) -> ()) {
makeCall(completionHandler: completionHandler)
}
func makeCall(completionHandler: (responseObject: String?, error: NSError?) -> ()) {
Alamofire.request(.GET, loginUrlString)
.authenticate(user: "x", password: "y")
.responseString { request, response, responseObject, error in
completionHandler(responseObject: responseObject as? String, error: error)
}
}
I can't compile it, the makeCall call in authenticateUser method says "Extraneous argument label 'completionHandler' in call.
I can't see whats wrong in my code?
Try just calling makeCall(completionHandler).
From the error message, I would guess that that is what it is complaining about. Swift function label use often seems inconsistent and confusing.

How to use block/closure in swift

In one of my app I have used block for webservice calling and getting response. Now I want to write this app in swift, but I am getting trouble to use blocks/Closure in Swift.
Here is my objective C code which I want to migrate in swift:
calling a class method of Communicator
[[Communicator sharedInstance]callWebService:WS_LOGIN withMethod:POST_METHOD andParams:params showLoader:YES completionBlockSuccess:^(id obj) {
//Do play with data
}completionBlockFailiure:^(id obj) {
//Show alert with error
}];
in communicator class
-(void)callWebService:(NSString *)serviceName withMethod:(NSString *)methodName andParams:(NSDictionary *)params showLoader:(BOOL)showLoader completionBlockSuccess:(void (^)(id))aBlock completionBlockFailiure:(void (^)(id))aFailBlock
{
if (showLoader) {
// show loader
}
[self performRequestWithServiceName:serviceName method:methodName andParams:params successblock:aBlock failureblock:aFailBlock];
}
- (void)performRequestWithServiceName:(NSString *)serviceName method:(NSString*)methodName andParams:(NSDictionary*)params
successblock:(void (^)(id obj))successBlock
failureblock:(void (^)(id obj))failBlock {
if(callSuceess){
successBlock(#"Success");
}else{
successBlock(nil);
}
}
For Swift. Use AnyObject for id objc type.
func callWebservice (serviceName: String, withMethod method: String, andParams params: NSDictionary, showLoader loader: Bool, completionBlockSuccess aBlock: ((AnyObject) -> Void), andFailureBlock failBlock: ((AnyObject) -> Void)) {
if loader {
// Show loader
}
performRequestWithServiceName(serviceName, method: method, andParams: params, success: aBlock, failure: failBlock)
}
func performRequestWithServiceName(serviceName: String, method methodName: String, andParams params: NSDictionary, success successBlock: ((AnyObject) -> Void), failure failureBlock: ((AnyObject) -> Void)) {
if callSuceess {
successBlock("Success")
}else {
successBlock(nil)
}
}
UPDATE: An example when you want call web service. See code below
callWebservice("your-service-name", withMethod: "your-method", andParams: ["your-dic-key": "your dict value"], showLoader: true/*or false*/, completionBlockSuccess: { (success) -> Void in
// your successful handle
}) { (failure) -> Void in
// your failure handle
}
Your code might look like this:
func callWebService(serviceName: String, method: String, params: [String : AnyObject], showLoader: Bool, success: (responseObject: AnyObject) -> Void, failure: (responseObject: AnyObject) -> Void) {
if showLoader {
// show loader
}
performRequest(serviceName, method: method, params: params, success: success, failure: failure)
}
func performRequest(serviceName: String, method: String, params: [String : AnyObject], success: (responseObject: AnyObject) -> Void, failure: (responseObject: AnyObject) -> Void) {
}
I replaced NSDictionary with [String : AnyObject]. If you can replace any of the uses of AnyObject with more specific types, your code will be cleaner and more stable.
For Swift Closures we have to use ( ) -> ( )
For example:
func yourFunction(success: (response: AnyObject!) -> Void, failure: (error: NSError?) -> Void) {
}
You can call it as:
yourFunction({(response) -> Void in
// Success
}) { (error) -> Void in
// Handle Errors
}
Hope it will help you to create Closures with your requirements.
In the communicator class the method that cals the webservice would be defined something like this depending on the type of object you want to return
func performRequest(serviceName: NSString, methodName: NSString,paramaters:NSDictionary, successblock: (String)->(), failureBlock: () -> ()) {
if(callSuccess) {
successblock("Success")
} else {
failureBlock()
}
We define the success and failure blocks types as by their function signatures in the case above success is defined as a method that takes a string as an input parameter and returns nothing so we can then call successBlock passing in a string. The failure block is defined above as a block that takes no parameters and returns nothing.
To call this method
func callWebService(serviceName: NSString, method: NSString and parameters: NSDictionary, showLoader: Bool, completionBlockSuccess:(String) -> (), completionBlockFailiure:() -> ()) {
if (showLoader) {
// show loader
}
performRequest(serviceName: serviceName, methodName: method, parameters, successBlock:completionBlockSuccess, failureBlock: completionBlockFailiure)
}
Finally to call this
Communicator.sharedInstance().callWebService(serviceName: WS_LOGIN , method: POST_METHOD and parameters: params, showLoader: true, completionBlockSuccess:{ returnedString in
//Do play with data
}, completionBlockFailiure:{
//Show alert with error
})
For the completion block we define a variable returnedString to allow us to manipulate that input parameter (in the above example it would be the string "Success"). I assume your data is not just returning a string though so you will probably need to play around with they type depending on what your service returns.
Also here I tried to match your method signatures by using NSString and NSDictionary though depending on your needs the Swift equivalents String and [String: AnyObject] could be more appropriate.
func processingWithAnyObject(input: String, completion: #escaping (_ result: AnyObject) -> Void) {
...
completion(response.result.value! as AnyObject)
}
processingWithAnyObject("inputString") {
(result: AnyObject) in
print("back to caller: \(result)")
}

Global handling of 401 Responses with RestKit and Swift

I am currently working on an iOS App that is developed using Swift. For the REST calls, I am using the RestKit framework.
The next stage of my project is to start using authentication against the services. An issue that I have come up against is handling 401 (Not Authenticated) responses from the service. In all of these circumstances, I would like to display a login page. I want to avoid implementing the error handling of this multiple times.
I followed the tutorial at http://blog.higgsboson.tk/2013/09/03/global-request-management-with-restkit/. However, this is in Objective-C and I would like to do things slightly differently.
As such, I want to build a class that extends RKObjectRequestOperation as in the tutorial but using Swift. I have come up with an issue as I am receiving the error
Overriding method with selector 'setCompletionBlockWithSuccess:failure:' has incompatible type '((RKObjectRequestOperation, RKMappingResult) -> Void (RKObjectRequestOperation, NSError) -> Void) -> Void'
I am a bit stuck on this and so was hoping someone could help. The code for the method that is failing is below.
class CustomRequestOperation : RKObjectRequestOperation {
func setCompletionBlockWithSuccess(success: (operation: RKObjectRequestOperation, mappingResult: RKMappingResult) -> Void, failure: (operation: RKObjectRequestOperation, error: NSError) -> Void) -> Void {
}
}
Can anyone point out what is wrong with my method signature?
You're overriding the method so you can get Xcode to add the signature for you if you start typing the method name and hie escape.
It should be
func setCompletionBlockWithSuccess(success: (operation: RKObjectRequestOperation, mappingResult: RKMappingResult) -> Void, failure: (operation: RKObjectRequestOperation, error: NSError) -> Void) {
(you are adding a return spec that doesn't exist in the superclass method)
Here is the full class in swift...
class CustomRKObjectRequestOperation : RKObjectRequestOperation
{
override func setCompletionBlockWithSuccess(success: ((RKObjectRequestOperation!, RKMappingResult!) -> Void)!, failure: ((RKObjectRequestOperation!, NSError!) -> Void)!) {
super.setCompletionBlockWithSuccess({ (operation, RKMappingResult mappingResult) -> Void in
if ((success) != nil) {
success(operation, mappingResult);
}
}, failure: { (RKObjectRequestOperation operation, NSError error) -> Void in
NSNotificationCenter.defaultCenter().postNotificationName("connectionFailure",object:operation)
if ((failure) != nil) {
failure(operation, error);
}
})
}
}
App Delegate
Register notification
NSNotificationCenter.defaultCenter().addObserver(self, selector:"connectionFailedWithOperation:",name:"connectionFailure", object:nil)
func connectionFailedWithOperation(notification: NSNotification ){
let operation = notification.object as! RKObjectRequestOperation?;
if ((operation) != nil) {
let statusCode = operation!.HTTPRequestOperation.response.statusCode;
if (statusCode == 401) {
// log user out
}
}
}

Resources