Отправка строки из данных JSON в переменную вне функции

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

Когда я печатаю переменную, она по-прежнему отображается как пустая даже после загрузки функции. В функции строка отображается правильно.

Код:

var filmTitle = ""

override func viewDidLoad() {
    super.viewDidLoad()


loadFilms()
print(self.filmTitle) //Prints as an empty string

}

func loadFilms() {

    let id = filmId
    let apiKey = "97a0d64910120cbeae9df9cb675ad235"
    let url = URL(string: "https://api.themoviedb.org/3/movie/\(id)?api_key=\(apiKey)&language=en-US")
    let request = URLRequest(
        url: url! as URL,
        cachePolicy: URLRequest.CachePolicy.reloadIgnoringLocalCacheData,
        timeoutInterval: 10 )

    let session = URLSession (
        configuration: URLSessionConfiguration.default,
        delegate: nil,
        delegateQueue: OperationQueue.main
    )

    let task = session.dataTask(with: request, completionHandler: { (dataOrNil, response, error) in
        if let data = dataOrNil {
            do { let details = try! JSONDecoder().decode(Details.self, from: data)
        self.filmTitle = details.title
        print(self.filmTitle) //string prints correctly

                        }
                    }
                })


    task.resume()

}

Что мне не хватает, чтобы правильно установить строку в переменную?


person JSharpp    schedule 24.11.2018    source источник
comment
Ваш код в порядке. Просто данные загружаются асинхронно, поэтому print в viewDidLoad вызывается задолго до загрузки данных.   -  person rmaddy    schedule 24.11.2018
comment
Как я могу это исправить, так как мне нужно использовать данные для другой функции.   -  person JSharpp    schedule 24.11.2018
comment
Я обновил вопрос, чтобы объявить, что планирую использовать переменную внутри другой функции. Новая функция также декодирует данные JSON. Сначала мне нужно название фильма, чтобы запустить другие функции.   -  person JSharpp    schedule 24.11.2018


Ответы (3)


Загрузка данных из Интернета — это асинхронный метод. Оператор печати вызывается до завершения loadFilms().

Используйте обратный вызов, чтобы получить данные после его завершения.

func loadFilms(completion: @escaping (Details?, Error?) -> Void) { 
    //...

 let task = session.dataTask(with: request, completionHandler: { (dataOrNil, response, error) in
    if let data = dataOrNil {
        do { let details = try JSONDecoder().decode(Details.self, from: data)
     completion(details, nil) 


           } catch { 
               completion(nil, error) 
                }
            })

}

На месте вызова:

override func viewDidLoad() { 

   loadFilms { details, error in 

     if error { //* Handle Error */ } 

     self.filmTitle = details.title
     print(filmTitle)

    }

}
person SirCJ    schedule 24.11.2018
comment
Под местом вызова вы имеете в виду, где я запускаю функцию? Будучи viewDidLoad из моего кода. - person JSharpp; 24.11.2018
comment
@JSharpp Да, откуда бы вы ни запускали метод. - person SirCJ; 24.11.2018

Веб-запрос является асинхронным и, с точки зрения CP, занимает много времени. Когда вы вызываете это:

override func viewDidLoad() {
    super.viewDidLoad()

    loadFilms()
    print(self.filmTitle) // loadFilms() hasn't finished so `filmTitle` is empty
}

Лучше установить наблюдателя свойств на filmTitle:

var filmTitle: String? = nil {
    didSet {
        print(filmTitle)

        Dispatch.main.async {
            // update your GUI
        }
    }
}
person Code Different    schedule 24.11.2018

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

person JSharpp    schedule 28.12.2018