将核心数据条目标记为收藏 SWIFTUI

Mark Core Data entries as Favourites SWIFTUI

我正在开发一个应用程序,可以将来自 on-line Json 的帖子存储到 CoreData 以获取离线内容。这是一个房地产列表应用程序,用户可以在其中出售和出租房产。

到目前为止,我完成了在 CoreData 中保存 fethed 列表,但我无法为现有条目实现“添加到收藏夹”。我使用 NSPredicate 来过滤列表类型。在不同的视图中

  1. 我创建了一个实体“Listing”Core Data - Listing

  2. 还有一个“最喜欢的”实体Core Data - Favorites

我的问题是:如何实现“添加到收藏夹”

选项 A:在另一个实体中再次保存“最喜欢的”列表。

选项 B:在“列表”实体中创建另一个“最喜欢的”属性并在最喜欢的时间内将其保留在核心数据中?

// 这是我的清单 Model.swift

struct ListingModel:  Hashable, Decodable, Encodable, Identifiable {
    var id : Int
    var title : String
    var category : String
    var name : String
    var image : String
    var publishdate : String
    var saleprice : Int = 0
    var rentprice : Int = 0
    var listingtype : String
    var latitude : Double!
    var longitude : Double!
    
}
//  JSONViewModel.swift


import SwiftUI
import CoreData

class JSONViewModel: ObservableObject {

    @Published var listings: [ListingModel] = []
  //  @Published var tags :[Tags] = []
    // saving Json to Core Data...
    
    func saveData(contex: NSManagedObjectContext) {
        
        listings.forEach { (data) in
            
            let entity = Listing(context: contex)
            entity.title = data.title
            entity.name = data.name
            entity.category = data.category
            entity.image = data.image
            entity.publishdate = data.publishdate
            entity.tagline = data.tagline
            entity.content = data.content
            entity.coverimage = data.coverimage
            entity.saleprice = Int64(truncating: NSNumber(value: data.saleprice))
            entity.rentprice = Int64(truncating: NSNumber(value: data.rentprice))
            entity.phone = data.phone
            entity.email = data.email
            entity.area = Int64(truncating: NSNumber(value: data.area))
            entity.rooms = Int64(truncating: NSNumber(value: data.rooms))
            entity.beds = Int64(truncating: NSNumber(value: data.beds))
            entity.bathrooms = Int64(truncating: NSNumber(value: data.bathrooms))
            entity.expires = data.expires
            entity.adresa = data.adresa
            entity.listingtype = data.listingtype
            entity.latitude = Double(truncating: NSNumber(value: data.latitude))
            entity.longitude = Double(truncating: NSNumber(value: data.longitude))

        }
        
//        }
        // saving all pending data at once
        
        do{
            try contex.save()
           
            print("success")
        }
        catch{
            print(error.localizedDescription)
        }
    }
    
    func fetchData(context: NSManagedObjectContext){
        
        let url = "https://... my api adress"
        
        var request = URLRequest(url: URL(string: url)!)
        request.addValue("swiftui2.0", forHTTPHeaderField: "field")
        
        let session = URLSession(configuration: .default)
        
        session.dataTask(with: request) { (data, res, _) in
            
            guard let jsonData = data else{return}
            
            // check for errors
            
            let response = res as! HTTPURLResponse
            // checking by status code
            
            if response.statusCode == 404 {
                print("error Api Errror")
            }
            
            // fetching JSON Data ..
            do {
                let listings = try JSONDecoder().decode([ListingModel].self, from: jsonData)
                
                DispatchQueue.main.async {
                  
                    self.listings = listings
                    
                    self.saveData(contex: context)
                }
            }
            catch {
                print(error.localizedDescription)
            }
        }
        .resume()
    }

