Я использую HanekeSwift для извлечения кэшированных данных, а затем устанавливаю для них метки в swipeView каждый раз, когда появляется представление. Мой код без проблем извлекает данные, но поскольку cache.fetch()
асинхронный, когда я вызываю свой метод для обновления представления, мои ярлыки устанавливаются на nil
. Есть ли способ сказать Swift подождать, пока мои кэшированные данные не будут извлечены, прежде чем загружать представление?
Смотрите код ниже:
override func viewWillAppear(animated: Bool) {
updateEntries() // updates entries from cache when view appears
}
func updateEntries() {
guard let accessToken = NSUserDefaults.standardUserDefaults().valueForKey("accessToken") as? String else { return }
guard let cachedEntryKey = String(accessToken) + "food_entries.get" as? String else { return }
cache.fetch(key: cachedEntryKey).onSuccess { data in
...
// if successful, set labels in swipeView to data retrieved from cache
...
dispatch_group_leave(dispatchGroup)
} .onFailure { error in
print(error)
...
// if unsuccessful, call servers to retrieve data, set labels in swipeView to that data
...
dispatch_group_leave(dispatchGroup)
}
}
Когда я просматриваю приведенный выше код, он всегда отображает представление, а затем переходит в блок кеша. Как сделать так, чтобы viewWillAppear () разрешил updateEntries () завершиться и не возвращался из него, пока блок кеша не будет выполнен? Заранее спасибо!
Обновление 1:
Приведенное ниже решение работает довольно хорошо, и мои вызовы выполняются в правильной последовательности (мой оператор печати в блоке уведомлений выполняется после извлечения кеша), но мои представления обновляют свои метки только с ненулевыми значениями при вызове сервера. Может я ошибся код в группе уведомлений?
override func viewWillAppear(animated: Bool) {
self.addProgressHUD()
updateEntries() // updates entries from cache when view appears
}
func updateEntries() {
guard let accessToken = NSUserDefaults.standardUserDefaults().valueForKey("accessToken") as? String else { return }
guard let cachedEntryKey = String(accessToken) + "food_entries.get" as? String else { return }
let dispatchGroup = dispatch_group_create()
dispatch_group_enter(dispatchGroup)
dispatch_group_async(dispatchGroup, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)) {
cache.fetch(key: cachedEntryKey).onSuccess { data in
...
// if successful, set labels in swipeView to data retrieved from cache
...
} .onFailure { error in
print(error)
...
// if unsuccessful, call servers to retrieve data, set labels in swipeView to that data
...
}
}
dispatch_group_notify(dispatchGroup, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)) {
print("Retrieved Data")
self.removeProgressHUD()
}
}
Обновление 2:
Кроме того, я получаю это предупреждение в консоли, когда переключаю представления. Я думаю, что блокирую основной поток с помощью приведенного выше кода
«Это приложение изменяет механизм автоматического раскладки из фонового потока, что может привести к повреждению механизма и странным сбоям. Это вызовет исключение в следующем выпуске».
3 ответа
Хотя я полностью согласен с @ozgur относительно отображения какого-то индикатора загрузки с точки зрения UX, я понял, что полезно научиться использовать Grand Central Dispatch (собственное решение Apple для асинхронного ожидания) может помочь вам в долгосрочной перспективе.
Вы можете использовать dispatch_groups
, чтобы дождаться полного завершения работы блока (ов) кода, прежде чем запускать какой-либо обработчик завершения.
Группа диспетчеризации - это механизм мониторинга набора блоков. Ваше приложение может отслеживать блоки в группе синхронно или асинхронно в зависимости от ваших потребностей. В качестве расширения группа может быть полезна для синхронизации кода, который зависит от завершения других задач.
[...]
Группа отправки отслеживает количество ожидающих блоков, а GCD сохраняет группу до тех пор, пока все связанные с ней блоки не завершат выполнение.
Вот пример dispatch_groups
в действии:
let dispatchGroup = dispatch_group_create()
dispatch_group_async(dispatchGroup, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)) {
// Run whatever code you need to in here. It will only move to the final
// dispatch_group_notify block once it reaches the end of the block.
}
dispatch_group_notify(dispatchGroup, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0)) {
// Code in here only runs once all dispatch_group_async blocks associated
// with the dispatchGroup have finished completely.
}
Самое замечательное в dispatch_groups
то, что они позволяют вам запускать несколько асинхронных блоков одновременно и ждать, пока все они закончатся, прежде чем запускать последний обработчик завершения. Другими словами, вы можете связать столько блоков dispatch_group_async
с dispatchGroup
, сколько хотите.
Если вы хотите использовать подход с индикатором загрузки (что вам и следовало бы), вы можете запустить код для отображения индикатора загрузки, а затем перейти к dispatch_group
с обработчиком завершения, чтобы удалить индикатор загрузки и загрузить данные в представление один раз dispatch_group
завершается.
Вот простой пример создания экрана загрузки. Я просто создаю представление предупреждений, также вместо этого вы можете создать собственное представление индикатора загрузки .
let alert = UIAlertController(title: "", message: "please wait ...", preferredStyle: .alert)
override func viewWillAppear(animated: Bool) {
self.present(alert, animated: true, completion: nil)
updateEntries() // updates entries from cache when view appears
}
func updateEntries() {
guard let accessToken = UserDefaults.standard.value(forKey: "accessToken") as? String,
let cachedEntryKey = (accessToken + "food_entries.get") as? String else {
return
}
cache.fetch(key: cachedEntryKey).onSuccess { data in
...
// update value in your UI
alert.dismiss(animated: true, completion: nil)
...
} .onFailure { error in
print(error)
...
// if unsuccessful, call servers to retrieve data, set labels in swipeView to that data
...
}
}
Хорошо, предложения от всех очень помогли в этом. Думаю, я понял. Мне нужно убедиться, что мой блок кеша не блокирует основную очередь. См. Код ниже
ИЗМЕНИТЬ
Спасибо @Rob за то, что помог мне внести необходимые изменения, чтобы эта работа работала.
let dispatchGroup = dispatch_group_create()
dispatch_group_enter(dispatchGroup)
cache.fetch(key: cachedEntryKey).onSuccess { data in
...
// if successful, set labels in swipeView to data retrieved from cache
...
dispatch_group_leave(dispatchGroup)
} .onFailure { error in
print(error)
...
// if unsuccessful, call servers to retrieve data, set labels in swipeView to that data
...
dispatch_group_leave(dispatchGroup)
}
dispatch_group_notify(dispatchGroup, dispatch_get_main_queue()) {
print("Retrieved Data")
self.removeProgressHUD()
}
Похожие вопросы
Новые вопросы
swift
Swift — это язык программирования общего назначения, разработанный Apple Inc., впервые выпущенный в 2014 году для своих платформ и Linux. Swift имеет открытый исходный код. Используйте тег только для вопросов о языковых функциях или необходимости кода в Swift. Используйте теги [ios], [ipados], [macos], [watch-os], [tvos], [swiftui], [cocoa-touch] и [cocoa] для (не зависящих от языка) вопросов о платформах или рамки.