Проблема с обработчиком завершения / пользовательским обработчиком завершения — Swift iOS

#ios #swift #completionhandler

#iOS #swift #обработчик завершения

Вопрос:

У меня есть две функции ниже:

 func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
    let cell = tableView.dequeueReusableCell(withIdentifier: "MessageCell", for: indexPath)
    let message = self.messages[indexPath.row]

    // Set table cell values

    getUserFriendlyName(sid: message.sid!, arg: true, completion: { (success) -> Void in

        cell.detailTextLabel?.text = self.friendly_name
        cell.textLabel?.text = message.body
        cell.selectionStyle = .none
        //self.tableView.reloadData()
    })

    return cell
}
  

и

 func getUserFriendlyName(sid: String, arg: Bool, completion: @escaping (Bool) -> ()) {

    let ACCOUNT_IDENTITY: String = "AC***redacted"
    let AUTH_TOKEN: String = "**redacted"
    let SERVICE_ID: String = "IS***redacted"

    let loginString = "(ACCOUNT_IDENTITY):(AUTH_TOKEN)"

    let loginData = loginString.data(using: String.Encoding.utf8)

    let base64LoginString = loginData!.base64EncodedString()

    let url = URL(string: "https://chat.twilio.com/v2/Services/IS***redacted/Users/US***redacted")

    var request = URLRequest(url: url!)
    var session = URLSession.shared
    request.httpMethod = "GET"
    request.setValue("Basic (base64LoginString)", forHTTPHeaderField: "Authorization")
    request.addValue("application/json", forHTTPHeaderField: "Accept")
    //request.addValue(AUTH_TOKEN, forHTTPHeaderField: "Authorization")
    var fn: String!
    let task = session.dataTask(with: request, completionHandler: { data, response, error in
        var strData = String(data: data!, encoding: String.Encoding.utf8)
        print("Body: (strData)")
        guard error == nil else {
            print(error!)
            return
        }
        guard let data = data else {
            print("Data is empty")
            return
        }

        let json = try! JSONSerialization.jsonObject(with: data, options: []) as! NSDictionary

        //getting the json response
        print(json)
        fn = json["friendly_name"] as! String?
        self.friendly_name = fn

        completion(arg)
        //return fn
        //print(fn)
    })

    task.resume()

}
  

Вот проблема, с которой я столкнулся… При обнаружении нового сообщения чата вызывается функция TableView. Создается новая ячейка с текстом и подтекстом. Поскольку собственный код Twilio не позволяет мне отображать понятное имя того, кто написал чат, я должен пойти и выполнить HTTP-вызов, поэтому я вызываю getUserFriendlyName() . Поскольку resumeTask() является асинхронным, выполнение программы происходит следующим образом:

  1. Введена функция просмотра таблицы
  2. Я вызываю функцию getUserFriendlyNameFunction(), которая вводится
  3. Когда мы переходим к HTTP-запросу, вызывается resumeTask(), а затем поток возвращается обратно к функции TableView, в частности, к концу вызова функции удобного имени пользователя, и она выполняет строку возврата ячейки, что означает, что ячейка создана, но содержит данные прототипа, а именно «Заголовок» для текста и «Субтитры» для текста описания.
  4. Затем поток возвращается к HTTP-запросу, и он выполняется
  5. Поток возвращается к строке кода непосредственно после вызова getUserFriendlyName() внутри функции TableView. Этот код устанавливает текстовые и подробные текстовые значения в данные, возвращаемые HTTP-вызовом. Но на данный момент ячейка уже существует в моем представлении таблицы. Итак, у меня осталась только ячейка, содержащая «Текст» и «Подтекст».

Что я пробовал:

  1. Добавление TableView.reloadData() после выполнения последних строк кода в функции TableView. Это не обновило TableView данными из HTTP-вызова. Фактически, он постоянно обновлял TableView снова и снова.

  2. Перемещение последних трех строк кода в то же место, что и строка «возвращаемая ячейка». Это также не сработало.

Я пришел к выводу, что моя единственная надежда — это completionHandler / userCompletionHandler. Я долго играл с ними и не могу получить желаемый результат, который заключался бы в том, чтобы первое входящее сообщение чата было заполнено данными из HTTP-запроса.

Кто-нибудь может помочь?

Комментарии:

1. 1. Удалите асинхронный вызов из cellForRowAt . 2. Выполните асинхронный вызов, а затем обновите данные вашей таблицы (self.messages). 3. Вызовите TableView.reloadData(), убедившись, что вы находитесь в основном потоке..

Ответ №1:

При получении сообщения не добавляйте его непосредственно в массив сообщений, а сначала вызывайте

 // this goes inside some function not cellForRowAt

let message =  // received 

getUserFriendlyName(sid: message.sid!, arg: true, completion: { (success) -> Void in 
       message....// set all needed properties
       self.messages.append(message)
       self.tableview.reloadRows(at:[IndexPath(row:messages.count - 1,section:0)],with:.none)
}
  

Не выполняйте никакой асинхронной работы внутри cellForRowAt , поскольку это будет выполняться при каждой прокрутке, кроме того, он не отображает полные данные одновременно


Завершение session.dataTask(with: request выполняется в фоновом потоке, поэтому рассмотрите

 DispatchQueue.main.async {
  completion(arg)
}
  

Вы делаете self.friendly_name

 self.friendly_name = fn
  

Свойство vc, хотя оно должно быть свойством для каждого объекта сообщения, чтобы не смешиваться с другими отправителями сообщений

Комментарии:

1. Хорошо, я понимаю. Я думаю, я понимаю, что вы говорите о Cellforrowat… Я не знал, что он также выполняется асинхронно. Я перестрою свой код, и как только он заработает, я опубликую завершенные два метода. Спасибо всем!