  // try to extend the function 

func addListingToFavourites(favouritelisting:ListingModel) {
    addRecordToFavourites(favouritelisting:favouritelisting.title)
}
func isListingFavourite(favouritelisting:ListingModel) -> Bool {
    if let _ = fetchRecord(favouritelisting:favouritelisting.title) {
        return true
    }
    return false
}

func removeListingFromFavourites(favouritelisting:ListingModel) {
    removeRecordFromFavourites(favouritelisting:favouritelisting.title)
}

func toggleFavourite(favouritelisting:ListingModel) {
    if isListingFavourite(favouritelisting: favouritelisting) {
        removeListingFromFavourites(favouritelisting: favouritelisting)
    }
    else {
        addListingToFavourites(favouritelisting: favouritelisting)
    }
}
}

我还创建了扩展 JSONViewModel:

extension JSONViewModel {
    
    private func addRecordToFavourites(favouritelisting:String) {
        guard let context = managedContext else {return}
        if let record = fetchRecord(favouritelisting:favouritelisting) {
            print("record \(record) already exists")
            return
        }
        
        let entity = NSEntityDescription.entity(forEntityName: "Favourite",
                                                in: context)!
        let favourite = NSManagedObject(entity:entity, insertInto:context)
        favourite.setValue(favouritelisting, forKeyPath:"favouritelisting")
        
        do {
          try context.save()
        }
        catch let error as NSError {
          print("Could not save. \(error), \(error.userInfo)")
        }
        self.changed = true
    }
    
    private func fetchRecord(favouritelisting:String) -> Favourite? {
        guard let context = managedContext else {return nil}
        
        let request = NSFetchRequest<Favourite>(entityName: "Favourite")
        request.predicate = NSPredicate(format: "favouritelisting == %@", favouritelisting)
        
        if let users = try? context.fetch(request) {
            if users.count > 0 {
                return users[0]
            }
        }
        return nil
    }
    private func removeRecordFromFavourites(favouritelisting:String) {
        guard let context = managedContext else {return}
        if let record = fetchRecord(favouritelisting:favouritelisting) {
            context.delete(record)
            self.changed = true
        }
    }
}

::: 我走对了吗?我仍然不知道这是我应该做的!

请在下面找到最新列表查看

import SwiftUI

struct latestListings: View {
    @StateObject var jsonModel = JSONViewModel()
    @Environment(\.managedObjectContext) var context

    @FetchRequest(entity: Listing.entity(),
    sortDescriptors:
    [NSSortDescriptor(keyPath: \Listing.publishdate, ascending: false)])
    
    
    
    var results : FetchedResults<Listing>
    
    var textHeight: CGFloat = 60
    var fullWidth: CGFloat = UIScreen.main.bounds.width
    var cardWidthHalf: CGFloat = UIScreen.main.bounds.width / 2 + UIScreen.main.bounds.width / 3
    var spacing: CGFloat = 10
    var viewHeight: CGFloat = UIScreen.main.bounds.height / 2
    
    @State private var isError = false
    
