Post

Replies

Boosts

Views

Activity

Reply to What is the proper time to deploy CloudKit Schema to Production?
hi, my suggested answers are: (1) appropriate time to deploy? see (2). (2) Should it be done before beta testing via TestFlight? yes, you must move to production before users can test via TestFlight. (3) does that mean I won't be able to reset the schema if testers discover critical bugs related to the data model? well, this one's more nuanced. as you know, once you are in production, that CloudKit schema only allows additive changes (add an attribute to an existing entity, or add a new entity). if there are problems in the database schema, there's no reason you cannot make any necessary additive changes, and then simply ignore values of entities and attributes that are no longer needed. for example, if you want to change a relationship from one-to-one to one-to-many, add a new relationship between the two entities; read the existing one-to-one relationship's value; and set the new one-to-many relationship. if you really want to keep the CloudKit container trimmed, you could take the time to remove all objects of a given entity type (the entity type would still be in the CloudKit schema; there just would be no objects of that type), and you could set attributes no longer in use in a given entity to some small space-consuming value (e.g., "" for a String, or nil for a Data). nullify any relationships no longer in use. i would suggest that you consider an explicit version number attribute to all the entities in your schema, with default value 1 (or whatever is your current core data model version). if you do make (additive) changes in the future, you can update the version number; and you can later test this in code to know what record type you're working with and what fields it has by first checking its version number, and then use appropriate logic to work with the object. the best reference on this might be to start with David Stites's WWDC2022 video named "Evolve Your Core Data Schema". the last third of this covers the CloudKit issues. finally, if you really do want to reconfigure the data model once it's in production, could you possibly define the new version of your core data model, which takes care of the local data store ... but then assign a new CloudKit container to your app? i don't know the answer to this one, but perhaps you won't need go there. hope that helps, DMG
Mar ’24
Reply to SwiftData synced to iCloud with one-to-many relationship between 2 models results in a crash when each model contains a reference to the other
hi, my guess on this is that you never inserted either model into the modelContext; linking them together either by appending the film to the director's films, or setting the film's director reference would cause an issue. as to which you should do in linking objects together, it used to be simple in Core Data: you could do either (and the other direction would bee handled automatically). however, for at least the early betas and the official release of Xcode 15 and iOS 17, it appeared to me that you were better off writing newDirector.films?.append(film) than film.director = newDirector, because the former was more likely to work with observation. (that could be fixed by now). finally, setting self.director = director in a Film's init() is probably a bad thing to do; i'd want to insert a new Film into the modelContext before setting the director reference. hope that helps, DMG
Mar ’24
Reply to Query SwiftData model for max value of an attribute
hi, i don't think that SwiftData will do this automatically for you (there are aggregate functions available in Core Data for this, i think, but they are not there yet in SwiftData -- if they are, i'd like to know about that). in the meantime, yes, just go ahead and fetch all records, but limit what's fetched to only the integer property you're interested in. for example, i have an app concerning a model type Location and a model property position: Int and i want to find the maximum position value among all Locations. private func lastLocationPosition() -> Int? { var fetchDescriptor = FetchDescriptor<Location>() fetchDescriptor.propertiesToFetch = [\.position] // only the positions values come back fully realized do { let locations = try fetch<Location>(fetchDescriptor) return locations.map({ $0.position }).max() } catch let error { print("*** cannot fetch locations: \(error.localizedDescription)") return nil } } it would be easy to slightly modify this function to return a Location? that realizes this maximum value -- compute the maximum and just find the first object among the fetched locations where the maximum is achieved, returning it instead of an Int?). (all remaining properties of the Location returned will be faulted, so you can use the model object with no problem: any property values you want will be faulted in when needed.) hope that helps, DMG
Dec ’23
Reply to Help with trailing closure errors
hi, the error message about "parameter of type 'FormStyleConfiguration'" does not give the precise cause of the issue. in short, error messages from the SwiftUI compiler can be confusing and simply not point you to the real issues, especially when you have lots of code defining the view where the nesting levels go pretty deep. in this case, commenting out the first Section will better point you to the problem that exists in the second section. indeed, the second Section in your DetailView starts with ForEach($clients.enclosures) { .... but you have not defined an enclosures property on Client, which should be a @Relationship, and Enclosure might want to have an inverse @Relationship property back to Client. hope that helps, DMG
Nov ’23
Reply to #Predicate and computed properties
spent the weekend playing with something similar ... and i think that two things that don't work with #predicates are (1) you can't use computed properties (makes sense if you think about it, since these are not key paths referencing properties of a model as stored on disk) and (2) you can't query using relationships (this is something i expect will get fixed). //DMG
Oct ’23
Reply to ToolbarItemGroup(placement: .keyboard) is not showed with Sheet
hi, i'm getting something similar in behavior related to toolbar items for the keyboard, although it's not exactly the same. (i am using Xcode 15 official release, not a beta or the RC.) in my case, i have a view that can be shown either by navigating to it, or opening it in a sheet (wrapped inside a navigationStack). when the view is navigated to, the button i place on the keyboard (to dismiss the keyboard) works as expected. when the view is opened in a sheet (view is wrapped inside a navigationStack), the button does indeed appear, but it is disabled. i'll try to put together a minimal example to file a feedback, but i'll be curious to see if there are more reports. DMG
Sep ’23
Reply to For loop iterates an array in Swift Playground in random order
hi, your iteration is over the elements of a dictionary that has three key-value pairs: for (key ‚numbers) in interestingNumbers { // interestingNumbers is not an array dictionaries are not ordered; so one time you may get Fibonacci, Prime, Square ... another time it may come out in a different order. this is exactly the behavior that you're seeing. hope that helps, DMG
Sep ’23
Reply to SwiftUI, FetchedResults, and uninitliazed?
hi, basically, you cannot use the properties of self on the right side of the assignments in the init() to initialize other properties of self ... it's a logical problem because self does not fully exist until all its properties are set. right now, you attempt to set self.predicate using the value of self.home; and you try to set _rooms using the values of self.sortBy and self.predicate. use a local variable or two, and rely on the incoming parameter as well: init(home: Home) { self.home = home let descriptor = SortDescriptor<Room>(\.name) _sortBy = State(initialValue: descriptor) let myPredicate = NSPredicate(format: "%K = %@", "home", home) // note use of incoming parameter home, not self.home self.predicate = myPredicate _rooms = FetchRequest<Room>(sortDescriptors: [descriptor], predicate: myPredicate) // note use of local variables here } hope that helps, DMG FWIW: i am curious as to why sortBy is marked as a @State property. this gives you the ability to change it, yes, but you'd also want to then also update the FetchRequest that relies on it.
Sep ’23
Reply to List with subsections and deletable Elements.
hi, when the delete function is called, the indices handed to the delete function are relative to the array indices in the section where the .onDelete modifier is attached ... for example, splitArray(schueler)[3] has chunkSize (or possibly fewer) elements with indices in the range 0 ... chunkSize - 1 ; yet your 'delete function removes elements in the main schueler array using those indices directly, without regard to which section triggered the deletion call. if you can pass the section/group number information along to your delete function, you can first adjust the incoming indices by section. for example, in the third group with a chunkSize of 7, adjust the incoming indices 0 ... 6 to instead be 14 ... 20, and then delete from the main schueler array using the adjusted indices. first, make a simple syntax modification to the .onDelete invocations in each section, so that your delete function will also receive section/group information. for example, in the third section: Section("Group 3") { ForEach(splitArray(schueler)[2], id: \.self) { SchuelerName in Text(SchuelerName) } .onDelete { indexSet in delete(indexSet: indexSet, inGroup: 3) } } update your delete function's signature to handle a second parameter, and begin by adjusting the incoming indices accordingly to determine matching indices in the main schueler array. then do the appropriate deletions. func indexSet in delete(indexSet: IndexSet, inGroup group: Int) { // this is your code to write ... i would think indices in the indexSet would have to be // offset by (group - 1) * chunkSize first before doing any deletions. } hope that helps, DMG
Sep ’23
Reply to SwiftData - What is Best Practice for returning an object from a sheet
hi Richard, i'll answer this from the Core Data perspective, where a list is driven with a @FetchRequest. from all that i've read, it should work the same with SwiftData, where a list is driven by @Query. my simple answer is: do nothing. in your "Add New Object view", the outcomes are: Cancel = dismiss and do nothing. Save = Create and save a new object to Core Data (SwiftData) and then dismiss. but do nothing else! the @FetchRequest (@Query) in your list view will pick up that a new object has been added, and redraw the list. if your list view wanted to use what was the new object created in your list view, then i would have the "Add New Object view" take a closure as a parameter with signature (NewObject) -> Void that is defined in your list view, and have the save routine invoke that closure. hope that helps, DMG
Aug ’23
Reply to SwiftData Aggregation
hi, if you only need to count people within a certain age range, provide a predicate and use .fetchCount, rather than using .fetch and counting what comes back. no records are pulled for this and the operation should be pretty speedy. if you need to average out the ages of all people, you can specify the .propertiesToFetch in the FetchDescriptor to return only the age property values (not whole records) and then add and divide. at least that's what the documentation suggests right now (i am looking at beta 7, and Core Data already supports these). hope that helps, DMG
Aug ’23