SwiftUI: Реализация просмотра рейтинга с подключением к базе данных - PullRequest
1 голос
/ 05 февраля 2020

В моем приложении я хочу, чтобы пользователь оценил MOV ie. Фильмы загружаются из REST-интерфейса, рейтинг должен храниться локально. Проблема в том, что я не знаю, как сохранить новый рейтинг пользователя.

Я могу загрузить рейтинг из своей базы данных и изменить значение в MovieRow, но я не знаю, как получить обратный вызов для сохранение измененного рейтинга.

Вот что у меня есть:

struct RatingView: View {

    @Binding var rating: Int

    var maximumRating = 5
    var offImage: Image?
    var onImage = Image(systemName: "star.fill")
    var offColor = Color.gray
    var onColor = Color.yellow

    var body: some View {
        HStack {

            ForEach(1..<maximumRating + 1) { number in
                self.image(for: number)
                    .foregroundColor(number > self.rating ? self.offColor : self.onColor)
                    .onTapGesture {
                        self.rating = number
                    }
            }
        }
    }

    func image(for number: Int) -> Image {
        if number > rating {
            return offImage ?? onImage
        } else {
            return onImage
        }
    }
}

struct MovieRow: SwiftUI.View {

    let movie: Movie
    @State var ownRating: Int

    var body: some SwiftUI.View {
        ZStack(alignment: Alignment(horizontal: .leading, vertical: .bottom)) {
            ZStack(alignment: Alignment(horizontal: .trailing, vertical: .top
            )) {
                KFImage(movie.fullPosterURL)
                    .cancelOnDisappear(true)
                    .resizable()
                    .frame(height: 250)
                RatingView(rating: $ownRating)
                    .padding([.top, .trailing], 20.0)
            }
            Text(movie.title)
                .font(.largeTitle)
                .padding(.all)
                .background(Color(.darkGray)
                    .opacity(0.5))
                .foregroundColor(.white)
        }
    }
}

struct MovieListView: View {

    @ObservedObject var viewModel = MovieViewModel()

    var body: some View {
        List{
            ForEach(viewModel.movies) { movie in
                MovieRow(movie: movie, ownRating: self.viewModel.ratingForMovieId(id: movie.id))
                    .listRowInsets(EdgeInsets())
            }
        }
    }
}

class MovieViewModel: ObservableObject{

    private let provider: NetworkManager?
    private let ratingModel = RatingModel()

    @Published var movies = [Movie]()

    init(provider: NetworkManager? = NetworkManager(), movies: [Movie] = []) {
        self.provider = provider
        self.movies = movies

        loadNewMovies()
    }

    func loadNewMovies(){
         provider?.getNewMovies(page: 1) {[weak self] movies in
                   print("\(movies.count) new movies loaded")
                   self?.movies.removeAll()
            self?.movies.append(contentsOf: movies)}
    }

    func ratingForMovieId(id: Int)->Int{
        return ratingModel.ratingForMovieId(movieId: id)
    }
}

class RatingModel{

    let realm = try! Realm()

    func ratingForMovieId(movieId: Int)->Int{

        let result = realm.objects(MovieRating.self).filter("movieId = %@", movieId)

        guard let movieRating = result.first else{
            return 0
        }
        return movieRating.rating
    }

    func updateRating(movieId: Int, rating: Int){

        try! realm.write {
            realm.create(MovieRating.self, value: ["movieId": movieId, "rating": rating], update: .modified)
        }
    }

}

1 Ответ

1 голос
/ 05 февраля 2020

Чтобы дать конкретный c ответ на этот вопрос, вам потребуется предоставить дополнительную информацию о базе данных / ViewModel / REST-интерфейсе

Некоторые вещи, которые могут решить вашу проблему с @State var ownRating: Int до @Binding var ownRating: Int. State предназначен для использования только в пределах вида и должен быть объявлен private, Binding передаст соединение с ViewModel

Когда onTapGesture активирован, укажите ваш ViewModel и используйте предварительно установленный save () / update () REST-интерфейса оттуда.

Возможно, вам придется передать весь объект Movie или переменную id в RatingView, если вы сохраните / Метод обновления нуждается в объекте / идентификаторе для сохранения.

Обновление пользовательского интерфейса при изменении рейтинга зависит от «пинга» или «уведомления» о том, что что-то произошло. SwiftUI и его учебные пособия в значительной степени полагаются на CoreData для этой части.

Я не знаком с Realm, но это то, что я мог бы узнать об этом. Realm имеет NotificationTokens, который предоставит необходимое уведомление для обновления SwiftUI в наблюдаемом объекте.

Получение объектов для ListView - Как использовать Realm с SwiftUI

Руководство по началу работы - https://realm.io/docs/swift/latest/

Учебное пособие с кодом запроса UITableView должно быть тем, что вам нужно - https://academy.realm.io/posts/meetup-jp-simard-mastering-realm-notifications/

Устранение неполадок a ListView с observed объектами области - Индекс за пределами границ при использовании области с SwiftUI

Создание моделей данных области и сохранение - https://learnappmaking.com/realm-database-swift-getting-started/

Вы должны создать ObservableObject со стандартными методами CRUD.

Movie объект из пользовательского интерфейса -> ViewModel.create(newMovie: Movie) в Realm -> Уведомить / Сохранить @Published var movies с новым запросом из Realm

В ViewModel init() Получить [Movie] из Realm -> Настройка NotifiationToken до observe и @Published var movies с новым запросом из области

Обновлен Mov ie. оценка из пользовательского интерфейса -> ViewModel.update (updatedMov ie) в Realm-> Уведомить / обновить @Published var movies с новым q Uery из Realm

Удаление работает так же, как и другие.

Существует много разных способов сохранения, и это, безусловно, не самый эффективный способ, поскольку он извлекает весь запрос каждый раз, но он должен помочь вам начать работу. Кажется, NotificationToken поддерживает обновления вплоть до буквы с правильным NotificationToken.observer.

Специально для вашего кода. Когда у пользователя onTapGesture новый рейтинг, вы должны ссылаться на viewModel.update(updatedMovie: updatedMovie), тогда представление изменится, когда он получит новый список movies из NotificationToken.

.
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...