简体   繁体   中英

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

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

You can give the Sync class a completion handler closure.

In your UIViewController :

S.completion = {
 information in
 UIElement.updateWith(information)
}

and of course you'll need to add a member to your Syncronization class:

var completion:((information:String)->())!

and you can call completion("here's some info!") from inside parseJSON() or URLSession() of the Syncronization class

Here's some reading on closures in Swift

this might help you https://thatthinginswift.com/background-threads/

let priority = DISPATCH_QUEUE_PRIORITY_DEFAULT
dispatch_async(dispatch_get_global_queue(priority, 0)) {

    // do downloading or sync task here

    dispatch_async(dispatch_get_main_queue()) {

        // update some UI with downloaded data/sync data

    }
}

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.

let qualityOfServiceClass = QOS_CLASS_BACKGROUND
let backgroundQueue = dispatch_get_global_queue(qualityOfServiceClass, 0)
dispatch_async(backgroundQueue, {
    print("This is run on the background queue")

   dispatch_async(dispatch_get_main_queue(), { () -> Void in
        print("This is run on the main queue, after the previous code in outer block")
    })
})

Found here

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM