WWDC 2021 Questions & Answers From Slack (The Unofficial Archive)
Mark Moeykens
Mark Moeykens
Big Mountain Studio was started by Mark Moeykens. The company specializes in providing visual picture books to be used as a reference.
Jun 12, 2021
I wanted to document some of the questions and answers posted to this year's official WWDC 2021 Slack Workspace. I'm missing a lot of the last day's questions though because it was removed before I could get to them! But I did manage to accumulate 43 pages worth.
Notes:
Not all questions are posted here (some didn't have answers).
Some questions with no answers are posted because they're good questions that you too might have.
Questions and answers are in no particular order.
If grammar/spelling mistakes made a question/answer harder to understand then I corrected it.
Excluded replies to answers like "Thanks" to reduce clutter.
Excluded non-technical questions like, "What are your hobbies?"
QUESTIONS AND ANSWERS
@Jeff has asked:
Is there a SwiftUI equivalent for preferredStatusBarStyle in iOS 15?
Curt (Apple)
I’m afraid there is no new API for that at this time. That would be a great enhancement Feedback to help us gauge interest.
@Seyed Parsa has asked:
How to force SF Symbols not to be automatically filled/unfilled based on the context (as shown in tab bars)? Is there any way to force them to be non-filled?
Jacob (Apple)
You can use .symbolVariant(.fill) to opt-in a view or view hierarchy to fill variants, and see the SymbolVariants type for other variants you can use with it.
@Shubham has asked:
How do we assign a TextField in SwiftUI to gain focus as soon as a View opens?
Matt (Apple)
You can use the new @FocusState property wrapper and focused() modifier to programmatically control focus in many different ways, including when your UI first appears. For example, you could trigger a specific view to be focused using onAppear or any other event callback.
@Seyed Parsa has asked:
Can we customize and put a "searchable" search box anywhere? (Without going the old way, adding text views)
Harry (Apple)
The searchable modifier supports a few customization points with the SearchFieldPlacement type. You can pass a placement into the modifier: .searchable(text: $text, placement: .navigationBarDrawer(displayMode: .always)).
If those placements aren't what you need, you will need to roll your own solution. Though feedback on other placements you would like to see would be helpful!
@Matthew has asked:
Can we use the new buttons with non-SF single-color graphics?
Taylor (Apple)
Yep, in fact, the new buttons can have a label of any view, including shapes and more!
The one thing to be careful of with custom images is that they use template rendering if you want the standard foreground styling within the button. (otherwise, they’ll be the exact color of the image’s pixels) (edited)
@Jonathan has asked:
Can the new `.refreshable` API be used with custom refresh controls/spinners?
Harry (Apple)
Take a look at the EnvironmentValues.refresh property. The refreshable modifier sets this property up with closure provided to the modifier. You can query this property ( @Environment(\.refresh) private var refresh ) to hook up to your own UI.
@Shaun has asked:
The new `detents` for sheets in UIKit, to allow them to sit at different heights, looks amazing! Unfortunately, there doesn't seem to be an equivalent in SwiftUI – is there any plan to bring it across?
Curt (Apple)
I’m sorry, but we can’t comment on future plans.
This is something we know folks would appreciate. Enhancement request Feedback with a particular use case would really help us.
We’re particularly interested in use cases around variable heights in SwiftUI sheets.
@Clément has asked:
does the modifier of importItemProviders work with other commands, or other stuff to import things in the app?
Taylor (Apple)
It works with any macOS system service that vends data. So if you had a service or shortcut that didn’t take input, but produced output — your app’s importItemProviders could consume that data when the user invokes that service
@Fernando has asked:
When using the WatchKit way to get text input, do we still get the text input improvements from SwiftUI? E.g. remembering the user choice of scribble vs. voice input
Scott (Apple)
@Fernando if don't pass any suggestions to the WatchKit API, then yes, you get all of the new behavior.
@Seyed Parsa has asked:
Does using more SF Symbols have an impact on total app size? Or they all are stored in the OS?
Taylor (Apple)
These are part of the OS, so you can feel free to go wild with all the symbols you want with no impact to app size.
@Aimin has asked:
Using `@Environment(\.isSearching)`, how would multiple navigation bars in an app work?
Harry (Apple)
The isSearching environment property is tied to the searchable modifier that sets it up. If you have the following:
NavigationView {
ContentView()
DetailView()
}
.searchable(text: $text)
Then you could query the isSearching property inside of ContentView or DetailView. If you have the following:
NavigationView {
ContentView()
DetailView()
.searchable(text: $text)
}
Then you could only query it inside of DetailView. Similarly, if you have the following:
NavigationView {
ContentView()
MiddleView()
.searchable(text: $text)
DetailView()
.searchable(text: $text)
}
Then you could query isSearching in either MiddleView or DetailView and the property would relate to the enclosing searchable modifier.
@Abdul has asked:
is the refreshable property the only SwiftUI property that supports async code?
Sam (Apple)
The task modifier also provides support for async code out of the box! Generally, we only make user-provided closures async if we can provide some additional utility to you by doing so, such as canceling a task when the attached view’s lifetime ends, or finishing the refresh animation. If you want to dispatch to async code in other places, you can use an async block!
@Sahand has asked:
What is the proper way to dismiss a .fullscreenCover when it is being presented based on an item: setting the item to nil or dismissing using the presentationMode?
Josh (Apple)
That really depends on what is doing the dismissing. They both have the same effect in setting the item binding back to nil. It is more about where you are driving the dismissal state from. If you're dismissing from within the sheet's content, then using the presentation mode will be more flexible because that will work no matter what is controlling the presentation.
There is also a new environment property introduced this year that can accomplish this as well, which is dismiss.
@N'rick has asked:
Is there a way to make refreshable work with LazyVGrid or do we have to adapt List as a Grid layout?
Sam (Apple)
Refreshable is currently only supported by List.
@Darko has asked:
Does AsyncImage support caching, and will there be an API to customize that cache?
Sam (Apple)
AsyncImage uses the shared URLSession , and so uses the shared URLCache. There’s currently no support for customizing the cache.
If that’s something you’d like support for though, feel free to file feedback with that request
@Andrew has asked:
Is there a way to animate text font size changes? Currently, in iOS14 a view can smoothly animate between two sizes or colored backgrounds, but fonts will just jump from the before animation size to the after size with no interpolation in between.
Jake (Apple)
Please file feedback for this!
If you’re interested in a workaround, the Fruta sample code project has an AnimatableFontModifier that uses an explicit font size as its animatable data. This is used for the ingredient cards in the main smoothie detail view. This works for Fruta because the use-case is limited and the text using this modifier is for primarily for graphical purposes. [Fruta App Link]
@Christopher has asked:
Can .importsFromDevices (i.e. continuity camera) work as a source for AsyncImage?
Taylor (Apple)
No, continuity camera will callback your importsItemProviders with an item provider that will give the full image data and not a URL.
But if you have a use case where there could be some better integration between the two, please file a feedback
@Dan has asked:
Hi! intro? I’m a hobbyist who is getting better at programming mostly because SwiftUI is so productive!
Can you talk about ways to have existing SwiftUI code inherit the newer features so I could support both iOS 14 and 15 with one codebase?
Matt (Apple)
Most new features do not back-deploy to earlier OS versions. You can use e.g.
if #available(iOS 15, *) {
to check for whether a feature can be used.
That said, some functionality is able to back-deploy. For example, the new ability to pass bindings to collections into List and ForEach, and get back a binding to each element, e.g.
ForEach($elements) { $element in ...
is able to back-deploy to any earlier release supported by SwiftUI.
Dan
I thought you said that - So the new feature can actually work in older code?
Matt (Apple)
Correct
@Emory has asked:
Are you able to programmatically change the focus of text fields natively in SwiftUI this year? For example, having a button at the bottom of the keyboard that says “next” and focus on the next text field?
Paul (Apple)
Indeed, I would take a look at the new FocusState APIs!
Is `.searchable` for local data only, or can it be used to query services?
Curt (Apple)
Searchable specifies that the view supports search. You can implement that search however works best for your app.
For example, you could kick off a query using the bound search term, then update the results when your query completes.
@Will has asked:
How would one go about adding the .searchable modifier to a PDFView?
Harry (Apple)
On iOS, the searchable modifier works with a navigation view to render its search field. On macOS, it works with a navigation view or the root view of your scene. If you combine a `PDFView` with a navigation view, things should hopefully work as you expect. If not, please do file a feedback with some more details on what you would like to achieve.
@Majid has asked:
Refreshable doesn’t work with scroll view. Is it a bug or desired behaviour?
Luca (Apple)
Currently refreshable only supports List. Please file a feedback request if you have other use-cases you’d like to see supported.
@Emory has asked:
Is there a way to change the background colour of a list? Last year this wasn’t possible
Natalia (Apple)
Currently, it’s only possible to change background color of list rows, not the List itself.
Emory
Thanks, @Natalia (Apple), is this intended, or something that might change in the future? If so, what is the best way to change the background colour currently? I’d like to use system materials specifically
Natalia (Apple)
Unfortunately, it’s not possible to use system materials with List at the moment, please file a feedback and we will take a look at it. In the meantime, you could try using a ScrollView instead of a List.
@Deniz has asked:
Not entirely SwiftUI exclusive question, but worth a try:
Xcode 13 release notes list as "known issue": "Swift Concurrency requires a deployment target of macOS 12, iOS 15, tvOS 15, and watchOS 8 or newer. (70738378)
Does this suggest that concurrency might be backward deployed? This seems too good to be true because I would need runtime support, but perhaps you guys can shed some light on this?
Matt (Apple)
For now, you should assume that Swift concurrency cannot be back-deployed, however, the team understands that this is a popular request and is investigating.
The fun part is that Swift is open source, and so you can visit https://forums.swift.org to peek in on the active development process for Swift concurrency, and they will likely discuss this topic more there as well.
@Dan has asked:
Is there a limit to where SFSymbols change size? I put them as items in a TabView but it doesn’t seem like they are scaling with dynamic text …
Taylor (Apple)
TabViews do have a standard symbol size across all apps and how far they scale
@Pavel has asked:
Is there a way to hide a default green scroll indicator on watchOS when using digitalCrownRotation modifier?
Josh (Apple)
I believe it does hide if you make it "continuous"
@Matthew has asked:
What is the recommended way to account for a NavigationView title that may take up multiple lines? I tried setting .lineLimit to nil, but I am not getting the results that I want. For instance, I have "XYZ Club Executive Meeting" for the navigationBarTitle text, but it only shows "XYZ Club Execut..."
Curt (Apple)
Take a look at the toolbar modifier and the .principal placement.
If that doesn’t meet your needs, please file a Feedback so we can look into your use case.
Scott (Apple)
I'd also be curious what platform you are asking about here. Cause there might be slightly different advice on watch
Matthew
@Scott (Apple) This is on iOS, but I am also interested in knowing how to fix the issue on watchOS since I am planning on doing some watchOS development in the future
Sam (Apple)
As a note, the .principal toolbar placement doesn’t apply across all platforms. If you need support for this on other platforms, feel free to file feedback requesting support for this on other platforms.
Kedar
I had a similar issue in a certain part of an app I’m working on , and I fixed it with these additional declarative statements on the Text element:
.multilineTextAlignment(.leading)
.lineLimit(nil)
.fixedSize(horizontal: false, vertical: true)
.padding()
I had also set the font size prior to this, and had a Spacer() before and after the text block. Took me a little bit to figure that out. Let me know if that helps @Matthew (mods sorry if this isn’t allowed)
Curt (Apple)
Totally fine. Thanks for sharing @Kedar!
@Brett has asked:
On watchOS, I'm using `.onContinueUserActivity(NSUserActivityTypeBrowsingWeb)` and `.onOpenURL`, however these only receive callbacks once the app is already open. Is it possible to use these in a way that they receive the callback from a cold launch?
Josh (Apple)
Those should function appropriately on cold launch. Please file a feedback with a sample showing where it isn't working if possible.
Brett
What’s the easiest way to set up the site association file for the example?
Josh (Apple)
That is an excellent question. Just the general shape of the application and where it's wired in will go a long way, even if the entire association isn't hooked up.
@Will has asked:
Are NavigationLinks "lazy" in iOS 15?
Matt (Apple)
NavigationLinks do not fully resolve their destinations until they are triggered, though the value of the destination view is created when the NavigationLink is created.
In general, we recommend avoiding as much work as possible when a view is initialized, which would avoid potential issues here. This is important for performance. Instead, have that work be triggered within the view’s body, such as using onAppear or the new task() modifier.
SwiftUI may reinitialize views for any number of reasons, and this is a normal part of the update process. (edited)
Will
Thanks so much, @Matt (Apple)!
Matt (Apple)
I’d recommend watching tomorrow’s “Demystify SwiftUI” talk, which helps explain some of these concepts in more detail.
@Hudson has asked:
I saw in the "What's new with SwiftUI" that TextFields will now have .onSubmit for when the return key is pressed for software and hardware keyboards. Does this mean that .onCommit will be depreciated or does .onSubmit serve a different function?
Curt (Apple)
The initializers with .onCommit are soft deprecated. So no warnings in Xcode, but we’re encouraging new code to use .onSubmit.
@Deniz has asked:
Is there a suggested way or best practice to show a UIDocumentPickerViewController in a SwiftUI sheet without triggering an "Attempt to present <X> on <Y> which is already presenting <Z>?
If you are encountering a specific issue that you think might be a bug, please file a Feedback report!
@Andrew has asked:
Does SwiftUI have an equivalent to UIview’s drawHierarchy to render the view to an image?
Sam (Apple)
SwiftUI doesn’t have API that supports this. That said, you can accomplish this by wrapping your SwiftUI view hierarchy in a UIHostingController and applying drawHierarchy to the hosting controller’s view!
@Gene has asked:
With the new Markdown support for Text, are you able to control what specific Markdown elements look like? For example, can we specify that **bold** is supposed to display as Demi-bold rather than bold? Or what the font, font size, or font weight of an H1 header should be? In general, how much flexibility do we have in styling the markdown?
Natalia (Apple)
At the moment, it’s not possible to control the default styling in Markdown for SwiftUI. It would be great if you could file a feedback as a feature request/suggestion. Another thing to note, SwiftUI only supports inline styles, paragraph styling such as headers (H1 for example) is not supported.
@Tim has asked:
Let’s say I have a purely SwiftUI flow. I have a ListView with a `@StateObject var listFetcher`, that makes requests for a list of items. Tapping on an item in the list navigates to a DetailView, which has an ObservableObject property `detailFetcher`, that makes requests for details on the item.
What’s the best way to structure DetailView and which property wrapper would we use for `detailFetcher` in DetailView?
Have an initializer like `init(itemID: Int)`, and use `@StateObject`? This would require us to eventually update the detailFetcher property with something like `detailFetcher.itemID = itemID` in the body’s `onAppear`
Pass in the `detailFetcher` into the initializer like `init(detailFetcher: ObservableObject)` and make the property @ObservableObject? If this is preferred, where would this `detailFetcher` live if not in SwiftUI?
Matt (Apple)
In general, use @StateObject when the view in question owns the associated object, i.e. the object will be created when the view is created, and should be destroyed when the view is removed.
In contrast, use @ObservedObject when the view needs to reference an object that is owned by another view or something else. So the view is dependent on the object, but their lifetimes are not tied together.
For example, you could have a main screen that uses @StateObject to initialize your app’s model, and then pass that object off to detail screens using @ObservedObject.
Also, check out the “Demystify SwiftUI” talk tomorrow to learn more about this!
@Christopher has asked:
Is there a modifier to control how links in AttributedString are handled?
Specifically, I'd like to be able to handle some links in-app, rather than booting the user out to Safari.
Natalia (Apple)
Currently, the links will be handled by using the default OpenURLAction from the environment, it’s not possible to override it at the moment.
You could however use a custom URL scheme that will open the URL in your app, for example, myapp://
@Gene has asked:
Is there a new SwiftUI way to convert a view into an image or pdf? Currently, this can be done, but it is one of those round about, hacks that no one likes using.
Sam (Apple)
Unfortunately, there’s no native SwiftUI api for this right now. As was mentioned elsewhere in the thread though and in your question though, it can be done by wrapping in a UIHostingController. Feel free to file a feedback requesting the feature though!
@Ron has asked:
What is the layout and sizing behavior of Canvas?
Curt (Apple)
A Canvas will consume the space offered to it.
You can put it in a .frame to control the size if needed.
@HUNTER has asked:
There's a lot of sample code out there where people use GeometryReader inside of clear overlays and the like to not impact layout... is that "ok"? Seems not like the way it was designed to work.
Josh (Apple)
In general, we'd consider that a bit of a misuse of the API. If you have specific use-cases where you're needing to do this we'd love to hear about it in a feedback report!
@Eric has asked:
We wanted one View to take up 50% of the space and two others to take up 25% each and without GeometryReader you can't easily do that (I think?) but then it messes up the spacing.
Josh (Apple)
@Eric you could use .layoutPriority to help with that some. Or nesting stacks can provide some ways to achieve that as well.
Eric
I'm not sure how those would give me the exact percentages, though. Let's say for simplicity’s sake I have three Images. I can make an HStack { Image, HStack { Image Image} } but that's not going to split up the screen 50% x 25% x 25%, will it?
Josh (Apple)
That’s exactly what it'll do, assuming all the images are .resizable
HStack {
Color.green
HStack {
Color.red
Color.blue
}
}
Will give you 50% green and 25% the other two.
@Matthew has asked:
How would someone go about turning a List cell into a TextField on tap, such as to edit the name (similar to the Reminders app)?
Curt (Apple)
I’d try putting a TextField in an overlay of your cell content with an opacity of 0 and disabled. Then use a tap gesture to switch the opacity and un-disable it.
You could use the new FocusState or onSubmit when the user is done editing to switch back.
The trickiest bit will be getting your TextField and the regular Text to line up.
You might need some custom padding for that. Take a look at the ScaledMetric property wrapper to make padding that adapts to dynamic text size.
If the TextField is in an overlay on the Text, it should be constrained to the same size. The challenge is that editable fields (on iOS at least) include some insets that static text does not.
Slack is a terrible compiler, but I’m imaging something like:
Text(itemText)
.padding(2)
.overlay {
TextField($itemText)
}
That’s missing the bits to hide and show the pieces.
The trouble is if itemText is a short string, the whole assemblage will be short.
I think the toggling is the hard part and the rest will take some experimentation.
Yeah, a flexible frame seems like the right answer. Perhaps with a layout priority greater than 0 so it consumes most of the space.
@Vaccor has asked:
Is it possible to prevent a Button inside a List item from being activated when the list row is selected? Right now, if you have multiple Buttons they will all be triggered when the row is selected, so can't really have a secondary button in a list row.
Josh (Apple)
Explicitly setting the buttonStyle of the nested buttons will stop the List from capturing the event.
@Jason has asked:
What’s the recommended practice for custom sheet presentations? SwiftUI’s .sheet only supports the standard card-style modal presentation. It doesn’t have the flexibility of e.g. UIKit’s transitioning delegates and presentation controllers to let us size and position modally presented controllers.
Curt (Apple)
Thanks for the question.
For custom presentations, I’ve had good luck putting the presentation content in an overlay, then using an offset modifier to shift it off screen.
To bring it on screen, I use State to zero out the offset.
If the state is updated inside withAnimation to transition is animated.
Jason
That content won’t be lazy then, right? It’ll be computed in advance and off-screen.
Curt (Apple)
You could conditionalize the content too.
An offscreen empty view would be essentially free.
Jason
Okay, I see. Thank you!
Curt (Apple)
As would an offscreen view with opacity of 0.
You might need to play with the transitions to get the appearance just right.
Jason
And it’s essentially free because the framework is smart enough to not bother rendering content that’s invisible?
Curt (Apple)
Yep!
Brett
Here is how I’m doing it currently, hope don’t mind me jumping in:
struct ValidationSheet: View {
var isPresented: Binding<Bool>
var body: some View {
ZStack {
if isPresented.wrappedValue {
Group {
cardOverlay() // black 50%
sheetContents() // the actual sheet
}.onDisappear {
isPresented.wrappedValue = false
}
.accessibilityElement(children: .contain)
}
}
}
}
@Andrew has asked:
Is it possible to change the spacing between views in a List (or stack view) as the list scrolls up and down in a springlike manner? (i.e. is there a SwiftUI equivalent to UIKitDynamics?)
Luca (Apple)
Hi Andrew, we don’t have any support for this in SwiftUI. Could you please file a feedback with the use case you are interested in? Thanks!
@Dan has asked:
How do I get a SwiftUI time picker to change to 5min instead of 1min? I tried to find that but so far I haven't found how to do that...
Curt (Apple)
And after all that clarification, I’m afraid the answer is that we don’t provide customization for that currently.
That would be a great enhancement request Feedback.
@Andrew has asked:
Is it possible to place a CALayer inside of a SwiftUI view without using a UIViewRepresentable?
Josh (Apple)
Not currently. UIViewRepresentable is the way to go if you want to expose custom layers inside of your SwiftUI content. If you have a specific use-case where this can't work, please do file a feedback request!
@Christopher has asked:
Is it possible to specify paragraph styles AttributedStrings used by SwiftUI Text views? I don't see any mention of paragraph style in the SwiftUI AttributeScope (but it's there for the UIKit scope)
Natalia (Apple)
SwiftUI currently supports all of the attributes in the SwiftUI scope and some in the nested Foundation scope, the supported Foundation attributes are links and inlinePresentationIntent. So it’s not possible to specify paragraph styles
@Andrew has asked:
When to iterate a collection directly from List(items) { items in
// Code here
vs.
List {
ForEach(items) { items in
// Code here
Josh (Apple)
Fundamentally these should provide the same behavior, in almost all use cases this would be a stylistic choice over which to use. The former works great if you know you'll only ever have that single array of items in your list. The latter is where I generally start because its less things to change if I want to provide content from multiple sources into my list.
@Todd has asked:
Is there an efficient way to set the values of individual pixels in a view in SwiftUI? Sorry I haven't fully examined the new Canvas API yet. For the record, I don't really know how to do this in UIKit either, other than writing a custom fragment shader for a Metal view. Thank you!
Josh (Apple)
Canvas is really the best bet here. You could fill a 1px x 1px rectangle of the color you want, and that would be the most efficient expression of that within SwiftUI itself.
And to get the size of a pixel you would request the pixelLength from the environment of the GraphicsContent of the Canvas
@Michel has asked:
Any way to control when the @SceneStorage gets saved? Many are in my root app view, and they only seem to be written in app background, not before.
Curt (Apple)
I’m sorry, but we don’t provide API to control scene storage. The intent is that it’s saved on backgrounding and restored on foreground/scene recreation.
@Dan has asked:
I've started an app where I've got 3 screens - selected by tab views. I'd like to create a setting to default to one of the screens or the other. What's an efficient way to do that? Maybe a lab type question...
Josh (Apple)
You would use the selection Binding passed into the TabView initializer to decide which view will be selected by default.
struct ContentView: View {
@State var selectedTab = 1
var body: some View {
TabView(selection: $selectedTab) {
Text("First")
.tag(0)
Text("Second")
.tag(1)
Text("Third")
.tag(2)
}
}
}
That will have the view start with the 2nd tab selected
@HUNTER has asked:
Still hoping to get any info on if you can hide the tab bar when using TabView and pushing items onto a NavigationView like you can in UIKit.
Paul (Apple)
I do not believe there is a TabViewStyle that will allow this. However, please file a request using feedback assistant describing your use case. Thanks!
@Adam has asked:
Is there a way to debug AttributeGraph crashes? I’m getting `AttributeGraph precondition failure: "setting value during update": 696.`, probably due to a hosting controller somewhere, but don’t know how to track it down.
Curt (Apple)
Thanks for the question!
That error usually means that some code evaluated in body or updateUIViewController (or NS…) is mutating state.
We have a new debugging tool in the new SDK that might help narrow it down.
Inside body if you write Self._printChanges() SwiftUI will log the name of the property that changed to cause the view to be redrawn.
(Note the underscore. This isn’t API, but is for exposed for debugging.)
@Paul has asked:
Do you have any suggestions for why `Image("AppIcon")` would fail to work, when `Image(uiImage: UIImage(named: "AppIcon")!)` works just fine? Is that intentional behavior?
Paul (Apple)
Is the image located in an asset catalog?
Paul
Yes. It’s the AppIcon in the asset catalog.
(As in, the one used for the app icon on the home screen.)
Paul (Apple)
Please file a report through feedback assistant if you haven’t already. If you have, can you post the number here?
Curt (Apple)
I’d wonder whether the asset compiler is treating the app icon specially.
We should fix it, but perhaps the app icon is being copied elsewhere in the bundle so the system can reach it.
Karthikkeyan has asked:
Is there a way to do a binding of an iterated element,
Just to be clear, we shouldn’t be doing id = UUID () for our identifiers?
Raj (Apple)
It's often fine to have stored UUIDs. It's computed UUIDs that turn out to be a problem in this case.
@Seyed Parsa has asked:
Do situations exist when the developers really need to use AnyView and there are totally no alternatives using Swift constructs?
Matt (Apple)
Lot’s of questions about when it is okay or not to use AnyView!
If you can avoid AnyView, we recommend that. For example, use @ViewBuilder or pass view values around using generics instead of using AnyView.
However, we offer AnyView as API because we understand that there are some situations where no other solution is possible, or comes with other important tradeoffs.
One rule of thumb: AnyView can be okay if used in situations where the wrapped view will rarely or never change. But using AnyView to switch back and forth between different values can cause performance issues, since SwiftUI has to do extra work to manage that.
I will say though that it’s more and more rare these days that we find situations where AnyView is the best tool. So definitely pause and consider for a bit whenever you think it might be necessary — there are often other ways to do the same thing!
@Christopher has asked:
Is the computed UUID not stable because we cannot predict the position of the value, therefore where it will be placed in the view?
Raj (Apple)
The computed UUID is not stable because each time the property is accessed, you'll generate a new UUID.
So yes, you'd often want to use a stored property if you're using UUID.
@Axel has asked:
From the dependency graph part of "Demystifying SwiftUI":
The video mentioned that both views, two views that are dependent on the same dependency, would need to generate a new body. Which view would need to generate a new body first if one view is the child of the other?
Luca (Apple)
The parent view will generate its body first, and recursively traverse all the child views.
@Giulio has asked:
I have created a ViewModifier that adds a custom modal “overlay” on top of a view, somewhat similar to a sheet. Is there a way to pass a View as parameter of this ViewModifier initializer, without resorting to AnyView? I want to be able to pass the view that will be the actual “content” of the overlay.
Raj (Apple)
You can do this by making your custom ViewModifier generic on the content, something like:
struct MyModifier<C: View>: ViewModifier { ... },
then using a property with the generic like so:
var content: C
@Todd has asked:
If the only reasonable property for establishing stable identity in a model from, say, an existing JSON API, is a string that might be quite long (maybe it's more of a description than a name), is that large ID enough of a potential performance problem that we should consider modifying the model/API to add a more compact ID? Thanks much!
Raj (Apple)
Great question! As with any performance problem, it's best to measure first before over-optimizing. Long strings can often be expensive, so it might make sense to optimize the identifier, but I'd recommend measuring first.
@Andreas has asked:
Should `@State` and `@StateObject` be usually defined as `private`?
Raj (Apple)
Yes! It's often helpful to make them private to indicate that the state is for this view and its descendants.
@Edward has asked:
Is there any difference between the persistence of @State vs @StateObject vs @ObservedObject when identity changes?
Raj (Apple)
Yes! ObservedObject does not have any lifetime implications— when you use that property wrapper, you are responsible for managing the lifetime yourself. The object can live anywhere— either in StateObject or possibly even in another data structure in your app.
Edward
while @State and @StateObject behave in the same way?
Raj (Apple)
Right, they define a new source of truth.
@Majid has asked:
I have a class Store<State, Action>: ObservableObject that holds the whole app state. It lives as @StateObject in the root of the App lifecycle and passed via environment to all views. View send actions to the store and update as soon as store's state updated. It allows me to keep the app consistent. Could you please mention any downsides of this approach from your perspective?
Curt (Apple)
That approach makes every view in your app dependent on a single Observable object. Any change to a Published property forces every view that references the environment object to be updated.
Khoa
@Curt (Apple) by "forces every view that references the environment object to be updated" you mean that view produces new body to diff with old body?
Curt (Apple)
That view produces a new body.
SwiftUI doesn’t diff views. It regenerates views when dependencies change.
@Matthew has asked:
The Dependency Graph — can it have loops, or is it acyclic?
Raj (Apple)
Graph cycles are not allowed and will trap at runtime.
@Michel has asked:
Fun question on conditions: We need to put them at least in
Group { if whatever { ... } else { ... } }.
So ... is it preferable to use ViewBuilder commands to create it, like ViewBuilder.buildBlock(pageInfo == nil ? ViewBuilder.buildEither(first: EmptyView()) : ViewBuilder.buildEither(second: renderPage) )
Awful to read, but is it better?
Raj (Apple)
Using a Group is preferred in this case.
Christopher
Is that for readability reasons, or is there a performance (or other) benefit here? Isn’t the if/else above going to create the same conditional content block nested in the Group?
Raj (Apple)
Mostly readability— it's generally not recommended to call the result builder implementation directly and instead let the compiler handle it.
Michel
So mostly Group is very low - or no - cost?
Raj (Apple)
Low enough that you shouldn't try to optimize for avoiding it.
@Ivan has asked:
Is there a way to animate a View moving from one place in hierarchy to another? Like when a View changes parents?
What is a good way to have a custom order of elements in a list so the user can change the order (using a stable ID)?
Matt (Apple)
A List or ForEach will preserve the same order as used in the collection passed into them. The identity of each element should be independent of that order — even if a collection is reordered, each element should maintain the same ID.
If you maintain a stable identity like that, then you should be able to reorder the collection no problem in response to user actions.
@Edward has asked:
Are there any difference between:
Passing a State as a binding through each view in the hierarchy
vs
Passing it as an environment object and only access it in the subviews that use it, say in a Text view
Would the first one have a worse dependency graph that needs more updates?
Luca (Apple)
Environment object is a slightly different tool than State since it requires an ObservableObject. Environment object is optimized to invalidate only the views that read its value.
As for State, when you pass the binding down to subviews, changing the binding will mutate the state that in turn will invalidate the view holding the state and its children.
@Karthikkeyan has asked:
If we apply the same id in the condition, will SwiftUI see this as the same view?
var body: some View {
if isTrue {
Text("Hello")
.id(viewID)
} else {
Text("World")
.id(viewID)
}
}
Raj (Apple)
No, these will be two different views.
This is because body is implicitly a ViewBuilder. If you don't use a ViewBuilder, such as in another property, it would be the same view.
It's only going to compile if the types line up on each side.
Lior has asked:
@Raj (Apple) are there any other examples except body that are implicitly a ViewBuilders?
Raj (Apple)
Yes, ViewModifier's body function, makeBody for view styles, preview providers, and many more.
Víctor has asked:
So… we should avoid conditionals in view builders as a pretty much blanket rule? :/
Raj (Apple)
Definitely not! They exist for a reason. The message here is to just be careful when using them over-aggressively.
@Seyed Parsa has asked:
Is there any situation in which Hashable may be preferred to Identifiable?
Matt (Apple)
If you just need to be able to identify a value, that’s what Identifiable is for, which means that only the id needs to be Hashable, not the whole type.
@Khoa has asked:
How can we conditionally set different modifier, for example, list styles?
List {
}
.listStyle(isIpad ? .sidebar : .insets)
Raj (Apple)
Styles in SwiftUI are static, and are not permitted to change at runtime. This is a case where a branch is more appropriate, but consider whether you really want to change the style— a single style is almost always the correct choice.
@Yugantar has asked:
Is there a good way to apply a modifier to a SwiftUI view conditionally? I'm using a custom .if modifier, and it refreshes the whole view on state change :(
Raj (Apple)
Consider making an inert version of the modifier as discussed in the session. (https://developer.apple.com/videos/play/wwdc2021/10022) If there is a modifier that lacks an inert version that you'd like to see, please file a feedback.
@Inal has asked:
Performance-wise, is it preferred to pass `ObservableObject`s down to subviews explicitly, or use `EnvironmentObject`s?
Matt (Apple)
Using one or the other shouldn’t make much difference in any given view, but if you don’t need to use the object in some views then EnvironmentObject is a great way to avoid the boilerplate of pass it down through intermediary layers, and can avoid creating unintentional dependencies.
@Andrew has asked:
Are there performance considerations when generating UUIDs? (I.e. generating a lot of UUIDs for elements in an array to display in a list).
Raj (Apple)
It depends. You'll need to measure. However, consider generating IDs lazily (make sure you store them though!)
@Loic has asked:
Is the @ViewBuilder to remove AnyView in "Demystify SwiftUI" only available with iOS 15?
Matt (Apple)
No, in fact, it can back-deploy to any previous version!
Philip
I think the important part to understand was:
ViewBuilder was always there. Just implicitly added to the body without you knowing.
@Andrew has asked:
Is there a good way to switch between HStacks and VStacks while allowing SwiftUI to understand that the contents of those stacks are the same?
Matt (Apple)
Lots of questions similar to this, so repeating an answer from earlier:
What happens when you have three views in a hierarchy, where the root and leaf views share dependency, but neither share dependencies with the middle view. Would the middle view get redrawn?
Curt (Apple)
If the middle view isn’t dependent on the others, it isn’t redrawn.
Note that if the root view passes parameters to the middle view, then it has a dependency.
Finn
Is it reevaluated? So second view created on the heap but not drawn?
Curt (Apple)
It’s all structs. No heap.
If the second view has no dependencies, we ignore it.
The state is on the heap so that it persists.
Q&A SESSION WITH JOSH SHAFFER AND JACOB XIAO
(Note: These questions were re-posted by Betsy (Apple) so they don’t show who originally asked them.)
Is it possible to set a specific percentage for the SwiftUI previews?
When working on a 13" MBP, the 50% canvas is just too small, and the 75% sized canvas is too big.
Josh (Apple)
From the drop-down menu you can only choose the pre-defined scales, but you can always pinch on a trackpad to zoom the canvas to any specific scale you’d like.
Which property wrapper should be used in the following situation:
class AppState: ObservableObject {
static let shared = AppState()
var manualPauseOfSpeaking: Bool = false
}
struct SceneTitleView: View {
@StateObject var appState = AppState.shared
...
}
Is it: @State, @StateObject, or @ObservedObject?
Josh (Apple)
@ObservedObject is really what you want here, because the only behavior you need is invalidation when the shared AppState instance changes, and that’s all ObservedObject gives you.
@StateObject would also work, but in addition to invalidation, it will also provide storage. This is useful when you’re trying to have the content of the property live across multiple view updates, and would be what you want if you had instead written @StateObject var appState = AppState() because in that case you want to allocate an AppState instance once and re-use it even if SceneTitleView gets re-created. In your case the persistent storage is provided by the static let shared property on AppState, so you don’t need a property wrapper that provides storage.
@State is the wrong tool here. It provides the storage that @StateObject does, which as we just said you don’t need, but it doesn’t provide invalidation when the AppState instance changes, so it won’t do what you need.
Architecturally the community seems to be trending toward something like TCA (The Composable Architecture) (basically single store) while some are trying to do something that is closer to MVVM (or MVC). I’m curious what your thoughts are on this?
Josh (Apple)
For me, the most important thing is that you have a software architecture in mind that works well for you, and helps you craft maintainable apps with a good separation of concerns. MVC has been a very common pattern over the years, but people have always chosen their own in the past as well. TCA (The Composable Architecture) and MVVM are absolutely ways to achieve that, but they’re by no means the only one. If you’ve got a well-considered architecture that works well for you, I think that’s great
QUESTIONS DURING OFFICE HOURS - SWIFTUI, FOCUS & ACCESSIBILITY
When using the new SwiftUI Table View, can you Group to have more than 10 TableColumns?
Taylor (Apple)
Yes, you can; just like Views!
Alexey
Do I understand correctly that there are no more limitations on the number of objects in @ViewBuilder?
Taylor (Apple)
There is no change to @ViewBuilder this year, so it is still limited in the number of elements it can build into a block. But Group, as well as nested builders, are great tools to allow for as many views to be combined together as you want
Oh and to clarify what I mean by “nested builders”, patterns like the below:
var body: some View {
VStack {
header
content
footer
}
}
@ViewBuilder
private var header: some View {
// A new view builder context with up to 10 views
}
@ViewBuilder
private var content: some View {
// A new view builder context with up to 10 views
}
@ViewBuilder
private var footer: some View {
// A new view builder context with up to 10 views
}
Nicholas has asked:
Hi Taylor, it seems like Xcode 13 while grouping any TableColumn within the Table, fails to type check the expression. Even if I do only 2 TableColumns with 1 grouped. Created FB9155454 with an example project.
Taylor (Apple)
Thanks @Nicholas! It looks like that can be worked around by making the compiler’s job a little easier with some extra type information. e.g. for the TableColumn in the group adding a type for the incoming number value:
Group {
TableColumn("J") { (number: Number) in
Text(number.num.formatted())
}
}
@HUNTER has asked:
When using SwiftUI with something like Core Data and UIHostingController, how can we avoid using AnyView if the environment modifier changes the type of the view.
See example below.
import UIKit
import SwiftUI
import CoreData
struct MyView: View {
var body: some View {
Text("!")
}
}
class MyHostingController: UIHostingController<MyView> {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
// custom stuff here
}
}
class TestViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
let persistentContainer = NSPersistentContainer(name: "MyStore")
let rootView = MyView().environment(\.managedObjectContext, persistentContainer.viewContext)
let hostingController = MyHostingController(rootView: rootView) // <---- this will not work anymore because type has changed with the environment modifier
// more stuff
}
}
Curt (Apple)
Great question! Here’s a technique, though it runs the risk of angle-bracket-blindness …
line, you’re probably getting an error message about the types not matching. That error message will include the full type of the right-hand side of the assignment.
Something like
ModifiedContent<MyView, …>
with lots of stuff inside the … there.
What I like to do is (1) copy that type, then (2) add a top-level type alias:
where the right-hand side is the value I copied from the error message.
Then you can write:
class MyHostingController: UIHostingController<MyModifiedView>
@Arie has asked:
What are the best practices for making a SwiftUI view accessible?
Michael (Apple)
You should get a lot of Accessibility automatically. The best way to determine whether you need to make tweaks, using our Accessibility APIs, is to try your view with some of our features, like Large Text, VoiceOver, etc.
You can also take a look at the new Accessibility Inspector in SwiftUI and see what it shows for your Preview view.
In general, system standard controls tend to be accessible by default, but when you use a lot of Shape or other manual drawing, you may need to do more work.
Nathan (Apple)
Hi Arie. A great place to start is our introduction video from 2019.
Is it okay to use child CoreData context as @StateObject vars in SwiftUI views, or would you recommend passing those in the environment and holding on to them outside of SwiftUI?
Luca (Apple)
There is no need to use a @StateObject since the context is not an ObservableObject. Passing it down using the environment is a good solution.
@Gernot has asked:
I have an app that needs to hide the home indicator, but that does not seem to be possible in SwiftUI. I know I can do it with exchanging the root ViewController but that has a lot of side effects in a pure SwiftUI app. Is there a better workaround?
Curt (Apple)
Thanks for the question. I’m afraid we don’t have SwiftUI API for this currently. Using UIKit is the best option for now.
If you could submit an enhancement request Feedback, I’d really appreciate it.
@Jon has asked:
Can the new `AtrributedString` be used to provide accessibility notations in SwiftUI like `NSAttributedString`s can in UIKit? Should we report any attributes from `NSAttributedString` we need on `AttributedString`?
Curt (Apple)
AttributedString should have the full suite of accessibility attributes and they should now be standardized across platforms!
But if you see any we missed, please do file Feedback so we can fix any oversights.
Thanks for the question. We’re super excited about the AttributedString support!
Localization, accessibility, formatting, and morphology agreement in one tidy package.
Jon
I was looking for pronunciation control but couldn’t find it. Does it have a different name?
Michael (Apple)
Those are not yet available. But we are very aware of the issue.
In the meanwhile, there is some new AX API that can be applied to Text itself, such as speechSpellsOutCharacters, accessibilityTextContentType, and more.
@Aimin has asked:
For when using @EnvironmentObjects, it seems like I need to pass the .environmentObject modifier for every layer of subview. If I don't do that, it will give the error "view environmentObject may be missing as an ancestor of this view"
But I thought the point of Environment Objects is to make data available to all views without writing a lot of code. Is that a design choice or am I doing something wrong?
Curt (Apple)
Thanks for the question. .environmentObject(…) mostly flows down the view hierarchy, however there are some places where we intentionally do not forward it.
For example, the destination of a navigation link does not get the environment object, because there is a conflict as to whether the destination should get the object from the originating link or the placeholder of the destination, or even the previous view on the stack.
There was also a bug where sheets and popovers didn’t get the environment object, but that was fixed.
If you find other places where the environment object does not propagate as expected, please file a Feedback. It’s important to me that we get this right!
@Alexey has asked:
Is there any way to open an additional window from the SwiftUI Life cycle? I mean call additional WindowGroup. For example Inspector panel.
Jeffrey (Apple)
Hi - we currently do not have any API for this, though we’d love if you could file a feedback with any specifics you have for your use case. One option for something like an inspector panel is that you could use NSApplicationDelegateAdaptor, and open your window by communicating with that. A secondary WindowGroup might not be the best match for something like an inspector panel, since WindowGroup supports multiple windows, and will should up in the File -> New menu.
Oh, I should mention that the window created via the delegate adaptor would be an NSWindow instance, with an NSHostingView as the contentView.
Alexey
Thank you! Previously you suggest to use NavigationLink in a commands context to open a new window. I have tried and it works. It looks a little tricky. Is it a good solution or is it better to avoid it?
Jeffrey (Apple)
Yes, that is also supported - we will open a new window with the destination view as the content. This could also be a good fit for your case, since it will only ever make one window for that destination (it will be brought to the front if its already open and the user selects the menu item again)
Alexey has asked:
And is there any way to make WindowGroup open only single window?
Or this is the only way CommandGroup(replacing: .newItem, addition: {})
Jeffrey (Apple)
No, the design of WindowGroup is to support multiple windows on platforms that support it (iPadOS and macOS). What you have there will effectively remove the menu item. The problem with this approach is that if the user closes the window, they will not have an easy way to get it back, short of clicking the dock icon. If your app is one that makes sense to only have one window, we would certainly love a feedback about that, though.
Alexey has asked
One more question about using NavigationLink. It works when I put it into .commands. Is it possible to add it to toolbar?
I do not believe we support that, though feedbacks are always welcome for this as well.
@Jon has asked:
How can I control the ideal size of a UIViewRepresentable View? I have a lot of trouble getting correct automatic sizing of wrapped views, especially if they wrap UIStackView. Any recommendations for getting proper automatic sizing so I don't need to use fixedSize so much?
Raj (Apple)
Try implementing intrinsicContentSize on your view.
@Loic has asked:
Hello! I have tried to implement with SwiftUI a common feature in iOS apps that is when tapping again a tab item of a TabView with a nested NavigationView, it pops to the root view of the navigation view. Using presentationMode.wrappedValue.dismiss() it just chains the view dismiss, any idea of how to implement this in a more clean way? (mine feels "hacky")
Curt (Apple)
Thanks for the question! I’m afraid I don’t have a great answer here, but there are a couple of options you can try.
One option is to use a representable to embed a UITabBarController, so you can hook the delegate methods.
Another option, if you can detect when the user taps the same SwiftUI tab again, is to decorate your NavigationView with .id(counter), where counter is @State private var counter = 0.
Then when the user taps the same SwiftUI tab again, you can increment counter, which changes the identity of the navigation view, causing SwiftUI to replace it.
@Jason has asked:
When I've needed to inject data into a detail view, but still let the view have ownership of its StateObject, I've used the StateObject(wrappedValue:) initializer directly in my view initializer, for example:
Is this an acceptable use of the initializer? I know StateObject is only supposed to initialize at the start of the View's lifetime, and not on subsequent instantiations of the View value, so I want to make sure I'm not forcing it to re-allocate new storage each time the View is re-instantiated.
Luca (Apple)
Yes, this is an acceptable use of the initializer and your understanding is correct: that object will be created only at the beginning of the view lifetime and kept alive. The StateObject ’s wrapped value is an autoclosure that is invoked only once at the beginning of the view lifetime. That also means that SwiftUI will capture the value of the plan when is firstly created; something to keep in mind is that if you view identity doesn’t change but you pass a different plan SwiftUI will not notice that.
@Abizer has asked:
I have a framework that vends a SwiftUI View to the main app via a HostingController. The view handles everything it needs internally, so all the types are internal. The only public method is the one that vends the HostingController. In order to maintain the isolation I do it this way:
Yes, that’s an OK usage, particularly because it’s at the base of the hierarchy view and not used to add dynamism.
But there are other ways to encapsulate the exact type of the hosting controller, for instance returning an upcast or custom protocol type
Returning it typed as UIViewController instead its actual `UIHostingController<..>` type
Creating a protocol with the extra API that clients might expect from the view controller, and returning it typed as that
Or by using a container UIViewController that has a child of your hosting controller.
@Samuel has asked:
Without having to jump to ios15 for focusedSceneValue, is there a recommended way for non-document windows in a window group to surface a kind of "current object" that can be used to activate/deactivate menus & send commands to, even if no editable control as focus? (in SwiftUI)
Taylor (Apple)
Good question! focusedSceneValue provides a way for the current focused/key window to surface its values to @FocusedValues in menus and other places; this is especially important on macOS since an app can have many windows, but only one of those is key.
As for other approaches, it somewhat depends on your use case: if you do need your current object to be tied to the key window specifically, there isn’t a substitute. But if you more want to aggregate information across all of your windows, you could use things like preferences and the environment to manually pass that data around
@James Michael has asked:
If we create custom property wrappers that embed existing ones (like State, ObservedObject, etc) does SwiftUI still "see" those embedded wrappers and do the right thing?
For example, can I create a `@Whatever` wrapper as a convenience for `@Environment(\.whatever)` and still expect that to work the same way?
Luca (Apple)
Yes, you can make it do that. As long as you add conformance to the DynamicProperty to your wrapper this will work.
James Michael
Thanks, Luca! I didn't know about that protocol.
Would I also need to implement update() or is the default implementation sufficient?
Luca (Apple)
No need to implement the update() requirement.
Chang
When implementing DynamicProperty to my custom property wrapper, only those have SwiftUI’s property wrapper (like State, ObservedObject, etc) will trigger SwiftUI view’s update, isn’t it?
Josh (Apple)
Or any other DynamicProperty . State ObservedObject and company all implement the DynamicProperty protocol as well.
Chang
For example, I want to make a property wrapper called OptionalObservableObject, that when an optional state changes or an internal observable object fires a change notification, this wrapper will trigger SwiftUI update. But I thought I cannot do it.
Oh, that’s it. So I cannot implement DynamicProperty by myself.
Josh (Apple)
Your DynamicProperty should be implemented in terms of existing DynamicProperties, correct
@Richard has asked:
Is it possible to trigger keyboard shortcuts on buttons in SwiftUI Menus? I've tried a few methods and can't seem to get it working. Filed as FB8895536.
struct ContentView: View {
@State private var showColor = false
@State private var showText = false
var body: some View{
NavigationView{
Color.yellow
.frame(width: 200, height: 200)
.cornerRadius(10)
.navigationBarTitle("Keyboard")
.sheet(isPresented: $showColor){
Color.yellow
}
.sheet(isPresented: $showText){
Text("Hello, World!")
}
.toolbar{
ToolbarItem(placement: .confirmationAction) {
Menu{
Button("Show Color"){
showColor.toggle()
}.keyboardShortcut("1", modifiers: .command)
Button("Show Text"){
showText.toggle()
}.keyboardShortcut("2", modifiers: .command)
} label: {
Image(systemName: "ellipsis.circle")
}
}
}
}
.navigationViewStyle(StackNavigationViewStyle())
}
}
Harry (Apple)
Currently, only stand-alone buttons or toggles work with the keyboard shortcut modifier. Menus, buttons within a menu are not currently supported, though a feedback detailing this request would always be helpful!
@Rhys has asked:
Is there a way to disable the scrolling on a List so I can use it within a ScrollView? I have a ScrollView, inside which I'm placing an Image, some Text, and then I would like to be able to place a List. However, currently, the List doesn't appear unless I give it a fixed frame size, and the List is also separately scrollable. Is there a way around this?
Curt (Apple)
Thanks for the question!
I’m sorry, but that’s not currently supported. :pensive:
If you would be able to file an enhancement request Feedback with your use case, I’d really appreciate it! I know Feedback can seem like a black box (sorry about that), but they really do help us, even if we can’t respond directly to every request.
@Samuel has asked:
Is there a way for a view to know when the window it is hosted in is key or not on macOS?
Jeffrey (Apple)
Could you elaborate some on what you’re trying to achieve? There is the controlActiveState property on the Environment, but there is also focusedValue(:_,:_) and @FocusedValue which are used in the context of the key window on macOS.
Samuel
Track when a window that has no "focusable" controls becomes key. I wasn't aware of controlActiveState ... I will try it out (hoping it works for the "root" view of a key window) (edited)
I just tested & it does update as expected.
maybe a little bit of a discovery issue with swiftui. I had never run across that environment property before & how it can be used.
Jeffrey (Apple)
This can be useful if you are building a custom control that needs to change its appearance based on the window’s active state.
@William has asked:
The `State` documentation states “It is safe to mutate state properties from any thread.” What does it refer to considering that SwiftUI complains with runtime warnings when a `PassthroughSubject` published from a non-main thread?
Luca (Apple)
State is thread-safe and can be mutated from any thread.
When you mention PassthroughSubject I would imagine you are using that in the context of an ObservableObject with either one of @StateObject, @ObserverdObject, or @EnvironmentObject. ObservableObject does require that you mutate all the properties observed by the view, and publish objectWillChange on the Main Thread.
I am using it with onReceive and a CurrentValueSubject to set a State within a modifier. Should I file a feedback?
@Luca (Apple) I’ll mention you just in case you haven’t seen my reply. Thanks!
Luca (Apple)
I see. Yes, currently onReceive expects you to publish on the main thread.
Is there a reason why you could not hop on the main thread?
In general, notifying and doing mutation for code that interacts with the UI on the main thread is going to help you avoid a lot of headaches.
@Luis has asked:
Is there a way to customize NavigationView's navbar other than `UINavigationBar.appearance`?
Sam (Apple)
Unfortunately, there isn’t any SwiftUI native way to do this right now. If this is something you want, please file a feedback report requesting the feature!
@Linus has asked:
What is the handlesExternalEvents modifier? I’ve seen it used, but don’t know what it is.
Jeffrey (Apple)
This modifier allows you to specify a Scene to be used when external data is sent to the app - a URL or an NSUserActivity . For example, on macOS, when a URL comes in that matches the pattern, we will either create a new window for that Scene, or use an existing one, if a view has been modified with handlesExternalEvents to prefer that incoming value.
Alexey
About that. In my case, I always get a new window when I use handlesExternalEvents. How I can tell the system to use an already existing window and not to create another one?
You can apply the view modifier with the preferring: parameter - if an existing window prefers the incoming value, we will use that rather than creating a new one.
Jeffrey (Apple)
Depending on your use case, you may wish to use this in tandem with onOpenURL.
Linus
How so?
Jeffrey (Apple)
The handlesExternalEvents modifiers are a way to tell the system which Scene to choose, if you have more than one. onOpenURL will get the actual data, though.
Linus
oooh, that makes sense! Thanks!
Alexey
@Jeffrey (Apple) Thank you for preferring:! Now it works perfectly.
@Christopher has asked:
Does SwiftUI expose enough API that would allow us to build our own `Lazy{H,V}{Stack,Grid}` with a fully custom layout, or are there still a lot of "magical bits" under the hood that prevent us from doing so?
Sam (Apple)
Unfortunately, we don’t offer support for building custom layouts today.
@Chang has asked:
In most cases, the layout behavior with `Spacer` can be replaced with `.frame(maxWidth:,alignment:)` (or height) seamlessly. Since Spacer is an actual View that is arranged within the view hierarchy, using Spacer will consume more memory and CPU resources. And Demystify SwiftUI also says "modifier is cheap".
So should I use `.frame` instead of `Spacer` as much as possible?
Josh (Apple)
While Spacer is a view, it doesn't end up displaying anything of its own so it is plenty lightweight. Using .frame can have other behavior introduced to the way the view gets laid out beyond just changing its size. They both have their uses, so use them each where appropriate.
Sam (Apple)
To add a little more to this, even in cases where you will get almost entirely the same behavior between the two, the performance difference will be so minimal that I would strongly suggest prioritizing code readability over performance/memory use to make this decision. If Spacer makes it more clear what layout you’re trying to specify, use that, and vice versa.
@Wilson has asked:
Is there a way to disable pixel rounding in SwiftUI? The hover effect from my OS X Dock yesterday is jittery because the padding is being applied to many views in a row, but rounded to the nearest pixel for each view. This inaccuracy in padding adds up, making the entire view jitter by a few pixels whenever I move the mouse.
Josh (Apple)
SwiftUI layout is always rounded to the nearest pixel. But using any GeometryEffect won't take on the snapping behavior. Things like .offset and .scaleEffect are existing ways to achieve this, but you can also implement your own GeometryEffect if you need something custom.
Wilson
@Josh (Apple) How about .frame?
Josh (Apple)
.frame is a layout modifier and not based on a GeometryEffect.
@Natanel has asked:
Can you explain the AttributeGraph that comes up while debugging sometimes?
Curt (Apple)
I’m afraid we can’t discuss implementation details, but be sure to check out the talk “Demystify SwiftUI” for the details of the dependency graph. https://developer.apple.com/wwdc21/10022
It’s a great talk and should help provide intuition on how things work.
@Michael has asked:
Is there any way to set/control backButtonDisplayMode from SwiftUI?
Curt (Apple)
Thanks for the question. I’m afraid there’s no SwiftUI API for this currently.
@Víctor has asked:
When using View.frame(minWidth:, idealWidth, maxWidth:…) are there any combinations of parameters that are more performant than others? Any combination to avoid?
Josh (Apple)
There isn't really any performance difference between any combination of parameters. You should just use the appropriate values to get the layout you're looking for.
Víctor
Does this also apply when specifying a maxWidth/maxHeight of .infinity?
Josh (Apple)
Specifying an infinite max width just means that the view will try to take up all the space it is offered.
By setting a minimum width of 0 and a maximum width of .infinitiy you're making the view completely flexible, which potentially allows SwiftUI to take some shortcuts with the layout calculations. I wouldn't expect it to be massively different performance-wise though.
@William has asked:
How do we avoid incurring in `Bound preference SizePreferenceKey tried to update multiple times per frame`?
Curt (Apple)
It sounds like you have a cycle in your updates. For example, a GeometryReader that writes a preference, that causes the containing view to resize, which causes the GeometryReader to write the preference again.
It’s important to avoid creating such cycles. Often that can be done by moving the GeometryReader higher in the view hierarchy so that its size doesn’t change and it can communicate size to its subviews instead of using a preference.
I’m afraid I can’t give any more specific guidance than that without seeing your code, but hopefully, that helps you track down the issue!
@William has asked:
Why does a `UIViewRepresentable` update once after `makeUIView` and once before `dismantleUIView`?
Raj (Apple)
The update function can be called for a number of reasons. It will be called at least once after make as the UIView becomes extant. It may be called multiple times before the UIView is eventually dismantled. You should not rely on any frequency (or lack thereof) of update calls.
William
@Raj (Apple) I have a UIViewDiffableRepresentable that conforms to Hashable to check whether properties have effectively updated and therefore prevent the updateUIView call to go through to expensive logic. Is it overkill? Is there a more sensible approach? (edited)
Raj (Apple)
It's overkill— the framework is able to call updateUIView when properties outside of the representable struct have changed, like the environment. So, you'll probably drop updates on the floor with this approach.
@Chang has asked:
If I have to embed a SwiftUI View into a UIViewController to do something only available for UIViewController (like customizing the appearance that SwiftUI not support), is there any problem with the following two ways?
1. Create a UIHostingController<MySwiftUIView> A, add A's view as a subview to my own UIViewController B's view. (of course do some layout and `addChild` `didMove` stuff)
2. Inherit UIHostingController<MySwiftUIView> directly to override some VC methods.
Josh (Apple)
Either approach is fine! Please do file feedback though about what you're trying to achieve that requires wrapping the SwiftUI content like this.
@Yueran has asked:
Could we use .prominent Window Scene Presentation Style in SwiftUI?
Jeffrey (Apple)
Hi, we do not have support for this, but would love a feedback with any specifics you could provide about your use case.
@Thomas has asked:
What is the recommended architecture when using SwiftUI?
In a lot of the demos (and this is probably my inexperience showing) it seems that the business logic leaks into the view.
Would you recommend using MVVM for example and how would you set that up?
Curt (Apple)
Thanks for the question!
Josh S. gave a great answer to this in the Q&A yesterday:
“For me, the most important thing is that you have a software architecture in mind that works well for you, and helps you craft maintainable apps with a good separation ways to achieve that, but they’re by no means the only one. If you’ve got a well-considered architecture that works well for you, I think that’s great and helps you craft maintainable apps with a good separation ways to achieve that,”
@Adrien has asked:
When an ObservedObject is passed into a view, does SwiftUI differentiate between views that actually use it (the object is used in the body) and 'intermediate' views (which just pass that object to a child? )? Or are all views just invalidated?
Luca (Apple)
Yes, there is a difference. If you don’t use any of the ObservableObject property wrappers (@StateObject, @ObservedObject ) the view would not observe and update the instance. So you just need to pass an ObservableObject through some intermediary view just make it a regular property on the view but make sure to use the property wrapper if you ever read any of the value in the view, otherwise, your view will no be consistent with your data.
Also, @EnvironmetObject is a great tool when you have an ObservableObject that you want to pass down multiple levels of your view hierarchy without having to manually do it every step of the way.
@Jason has asked:
What's the recommended way to share SwiftUI code between multiple platform targets? Recent Xcode multiplatform templates simply make source files members of multiple targets, but a few years ago the recommendation for sharing code between e.g. an app target and an app extension was to create a shared dynamic framework (e.g. MyApp, MyAppExtension, and MyAppKit). What would be the pros and cons of either approach?
Curt (Apple)
It really depends on the complexity of your particular app.
The templates are the fastest way to get started and aim to minimize complexity. A shared dynamic framework is a great approach as your app grows.
Jason
How about dynamic frameworks vs. SPM packages?
Curt (Apple)
I believe an SPM package can produce a dynamic framework now, but that’s outside my expertise. It might be a good question for the DevTools Lounge or a Swift lab though!
@Brenden has asked:
I was just playing around with the code in the Advanced Graphics section of “What’s new in SwiftUI”. Taylor showed a symbols browser.... I copied the code and when I run the performance is really bad... he showed the smooth gesture with the fisheye and the timeline view animation... on my side, it is super jerky with seconds of delay to render... is that right?
Taylor (Apple)
Hey @Brenden, sorry about that! That’s a known issue in beta 1.
As a quick workaround to see the same smooth effect today, you can manually cache the resolved images.
Brenden
Oh, interesting! Is that on the context?
Or a pointer to how to cache those?
Taylor (Apple)
One thing you could try is moving that resolution to be outside of the inner for loop, so it only happens once. (Cheating since for the code snippet it only uses the swift bird rather than every symbol like my demo had.)
@William has asked:
Is it safe to employ multiple `TimelineView` with the `animation` schedule or is it equivalent to instantiating a number of `CADisplayLink`s? I was thinking of the adage around “reusing” `CADisplayLink`s as much as possible.
Josh (Apple)
Yes! You should be able to use as many TimelineView as appropriate to get your interface behaving how you want. SwiftUI will take care of scheduling things so they update as you want.
The thing to be careful of is to not have too much "different" between each update of the timeline content.
@Jon has asked:
If we break our Views into separate some View properties to help readability, is there much cost to marking those other properties as ViewBuilders to get the nicer syntax? Is that something we need to worry about?
Sam (Apple)
Nope, in fact, we encourage you to do so! Using the @ViewBuilder syntax helps nudge you towards structuring your code in a way that SwiftUI can make use of intelligently, so using it in more places is never a problem. Check out the talk, Demystify SwiftUI for more on this!
I'm wondering about using it to render a single drawn surface that can be nested in a `ScrollView` which is then panned, zoomed into etc.
Is this a reasonable solution and in terms of performance, will there be any guidelines on how far to push it?
I intend to put a whole bunch of images as well as other shape data in there.
Curt (Apple)
This sounds like a good application for Canvas.
Like anything, performance will depend on how far you push it and the hardware it’s running on. I’m afraid that’s not a very actionable answer, but every app is different.
Tristan
Thanks @Curt (Apple), are there any further docs on Canvas we can find?
When using TCA (The Composable Architecture), a new instance of `ObservableObject` (the `ViewStore`) is frequently installed as `@ObservedObject` when the view tree invalidates.
What are the consequences of vending a new instance (possibly with the same properties' values) in terms of performances?
Would it be absolutely better to reuse the same instance if possible, or is it acceptable as `@ObservedObject` installation is quite efficient?
Thanks!
Curt (Apple) 1 day ago
I can’t speak to a third-party framework, but in general, if the observable object is replaced, every view that has a dependency on it must be re-rendered. Check out the talk Demystify SwiftUI for more details on dependencies.