Post

Replies

Boosts

Views

Activity

Reply to Correctly initializing observable classes in modern SwiftUI
Thanks for getting back to me. I'm thinking specifically about the instantiation of a manager-style class that is global to the app and placed into the environment at launch time. The documentation that you linked to gives this specific example: @main struct BookReaderApp: App { @State private var library = Library() var body: some Scene { WindowGroup { LibraryView() .environment(library) } } } In the example, the Library class is created as a @State var and put into the environment. My first question (which is more theoretical) is: Why does the variable need to be annotated with @State. My (perhaps naive) assumption was that the App struct is special and is initialized only once, so it's not immediately obvious to me why its properties need to be held in state. Indeed, making it a let constant without the @State annotation seems to result in the same behavior. My second, more practical question is: What is the correct way to manually instantiate the Library (rather than doing so in the property declarations). There are various reasons why you might want to do this, but for the sake of example, I'm thinking of something along these lines: @main struct BookReaderApp: App { @State private var library init() { let thing = Thing() self.library = Library(thing: thing) } var body: some Scene { WindowGroup { LibraryView() .environment(library) } } } Note that in this case, Library is not created in the property declarations, but in the init. Searching around on GitHub, I find some examples where people do: self.library = Library(thing: thing) and other cases where people do: self._library = State(initialValue: Library(thing: thing)) I'm trying to understand what the difference is between these two (if anything). Thanks for any light you can shed on these (possibly newbie) questions!
Topic: UI Frameworks SubTopic: SwiftUI Tags:
Nov ’25
Reply to Some issues and questions regarding the use of the BGContinuedProcessingTask API
The engineering team agrees that differentiating between "system expiration" and "user cancellation" is a significant oversight in the current API. I can't comment on future plans/scheduling, but this is something I expect the API to address. Thanks, this is good to know! if you're seeing UI when there are NOT tasks from other apps active, then that's worth filing a bug on. This is definitely happening to me. The BGContinuedProcessingTask UI (notification banner thing) shows up immediately every time I start such a task, even if there are no other apps running a background task (iPhone 12 mini, running iOS 26.1). I didn't realize that this was not the intended behavior. It's kinda hard to file a bug because I don't think there's any specific documentation that states that this is not how the API is supposed to work.
Nov ’25
Reply to BGContinuedProcessingTask expiring unpredictably
Thanks for the advice! Informally, I've generally found that a progress bar needs to update every ~0.1s (10x/sec) to ~1-2s in order to look "right". I guess this depends on the magnitude of the task. If the task takes an hour, then updating every 1s would mean that each update moves the progress bar by a fraction of a pixel. My current setup moves the progress bar about 4px at a time, which looks pretty smooth to me. You can use the progress delegate that provides "raw" rate data; however, the easier option is to use the URLSessionTask.Progress object. With single downloads, you can use that Progress object directly, while more complicated scenarios can use child progress and the flow described here. Thanks for the pointers. I looked into these options but, in the end, it feels like trying to tie the progress to bytes downloaded (rather than number of files completed) is going to open up other issues, notably: If a file fails to download for some reason (and has to be restarted), I will need to subtract out the bytes downloaded from the progress or, in some sense, track each file's progress. Not impossible, but feels fraught with state management bug potential. My task involves downloading and processing files. In some cases, the processing is very small relative to the download time, but in other cases, the processing can take longer than the download, so linking progress only to the download part is not ideal. That's why I originally chose to link progress to file completion. The modifications I'd need to make to my code (passing download progress from the URLSession delegate through an AsyncStream back to the calling task) will result in more complexity that will be hard to test, and background URLSessions are already really hard to work with. Overall, my sense is that BGContinuedProcessingTask is better suited to tasks that are primarily CPU-bound where the rate of progress and time-to-completion is fairly predictable a-priori. For tasks that are more network-bound and where progress is heavily dependent on the volume of user data, it becomes hard to guarantee a particular rate of progress. Since BGContinuedProcessingTask seems to be quite trigger happy and doesn't distinguish between user cancellation and system cancellation, I don't think it's going to be the right option for me. Which is a shame because I was quite excited about it when I saw it at WWDC. Anyway, thanks for the advice, Kevin, and for being very responsive (also to other threads on this forum). It's much appreciated!
Nov ’25
Reply to Some issues and questions regarding the use of the BGContinuedProcessingTask API
I tried different device models, and some could trigger the task normally, such as the 15 Pro Max and 12 Pro Max. However, there were also some models, such as the 17 Pro, 15 Pro, and 15, that could not trigger the task properly. Moreover, there was no additional error information to help locate the issue. I assume you're doing this on device and not the simulator, right? And did you check the Console logs to see if an error was reported there? (look for the dasd process) I also just wanted to reiterate your other points about the BGContinuedProcessingTask API, in case someone from Apple is listening: I agree that the experience on non-Dynamic Island devices is not great. The task notification banner thing is a obtrusive and redundant. My app already displays a progress bar, so displaying another progress indicator is redundant. Moreover, the banner obscures my app's UI and does not disappear automatically like normal notifications. This leaves the user uncertain if they can safely dismiss it, or if dismissing it will cancel the task. The banner is important to make the user aware that a background task is running, but it should only appear if/when the user backgrounds the app – that's when it becomes relevant. Likewise, I've also noticed that the banner text is sometimes the wrong color – sometimes black text in dark mode, or white text in light mode. But I haven't been able to reproduce it consistently. I totally agree that the API is missing a distinction between (1) the user has stopped the task and (2) the system wants to stop the task. In the case of (1), I want to completely cancel the task. In the case of (2), I want to put the task into a paused state until the user is ready to resume it. Ideally, the UI should inform the user that the task has been paused.
Nov ’25
Reply to BGContinuedProcessingTask expiring unpredictably
Based on the response from the engineer over on this post, I looked at the Console logs for the dasd process and found this: Task has not reported progress within expected cadence, marking stalled (time without update: -36.27811694145203) Does -36 mean 36 seconds since the last update? That seems plausible given my situation. What cadence do I need to hit to satisfy the system? Not sure what to do now. This limitation seems to make BGContinuedProcessingTask unsuitable for my use case, since the size of the downloaded files can vary, as can the speed of the user's connection. I could try to feed in fake progress updates to keep the task alive. Or I could try to monitor the byte count of downloading files and use that as the basis for progress updates (however, my previous experience trying to pass the byte count from URLSessionDelegate through an async stream was a nightmare). In any case, it would still be useful if someone from engineering could respond to my above queries, so that I can better understand if there might be other issues to contend with (not just the progress cadence).
Oct ’25
Reply to AlarmKit alarm UI strings are lost after updating OS
It turns out the situation is worse than I thought: it's not the OS update that triggers the bug, but merely restarting the device! If I schedule an alarm with the above code and then restart my device, the alarm triggers at the correct time, but the UI shows the localization keys instead of the strings. I just installed the iOS 26.1 beta (23B5044I) and it's still broken, so I've filed a bug report: FB20472264 As a temporary workaround, I will set my localization keys to English language strings.
Oct ’25
Reply to Safe areas ignored after navigating a WebView/WebPage back in a NavigationStack
I filed a bug report (FB20465338) but I also want to note some more details here in case this is useful to someone: When navigating back/forward in a WebView or WKWebView, the page suddenly jumps up. The reason appears to be that WebKit automatically adds some padding to the top of the webpage to account for the unsafe nav bar area, but it then forgets to account for this padding when navigating back/forward. The effect is especially pronounced if you swipe back/forward. When swiping you see a static preview of the incoming page, but when you release the swipe, the page suddenly jumps up. As far as I can tell, this has always been an issue, but before iOS 26 it wasn't really a problem because the WebView would usually be flush against a solid nav bar. However, in iOS 26, this bug is problematic because Apple is asking us to flow the web content behind the nav bar; indeed, this is the default behavior of a WebView. There's effectively no good solution in iOS 26 because you basically have two choices: Adopt the default behavior with the content flowing behind the nav bar; but then you get a janky back/forward experience. Set .ignoresSafeArea(.all, edges: .bottom) so that the content does not flow behind the top nav bar, but then the nav bar is plain white/black and it looks bad for the content to just disappear abruptly without some kind of border or fade effect. This can't simply be solved by making the nav bar a different color because then you get into issues with the glass buttons and the floating iPad sidebar. Would love to hear if someone figured out a workaround, even if it's just some styling to make Option 2 less bad.
Topic: Safari & Web SubTopic: General Tags:
Oct ’25
Reply to Safe areas ignored after navigating a WebView/WebPage back in a NavigationStack
Yes, I'm having the same problem (with a wrapped WKWebView, but presumably also the new WebView). The jumping-on-back-button issue was also a problem before iOS 26, but you could work around it by using .ignoresSafeArea(.all, edges: .bottom) However, now in iOS 26, Apple wants the content to flow behind the toolbar, meaning the above fix is no longer suitable. Did you manage to find a workaround?
Topic: Safari & Web SubTopic: General Tags:
Sep ’25
Reply to Large title is not visible in iOS 26
Did anyone manage to solve this issue? Exactly as @jkmazur says: the title text is being rendered under the background color, and it's still broken in beta 9! Here's a minimal example: struct ContentView: View { @State private var path = NavigationPath() var body: some View { NavigationStack(path: $path) { List { Text("Item 1") Text("Item 2") Text("Item 3") } .navigationTitle("Title") .toolbarBackground(Color(red: 0.5, green: 0.5, blue: 0.5), for: .navigationBar) .toolbarBackgroundVisibility(.visible, for: .navigationBar) } } } Expected result: Title is rendered above the gray background. Actual result: Title is rendered below the gray background.
Topic: UI Frameworks SubTopic: UIKit Tags:
Sep ’25