NSURLSession, блок завершения, Swift

Я работаю с NSURLSession. У меня есть массив с ресторанами, и я запрашиваю блюда для каждого ресторана в массиве в API. DataTask работает, мне просто очень трудно вызывать метод только тогда, когда все dataTasks завершены.

 self.findAllDishesOfRestaurants(self.restaurantsNearMe) { (result) -> Void in
        if result.count != 0 {
              self.updateDataSourceAndReloadTableView(result, term: "protein")
        } else {
            print("not ready yet")
        } 
    }

self.updateDataSourceAndREloadTableView никогда не вызывается, независимо от моего блока завершения. Вот моя функция findAllDishesOfRestaurants

func findAllDishesOfRestaurants(restaurants:NSArray, completion:(result: NSArray) -> Void) {
    let allDishesArray:NSMutableArray = NSMutableArray()
    for restaurant in restaurants as! [Resturant] {
        let currentRestaurant:Resturant? = restaurant
        if currentRestaurant == nil {
            print("restaurant is nil")
        } else {
            self.getDishesByRestaurantName(restaurant, completion: { (result) -> Void in
                                            if let dishesArray:NSArray = result {
                                                restaurant.dishes =  dishesArray
                                                print(restaurant.dishes?.count)
                                                allDishesArray.addObjectsFromArray(dishesArray as [AnyObject])
                                                self.allDishes.addObjectsFromArray(dishesArray as [AnyObject])
                                                print(self.allDishes.count)
                                            }
                                            else {
                                                print("not dishes found")
                                        }
                                          // completion(result:allDishesArray)
                                    })
             completion(result:allDishesArray)
        }
    }
}

И вот моя функция, в которой я выполняю dataTasks.

 func getDishesByRestaurantName(restaurant:Resturant, completion:(result:NSArray) ->Void) {

    var restaurantNameFormatted = String()
    if let name = restaurant.name {
    for charachter in name.characters {
        var newString = String()
        var sameCharacter:Character!
        if charachter == " " {
           newString = "%20"
            restaurantNameFormatted = restaurantNameFormatted + newString
        } else {
            sameCharacter = charachter
            restaurantNameFormatted.append(sameCharacter)
        }
       // print(restaurantNameFormatted)
    }
}
    var urlString:String!
        //not to myself, when using string with format, we need to igone all  the % marks arent ours to replace with a string, otherwise they will be expecting to be replaced by a value
         urlString = String(format:"https://api.nutritionix.com/v1_1/search/%@?results=0%%3A20&cal_min=0&cal_max=50000&fields=*&appId=XXXXXXXXXappKey=XXXXXXXXXXXXXXXXXXXXXXXXXXXX",restaurantNameFormatted)
    let URL = NSURL(string:urlString)
    let restaurantDishesArray = NSMutableArray()
  let session = NSURLSession.sharedSession()
                let dataTask = session.dataTaskWithURL(URL!) { (data:NSData?, response:NSURLResponse?, error:NSError?) -> Void in
                do {
                let anyObjectFromResponse:AnyObject = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.AllowFragments)
                    if let asNSDictionary = anyObjectFromResponse as? NSDictionary {
                        let hitsArray = asNSDictionary.valueForKey("hits") as? [AnyObject]
                                                for newDictionary in hitsArray! as! [NSDictionary]{
                                                    let fieldsDictionary = newDictionary.valueForKey("fields") as? NSDictionary
                                                    let newDish = Dish.init(dictionary:fieldsDictionary!, restaurant: restaurant)
                                                    restaurantDishesArray.addObject(newDish)
                        }
                    }
                    completion(result:restaurantDishesArray)
                } catch let error as NSError {
                    print("failed to connec to api")
                    print(error.localizedDescription)
                }
            }
            dataTask.resume()
}

Как я уже говорил, мне нужно подождать, пока не завершится веселье findAllDishesOfRestaurants. Я пытался написать свои блоки завершения, но я не уверен, что делаю это правильно. Любая помощь приветствуется. Благодарить


person Gamaliel Tellez Ortiz    schedule 16.10.2015    source источник


Ответы (1)


Проблема в том, что вы вызываете метод completion в findAllDishesOfRestaurants до завершения всех задач. На самом деле вы вызываете его один раз для каждого ресторана в списке, что, вероятно, не то, что вам нужно.

Я рекомендую вам изучить NSOperationQueue по двум причинам:

  1. Это позволит вам ограничить количество одновременных запросов к серверу, чтобы ваш сервер не был перегружен запросами.
  2. Это позволит вам легко контролировать, когда все операции завершены.

Однако, если вы ищете быстрое решение, вам нужно использовать группы GCD dispatch_group_create, dispatch_group_enter, dispatch_group_leave и dispatch_group_notify следующим образом.

func findAllDishesOfRestaurants(restaurants:NSArray, completion:(result: NSArray) -> Void) {
    let group = dispatch_group_create() // Create GCD group

    let allDishesArray:NSMutableArray = NSMutableArray()
    for restaurant in restaurants as! [Resturant] {
        let currentRestaurant:Resturant? = restaurant
        if currentRestaurant == nil {
            print("restaurant is nil")
        } else {
            dispatch_group_enter(group) // Enter group for this restaurant
            self.getDishesByRestaurantName(restaurant, completion: { (result) -> Void in
                if let dishesArray:NSArray = result {
                    restaurant.dishes =  dishesArray
                    print(restaurant.dishes?.count)
                    allDishesArray.addObjectsFromArray(dishesArray as [AnyObject])
                    // self.allDishes.addObjectsFromArray(dishesArray as [AnyObject])  <-- do not do this
                    // print(self.allDishes.count)
                }
                else {
                    print("not dishes found")
                }
                // completion(result:allDishesArray)  <-- No need for this, remove
                dispatch_group_leave(group) // Leave group, marking this restaurant as complete
            })
            // completion(result:allDishesArray) <-- Do not call here either
        }
    }

    // Wait for all groups to complete
    dispatch_group_notify(group, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)) {
        completion(result:allDishesArray)
    }
}
person Eneko Alonso    schedule 16.10.2015
comment
Да, я просто оставил часть завершения там, потому что иначе она не будет вызвана, пока не станет слишком поздно. Ваши предложения звучат потрясающе, я даже никогда не слышал об этом. Большое спасибо, и я дам вам знать, как это происходит, и приму ваш ответ. - person Gamaliel Tellez Ortiz; 16.10.2015
comment
чувак! Большое спасибо за предложение, быстрое исправление работает, но я больше смотрю на NSOpertationQueue. - person Gamaliel Tellez Ortiz; 17.10.2015