    var body: some View {
        
        
        VStack(alignment: .leading, spacing: 20) {
            VStack(alignment: .leading, spacing: 10) {
            HStack {
               
                    HStack {
                        Text("Latest Listings")
                            .modifier(textSectionTitle())
                        Spacer()
                        NavigationLink (destination: AllListingsVertical()) {
                            ViewMoreButton()
                        }.buttonStyle(PlainButtonStyle())
                    }
                    .padding(.trailing, 20)
            }
            Divider()
                .modifier(dividerStyle())
               
            HStack {
                Image(systemName: "wand.and.stars.inverse")
                    .modifier(textSectionIcon())
                Text("Manualy download listings to device storage. Useful for offline use.")
                    .modifier(textSectionTagline())
            }
            }
        .padding(.top, 10)
        .padding(.leading, 20)
        .padding(.bottom, 0)
            
            
            ScrollView(.horizontal, showsIndicators: false, content: {
                
                
                
                HStack {
                    // checkin if core data exists
                    if results.isEmpty{
                        
                        if jsonModel.listings.isEmpty{
                            
                            HStack(alignment: .center) {
                                HStack(spacing: 10) {
                                    ProgressView()
                        
                                        .progressViewStyle(CircularProgressViewStyle(tint: Color("dinamicPillsGrass")))
                                        .scaleEffect(2, anchor: .center)
                                        // fetching data
                                        .onAppear(perform: {
                                            jsonModel.fetchData(context: context)
                                            
                                    })
                                }.frame(width: UIScreen.main.bounds.width)
                            }.modifier(cardHeight())
                            
                            // when array is clear indicator appears
                            // as result data is fetched again
                        }
                        else{
                            
                            HStack(spacing: 20) {
                                
                                
                                ForEach(jsonModel.listings,id: \.self){listing in
                                    NavigationLink (destination: CardDetailView(listing: listing)) {
                                        
                                        HStack {
                                            CardViewOrizontal(listing: listing)
                                        }
                                    }.buttonStyle(PlainButtonStyle())
                                    // display fetched Json Data..
                                }
                            }
                        }
                        
                    }
                    
                    else{
                        // results.prefix(?) unde ? cata articole sa arate
                       HStack(spacing: 20) {
                            ForEach(results.prefix(10)){listing in
                                NavigationLink (destination: CardDetailView(fetchedData: listing)) {
                                    
                                    HStack {
                                        
                                        CardViewOrizontal(fetchedData: listing)
                                    }
                                }.buttonStyle(PlainButtonStyle())
                            }
                        }
                        .padding(.trailing, 15)
                        .padding(.leading, 15)
                        
                        
                        
                        
                    }
                    // update finish
                    
                }.padding(.top, 10)
                .padding(.bottom, 10)
                   
                
            })
            
            VStack(alignment: .center) {
                
                Button(action: {

                    // clearing data in core data..

                    if Reachability.isConnectedToNetwork() {
                      //
                        do{
                            jsonModel.listings.removeAll()
                            results.forEach { (listing) in context.delete(listing) }
                            try context.save()
                        }
                        catch{
                            print(error.localizedDescription)
                        }
                        print("Network is connected")
                        self.isError = false
                       
                        
                    } else {

                        print("Network is not connected")
                        self.isError = true
                    }
                    
                    

                }, label: {
                    HStack(alignment: .center) {
                        Image(systemName: "icloud.and.arrow.down")
                            .modifier(textSectionIcon())
                        Text("Update")
                            .modifier(textSectionTagline())
                    }
                    .padding(5)
                    .padding(.trailing, 5)
                    .background(Color("blueLeading"))
                    .cornerRadius(20)
                    .modifier(shadowPills())
                }).alert(isPresented: $isError) {
                    Alert(title: Text("Network is not connected"),
                          message: Text("WiFi or Cellular not availible. You can still browse offline content!"),
                          dismissButton: .default(Text("OK")))
                }
            
            
            }.frame(width: fullWidth)
        }
        .padding(.top, 10)
        .padding(.bottom, 60)
        .frame(width: fullWidth)
        .background(LinearGradient(gradient: Gradient(colors: [Color("dinamicGray1"), Color("dinamicGray2")]), startPoint: .top, endPoint: .bottom))
        .cornerRadius(20)
        .padding(.top, -50)
        .modifier(shadowSection())
    }
}

和 CardDetailView

//
//  CardDetailView.swift
//  WebyCoreData
//
//  Created by Marius Geageac on 20.12.2020.
//

import SwiftUI
import KingfisherSwiftUI
import MapKit




struct CardDetailView: View {
  // noul liked
    var fullWidth: CGFloat = UIScreen.main.bounds.width
    var halfScreenH: CGFloat =  UIScreen.main.bounds.height / 2
    
    @ObservedObject var settingsVM = SettingsViewModel()
    
    @State private var isVisible = false

