Post

Replies

Boosts

Views

Activity

What Is The Recommended View Hierarchy for SwiftData Many-Many Relationships?
I am building an app where tasks can be shown on multiple selected days. The user will select a day and the available tasks should be shown. I am struggling to build a view hierarchy where the visible tasks will update when added to the currently shown day. A simplified model looks like the below: @Model final class Day { private(set) var dayID: UUID = UUID() var show: [Item]? } @Model final class Item { private(set) var itemID: UUID = UUID() @Relationship(inverse: \Day.show) var showDays: [Day]? } I have a view representing a day, and I would like it to display a list of associated tasks below. The view doesn't update if I list a day's tasks directly, e.g. List(day.show) {} I get a compile error if I build a sub view that queries Item using the day's show array: let show = day.show ?? [] let predicate = #Predicate<Item> { item in show.contains(where: { $0.itemID == item.itemID }) } I get runtime error if I flip the predicate: let dayID = day.dayID let predicate: Predicate<Item> = #Predicate<Item> { item in item.showDays.flatMap { $0.contains(where: { $0.dayID == dayID }) } ?? false } I'm at a loss of how to approach this, other that just forcing the whole view hierarchy to update every time a make a change. Is there a recommended approach to this situation please?
1
0
158
Oct ’24
What is the best SwiftData approach to replicate NSManagedObject's willSave?
When using Core Data I would override willSave on NSManagedObject to compute alastModified value on a model object. This allowed one simple method to check changed values and set a date if necessary. It is possible set lastModified in SwiftData, but the approaches I have found all have drawbacks when compared to the previous approach. Hide saved model properties behind transient versions private var textSaved: String = "" var text: String { get { textSaved } set { textSaved = newValue lastModified = .now } } I could hide every property that should update the lastModified behind a computed value, but this requires additional code for each new property and obfuscates the model definition. Update all properties through an update function func update<T>(keyPath: ReferenceWritableKeyPath<Player, T>, to value: T) Paul Hudson notes a workaround where any changes are made to the model through an update function that takes a keyPath. This will add complexity to every view that wants to modify model properties, and also leaves those properties open to change through other approaches. Use ModelContext.willSave ModelContext sends a notification when it is about to save. This could be caught in .onReceive in a view or perhaps in some model-holding singleton, then ALL changes queried and dealt with accordingly. Perhaps the best approach here would to add willSave to all model objects so code external to the model isn't doing the lastModified logic. This last solution feels like the best way forward that I know (ideally avoiding any .onReceive code in views). Should I prefer another solution or are there better ones I have missed?
1
0
235
Sep ’24
NavigationSplitView TabView Toolbar Clash
I would like to create a master-detail view inside the Settings screen for my Mac app. I am trying to use a NavigationSplitView nested inside the top-level Settings TabView. (Reduced code below). However, the sidebar of the NavigationSplitView interferes with the TabView - the TabView appears to be in the detail of the NavigationSplitView even though it is its parent. I have seen others having the reverse issues with TabViews in NavigationSplitViews. Is this a known bug please? struct SettingsView: View { var body: some View { TabView { GeneralSettingsView() .tabItem { Label("General", systemImage: "gear") } .tag(SettingsSection.general) TypesView() .tabItem { Label("Types", systemImage: "star") } .tag(SettingsSection.types) } .frame(width: 375, height: 150) } } struct TypesView: View { @Environment(\.modelContext) private var modelContext @Query private var types: [ItemType] @State private var selectedType: ItemType? var body: some View { NavigationSplitView { List { ForEach(types) { type in NavigationLink { TypeView(type: type) } label: { Text(type.name) } } } } detail: { Text("Select a Type") } } }
4
1
673
Jul ’24
NSItemProvider.loadFileRepresentation Error when sharing from Photos
When trying to copy photos from the Photos app using my Share Extension I am regularly getting an error: Error copying file type public.jpeg. Error: Error Domain=NSItemProviderErrorDomain Code=-1000 "Cannot load representation of type public.jpeg" UserInfo={NSLocalizedDescription=Cannot load representation of type public.jpeg, NSUnderlyingError=0x117f16640 {Error Domain=NSItemProviderErrorDomain Code=-1 "Cannot copy file at URL ...[].jpg., NSUnderlyingError=0x117f45ce0 {Error Domain=NSItemProviderErrorDomain Code=-1 "Cannot create a temporary file. Error: Undefined error: 0" UserInfo={NSLocalizedDescription=Cannot create a temporary file. Error: Undefined error: 0}}}}} I have seen this error elsewhere on the forums linked to PHPickerViewController and bug 64630315. Bug 64630315 is claimed fixed on iOS, but might it still be an issue on macOS? My Share Extension works correctly on iOS. On macOS I see this error when trying to share photos that I didn't take, but have been added to my library manually, via WhatsApp for example. Everything works as expected when sharing Live or Portrait images taken by me. I am trying to share as jpg. Photos that fail on macOS share as expected on iPhone from the same library.
0
0
993
Aug ’22
Are CKRecords Guaranteed to be Added to an NSPersistentCloudKitContainer Store in creationDate order?
I am trying to deduplicate data created by NSPersistentCloudKitContainer in my app. I have a universal app, with Share Extensions on both macOS and iOS. On each platform I share a store between the app and the extension with an App Group. The app and the extensions are both configured to sync to CloudKit. (This means local sharing is handled when offline, and a remote share extension will sync to CloudKit work when the main app is closed) This configuration is causing duplicates to be generated. I believe this is because when the macOS app is open, both it and the macOS share extension will try and (almost simultaneously) sync a newly shared object, resulting in two copies in CloudKit. On the macOS app, I can look through the persistent history and see the insertion 'author'. The first insertion is made by the extension "macOSShareExtension", the second is made by "NSCloudKitMirroringDelegate.import". I could easily make a choice to delete the second object. However, at the same time, on the iOS app, I will get two insertions, both with the author "NSCloudKitMirroringDelegate.import". I don't want to deduplicate only on the macOS app in this case. That would mean the the iOS app has duplicate objects until the deduplication propagates. If I use CKRecord's creationDate to keep the first syncd Object, can I guarantee that if one Object has an associated CKRecord and the other doesn't, the one with out will subsequently gain a record with a later creationDate? Should I be taking a different approach? Thank you.
2
0
726
Jul ’22
Does NSImage not conform to _ObjectiveCBridgeable?
I want to check the contents of an NSItemProvider to validate a drop action in SwiftUI on macOS. I use the code: let urlProviders = info.itemProviders(for: [.url]) if urlProviders.filter({ $0.canLoadObject(ofClass: NSImage.self) }).some { return true } But I get the compilation error: Instance method 'canLoadObject(ofClass:)' requires that 'NSImage' conform to '_ObjectiveCBridgeable' Is this a bug? The same code works with UIImage when compiling for iOS. Xcode 13.4
2
1
838
May ’22
LPMetadataProvider never returns in SFSafariExtensionHandler
I am creating a Safari App Extension for users to save web links and quotes to my app. I would like to cache web page metadata when links are saved by the extension. I am using a context menu command to capture information with the code below. override func contextMenuItemSelected( withCommand command: String, in page: SFSafariPage, userInfo: [String : Any]? = nil) { switch command { case "sendToApp": Task { guard let properties = await page.properties(), let pageURL = properties.url else { return }                 let provider = LPMetadataProvider()                 provider.timeout = 0.01                 os_log("*** Starting metadata call ***")                 let metadata = try? await provider.startFetchingMetadata(for: pageURL)                 os_log("*** Continued past metadata call ***") // ...             }         } I get the log: *** Starting metadata call *** LPMetadataProvider<1>: start fetching for URL ...but I am never seeing the log "*** Continued past metadata call ***" I wonder if the task is being killed for some reason? I thought maybe async code was an issue in SFSafariExtensionHandler, but the first await call in the guard passes successfully. I thought that the default timeout of 30s on LPMetadataProvider may be too great, but it still fails with a tiny timeout of 0.01s. I have added com.apple.security.network.client to the entitlements of the extension. Is there something I am missing please?
1
0
1.5k
Apr ’22
How Do I Have Transparent NSTableView/NSOutlineView Floating Group Rows in Big Sur
Hello My application uses NSOutlineViews with a floating group row as the title. In previous versions of macOS I have had a semi-transparent background to my heading, so scrolling rows can be seen underneath. In Big Sur there is a solid background to my floating row, and a thin border drawn on the bottom edge. I can see no way of stopping this background being rendered! I have a custom NSOutlineView with all drawing overridden: override func draw(_ dirtyRect: NSRect) {} I have a custom NSTableRowView with the same override. If I set the NSTableRowView that is floating to hidden, the background and border are still visible. The parent view of the floating row (a subview of NSScrollView) has the same dimensions as the whole scroll view, so I suspect the background is being drawn into this view, but by what!? Any ideas welcome please.
0
0
611
Dec ’20
Extra Incorrect KVO Insertion on NSOrderedSet When Saving Child NSManagedObjectContext
I have an NSManagedObject with an NSOrderedSet to-many relationship named 'children'. I am observing changes to this set using the following:addObserver(self, forKeyPath: #keyPath(children), options: [], context: nil)This can be called only once in the initialisation of a lazy property on the NSManagedObject. Any new relations are added in a child NSManagedObjectContext, and then saved down to my main context. When I set the relationship, I use the following code, only setting the 'many' side of the relationship from the 'one':superpoint.insertIntoChildren(self, at: 0)insertIntoSubpointChildren is an autogenerated accessor.In the child NSManagedObjectContext I see a single correct call to observeValue(forKeyPath..., that gives me the correct insertion index on children, when the above insertion code is called.However, when I save to the parent NSManagedObjectContext I am seeing two calls to observeValue(forKeyPath... on the same object. The first call is for an insertion at the end of the NSOrderedSet. The second call is for an insertion at the correct position.When evaluating the NSOrderedSet in each observation call, the new object is indeed at the position described - it is on the end of the set on the first call, and then moved to the correct position on the second call. However, I get no call to say that it was removed from the end position.I am not sure if I have set something up incorrectly here. The many objects in my one-many relationship will be unique, so I could write the code to work around this, but that feels wrong.I have replicated this issue in a simple project here: Github: /GilesHammond/KVO-Core-Data-Extra/Run in debug and select "ADD CHILD" multiple times from the app Main Menu. Observe the debug output showing the extra erroneous observation on the main NSManagedObjectContext.Any thoughts on what I might be doing wrong?
0
0
506
Dec ’19