Post

Replies

Boosts

Views

Activity

SwiftData Predicate with "format:"
Hi, if you have an array of object IDs, with CoreData you can fetch all of the objects for those IDs at once with the right predicate. It would look something like this: let objectIDs: [NSManagedObjectID] = [id1,id2,id3,id4] let predicate = NSPredicate(format: "self in %@", objectIDs) Can something similar be created with a SwiftData predicate? For now I fetch all ids one by one and as you might guess, performance wise this is not great.
0
0
385
Feb ’24
SwiftData appending values to Query
Hi, I am trying to create somethings like an infinite scroll view with SwiftData but I am not able to figure out how to append elements to the @Query result. Let's say I have 1000 entries in my DB but I only want to load 40 at a time. If the user scrolls to the last element of the result I want to load the next batch of 40 entries. That is the code: import SwiftData import OSLog private let logger = Logger(subsystem: "SetsTab", category: "SetsTab") struct SetsTab: View { @Environment(\.modelContext) private var modelContext @Binding var searchText: String @Query private var sets: [SetModel] @State private var fetchLimit = 40 @State private var offset = 0 init(searchText: Binding<String>) { _searchText = searchText if searchText.wrappedValue.isEmpty { var fetchDesc = FetchDescriptor<SetModel>() fetchDesc.fetchLimit = fetchLimit fetchDesc.fetchOffset = offset _sets = Query(fetchDesc) } else { let term = searchText.wrappedValue let setCodePred = #Predicate<SetModel> { $0.name.contains(term) } var fetchDesc = FetchDescriptor<SetModel>(predicate: setCodePred) fetchDesc.fetchLimit = fetchLimit fetchDesc.fetchOffset = offset _sets = Query(fetchDesc) } } var body: some View { ScrollView{ LazyVStack{ ForEach(sets){actSet in VStack{ Text("Set: \(actSet.name)") } .onAppear { self.loadMoreContentIfNeeded(currentItem: actSet) } } }} .onAppear { for i in 1...1000 { let set = SetModel(name: "Set \(i)") modelContext.insert(set) } try! modelContext.save() } } func loadMoreContentIfNeeded(currentItem item: SetModel?) { if sets.endIndex < self.fetchLimit { return } guard let item = item else { loadMoreContent() return } let thresholdIndex = sets.index(sets.endIndex, offsetBy: -5) if sets.firstIndex(where: { $0.id == item.id }) == thresholdIndex { loadMoreContent() } } private func loadMoreContent() { self.offset += self.fetchLimit loadMoreSets() } private func loadMoreSets(){ logger.info("Loading more Sets with offset \(self.offset)") let term = searchText let setCodePred = #Predicate<SetModel> { $0.name.contains(term) } do{ var fetchDesc = FetchDescriptor<SetModel>(predicate: setCodePred) fetchDesc.fetchLimit = self.fetchLimit fetchDesc.fetchOffset = self.offset var newBatchOfSets = try modelContext.fetch(fetchDesc) logger.info("new Batch of Sets count: \(newBatchOfSets.count)") // sets.append(contentsOf: newBatchOfSets) } catch{ logger.error("\(error.localizedDescription)") } } } The logic for registering when to load the new batch works so loadMoreSets() is called correctly but to actually fetch the new batch and appending to the already fetched data is missing. I commented this part: sets.append(contentsOf: newBatchOfSets) because it is giving me the error: Cannot use mutating member on immutable value: 'sets' is a get-only property so basically telling me that the @Query var sets can only be set inside the initializer. So how can we realise something like an infinite scroll view with SwiftData? I feel that this is an valid and for me mandatory use case when working with thousands of db entries in order to be performant. Has anybody realised this in a different way? Best regards, Sven
0
0
758
Aug ’23
SwiftData with two Stores
Hi, has anybody managed to get two sqlite stores working? If I define the stores with a configuration for each it seems like that only the first configuration and and therefore the store is recognised. This is how I define the configuration and container: import SwiftData @main struct SwiftDataTestApp: App { var modelContainer: ModelContainer init() { let fullSchema = Schema([ SetModel.self, NewsModel.self ]) let setConfiguration = ModelConfiguration( "setconfig", schema: Schema([SetModel.self]), url: FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!.appendingPathComponent("Sets.sqlite"), readOnly: false) let newsConfiguration = ModelConfiguration( "newsconfig", schema: Schema([NewsModel.self]), url: FileManager.default.urls(for: .cachesDirectory, in: .userDomainMask).first!.appendingPathComponent("News.sqlite"), readOnly: false) modelContainer = try! ModelContainer(for: fullSchema, configurations: [setConfiguration,newsConfiguration]) } var body: some Scene { WindowGroup { ContentView() } .modelContainer(modelContainer) } } ContentView is just a basic TabView with a tab for news and a tab for sets. If I run the program this way the sets tab is shown correctly but switching to News fails. If I change the order of the configurations and write the one for news first like this: modelContainer = try! ModelContainer(for: fullSchema, configurations: [newsConfiguration, setConfiguration]) then the news tab is shown correctly and switching to sets tab fails. NewsModel and SetModel only differ in the class name Import Foundation import SwiftData @Model public class NewsModel{ public var name: String init(name: String) { self.name = name } } Also the tab content differs only for referencing the respecting model and the name: import SwiftData struct NewsTab: View { @Query private var news: [NewsModel] @Environment(\.modelContext) private var modelContext var body: some View { ScrollView{ LazyVStack{ ForEach(news){actNews in Text("Hello, News \(actNews.name)") } } .onAppear { let news = NewsModel(name: "News from \(Date())") modelContext.insert(news) try! modelContext.save() } } } } The error message is "NSFetchRequest could not locate an NSEntityDescription for entity name 'NewsModel'" (and SetsModel respectively when change the order of the configuration) Do I explicitly need to tell the modelContext which configuration it should use or is this done automatically? I'm a little lost here and hope someone can help me. Best regards, Sven
5
0
2.6k
Aug ’23