3

I have four different requests in my application, three of them requires one call only and the last requires from 1 - 10.

All works fine until the last request when I´m iterating through my data and making the calls. This is my code in Class1:

var data = ...
var points = ...

// I create a new group
let getPointGroup = dispatch_group_create()

// I iterate through my data
for d in data{
    // I enter the group
    dispatch_group_enter(getPointGroup)
    dataService.getPoints(d.point), success: { points -> Void in
        points.append(points)

        // I leave the group so that I go to the next iteration
        dispatch_group_leave(getPointGroup)
    }
}

Alamofire request looks like this in Class2:

let headers = [
    "Authorization": "Bearer \(token)",
    "Content-Type": "application/x-www-form-urlencoded"
]

Alamofire.request(.GET, url, headers:headers)
    .responseJSON { response in
        switch response.result {
        case .Success:
            let json = JSON(data: response.data!)
            print(json)
            success(json)
        case .Failure(let error):
            print(error)
        }
}

But I never hit the GET request, if I remove the iteration completely and just call the Alamofire request once it works perfectly.

Any ideas of how to solve the Alamofire iteration request?

Edit
Not really a duplicate, I have the snippets below in the different classes and the example does not really solve my issue

user5855868
  • 97
  • 2
  • 15
  • There is a possibility that you might have run into a deadlock. Are you doing this grouping `dispatch_group_enter()` thing within the main thread? If so, you might want to look at [this answer](http://stackoverflow.com/questions/39264454/how-to-wait-until-all-nsoperations-is-finished/39265681#39265681). – Ozgur Vatansever Sep 12 '16 at 20:52
  • @ozgur, that answer is good but I have these snippets in two different classes so it does not really help me. – user5855868 Sep 12 '16 at 21:09
  • @Rob, .Failure is never called. – user5855868 Sep 12 '16 at 21:10
  • @Rob ok. But how do I do if have my iteration in one class and the Alamo request in another? – user5855868 Sep 12 '16 at 21:14
  • Could you please answer with an example, this is kind of hard for me to understand @Rob – user5855868 Sep 12 '16 at 21:22
  • I'd suggest you edit your question, showing us what the signature of `getPoints` looks like. I'd also suggest showing us how you're using `getPointsGroup` after the `for` loop, as that's where the root of the problem may well rest. – Rob Sep 12 '16 at 21:46

1 Answers1

2

If this is not running, you could be deadlocking if you use dispatch_group_wait on the main thread, thereby blocking that thread, and preventing Alamofire from running any of its completion handlers (which also require the main thread). This is solved (assuming you are, indeed, using dispatch_group_wait), by replacing that with dispatch_group_notify.

Thus:

let group = dispatch_group_create()

for d in data {
    // I enter the group
    dispatch_group_enter(group)
    dataService.getPoints(d.point)) { additionalPoints, error in
        defer { dispatch_group_leave(group) }

        guard let let additionalPoints = additionalPoints else {
            print(error)
            return
        }

        points.append(additionalPoints)
    }
}

dispatch_group_notify(group, dispatch_get_main_queue()) {
    // go to the next iteration here
}

Where:

func getPoints(point: WhateverThisIs, completionHandler: (JSON?, NSError?) -> ()) {
    let headers = [
        "Authorization": "Bearer \(token)"
    ]

    Alamofire.request(.GET, url, headers: headers)
        .responseJSON { response in
            switch response.result {
            case .Success:
                let json = JSON(data: response.data!)
                completionHandler(json, nil)
            case .Failure(let error):
                completionHandler(nil, error)
            }
    }
}

Now, I don't know what your various parameter types were, so I was left to guess, so don't get lost in that. But the key is that (a) you should make sure that all paths within your Alamofire method will call the completion handler; and (b) the caller should use dispatch_group_notify rather than dispatch_group_wait, avoiding the blocking of any threads.

Note, in order to make the completion handler callable even if the network request failed, I had to make the parameter to that closure optional. And while I was there, I added an optional error parameter, too.


A few unrelated changes included in the above sample:

  1. I'd suggest using a different variable name for the parameter of your closure. The points.append(points) in your original code snippet suggests some confusion between your points array and the points that is passed back in the closure.

  2. You don't have to set the Content-Type header, as Alamofire does that for you.

  3. I didn't change it above, but it is inefficient to use responseJSON (which uses NSJSONSerialization to parse the JSON) and then use SwiftyJSON to parse the raw data with NSJSONSerialization again. Personally, I don't bother with SwiftyJSON, but if you want to use it, I'd suggest use Alamofire's response method rather responseJSON. There's no point in parsing the JSON twice.

Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • Thanks for your reply, one thing I don´t understand is `dispatch_group_notify(group, dispatch_get_main_queue()) { // go to the next iteration here }` how do I proceed with the next iteration in there? – user5855868 Sep 14 '16 at 06:59
  • You might have each of the iterations in their own methods, so you'd call the next method inside the braces where it says "go to the next iteration here". – Rob Sep 14 '16 at 07:09
  • I made some minor changes from your code, but now it´s working fine. Thanks a lot for your help Rob. – user5855868 Sep 14 '16 at 19:44