2

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

Ketan P
  • 4,259
  • 3
  • 30
  • 36
  • what do you mean by pause the UI, and why do you want to do this? you can disable elements of the UI while it's being synced by setting the element's `enabled:Bool` to `false` – olympia Feb 16 '16 at 09:04
  • @mmarkman It's just that when I call that method. I want to wait till I get the result, cause I want to use those data to proceed. –  Feb 16 '16 at 09:08

4 Answers4

3

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

olympia
  • 362
  • 2
  • 20
2

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

    }
}
Madan gupta
  • 668
  • 6
  • 19
  • how do I hold the sync class so that I will get the JSON data to the method? –  Feb 16 '16 at 09:20
2

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.

pbodsk
  • 6,787
  • 3
  • 21
  • 51
1
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

Community
  • 1
  • 1
David Seek
  • 16,783
  • 19
  • 105
  • 136
  • how do I hold the sync class so that I will get the JSON data to the method? –  Feb 16 '16 at 09:17
  • just add the tasks you want to perform in the background here: print("This is run on the background queue") – David Seek Feb 16 '16 at 09:21
  • I understand that it's alright. Method is working only with the internal methods. How do I stop till I get data from server? print("This is run on the main queue... executed before I get the data from server –  Feb 16 '16 at 09:36