How to use asynchronous methods (JSON Restful services) in iOS?

advertisements

I have an iOS application that sync data from a JSON restful web service. This method is called from an external class (not UI controller). This class has a sync method and It sends and retrieves data without any problem. My problem is how do I pause the UI till I get my result.

The following would provide the idea about the code.

UIController Class

let C : Customer = Customer(UserName: UserName!, Password: Password!)
let S : Syncronization = Syncronization()
S.Sync(C)

Syncronization class

Class Syncronization : NSObject, NSURLSessionDataDelegate

func Sync(C : Customer){
        var datastr = ""
        datastr = "http://192.168.248.134:8008/MobileWeb.svc/GetFirstTimeSync/" + C.UserName + "/" + C.Password
        let url:NSURL = NSURL(string: datastr)!
        self.buffer = NSMutableData()
        let defaultConfigObject:NSURLSessionConfiguration = NSURLSessionConfiguration.defaultSessionConfiguration()
        let session:NSURLSession = NSURLSession(configuration: defaultConfigObject, delegate: self, delegateQueue: NSOperationQueue.mainQueue())
        let req:NSMutableURLRequest = NSMutableURLRequest(URL: url)
        req.HTTPMethod = "POST"
        session.dataTaskWithURL(url).resume()
    }

func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveData data: NSData) {
        print("Recieved with data")
        buffer.appendData(data)
    }

func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?) {
            if error == nil {
                print("Download Successful")
                print("Done with Bytes " + String(buffer.length))
                self.parseJSONA(self.buffer)
            }
            else  {
                print("Error %@",error!.userInfo);
                print("Error description %@", error!.localizedDescription);
                print("Error domain %@", error!.domain);
            }
    }

    func parseJSONA(data:NSMutableData) {
         do {
                let json = try NSJSONSerialization.JSONObjectWithData(data, options: []) as! Array<AnyObject>
        } catch let error as NSError {
                print("Failed to load: \(error.localizedDescription)")
        }
   }

I have tried dispacther methods, but I believe I do not know how to use that so far cause most of the examples have down the services & data exchange on UI Controllers.

Any kind of help is appreciated. Thanks


If I understand you correctly, you want to be able to do something in your UI based on the outcome of your call to S.Sync(C)

One way of doing that is to include a closure as a parameter to your Sync function.

Here's how I would do that (Disclaimer...I haven't checked everything in a compiler, so there might be errors along the way. See how far you get, and if there are problems, just write again :-)):

enum SynchronizationResult {
    case Success(Array<AnyObject>)
    case Failure(NSError)
}

class Syncronization : NSObject, NSURLSessionDataDelegate {

    var functionToExecuteWhenDone: ((SynchronizationResult) -> Void)?

    func Sync(C : Customer, callback: (SynchronizationResult) -> Void){
        functionToExecuteWhenDone = callback
        var datastr = ""
        datastr = "http://192.168.248.134:8008/MobileWeb.svc/GetFirstTimeSync/" + C.UserName + "/" + C.Password
        let url:NSURL = NSURL(string: datastr)!
        self.buffer = NSMutableData()
        let defaultConfigObject:NSURLSessionConfiguration = NSURLSessionConfiguration.defaultSessionConfiguration()
        let session:NSURLSession = NSURLSession(configuration: defaultConfigObject, delegate: self, delegateQueue: NSOperationQueue.mainQueue())
        let req:NSMutableURLRequest = NSMutableURLRequest(URL: url)
        req.HTTPMethod = "POST"

        session.dataTaskWithURL(url).resume()
     }

    func URLSession(session: NSURLSession, dataTask: NSURLSessionDataTask, didReceiveData data: NSData) {
        print("Recieved with data")
        buffer.appendData(data)
    }

    func URLSession(session: NSURLSession, task: NSURLSessionTask, didCompleteWithError error: NSError?) {
        if error == nil {
            print("Download Successful")
            print("Done with Bytes " + String(buffer.length))
            self.parseJSONA(self.buffer)
        } else  {
            print("Error %@",error!.userInfo);
            print("Error description %@", error!.localizedDescription);
            print("Error domain %@", error!.domain);
            let result = SynchronizationResult.Failure(error!)
            if let functionToExecuteWhenDone = functionToExecuteWhenDone {
                functionToExecuteWhenDone(result)
            }
        }
    }

    func parseJSONA(data:NSMutableData) {
        do {
            let json = try NSJSONSerialization.JSONObjectWithData(data, options: []) as! Array<AnyObject>
            let result = SynchronizationResult.Success(json)
            if let functionToExecuteWhenDone = functionToExecuteWhenDone {
                functionToExecuteWhenDone(result)
            }
         } catch let error as NSError {
             print("Failed to load: \(error.localizedDescription)")
             let result = SynchronizationResult.Failure(error)
             if let functionToExecuteWhenDone = functionToExecuteWhenDone {
                 functionToExecuteWhenDone(result)
             }
         }
     }
}

So...we introduce an Enum called SynchronizationResult to handle the outcome of fetching data.

We then add a function to be called when done, as a parameter to the Sync function:

func Sync(C : Customer, callback: (SynchronizationResult) -> Void)

This method will be called with a SynchronizationResult as a parameter and returns void.

We store that callback in functionToExecuteWhenDone for later usage.

Depending on whether you see any errors along the way or everything is sunshine, we generate different SynchronizationResult values along the way and call your functionToExecuteWhenDone with the current SynchronizationResult when we are ready (when parsing is done or we have failed)

And in your ViewController you'd do something along the lines of

let C : Customer = Customer(UserName: UserName!, Password: Password!)
let S : Syncronization = Syncronization()
S.Sync(C) { (result) in
    switch result {
        case .Success(let json):
            //Your code to update UI based on json goes here
        case .Failure(let error):
            //Your code to handle error goes here
    }
}

I hope this makes sense and is what you needed.