    var listing: ListingModel?
    var fetchedData: Listing?
    // Modifiers
    var cardWidth: CGFloat = UIScreen.main.bounds.width
    var imageWidth: CGFloat = UIScreen.main.bounds.width
    /// map
    var locationCoordinate: CLLocationCoordinate2D {
        CLLocationCoordinate2D(
            latitude: listing == nil ? fetchedData!.latitude : listing!.latitude,
            longitude: listing == nil ? fetchedData!.longitude : listing!.longitude)
    }
   
    let paddingPills: CGFloat = 5
    let textSizePills: CGFloat = 14
    
    var body: some View {
        
        
        ScrollView {
        VStack(alignment: .leading, spacing: 5) {
                        HStack {
                            Text(listing == nil ? fetchedData!.title! : listing!.title)
                                .modifier(textSectionTitle())
                        }
                        Divider()
                            .modifier(dividerStyle())
                        
                        HStack {
                            Image(systemName: "info.circle")
                                .modifier(textSectionIcon())
                            Text(listing == nil ? fetchedData!.tagline! : listing!.tagline)
                                .modifier(textSectionTagline())
                        }
                    }
                    .padding(.top, 10)
                    .padding(.leading, 20)
                    .padding(.bottom, 0)
                    if self.fetchedData!.isFavorite == false {
                    Button(action: {
                        fetchedData!.isFavorite.toggle()
                      }) {
                        Image(systemName: "heart.circle")
                            .modifier(textSectionIcon())
                    }.padding()
                    }
                    else {
                        Button(action: {
                            fetchedData!.isFavorite.toggle()
                          }) {
                            Image(systemName: "heart.circle.fill")
                                .modifier(textSectionIcon())
                        }.padding()
                    }
        
            }
                
        
        
    }
}

导入 SwiftUI

构造收藏夹:查看 {

@StateObject var jsonModel = JSONViewModel()

var cardWidth: CGFloat = UIScreen.main.bounds.width - 30
var fullWidth: CGFloat = UIScreen.main.bounds.width

// @StateObject var jsonModel = JSONViewModel() @Environment(.managedObjectContext) 变量上下文

// Fetching Data From Core Data..
@FetchRequest(entity: Listing.entity(), sortDescriptors:

// [NSSortDescriptor(keyPath: \Listing.publishdate, 升序: false)]) [NSSortDescriptor(keyPath: \Listing.publishdate, ascending: false),]predicate: NSPredicate(format: "isFavourite == %@", NSNumber(value: true)))

var results : FetchedResults<Listing>



var body: some View {

    
    ScrollView(.vertical) {

        VStack(alignment: .center) {
        VStack(alignment: .center) {

            LazyVStack(spacing: 20) {
                    ForEach(results){listing in
                        NavigationLink (destination: CardDetailView(fetchedData: listing)) {
                            VStack {
                                CardView(fetchedData: listing)
                            }.frame(width: UIScreen.main.bounds.width)
                             .modifier(cardHeight())
                        }.buttonStyle(PlainButtonStyle())
                    }
                }

        }
       
    }.padding(.top, 20)
    }
    .navigationBarTitleDisplayMode(.inline)
    .toolbar {
        ToolbarItem(placement: .principal) {
           
            allListingsTitlePill() // Title
        
        }
        ToolbarItem(placement: .navigationBarTrailing){
            HStack {
                Button(action: {
                }, label: {
                    Image(systemName: "heart.circle.fill")
                        .font(.system(size: 40, weight: .regular))
                })
            }
        }
    }
    
}

}

我个人会在您的列表实体中添加另一个名为 isFavourite 的 属性 并将其设置为布尔值。最初将其设置为 false。

然后当您执行提取请求时,您只能使用这样的谓词来显示收藏夹。

let predicateIsFavourite = NSPredicate(format: "isFavourite == %@", NSNumber(value: true))

并且在列表/ForEach 中,您显示它是否是收藏夹并使用按钮将其切换为收藏夹。 切换只会将 isFavourite 的值设置为 true(如果您想要一些代码,请告诉我,但看看您的问题,您似乎知道该怎么做)