Post

Replies

Boosts

Views

Activity

Reply to SwiftUI + MVVM + async tasks in model
Using a class instead of a struct for the model allows me to use a DispatchQueue, but even worse the UI does not get updated at all. (The usage of DispatchQueue does not play any role in here.) I don't know whether you should use a struct or class for Model, but if you do use a class, then for the UI to update, there needs to be some connection between the Model changing and the ViewModel firing objectWillChange. That happens without any code if you have a @Published struct property, but with a class it needs something like this... (I didn't test run this code but it's close.) class ViewModel: ObservableObject { &#9;&#9;var model = Model() &#9;&#9;var sub: AnyCancellable? &#9;&#9;init() { &#9;&#9;&#9;&#9;sub = model.willChange.sink { self.objectWillChange.send() } &#9;&#9;} } class Model { &#9;&#9;var numberToDisplay: Int = 0 { &#9;&#9;&#9;&#9;willSet { willChange.send() } &#9;&#9;} &#9;&#9;var willChange = PassthroughSubject&lt;Void,Never&gt;() &#9;&#9;func increase() {&#9;&#9;&#9;&#9; &#9;&#9;&#9;&#9;numberToDisplay += 1 &#9;&#9;&#9;&#9;if numberToDisplay < 5 { &#9;&#9;&#9;&#9;&#9;&#9;DispatchQueue.global().asyncAfter(deadline: .now() + 1) { self.increase() }&#9;&#9;&#9;&#9; &#9;&#9;&#9;&#9;} &#9;&#9;} }
Jun ’20
Reply to Why does Publisher.multicast take a Subject?
It makes sense that that Publishers.Multicast uses the PassthroughSubject internally to implement its behavior, but that doesn't explain why the subject is exposed as an argument that you can pass in. Multicast could simply create it internally in its initializer. Taking it as an argument suggests there is some use case where you want to call send or otherwise do something with that subject, after you pass it in to multicast. I can't imagine such a use case. Can you think of an example?
Jun ’20
Reply to 2020 Forums Wishlist/Feature Request Thread
It would be good to have a few search operators like StackOverflow. Besides dates (which has already been mentioned) it's nice to search for things with or without answers. If you want to help others and answer questions, you can say, for example, "Show me questions about Swift in the last year with no accepted answer". Something like: [Swift] created:2019-2020 accepted:no
Jun ’20
Reply to Spurious pop and re-push during state changes
You wrote: "[objectID] certainly changes from run to run"I'm not seeing that. It seems to be completely consistent. I was using `objectID.uriRepresentation()` to implement `Identifiable`, and the docs for `uriRepresentation` say it is "archivable", which suggests to me that it doesn't change. (?) I'm using `NSPersistentCloudKitContainer`, if that matters.I think that what's getting me now is...ForEach(dataSource.fetchExercises(moc:moc)) { ex in NavigationLink(destination: ExerciseView(exercise: ex)) { ListItemView(exercise: ex) } }If that detail view (ExerciseView), changes the exercise in way that should _reorder_ the results of fetchExercises and the ForEach, then I make `dataSource` (which is an observable object) emit `objectWillChange`. As expected, SwiftUI re-calls `fetchExercises`. But sadly, it pops off the detail view (ExerciseView) and returns to this list view.I thought it should see that the exercise objects have the same IDs, so it would keep the ExerciseView "pushed".I too am interested to see the SwiftUI 2020 changes next week, but I can't wait until September to release my app, so I need to get this working with the current SwiftUI.
Jun ’20
Reply to Spurious pop and re-push during state changes
There's a lot of code. I'm trying to boil it down now, so I can post it.Of note: I'm getting the following warning from SwiftUI on the console. I set the breakpoint it advises, and I see that breakpoint is getting hit right before the my problem occurs.2020-06-14 11:24:37.906650-0500 MyAppName[11953:3649945] [TableView] Warning once only: UITableView was told to layout its visible cells and other contents without being in the view hierarchy (the table view or one of its superviews has not been added to a window). This may cause bugs by forcing views inside the table view to load and perform layout without accurate information (e.g. table view bounds, trait collection, layout margins, safe area insets, etc), and will also cause unnecessary performance overhead due to extra layout passes. Make a symbolic breakpoint at UITableViewAlertForLayoutOutsideViewHierarchy to catch this in the debugger and see what caused this to occur, so you can avoid this action altogether if possible, or defer it until the table view has been added to a window. Table view: &lt;_TtC7SwiftUIP33_BFB370BA5F1BADDC9D83021565761A4925UpdateCoalescingTableView: 0x102080200; baseClass = UITableView; frame = (0 0; 320 567.5); clipsToBounds = YES; autoresize = W+H; gestureRecognizers = &lt;NSArray: 0x280b3bb70&gt;; layer = &lt;CALayer: 0x2805fbae0&gt;; contentOffset: {0, 144.5}; contentSize: {320, 704}; adjustedContentInset: {64, 0, 0, 0}; dataSource: &lt;_TtGC7SwiftUIP10$1b8848e2419ListCoreCoordinatorGVS_20SystemListDataSourceOs5Never_GOS_19SelectionManagerBoxS2___: 0x101265b80&gt;&gt;When the breakpoint is hit, the entire stack (pasted below) is Apple's code, not mine. So it's hard to see if I'm causing this, or if it's a bug in SwiftUI.#00x0000000185947f5c in UITableViewAlertForLayoutOutsideViewHierarchy ()#10x0000000185946fb0 in -[UITableView _updateVisibleCellsNow:] ()#20x0000000185959b0c in -[UITableView _cellForRowAtIndexPath:usingPresentationValues:] ()#30x00000001859599ec in -[UITableView cellForRowAtIndexPath:] ()#40x00000001b851525c in ListCoreCoordinator.updateListContents(_:) ()#50x00000001b8514560 in ListCoreCoordinator.updateUITableView(_:to:) ()#60x00000001b8512ff0 in ListRepresentable.updateUIView(_:context:) ()#70x00000001b87d98f4 in PlatformViewRepresentableAdaptor.updateViewProvider(_:context:) ()#80x00000001b85894f0 in closure #1 in closure #1 in PlatformViewChild.update(context:) ()#90x00000001b86e8d28 in ViewRendererHost.performExternalUpdate(_:) ()#100x00000001b8588a18 in perform #1 &lt;A&gt;(work:) in closure #1 in PlatformViewChild.update(context:) ()#110x00000001b85878c8 in closure #1 in PlatformViewChild.update(context:) ()#120x00000001b85816c4 in PlatformViewChild.update(context:) ()#130x00000001b8589a40 in protocol witness for static UntypedAttribute._update(_:graph:attribute:) in conformance PlatformViewChild&lt;A&gt; ()#140x00000001acd0c998 in partial apply ()#150x00000001accf59fc in AG::Graph::UpdateStack::update() ()#160x00000001accf5eb4 in AG::Graph::update_attribute(unsigned int, bool) ()#170x00000001accfac30 in AG::Subgraph::update(unsigned int) ()#180x00000001b84531dc in ViewGraph.runTransaction(in:) ()#190x00000001b8452fa0 in closure #1 in ViewGraph.flushTransactions() ()#200x00000001b8452c44 in ViewGraph.flushTransactions() ()#210x00000001b8452d9c in closure #1 in closure #1 in ViewGraph.asyncTransaction&lt;A&gt;(_:mutation:style:) ()#220x00000001b86e89f8 in ViewRendererHost.updateViewGraph&lt;A&gt;(body:) ()#230x00000001b8452d68 in closure #1 in ViewGraph.asyncTransaction&lt;A&gt;(_:mutation:style:) ()#240x00000001b846d804 in thunk for @escaping @callee_guaranteed () -&gt; () ()#250x00000001b82f68b4 in static NSRunLoop.flushObservers() ()#260x00000001b82f682c in closure #1 in static NSRunLoop.addObserver(_:) ()#270x00000001b82f6948 in @objc closure #1 in static NSRunLoop.addObserver(_:) ()#280x000000018165a06c in __CFRUNLOOP_IS_CALLING_OUT_TO_AN_OBSERVER_CALLBACK_FUNCTION__ ()#290x0000000181654f60 in __CFRunLoopDoObservers ()#300x00000001816553dc in __CFRunLoopRun ()#310x0000000181654ce8 in CFRunLoopRunSpecific ()#320x000000018b79f38c in GSEventRunModal ()#330x0000000185783444 in UIApplicationMain ()
Jun ’20
Reply to Spurious pop and re-push during state changes
Ah, thanks. By "id declaration”, do you mean making my objects implement `Identifiable`?I do have some code that looks like your example, I the code below, the `FetchResultsHelp` is doing a Core Data query (NSFetchRequest), and the `exercises` are NSManagedObjects. A StackOverflow answer recommend using the `id: \.self` in the ForEach, but maybe that’s wrong.I just tried making the NSManagedObject (Exercise) implement `Identifiable` by using the `objectID: NSManagedObjectID`. That _changes_ the behavior, but it’s still screwy. Instead of the “B” popping and re-pushing. I now get an animation that looks like a second “B” pushing, followed by a pop back to “Root &gt; A”. (The “A” view is the list of objects.)var body: some View { ... FetchedResultsHelp(type: Exercise.self, predicate: makePredicate(), sortDescriptors: makeSortDescriptors()) { exercises in ForEach(exercises, id: \.self) { ex in NavigationLink(destination: self.exerciseView(for: ex)) { Text(ex.name ?? “-“) } } ... }I changed it to:ForEach(exercises) { ex in
Jun ’20
Reply to Do we need a local modificationDate field?
I think we’re confusing “write” operations with “sync” operations between replicas.Sorry if this is obvious, but maybe I need to explicitly say that my app has a local cache/replica DB and can work offline. I thought that was normal when using CloudKit, so I didn’t mention it. When an app is working like this, it is effectively using an optimistically replicated database, where each device is a replica. CloudKit is like a replica too, but a special one.Let’s say a user has 2 devices and both are offline. Then…1. They save something with iPhone on Tuesday.2. They save it again with iPad on Wednesday.3. iPad reconnects and writes (syncs) the change to CloudKit.4. iPhone reconnects…The bottom line is that I think it makes sense here to have #2 (the iPad change) win. But if the iPhone uploads its change in step #4 with a savePolicy of `.allKeys` like you mentioned, won’t it simply overwrite the record from the iPad?I think that #2 is the "last write" from the user’s perspective. I also think it is what "last write" typically means in the context of replicated databases. The other write ops, to CloudKit, in steps #3 and #4, are not really writes to the overall database - they are the replicas syncing up their data - and their timing should be irrelevant to which record wins.
Jun ’20