Я создаю приложение электронной коммерции со SwiftUI, которому необходимо получить некоторые данные с сервера перед загрузкой многих представлений. Мне интересно, как правильно получить и сохранить эти данные.
В настоящее время на домашней странице приложение запрашивает у сервера имена некоторых изображений для отображения, а затем использует AsyncImage
для загрузки каждого изображения с сервера. В настоящее время я создаю переменную @State
в представлении для хранения имен изображений и загружаю данные в .task
:
struct CoverImagesTabView: View {
@State var coverImageNames: [String] = []
var body: some View {
TabView {
ForEach(coverImageNames, id: \.self) { coverImage in
CoverImageView(coverImage: coverImage)
.padding(.top, 10)
.padding(.horizontal, 15)
} //: LOOP
} //: TAB
.tabViewStyle(PageTabViewStyle(indexDisplayMode: .always))
.task {
await getCoverImageNames { imageNames in
coverImageNames = imageNames
}
}
}
}
с функцией getCoverImageNames
, похожей на
import Alamofire
func getCoverImageNames(_ callback: @escaping (_ decoded: [String]) -> Void) async {
AF.request("http://\(serverIP)/api/offerings/marketing/coverImages")
.responseDecodable(of: [String].self) { response in
if response.value != nil {
callback(response.value ?? [])
print("Cover image names retrieved")
} else {
print("Could not access cover image names")
}
}
}
Затем coverImageNames
используются внутри CoverImageView
для загрузки изображений. Я планирую использовать ту же стратегию для получения других данных, таких как список продуктов.
Этот метод немного неуклюж, в основном потому, что он вызывает API для установки значения coverImageNames
каждый раз при перезагрузке представления; эти данные действительно нужно загружать только один раз, при каждом запуске приложения. Есть ли особый способ сделать это в Swift?
Если я создам класс, соответствующий ObservableObject
, и передам его экземпляр как @StateObject
этому представлению, это решит проблему перезагрузки, но не очень эффективно, если мне придется создать совершенно новый класс только для представления [String]
для представления. единственная цель загрузки только один раз.
Я также рассматривал возможность использования Core Data для сохранения данных с сервера, но это кажется излишним.
Есть ли встроенный способ загрузить мои данные один раз, а затем продолжать использовать их каждый раз при перезагрузке представления? Или метод объекта состояния является наиболее эффективным/единственным способом?
@loremipsum Потому что я не ожидаю никаких асинхронных выражений в функции, которую я пометил как асинхронную, верно? Я полагаю, что мог быть в какой-то момент и забыл удалить его, но спасибо за внимание.
Асинхронное ожидание следует очень специфическому шаблону, и «обратные вызовы» не являются его частью.
Взгляните на эту ссылку, она дает вам несколько хороших примеров того, как управлять данными в вашем приложении Управление данными модели в вашем приложении
Я бы просто создал класс Observable и поделился его экземпляром со всем приложением через @EnvironmentObject.
Если он вообще не меняется после загрузки один раз, нет необходимости использовать @StateObject, потому что нет необходимости обновлять представления.
class YourModel : ObservableObject {
var coverImageNames: [String] = []
init() {
fetchImageNames()
}
func fetchImageNames() {
// load the names of `some` images from the server using URLSession or whatnot and update coverImageNames
}
}
struct YourApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.environmentObject(YourModel())
}
}
}
struct YourView1: View {
@EnvironmentObject var m: YourModel
var body: some View {
// use m.coverImageNames
}
}
struct YourView2: View {
@EnvironmentObject var m: YourModel
var body: some View {
// use m.coverImageNames
}
}
Если вам не нравится повторно вызывать AsyncImage в каждом представлении, я бы создал коллекцию UIImage во время инициализации, чтобы ее можно было повторно использовать в любых представлениях. Создание коллекции может быть синхронным или асинхронным, и оба имеют свои плюсы и минусы в зависимости от ситуации, так что это ваш выбор.
какая-то особая причина для определения fetchImageNames()
как метода класса YourModel
? Я реализовал эту функцию init()
для загрузки имен изображений (а затем изображений после имен, спасибо за этот совет), используя функцию, определенную в отдельном файле (полном сетевых функций), а затем назначил ответы с сервера в завершенииHandler указанной функции
Это просто личное предпочтение для организации кода. Не ахти какое дело :)
Ваш вопрос в значительной степени основан на мнении, но ключевые слова async/await в этом коде используются неправильно. Я бы посоветовал посмотреть «Знакомство с async/await» и Demystify SwiftUI.