1

Trying to understand how I can schedule one task behind the other, looked at GCD and NSOperations, but both seem to be at an abstraction to far removed from the core code; part of which executes on its very own thread!

I tried this code which was the most obvious I could find...

    let date = NSDate()
    print("getting Links \(date)")

    let operationQueue: NSOperationQueue = NSOperationQueue.mainQueue()
    let completionBlockOperation: NSBlockOperation = NSBlockOperation.init(
        block: {
            self.reportFini()
        }
    )

    let workerBlockOperation:NSBlockOperation = NSBlockOperation.init(
        block: {
            self.getLinks()
        }
    )
    completionBlockOperation.addDependency(workerBlockOperation)
    operationQueue.addOperation(workerBlockOperation)
    operationQueue.addOperation(completionBlockOperation)

Now reportFini does next to nothing ...

func reportFini() {
    let date = NSDate()
    print("got Links \(date)")
}

But getLinks is more complex, using sessions... in nutshell it runs

    let request = NSMutableURLRequest(URL: NSURL(string: "https://blah")!)
    let session = NSURLSession.sharedSession()
    request.HTTPMethod = "POST"

    request.addValue("application/json",forHTTPHeaderField: "Content-Type")
    request.addValue("path", forHTTPHeaderField: lePath)
    request.addValue("settings", forHTTPHeaderField: "requested_visibility\": \"public\"}")

    var nodeA:NSMutableDictionary? = ["path":lePath]
    let nodeB:NSMutableDictionary? = ["requested_visibility":"public"]
    nodeA!.setValue(nodeB, forKey: "settings")

    do {
        let jsonData = try NSJSONSerialization.dataWithJSONObject(nodeA!, options: [])
        request.HTTPBody = jsonData
    } catch {
        completion(string: nil, error: error)
    }
    var string2Return = ""
    var stringPath = ""
    let task = session.dataTaskWithRequest(request, completionHandler: {data, response, error -> Void in
        if let error = error {
            completion(string: nil, error: error)
            return
        }
        let strData = NSString(data: data!, encoding: NSUTF8StringEncoding)
        print("Body: \(strData)\n\n")
        do {
            let jsonResult = try NSJSONSerialization.JSONObjectWithData(data!, options:NSJSONReadingOptions.MutableContainers);
            self.jsonParser(jsonResult,field2file: "ignore")

            if let stringPath = (self.parsedJson["url"] as? String) {
                string2Return = stringPath
            } else {
                string2Return = (self.parsedJson["error_summary"] as? String)!
            }
            completion(string: string2Return, error: error)
        } catch {
            completion(string: nil, error: error)
        }
    })
    task.resume()

}

Now, getLinks and reportFini both execute @ the same time; basically get links goes off on its own thread! and immediately returns... BUT I need/want to be notified when it is finished, cause I got something else to do.

It get more complicated, since I need to run 10 sessions (fetching 10 links) concurrently, so in parallel; and want to be notified when they're all finished.

--- UPDATE ---

Tried GCD enclosing my sessions code in

    let workerQueue = dispatch_queue_create("getLinks", DISPATCH_QUEUE_CONCURRENT)
    let getLinksGroup = dispatch_group_create()

    dispatch_group_notify(getLinksGroup, dispatch_get_main_queue()) {
        print("All Links Downloaded")
    }

    dispatch_group_enter(getLinksGroup)
    dispatch_group_async(getLinksGroup, workerQueue) {

exiting with ...

    dispatch_group_leave(getLinksGroup)

Unfortunately it doesn't work; as I had already seen the sessions launch their own thread and I get notified almost immediately that the code has completed, clearly before it actually finishes the URL data download tasks.

Going to try with KVO ...

user3069232
  • 8,587
  • 7
  • 46
  • 87
  • 1
    may be this can help you http://stackoverflow.com/questions/13996621/downloading-multiple-files-in-batches-in-ios – Archit Dhupar Feb 08 '16 at 19:29
  • 1
    I don't see how you'd consider GCD an abstraction. It's about as low level of an API as you get. I think dispatch groups are what you need. – Dan Beaulieu Feb 08 '16 at 19:37
  • Ok; I just watched a video on using GCD groups, but I am still not quite online with the idea. When my session.dataTaskWithRequest launches it seems like it kicks off its own thread and almost immediately returns, even when it hasn't completed its task; the completion block firing before it has all the data. If I wrap it in a GCD object; will that not also almost immediately return, even if the session is still busy downloading data? – user3069232 Feb 08 '16 at 20:01
  • Just came across this following these links; an older post, but looks very promising, does this fit to the bill. A expert opinion needed. http://www.dribin.org/dave/blog/archives/2009/05/05/concurrent_operations/ – user3069232 Feb 08 '16 at 20:06

1 Answers1

2

Managed to solve this; using GCD & KVO, but not to manage the sessions; to manage a counter, and than throw a trigger by changing a monitored KVO value. A good solution, you tell me.

public class SynchronizedInt<T> {
private var blob:Int = 0
private let accessQueue = dispatch_queue_create("SynchronizedIntAccess", DISPATCH_QUEUE_SERIAL)

public func pending(queue2go:T)  {
    dispatch_sync(self.accessQueue) {
        self.blob = queue2go as! Int
    }
}

public func fulfilled() -> Bool {
    dispatch_sync(self.accessQueue) {
        self.blob = self.blob - 1
    }
    if self.blob == 0 {
        return true
    } else {
        return false
    }
}

public func copy() -> Int {
    return self.blob
}

}

Which I am using to keep track of the sessions launched, so I can notified when they all complete. The completion I track in the completion blocks, each of which checks to see if it was the final one. Beyond that I used a KVO of sorts based on the first Swifty solution presented here.

http://blog.scottlogic.com/2015/02/11/swift-kvo-alternatives.html

user3069232
  • 8,587
  • 7
  • 46
  • 87