Post

Replies

Boosts

Views

Activity

Reply to iOS autodelete Keychain items after uninstall?
Still the case with iOS 17.5 as of June 2024. The keychain is definitely preserved even after app uninstall/reinstall. Quinn's suggestion about implementing some kind of entanglement (or even stembermichal's suggestion of a Bool flag in UserDefaults) appears necessary. Basically you have to implement your own test to determine if the keychain item is a holdover from a previous install.
Jun ’24
Reply to SwiftUI and vars outside the SwiftUI world
@Polyphonic I guess my perception of the @EnvironmentObject (or @Environment in iOS 17) as the place where the app's data should be persisted is not quite right. It does seem telling that in Apple's article (https://developer.apple.com/documentation/swiftui/migrating-from-the-observable-object-protocol-to-the-observable-macro) about migrating to the Observation frameworks, the @Environment is only used in structs and not in classes. In your example of the pets app, is this the basic data flow: Data starts in long-term disk storage (either SQLite/Core Data or UserDefaults). On app startup, any data needed during app running is copied into the AppDataModel class. When a view opens, any data needed for display is copied into the view's XYZViewModel class, which only exists while XYZ view exists. If any changes are made to data during that time, that changes are migrated back to the AppDataModel class (and from there to SQLite/Core Data or UserDefaults). It makes me wonder if you really need the view model class, when you could just use@State vars for that view. I have read that ViewModel classes make for easier unit tests, which we do use in our app, and which makes sense I suppose since you can instantiate the class independent of the view for testing.
Dec ’23
Reply to SwiftUI and vars outside the SwiftUI world
@Polyphonic This is great stuff Polyphonic, thank you! Data Model vs View Model makes a lot of sense in particular. Do you think, conceptually, this could be seen as the "SwiftUI world" (aka what is handled by the View Model), and the "non-SwiftUI world" (aka the Data Model)? To me it seemed like that causes 2 sources of truth, like we have now...but maybe that is normal? As in, the View Model only manages the data as long as the view (the UI world) is alive, but once it disappears and is done, then its data must be transferred over to the Data Model? If that's the case, it still makes me wonder how a plain vanilla class like DoNetworkStuff has access to that data. I see what you're saying that starting the app with an AppViewModel as an .environmentObject is the way to persist the whole app's data in a single, non-replicated class, but it still seems like SwiftUI isn't really designed for passing that data to plain classes. To quote this StackOverflow comment, https://stackoverflow.com/a/65370757/1359088, A possible approach is to make it shared (and don't use @EnvironmentObject anywhere outside SwiftUI view - it is not designed for that) I found it interesting that he/she said the @EnvironmentObject isn't designed for being used outside the view. His/her answer is the same as your suggested solution, which is to create a shared instance of the ObservableObject, which serves both worlds: In the SwiftUI world, it uses its @Published vars to publish updates to views that need to display the appViewModel's values as they change; In the non-SwiftUI world, it makes itself available via the appViewModel.shared instance, which is where DoNetworkStuff should go to fetch the devId. Why is it that plain classes like DoNetworkStuff can't see an environmentObject? Why wasn't that made a feature? Why must you "bridge the gap" between the SwiftUI ObservableObject and this plain class by making the shared instance? Or, maybe you could make all classes like DoNetworkStuff "opt-in" to the SwiftUI world by importing SwiftUI and making them conform to the ObservableObject protocol--in which case they WOULD be able to see the environmentObject? Do you think that's a better solution, is to make all classes operate within the SwiftUI world, or is it better to leave them as plain classes and bring the data over to them via the shared instance?
Dec ’23
Reply to SwiftUI Navigation Bar appears behind Status Bar
I don't remember the exact fix, but based on my comments listed here, this seemed to be the fix: var body: some View { // You must have the TabView and the NavigationView at the top of the hierarchy, or you'll have an // issue of the navigation bar's breadcrumb and buttons such as "+" appearing behind the status bar ( // ie, then clock and date). I had a workaround hack where I would toggle the status bar on and off to // cause the navigation bar to "walkdown" to its correct location, keeping the hierarchy this way // eliminates it altogether. TabView(selection: $tabSelection) { NavigationView { GeometryReader { geomRdr in ZStack { VStack(spacing: 0) {
Dec ’23
Reply to Network.Framework vs URLSession in Background Task (BGTask)
Thank you so much for that info! That helped a ton. What steered me wrong was that the NWConnection would never stay connected once the app gets backgrounded, and even when I tried using it during the BGTask, it wasn't working. However, when I would start a new NWConnection with the NWListener, it did work, proving that what you said about the network being fully available during the BGTask, as long as requiresNetworkConnectivity was set to true like you said. Is it a better strategy, then, to consider the NWConnection as short-lived, rather than trying to keep it alive? That is, should I use the NWConnection to connect, do the necessary business, then close it, and start a new one the next night? These tasks will be occurring once each night. I'm also curious what you meant about the URLSession being special--did that mean it's given extra permissions or capabilities of some kind?
Nov ’23
Reply to Network.Framework vs URLSession in Background Task (BGTask)
Hi Quinn! The task will definitely be running from a BGTask at night, which means the app will be suspended (and therefore also backgrounded). Am I correct to assume that URLSession is the only way to communicate to the "outside world", so to speak? Because at that point any PeerConnections established with the Network framework will probably not be usable? We're trying to determine if the Network framework is a viable way for the app to fetch data using a BGTask, or if we will need to set up a web service for the app to communicate with. From my quick experiments, it seems like a suspended app running a BGTask can only use a URLSession at that point. It's interesting that you mentioned that URLSession is special--is that because it's given networking allowances that other frameworks (like Network) don't have?
Nov ’23
Reply to Network.Framework vs URLSession in Background Task (BGTask)
UPDATE: If I run a web server on another iPad using Telegraph as described here, https://github.com/Building42/Telegraph the BGTask is able to connect to that iPad and run a URLSession to download from it. (The iPad acting as a web server does need to be on with the server app running; the iPad can't be turned off and the app can't be in the background, so it's not like a true server in that regard.) That tells me that the Network framework doesn't have network access like URLSession does.
Nov ’23
Reply to Network Framework with multiple clients
UPDATE: The func refreshResults(results: Set<NWBrowser.Result>) callback is being called multiple times for the first client only, which is how the PeerConnections array is ending up with duplicate connections to the first client. However, the excellent thing is that the array of PeerConnection objects seems to be working as hoped.
Oct ’23
Reply to Why @Bindable and not @Binding in SwiftUI for iOS 17
Interesting about the change from the early betas! This is my first testing of it so I don't have any earlier experience. I did a quick test and you're absolutely right with @ObservedObject--if I pass a @StateObject into a subview and I don't prefix the var with @ObservedObject, then even if I'm just observing its properties, and even though the properties have @Published, the change doesn't appear in the subview. That's different from @State and @Binding, where you don't need @Binding if you're just observing it, just if you're going to change it. I just tried it with iOS 17 and confirmed that: If I have pass down an object, I don't need @Bindable just to observe changes--those do publish. Strangely, I'm able to update a property on the class in the subview and the change does propagate to the parent where @State resides, though, almost like @Bindable isn't needed even for making updates. Maybe it's one of those cases where it could work, but isn't guaranteed to work so you shouldn't rely on it. Thanks a lot for engaging me in this discussion and helping me understand it better, I appreciate it.
Aug ’23
Reply to Why @Bindable and not @Binding in SwiftUI for iOS 17
Hi, thanks so much for responding. For @State, you said, Use to create a single source of truth for a given value type in a view. Shouldn't be used for reference types as SwiftUI won't update the view when one of its properties changes. However, I don't think this is right (it was pre-iOS17, but not now). In the video at 4:50, he shows Donut being used with @State: and at 6:19 he shows that Donut is a class and not a struct, so it is a reference type: This makes sense to me, because from everything I've read, they've consolidated the pre-iOS17 @State (for value types) and @StateObject (for reference types) into @State (for both value types AND reference types). As Natascha says in her article, Before the Observation framework, we used @State for value types and @StateObject for reference types to let SwiftUI update views whenever changes occured. With Observation, both types are covered by @State: In the book "Working With Data" by Mark Moeykens of Big Mountain Studio, he has a great illustration of the pre-iOS 17 usage: and based on Natascha's article, the new usage will be like this: I'm curious why They merged @State and @StateObject into just @State for both value and ref types, but didn't do the same for @Binding and @ObservedObject into just @Binding, but rather kept @Binding as-is, and just changed the name of @ObservedObject into @Bindable (as Natascha says, "Before the Observation framework, we used @Binding for value types and @ObservedObject for reference types to create two-way bindings. With Observation, we use @Binding for value types and @Bindable for reference types"). @Binding actually seems to work for a ref type.
Aug ’23
Reply to Problem with PFX Certificate and Background Task
If the item has already been added to the Keychain, it won't update unless you explicitly delete it with SecItemDelete, or update it with SecItemUpdate. Once I deleted it and re-added it, it does work using kSecAttrAccessibleAfterFirstUnlock. Still curious if this could be done without the Keychain, but I'm guessing that's you only choice, or I would've come across more code examples showing alternate methods.
Sep ’22