Post

Replies

Boosts

Views

Activity

Reply to Swift - Map two elements from an array to a new array
hi, the expression allClosingValues.map { [$0.timeStamp! , $0.vooClose] } results in something that would have data looking something like [ [a date, a double], [a date, a double], [a date, a double] , ... ] i.e., where each element of the array is a two-element array of a date and a double, which has type [Any], and so the expression's result has type [[Any]]. however, you have defined closingValues as a dictionary of type [Date:Double], with keys being dates and values being doubles. that explains the error message. if you're happy with a result of 2-tuples, where the result is more like [ (a date, a double), (a date, a double), (a date, a double), ... ] then you could use closingValues = allClosingValues.map { ($0.timeStamp! , $0.vooClose) } where you define var closingValues: [ (Date, Double) ] to print out such a result, this should work: for closingValue in closingValues { print(closingValue.0, closingValue.1) } if you really do want closingValues to be a dictionary, let us know. hope that helps, DMG
Apr ’23
Reply to SwiftUI Core Data crash when deleting an object
hi, in many cases when you delete the Core Data object, SwiftUI may try to execute the body property of a view in which you reference the object as @ObservedObject. i recall checking the .isDeleted property and it did not always seem to return the right value ... but something that is true about the deleted object will be that all its attributes will be zeroed out. so, all optional properties will be nil, all numeric values will be 0, a date will be back in 1970 somewhere, all relationships will be nil, and so forth. if you have nil-coalesced all properties of the Core Data object, then everything should pretty much just work. for example: Text(myObject.name ?? "No Name") will not crash your app, but Text(myObject.name!) will certainly crash. i usually add an extension to a Core Data object so that every property is nil-coalesced. e.g., if i have a name_ attribute defined in the Core Data model, then i add var name: String { get { name_ ?? "No Name" } set { name_ = newValue } } as an extension to the entity, and now i can freely write myObject.name throughout SwiftUI views and not have to continually nil-coalesce everything when i use it. you might want to check out my "build and fail in public" ShoppingList16 app, which is where i found this problem back in iOS 13, and i have not had crashes with this strategy after finding out what was happening. there's plenty of commentary in the source code on this very point. hope that helps, DMG
Apr ’23
Reply to Core Data Plus CloudKit - Potential Issue / Question about Binary Data w/ External Storage
hi SpaceMan, the best reference i can find on this is in the developer documentation on Reading CloudKit records for Core Data. this documents how Core Data is encoded in CloudKit. in short, String, Binary Data, and Transformable attributes can be automatically converted to external assets during serialization. it looks like this happens when you begin approaching 1 MB, but there seems to be no hard-and-fast rule. however, you can certainly work with the CKRecords directly and determine whether data is in the record or farmed out to a CKAsset, as described in the article above: "When inspecting a CloudKit record directly, check the length of the original field’s value; if it is zero, look in the asset field." hope that helps, DMG
Apr ’23
Reply to Core Data Plus CloudKit - Potential Issue / Question about Binary Data w/ External Storage
hi, as long as you're OK with "I will never manipulate the CloudKit data directly," then i don't see what the problem is. i doubt that the NSPersistentCloudKitContainer designers ever intended for users to be interacting directly in code with CKRecords and making an end run around what NSPersistentCloudKitContainer does for you automatically. however, that aside, i do recall reading at one point that Core Data does not necessarily use external storage just because you checked "Allows External Storage." it could be the case that Core Data made such decisions about your data at some point; if so, the CloudKit representation of what NSPersistentCloudKitContainer does for you would probably respect that decision. hope that helps, DMG
Apr ’23
Reply to Update View when viewModel is modified
hi, each of TagViewPhone and WordView2 has its own WordViewModel. changing one does not change the other. you should create the WordViewModel as a @StateObject in MainView2, and make it available to the TagViewPhone and WordView2 subviews, either as a parameter (reference the model passed in as an @ObservedObject) or through the environment. should you still have updating problems ... my guess is that you will ... then you may want to explicitly precede any change to a Word or Tag by telling all associated objects (Tags associated with a Word, or Word associated a Tag) to initiate an objectWillChange.send() message. changing attributes of a Tag/Word at one end of a relationship is not seen as a change of any attributes on the other Word/Tag end of the relationship. hope that helps, DMG
Apr ’23
Reply to Type '()' cannot conform to 'view'
hi, what you list inside an HStack must be Views. the function viewModel.showCard() does not return a View (its signature is () -> Void). and your viewModel should not be in the business of creating Views anyway what you want is something like this for your higher-level View: ScrollView(.horizontal, showsIndicators: false){ ForEach(wallet.cards, id:\ .self) { card in CardView(card: card) .onTapGesture { viewModel.selectCard(card) } } } then add the function viewModel.selectCard to update the isSelected property of the cards it maintains in its wallet.cards array. hope that helps, DMG
Feb ’23
Reply to SwiftUI: reference types leaks memory when used in State property wrapper.
hi, how SwiftUI handles the initialization @State private var simple = SimpleClass() is not exactly what you expect; this SimpleClass instance is created for SwiftUI's internal use (i.e., you don't own it) and eventually space in the heap will be created for you that is a reference to it ... you are free to change that reference if you wish to something else, but SwiftUI still has ownership of what it created. when you replace what you think is the value of simple with a new instance of SimpleClass in .onAppear, you are updating the heap reference that you own to a second instance of a SimpleClass object; but SwiftUI still holds on to the SimpleClass object that it owns. your concern about "leaking memory" will eventually be taken care of by SwiftUI when ContentView goes away (remember, SwiftUI frequently discards and recreates View structs and thus does a lot of its own memory management). in your case, you only have one view at the main level and it is likely the view is never discarded; when the app quits, whatever memory SwiftUI owns will be cleaned up. so, consider the following code experiment: (1) it would help if you knew exactly when objects of type SimpleClass come and go, so i will add an id as well as an initializer. final class SimpleClass { let id = UUID() init() { print("initialize SimpleClass, id = \(id.uuidString.prefix(8))") } deinit { print("deinit SimpleClass, id = \(id.uuidString.prefix(8))") } } (2) suppose your ContentView is not the main view of the app. try this, where the main view is a list of one item, with a navigation link to open your view, which is now renamed to be LeakyView: struct ContentView: View { var body: some View { NavigationStack { List { NavigationLink(value: "LeakyView") { Text("Show LeakyView") } } .navigationDestination(for: String.self) {_ in LeakyView() } } } } struct LeakyView: View { @State private var simple = SimpleClass() init() { print("called init of LeakyView") } var body: some View { Text("Empty") .onAppear { print("on appear occurs") simple = .init() } } } (3) run the app. you'll see that when you navigate to and then back from LeakyView to the main ContentView, all SimpleClass instances will have been released. in my case, the output i received is (a) tap on navigation link. navigation occurs (yes, SwiftUI created a view struct, discarded it, then created a new one) initialize SimpleClass, id = 106EB234 called init of LeakyView deinit SimpleClass, id = 106EB234 initialize SimpleClass, id = 9646766A called init of LeakyView on appear occurs initialize SimpleClass, id = C9026337 (b) tap on Back button. even though it looks like the object with id 9646766A has been orphaned, SwiftUI will give it back when the view goes away. deinit SimpleClass, id = C9026337 deinit SimpleClass, id = 9646766A all is in order (!) hope that helps, DMG
Feb ’23
Reply to Core Data relationship counts not updating in view
hi pocket, whenever you make a change to a ToDo that could affect how a ToDoList is displayed in some view (in your case, the number of uncompleted todo items?), simply add the line toDo.toDoList?.objectWillChange.send() // .toDoList is the relationship to the associated toDoList that should trigger an update in any view that holds the associated ToDoList as an @ObservedObject. hope that helps, DMG
Dec ’22
Reply to How to save the ".onMove" rearrangement of the list in this code so it won't revert back to the original upon quit?
hi, i would first change the definition of your formulas array into a fixed dictionary to look up a navigation destination based on a string; so something like let formula: [ String : AnyView ] = [ "Unit Conversions" : AnyView(UnitConversion()), // and everything else ... ] and with this in place, your ContentView would have: @State private var names: [String] = [ "Unit Conversions", /* everything else */ ] // most other stuff as before, except change the ForEach to iterate over the names array ForEach(names) { name in NavigationLink (name, destination: formula[name]!) } this gets you to the point that you only need to save the array of strings for formula names. you will have to change the .onMove modifier now to move positions in the names array. now it's easy to save/restore the names array to retain order. Core Data seems like overkill for this (unless you already use Core Data in your app elsewhere), so i would suggest the simplest option: keep the array formula names in AppStorage and replace the @State property wrapper with @AppStorage. hope that helps, DMG
Dec ’22
Reply to SwiftUI with escaping closures?
hi lucid, i think it a good thing to provide actions to Buttons from outside; you probably do not want to define a Button that already knows exactly what it's supposed to do when tapped, since that makes it not all that reusable. as for syntax, your ChildView2 uses an explicit initializer, so you are required to use @escaping in the parameter list. your ChildView1 uses a default or synthesized initializer, which i'm guessing is in fact recognizing that the function being passed in is @escaping. hope that helps, DMG
Oct ’22
Reply to CloudKit - How to use Configurations to properly segregate public and private data
hi Chris, i did this separation of local and cloud configurations some time ago, and as i recall, the 2 problems that needs fixin' are: you cannot have a Core Data relationship between objects in the different configurations, so you have to implement any such relationship yourself; and you must designate that your cloud-based configuration attaches itself to the cloudkit container (while the local configuration does not). the second requires a setting in the Core Data .xcdatamedeld file: tap on a configuration and check "Used with CloudKit in the Data Model Inspector. the first is very cleverly, if not secretly, handled in CoreDataFetchedProperty in that there is a FetchRequest defined that makes it easy for objects in the local configuration to find associated objects in the cloud configuration by a UUID look-up (or it may be vice-versa, i can't remember right now). that is, instead of a one-to-one relationship from A (local) to B (cloud) being defined in the Core Data model, assume that all A and B objects have been assigned UUIDs and that each object has an attribute maybe called associate containing the other object's UUID. given an A, you find the associated B by doing a fetchRequest on all Bs to find the one that matches the UUID you saved in A.associate. you do the same to find any A associated with a given B. you can do either of these directly in code, maybe by adding a computing property as an extension to each; but in CoreDataFetchedProperty they pre-define such a Fetch directly in the Core Data model that can be called directly using an aggregate/computed property with a @fetch syntax. i'm sorry i can't recall the exact syntax, but examine the pre-define Fetch and the properties of the objects carefully to find the syntax for this. hope this helps, DMG
Oct ’22
Reply to Higher Order Function
hi, if you're willing to use the combinations function that's defined in the Algorithms package, this will work for you: import Algorithms print([1,2,3,4].combinations(ofCount: 2).filter({ $0[0] + $0[1] == 5 })) in the code above, combinations(ofCount : 2) gives all pairs, and then pull out all those pairs which add to 5. the output, by the way, is: [[1, 4], [2, 3]] if you don't want to use the combinations function, and you don't like doing your own iteration, then you could replace it with perhaps a recursive function. one such possibility would be: func allPairs(numbers: [Int]) -> [[Int]] { guard numbers.count >= 2 else { return [] } let firstElement = numbers[0] let remainingElements = numbers.dropFirst() let pairsWithFirstElement = remainingElements.map({ [firstElement, $0] }) let pairsWithoutFirstElement = allPairs(numbers: Array(remainingElements)) return pairsWithFirstElement + pairsWithoutFirstElement } so this code gives the same result as above: print(allPairs(numbers: [1,2,3,4]).filter({ $0[0] + $0[1] == 5 })) hope that's useful for you, DMG
Oct ’22
Reply to Can I use NSPersistentCloudKitContainer while at the same time adding extra fields to my CloudKit CKRecord?
hi, my guess is that you're playing with fire if you go behind NSPersistentCloudKitContainer's back (!) if you want to keep the notion of an ordered set for a relationship among two entities that is A <---->> B (many B entities associated with an A, but only one A entity is associated with a given B), then two options come to mind (and these can be handled directly on the Core Data side). first, add an integer attribute to B to indicate its relative position among all its sibling B objects. if A changes the order of the B objects, just rewrite the position of each (or as may as needed) associated B. second ... perhaps if each B object represents "lots of data" and you think it an expensive operation to rewrite many B objects when reordered ... then consider adding an intermediate entity C with one integer attribute (perhaps named position) and two relationships: one from C to A (many-to-one) and one from C to B (one-to-one), so that you have A <---->> C <----> B. in essence, each C object represents a single B object, but contains B's order for its associated A object. if you have an A object in hand, you then know the ordering of the associated B objects with let associatedCobjects = (A.c as? Set<C>) ?? [] // of type Set<C> let orderedBObjects = associatedCobjects.ordered(by: { $0.position }).map({ $0.b }) // of type [B] in the proper order rewriting the position attribute of C objects based on any intended re-ordering of the B object is fairly cheap (no B objects are being updated). finally, if your original relationship between A and B was many-to-many, i.e., A <<---->> B, this second technique continues to work. that is, you'll now have A <---->> C <<----> B. each C now represents an association of a single A with a single B, while indicating the relative position of B within some associated A object. (i'm not a real database guy, but i believe this is called a join). hope that helps, DMG
Sep ’22
Reply to App crashes after deleting list item from CoreData - no debug output
hi @ex- thanks for taking the time to post some extended code. three quick observations ... for CardEditView, it's interesting that such is created in an expression NavigationLink(destination: CourseEditView(isPreview: isPreview, course: course)), and by using an init() method, the CardEditView then builds its view model (which, by the way, i think you want to be a @StateObject, not an @ObservedObject). it appears that code allows for the optional properties of the course to be nil-coalesced into meaningful values. in contrast, for CardDetailsView, you're using an alternative syntax for creation inside a NavLink via CardDetailsView(viewModel: CardDetailsViewModel(manager: vm.persistenceManager, course: vm.course!)), which puts the view model into the view as an argument, rather than putting the course into the view as an argument and letting the view create its own view model. but here you're explicitly force-unwrapping the course? (and you mentioned this in the comment above.) we don't see and code for CardDetailsView and we don't know exactly how that works with the course through its own view model; and the same is true for CardLinkView that accepts an incoming course argument. as for recommendations in tracking down the problem, i'd certainly be suspicious of the force-unwrapped reference, but that may simply be a red herring (indeed, i think you'd first want to look at using an explicit init() for CardDetailsView that accepts an incoming course and that creates its own view model). the basic problem with core data deletions and SwiftUI is that SwiftUI may indeed try to access views that reference the deleted object ... but your views often expect to be accessed only with a valid object. in fact, the object may still exist for a short time (it is not itself nil), but it is a faulted Core Data object with all of its fields zeroed-out ... so its values are zero for any integer, false for any boolean, and nil for any optional. my fall-back: nil-coalesce everything. your original posting contained the expression Text("\(course.courseCards[0].cardHoles.count) holes "). if course is on its way to core data deletion, one wonders what the [0] array reference means. you might not need a full implementation of view models in your app. a simpler approach of referencing the incoming course argument as an @ObservedObject, properly nil-coalescing the properties of the course within the view, and using a number of functions within the view (rather than putting those in a separate view model) might be considered. finally, as long as we're talking view models, you may want CourseEditViewModel to subscribe to change notifications that come from its associated course and to relay them as a change of the view model itself to the associated view. this will save you a little grief in having to manually call the refresh() method when you change properties in a course. you can use Combine to do this (at least i think this is it): // in `CourseEditViewModel` import Combine var cancellables = Set<AnyCancellable>() course.objectWillChange.sink({ _ in self.objectWillChange.send() }).store(in: &cancellables) so, sorry, i may not have a silver bullet for you on this one, but i hope you'll find something useful above. DMG
Sep ’22