Загрузка данных в приложение iOS с сервера при запуске и их повторное использование при перезагрузке представлений

Я создаю приложение электронной коммерции со 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 для сохранения данных с сервера, но это кажется излишним.

Есть ли встроенный способ загрузить мои данные один раз, а затем продолжать использовать их каждый раз при перезагрузке представления? Или метод объекта состояния является наиболее эффективным/единственным способом?

Ваш вопрос в значительной степени основан на мнении, но ключевые слова async/await в этом коде используются неправильно. Я бы посоветовал посмотреть «Знакомство с async/await» и Demystify SwiftUI.

lorem ipsum 30.03.2023 23:06

@loremipsum Потому что я не ожидаю никаких асинхронных выражений в функции, которую я пометил как асинхронную, верно? Я полагаю, что мог быть в какой-то момент и забыл удалить его, но спасибо за внимание.

eric_b_231 30.03.2023 23:41

Асинхронное ожидание следует очень специфическому шаблону, и «обратные вызовы» не являются его частью.

lorem ipsum 31.03.2023 00:17

Взгляните на эту ссылку, она дает вам несколько хороших примеров того, как управлять данными в вашем приложении Управление данными модели в вашем приложении

workingdog support Ukraine 31.03.2023 01:43
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
4
93
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Я бы просто создал класс 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 указанной функции

eric_b_231 06.04.2023 19:52

Это просто личное предпочтение для организации кода. Не ахти какое дело :)

LearnLoop 06.04.2023 20:45

Другие вопросы по теме