Dans mon application, je veux que l'utilisateur évalue un film. Les films sont chargés à partir d'une interface REST, le classement doit être stocké localement. Le problème est que je ne sais pas comment faire persister la nouvelle évaluation d'un utilisateur.

Je peux charger la note à partir de ma base de données et modifier la valeur dans MovieRow, mais je ne sais pas comment obtenir le rappel pour enregistrer la note modifiée.

Voici ce que j'ai:

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
netshark1000 5 févr. 2020 à 16:45

1 réponse

Meilleure réponse

Pour donner une réponse spécifique à cela, vous devrez fournir plus d'informations sur la base de données/ViewModel/REST-Interface

Certaines choses qui pourraient résoudre votre problème @State var ownRating: Int à @Binding var ownRating: Int. State est destiné à être utilisé uniquement dans une vue et doit être déclaré private, Binding transmettra la connexion avec le ViewModel

Lorsque onTapGesture est activé, référencez votre ViewModel et utilisez la sauvegarde ()/update () préétablie de l'interface REST à partir de là.

Vous devrez peut-être transmettre l'intégralité de l'objet Movie ou une variable id à RatingView si votre méthode de sauvegarde/mise à jour a besoin que l'objet/l'identifiant persiste.

L'actualisation de l'interface utilisateur lorsqu'une évaluation est modifiée dépend d'un « ping » ou d'une « notification » indiquant que quelque chose s'est produit. SwiftUI et ses didacticiels s'appuient fortement sur CoreData pour cette partie.

Je ne connais pas Realm mais c'est ce que j'ai pu comprendre à ce sujet. Realm a NotificationTokens qui fournira la notification requise pour la mise à jour SwiftUI dans un objet observable.

Obtenir des objets pour un ListView - Comment utiliser Realm avec SwiftUI

Guide de démarrage - https://realm.io/docs/swift/latest/

Un tutoriel avec un code de requête UITableView devrait être ce dont vous avez besoin - https ://academy.realm.io/posts/meetup-jp-simard-mastering-realm-notifications/

Dépannage d'un ListView avec observed objets Realm - Index out des limites lors de l'utilisation de Realm avec SwiftUI

Création de modèles de données de domaine et enregistrement - https://learnappmaking.com/realm-database- démarrage-rapide/

Vous devez créer un ObservableObject avec les méthodes CRUD standard.

Movie objet de l'interface utilisateur -> ViewModel.create(newMovie: Movie) dans Realm -> Notify/Save the @Published var movies avec la nouvelle requête de Realm

À ViewModel init() Récupérez [Movie] de Realm -> configuration NotifiationToken vers observe et @Published var movies avec la nouvelle requête de Realm

Movie.rating mis à jour depuis l'interface utilisateur -> ViewModel.update(updatedMovie) dans Realm -> Notify/update @Published var movies avec la nouvelle requête de Realm

Supprimer fonctionne de la même manière que les autres.

Il existe de nombreuses manières différentes de persister et ce n'est certainement pas la plus efficace car elle récupère l'intégralité de la requête à chaque fois, mais elle devrait vous permettre de démarrer. Le NotificationToken semble prendre en charge les mises à jour à la lettre avec le bon NotificationToken.observer.

Spécifiquement à votre code. Lorsque l'utilisateur onTapGesture la nouvelle note, vous devez référencer viewModel.update(updatedMovie: updatedMovie), la vue changera lorsqu'elle recevra une nouvelle liste de movies de la NotificationToken.

1
lorem ipsum 6 févr. 2020 à 09:09