2015-09-30 5 views
1

Приносим извинения за простоту моего описания, я новичок в Swift как язык.Назначение данных после закрытия после dataTaskWithURL, приводящее к значениям nil

У меня есть REST api, который предоставляет все данные для моего приложения. Я пытаюсь заполнить табличный вид из канала JSON. По сути, я стараюсь изо всех сил придерживаться шаблонов MVC, которые я использую в веб-разработке. Поэтому я построил модель своего объекта данных, а затем модель списка, чтобы содержать отдельные экземпляры этого объекта.

Фактическая проблема, однако, когда я инициирую весь процесс (последний пример кода), число результатов равно 0, но это просто потому, что ответ данных для вызова API еще не произошел. Я бы предпочел не использовать синхронные вызовы, но я чувствую, что это единственный способ, которым я это исправлю, если только я не начну трахать свои вызовы API непосредственно в самом контроллере?

class NewsItem { 
    var id: Int 
    var headline: String 
    var summary: String 
    var created: String 
    var source: String 
    var company: String 
    var companyLogo: UIImage 

    init(headline: String, summary: String, created: String, source: String, company: String, companyLogoUrl: String, id: Int) { 
     self.id = id 
     self.headline = headline 
     self.summary = summary 
     self.created = created 
     self.source = source 
     self.company = company 

     if let url = NSURL(string: companyLogoUrl) { 
      if let data = NSData(contentsOfURL: url) { 
       companyLogo = UIImage(data: data)! 
      } else { 
       companyLogo = UIImage(named: "DefaultLogo")! 
      } 
     } else { 
      companyLogo = UIImage(named: "DefaultLogo")! 
     } 
    } 
} 

У меня тогда есть свой класс NewsList, который на самом деле создает массив новостей, используя закрытие для остального вызова.

class NewsList { 
    var name: String 
    var newsItems: [NewsItem] 

    init(named: String, includeNewsItems: [NewsItem]) { 
     name = named 
     newsItems = includeNewsItems 
    } 

    class func getNewsItems() -> [NewsList] { 
     return [self.parsedNews()] 
    } 

    private class func parsedNews() -> NewsList { 
     var newsItems = [NewsItem]() 

     api.getLatestActivityData({JSONData, error -> Void in 
      if (JSONData != nil) { 
       for (_, subJSON) in JSONData["data"] { 
        let headline = subJSON["headline"].string! 
        let summary = subJSON["summary"].string! 
        let created = subJSON["created"].string! 
        let source = subJSON["source"].string! 
        let company = subJSON["company"].string! 
        let companyLogo = subJSON["companyLogo"].string! 
        let id = subJSON["id"].int! 

        dispatch_async(dispatch_get_main_queue(), { 
         newsItems.append(NewsItem(headline: headline, summary: summary, created: created, source: source, company: company, companyLogoUrl: companyLogo, id: id)) 
        }) 
       } 
      } else { 
       print("api data fetch failed") 
       print(error) 
      } 
     }) 

     return NewsList(named: "News", includeNewsItems: newsItems) 
    } 
} 

Для приведенного выше разговора, у меня есть следующий метод в моем классе RemoteAPI

func getLatestActivityData(completionHandler: ((JSON!, NSError!) -> Void)!) -> Void { 
     let requestUrl = self.baseUrl + "xxxxxxx" 
     print("Request URL: " + requestUrl) 

     let url = NSURL(string: requestUrl)! 
     let request = NSMutableURLRequest(URL: url) 
     request.setValue("text/json; charset=utf-8", forHTTPHeaderField: "Content-Type") 

     let task = NSURLSession.sharedSession().dataTaskWithURL(url, completionHandler: {data, response, error -> Void in 
      if (error != nil) { 
       return completionHandler(nil, error) 
      } 

      print("Latest activity response received") 
      let returnString = NSString(data: data!, encoding: NSUTF8StringEncoding) 
      let jsonData = returnString!.dataUsingEncoding(NSUTF8StringEncoding)! 
      let readableJSON = JSON(data: jsonData, options: NSJSONReadingOptions.MutableContainers, error: nil) 

      if (error != nil) { 
       return completionHandler(nil, error) 
      } else { 
       return completionHandler(readableJSON, nil) 
      } 
     }) 

     task.resume() 
    } 

Чтобы действительно начать цельные выше свернутой процесса, у меня есть это, на мой взгляд контроллер

var newsItems: [NewsItem] { 
    var newsList = NewsList.getNewsItems() 
    return newsList[0].newsItems 
} 

Вот наша точка боли. Если я запустил print(newsItems.count) после вышеупомянутого вызова, результат равен 0. Однако через секунду или два обработчик обратного вызова фактически получит данные и начнет назначать элементы массиву newsList.

Как предотвратить назначение массива, пока данные фактически не получены? Я просто об этом не так?

Xcode 7 + Swift 2.0 + IOS 9.0.x

+0

Итак, после дальнейшего чтения я думаю, что здесь я ошибаюсь, я должен обновлять табличное представление внутри закрытия, а не просто назначать данные массиву в основном потоке и затем возвращать этот массив. Я попытаюсь сыграть с этим и ответить на свой вопрос в информационных целях, если это сработает. – Sean

ответ

0

Моя ошибка была в том, как я использую закрытия. Я был в зависимости от моего закрытия, чтобы назначать значения за пределами закрытия, а затем обработчик завершения для возврата этих значений.

Решение:

  1. Я удалил весь класс списка новостей, его на самом деле избыточный к тому, что я делаю

  2. Я изменил звонок на моем контроллере представления, чтобы сделать следующее

    var newsItems = [NewsItem]() 
    
    api.getLatestActivityData({JSONData, error -> Void in 
        if (JSONData != nil) { 
         dispatch_async(dispatch_get_main_queue(), { 
          for (_, subJSON) in JSONData["data"] { 
           let headline = subJSON["headline"].string! 
           let summary = subJSON["summary"].string! 
           let created = subJSON["created"].string! 
           let source = subJSON["source"].string! 
           let company = subJSON["company"].string! 
           let companyLogo = subJSON["companyLogo"].string! 
           let id = subJSON["id"].int! 
    
           newsItems.append(NewsItem(headline: headline, summary: summary, created: created, source: source, company: company, companyLogoUrl: companyLogo, id: id)) 
          } 
    
          self.activityTableView.reloadData() 
         }) 
        } else { 
         print("api data fetch failed") 
         print(error) 
        } 
    }) 
    

Теперь все, что я делаю uld нужно сделать, как только цикл for будет выполнен, я должен обновить таблицу с помощью перезагрузки.

Для «новых» пользователей, таких как я, я не понял, что бы вчера помогло узнать. Когда вы вызываете tableView.reloadData(), приложение вызывает все связанные с tabieView события, если ваш источник данных был обновленный с закрытием, так как мой выше, ваши cellForRowAtIndexPath и numberOfRowsInSection смогут правильно обновить. Просто назначьте свои данные в функции cellForRowAtIndexPath.

Надеюсь, это поможет кому-то!

 Смежные вопросы

  • Нет связанных вопросов^_^