-1

I'm using completion blocks for each of my functions (to avoid using a while isDoingSomething loop).

I get the expected array when all the blocks complete. But when I try to loop through this final array, it loops continually as expected but without ever resuming the NSURLSessionDataTask in Request.sendRequest(..) for each iteration.

ViewController.swift

import Cocoa

class ViewController: NSViewController {
    @IBOutlet weak var runButton: NSButton!
    @IBOutlet weak var visitsTextField: NSTextField!

    var accessToken = ""
    var cookies = [NSHTTPCookie]()
    var data = NSData?()
    var userIds = [String]()
    var usernames = [String]()
    var contentsOfURL = NSString()

    @IBAction func runButtonAction(sender: AnyObject) {
        run({
            // if I remove the loop and visit only one profile, it completes OK 
            for username in self.usernames {
                let profileVisitor = ProfileVisitor(profile: username)
                profileVisitor.visit({ 

                })
            }
        })
    }

    func run(completion: () -> Void) {
        let runManager = RunManager(desiredVisits: Int(visitsTextField.stringValue)!)
        runManager.parseJSON({
            self.usernames = runManager.usernames
            completion()
        })
    }
}

RunManger.swift

import Cocoa

class RunManager: NSObject {
    var data = NSData?()
    var desiredVisits = Int()
    var usernames = [String]()
    var userIds = [String]()

    init(desiredVisits: Int) {
        self.desiredVisits = desiredVisits
    }


    func parseJSON(completion: () -> Void) {
        let jsonLimit = 40
        var profileNames = [String]()
        let finalVisits = desiredVisits % jsonLimit
        let repeats = (desiredVisits / jsonLimit) + 1
        let json: [String:NSObject] = [..., "limit":jsonLimit]

        let url = "https://www.awebsite.com/1/path1/path2/path3"
        let URL = NSURL(string: url)!

        let vis = URLVisitor(URL: URL, params: "", method: "POST", jsonParams: json as! [String : NSObject])

        vis.execute({
            for i in 1..<repeats {

                if vis.data != nil {
                    do {
                        let data = vis.data!
                        let json = try NSJSONSerialization.JSONObjectWithData(data, options: .AllowFragments)

                        if let _usernames = json["data"] as? [[String: AnyObject]] {
                            for username in _usernames {
                                if let username = username["username"] as? String {
                                    self.usernames.append(username)
                                }
                            }   
                        }
                    } catch {

                    }
                }   
            }
            completion()
        })
    }
}

URLVisitor

import Cocoa

class URLVisitor: NSOperation {
    var authorizationHeader = ""
    var contentsOfURL = NSString()
    var jsonParams = [String: NSObject]()
    var isConnected = false
    var method = String()
    var params = String()
    var statusCode = Int()
    var cookies = [NSHTTPCookie]()
    var URL: NSURL?
    var isVisiting = false
    var task = NSURLSessionDataTask()
    var data = NSData?()

    init(URL: NSURL, params: String, method: String, jsonParams: [String:NSObject]) {
        self.URL = URL
        self.params = params
        self.method = method
        self.jsonParams = jsonParams
    }

    func execute(completion: () -> Void) {
        let request = Request(URL: URL!, params: params, method: method, jsonParams: jsonParams)

        if !self.cookies.isEmpty {
            request._setCookies(self.cookies)
        }

        request._setAuthorizationHeader(self.authorizationHeader)
        request.sendRequest ({
            self.contentsOfURL = request.contentsOfURL
            self.statusCode = request.getStatusCode()
            self.data = request.data
            completion()
        })
    }
}

Request

import Cocoa

class Request: NSOperation {
    var authorizationHeader = ""
    var contentsOfURL = NSString()
    var data: NSData?
    var jsonParams = [String:NSObject]()
    var isConnected = false
    var method = String()
    var params = String()
    var statusCode = NSHTTPURLResponse().statusCode
    var session = NSURLSession.sharedSession()
    var url = String()
    var URL = NSURL()
    var cookies = [NSHTTPCookie]()

    init(URL: NSURL, params: String, method: String, jsonParams: [String:NSObject]) {
        self.jsonParams = jsonParams
        self.method = method
        self.params = params
        self.URL = URL
    }

    func sendRequest(completion: () -> Void) {
        let session = NSURLSession.sharedSession()
        let request = NSMutableURLRequest(URL: URL)
        request.HTTPMethod = method

        if jsonParams.count != 0 {
            do {
                let jsonData = try NSJSONSerialization.dataWithJSONObject(jsonParams, options: .PrettyPrinted)

                request.setValue("aplication/json; charset=utf-8", forHTTPHeaderField: "Content-Type")
                request.HTTPBody = jsonData
            } catch {

            }
        } else {
            request.HTTPBody = self.params.dataUsingEncoding(NSUTF8StringEncoding)
        }

        let task = session.dataTaskWithRequest(request) {
            (data, response, error) in

            NSHTTPCookieStorage.sharedHTTPCookieStorage().setCookies(self.cookies, forURL: self.URL, mainDocumentURL: nil)
        if data != nil {
            self.data = data!
            print(data)
            do {
                swiftlet responseHeaders = response as! NSHTTPURLResponse
                self.statusCode = responseHeaders.statusCode

                switch self.statusCode {
                case 200:
                    print("200: OK. getting contentsOfURL and cookies")
                    self.contentsOfURL = try NSString(contentsOfURL: self.URL, encoding: NSUTF8StringEncoding)
                    self.cookies = NSHTTPCookieStorage.sharedHTTPCookieStorage().cookiesForURL(self.URL)!

                    case 400:
                        print("400: page not found on web")

                    case 404:
                        print("404: page not found on server")

                    case 407:
                        print("407: failed authenticate proxy credentials")

                    default:
                        print("unable to get statusCode")
                    }
                } catch {

                }
            } else {
                print("\(self.statusCode): unable to get response ")
            }
            print("completing")
            completion() // continue inside request call
        }
        task.resume()
    }

    func _setAuthorizationHeader(authorizationHeader: String) {
        self.authorizationHeader = authorizationHeader
    }

    func _setCookies(cookies: [NSHTTPCookie]) {
        self.cookies = cookies
    }

    func getStatusCode() -> Int {
        return self.statusCode
    }

    func getContentsOfURL() -> NSString {
        return self.contentsOfURL
    }
}
dbconfession
  • 1,017
  • 1
  • 19
  • 35

2 Answers2

1

I'm guessing that you didn't intend to declare a local variable for session in the sendRequest method of your Request class.

 let session = NSURLSession.sharedSession()

The local session variable hides your class's session member and will go out of scope as soon as the function finishes (which drops the whole session and task with it).

[EDIT] I just noticed your local variable is using the sharedSession so even if it goes out of scope, the task should remain alive because the session is supposed to keep a strong reference to it (according to the documentation).

The problem must be something else.

Alain T.
  • 24,524
  • 2
  • 27
  • 43
0

I ended up using a semaphore dispatch and this did the trick.

func sendRequest() { let semaphore = dispatch_semaphore_create(0)

let task = session.dataTaskWithRequest(request) {
    // ...
    dispatch_semaphore_signal(semaphore)
}
task.resume()
dispatch_semaphore_wait(semaphore, DISPATCH_TIME_FOREVER)

}

dbconfession
  • 1,017
  • 1
  • 19
  • 35