This has been the second year of WWDC Digital Lounges. They gave us a good opportunity to try getting our questions directly answered by the makers of SwiftUI (and other frameworks).
Not everyone has been able to attend, probably due to other commitments, lack of time, failure to register, unfortunate timezones, etc. However, again this year, I took my notes before the channel is taken down (at the moment it’s still available). I asked permission to share these with you, and fortunately they had no issue with it.
I have categorized, curated, and in some cases commented the SwiftUI Digital Lounge questions. For better and faster browsing, I simplified many of the questions to one (or two) lines. Still, the original text can be found inside. I have also removed all names, just in case someone is uncomfortable with it.
If you want to share a question, you may use the hash button at the bottom of each question. It will copy a link to the clipboard. Using this link will load the page, scroll to the question, and expand it.
In some of the questions, I added my own comments, by either expanding on the topic, or appending an example of what is discussed. These questions are marked with 💬.
When a question is related to a new feature introduced in WWDC ’22, I flagged it with a ♦️ symbol.
I also put a ⭐️ mark, on those questions that I found particularly interesting. Some of the reasons why a question gets the star are:
- The answer provides a brand new piece of information.
- The answer from Apple confirms something we suspected for a long time, but wasn’t documented anywhere.
- The answer from Apple confirms a pattern we’ve adopted in the community, but not seen used by Apple until now.
- The question discusses a topic rarely treated and deserves some attention.
- The answer provides some insight into the inner workings of the framework.
- The answer provides design/coding recommendations.
- Or just any other question that made me stop to think 🤔
In some cases, Apple engineers have provided tips that are not related to any specific questions. These have been marked with the 💡 symbol.
After the WWDC, with new betas, some of the answers in here are no longer valid. The cases I detected are marked with ⚠️ and I provide additional comments.
This is not a full transcript of the lounges. Not all questions made it to this page, although a very large number did. Some of them were just too specific to a user’s project, or simply addressed a very well documented and repeatedly discussed topic, others questions I may have filtered because they were either vague or their answer was vague. The lounges are a great part of the WWDC, but because its scopes everything, sometimes things can get lost in there. The idea was to make this page easy to read and reduce as much noise as possible.
If you are looking for last year’s digital lounges questions, you can find them here.
Backwards Compatibility
Are any of this year’s changes backward compatible? ♦️
Full Question: Are any of these changes back-deployable to previous OSes (e.g., like the new Section initialisers were last year).
Answer: Not this year! Last year we had some nice syntactic refinements that we were able to back deploy, but many of the features this year require fundamental new support in the OS.
Is it possible to use SwiftUI Navigation for iOS 15? How can we transition to it on the existing project? ♦️
To learn how to transition your existing projects to the new navigation types, see this handy migration guide: https://developer.apple.com/documentation/swiftui/migrating-to-new-navigation-types
Are there macOS cases that you would recommend using AppKit rather than SwiftUI? ⭐️
Full Question:
Are there any types of macOS apps or interfaces where you would still recommend using appkit rather than SwiftUI?
I’m yet to invest the considerable amount of time learning SwiftUI and I keep reading mentions of people saying it’s still a mixed bag for macOS development, so I don’t want to potentially waste time. Thanks!
Answer:
Good question! Across all platforms, we’d recommend comparing your needs to what SwiftUI provides (so no hard rules/recommendations) — and keeping in mind that you can adopt SwiftUI incrementally.
Within Apple’s own apps on macOS, we’re ourselves using the full spectrum of approaches. From just a specific view/views in an app, e.g. in Mail, iWork, Keychain Access; to an entire portion of the UI or a new feature, e.g. in Notes, Photos, Xcode; and all the way to the majority of an application, e.g. Control Center, Font Book, System Settings.
But in the end, I’d recommend starting with a part you’re comfortable with and building up from there! You should look at SwiftUI as another tool in your toolset in enabling you to build the best apps you can.
What is the recommended way to conditionalize code for view modifiers that are only available on newer versions of iOS? 💬
We recommend factoring out the common parts and then using if #available
checks to use the relevant modifier. Something like this:
let common = commonViewParts
if #available(iOS 16.0, macOS 13.0, *) {
return common.newModifier()
} else {
return common.oldModifier()
}
SwiftUI-Lab Comment: If it helps for code readability or reusability, you can also put those modifiers inside another modifier:
struct ContentView: View {
var body: some View {
Text("Section #1")
.modifier(SectionTitleModifier())
}
}
struct SectionTitleModifier: ViewModifier {
func body(content: Content) -> some View {
if #available(iOS 16.0, macOS 13.0, *) {
return content.underline()
} else {
return content.font(.largeTitle)
}
}
}
Charts
Are doughnut and pie charts also supported? ♦️
Engineer #1: That’s an interesting question about Pie charts specifically. The data visualization academic community right now has mixed feelings generally about pie charts. They aren’t supported in Swift Charts. I’m not data-vis expert though.
Engineer #2: Swift Charts does not support radial layouts so no pie charts or donut charts. However, you can usually show the same data as a bar chart, which may actually be easier to read and is more accessible to e.g. Voice Over users (linear layout is closer to the navigation order in other interface elements).
Can it be used to show realtime (e.g., sensor) data? ♦️
yes, you can use it with realtime data
Any hierarchy tree type charts? ♦️
There is no native support for hierarchical layouts in Swift Charts. You could use it to render the output of a tree layout algorithm (e.g., Treemap) with the rectangle mark but I don’t think you get that much benefit from Swift Charts over SwiftUI in that case.
Any support for rendering this to a scalable PDF beyond embedding as an image? ♦️💬
To create a PDF, you can use the ChartRenderer
API to render a chart into a CGPDFContext
.
SwiftUI-Lab Comment: Check my post about SwiftUI renderers for an example.
Do we get the Audio Charts for free when using native charts? ♦️
Full Question: Do we get the Audio Charts for free when using native charts or do we still have to provide a chart descriptor?
Answer: Yes, you get Audio Graph support automatically. However, the framework can only infer so much automatically so you may still want to override the default to get the best experience for your VoiceOver users.
Custom Grids vs Swift Charts. Which one is better in this case? ♦️
Full Answer: To create a chart-like layout like this, do you recommend using the new Chart library or building it custom with Grids? Are there any performance benefits for one or the other approach?
Answer: You can use Swift Charts. You will get the axes and color scale for free and also better performance (I think but would need to try).
Are charts interactive? ♦️
Full Question: Concerning interactions: is there any onTap
handler (available/planned) where we can react to a users tap and show (more) info of the mark below? (in particular not overlaying a GeometryReady and find out the position/mark on our own).
Answer: There is a chart reader that you can use for interactions. Check out the “Swift Charts: Raise the Bar” talk.
Is it be possible to add a gradient color filling the area underneath the line in a line chart? ♦️
It is! I made one like this while putting the talk together, I can’t remember exactly how I did it. I may have used an AreaMark
that fills the area under the line, and they also stack to make some really cool area charts.
When you’re making those kinds of charts, using Color.clear
as a part of your gradient makes the Chart look a lot neater in light and dark appearances IMO.
Can the new Charts API allow for scrolling? Similar to the health app’s charts. ♦️
Yes, charts should behave just like any other view in this regard. though you may have to set explicit frames on the chart to specify how big (wide? tall?) you want it to be.
Could we utilize a scrollview and a chart to display a live chart? ♦️ ⭐️
Full Question: Forgive me if this question has the same answer as the scrolling chart question, but could we utilize a scrollview and a chart to display a live chart? So new data added is always visible to the user, instead of having to manually scroll it into view.
Answer: Yes, this should work, but will extra work. You may have to observe the data and manually keep the scroll view positioned at the very end.
It sounds like you want to use to be able to scroll backwards through an otherwise-live feed, but if that isn’t the case, the data plotted in your chart could always be just the last 7 elements of an array or something conceptually similar.
User Reply: Yeah, basically keep a horizontal scrollview fixed to the end and allow the user to scroll back to the left (start) of the view. Is there a modifier or additional context to manually position the location of the scrollbar in a scrollview?
Answer: Have you seen ScrollViewReaders?
How would one go about creating a Bubble Chart with Swift Charts? ♦️
You can use the symbolSize(by:)
modifier (https://developer.apple.com/documentation/charts/chartcontent/symbolsize(by:)) to size the points by data.
To make the points look like bubbles, you can use .symbol(Circle().strokeBorder(lineWidth: 1))
so the symbols are drawn as stroked circles.
Here is an example:
Chart(data) {
PointMark(
x: .value("Wing Length", $0.wingLength),
y: .value("Wing Width", $0.wingWidth)
)
.symbolSize(by: .value("Weight", $0.weight))
.symbol(Circle().strokeBorder(lineWidth: 1))
}
How to balance large data sets and performance ♦️ ⭐️
Full Question: I currently have a large data set (over 10k datapoints) that I’m working with. Are there any tips on how to improve the graph performance when working with creating line graphs from data sets of that size or larger?
Context is cycling related data in a macOS app that is presenting 6 or more in a LazyVGrid.
Engineer: We recommend simplifying the data before rendering it. For your example, you could simplify the line or summarize the data points in small intervals (let’s say you have data for a year, you summarize the data for a day). The added advantage is that you can summarize using mean, min and max and show the full range within the small interval.
User #2: an Apple resource I found helpful with this kind of thing is using antialiasing filters to resample the data. https://developer.apple.com/documentation/accelerate/resampling_a_signal_with_decimation
Engineer: Another useful resource are the number and date bins we released this year: https://developer.apple.com/documentation/charts/datebins and https://developer.apple.com/documentation/charts/numberbins.
Can Swift Charts be adapted for non-discrete data, like curves, etc? ♦️💬
You can render curves by sampling data points along the function and then rendering that. You can add interpolation to make the line look smooth. Swift Charts cannot directly render functions.
SwiftUI-Lab Comment: To add a curved interpolation, use the interpolationMethod()
:
LineMark(x: .value("Label", dt.label), y: .value("Value", dt.value))
.foregroundStyle(.yellow)
.interpolationMethod(.catmullRom)
Is there any sample code or documentation on how to display vector fields with Swift Charts? ♦️ ⭐️ 💬
Answer: Like this?
This screenshot is from the Hello Swift Charts talk. You can use a custom symbol (Arrow
).
Chart(data, id: \.x) {
PointMark(x: .value("x", $0.x), y: .value("y", $0.y))
.symbol(Arrow(angle: CGFloat(angle))
.foregroundStyle(by: .value("angle", angle))
.opacity(0.7)
}
...
struct Arrow: ChartSymbolShape {
let angle: CGFloat
let size: CGFloat
func path(in rect: CGRect) -> Path {
let w = rect.width * size * 0.05 + 0.6
var path = Path()
path.move(to: CGPoint(x: 0, y: 1))
path.addLine(to: CGPoint(x: -0.2, y: -0.5))
path.addLine(to: CGPoint(x: 0.2, y: -0.5))
path.closeSubpath()
return path.applying(.init(rotationAngle: angle))
.applying(.init(scaleX: w, y: w))
.applying(.init(translationX: rect.midX, y: rect.midY))
}
var perceptualUnitRect: CGRect {
return CGRect(x: 0, y: 0, width: 1, height: 1)
}
}
This is not the complete code but should give you enough to make the example.
SwiftUI-Lab Comment: Remember to include chartForegoundStyleScale()
to have multiple colors, otherwise the chart would look monochromatic:
Chart {
...
}.chartForegroundStyleScale(range: Gradient(colors: [.green, .yellow, .red]))
In the example of the answer, the color is reflecting the angle of the vector, but since the angle is already shown by the symbol, you may want to use the color to depict the vector’s magnitude instead. You may do so, changing the .foregroundStyle(by:)
call accordingly.
How easy is it to support interactions like pan-to-move and pinch-to-zoom? ♦️ ⭐️
Answer: For pan-to-move, you can use a SwiftUI gesture in conjunction with ChartProxy
. From the gesture, you get the the pan distance, and then you can use ChartProxy
to figure out the pan distance in the data domain. Then, you can set the domain for the X scale with .chartXScale(domain: start + offset...end + offset)
, where you can adjust the offset to pan the chart.
User#2 Reply: Can a similar strategy be used for pinch-to-zoom?
Answer: Yes, you can use a pinch to zoom gesture (or any other gesture) and hook up the events in a similar way.
OP: Without location information (like the new SpatialTapGesture
) it would be a bit unnatural as we wouldn’t know where to center the zoom, right? MagnificationGesture
doesn’t provide location information, unless I’ve missed something.
Answer: Feel free to file a feedback to SwiftUI for providing location information to gestures.
For now I think you can try implement a UIView
with UIPinchGestureRecognizer
and then wrap the view with UIViewRepresentable
so it can be used in SwiftUI.
Is there multiple axis support? Logarithmic axis support? ♦️
A Swift Charts chart can only have one axis per x and y. Swift Charts supports logarithmic scales. For example, you can specify a log scale on x with .chartXScale(type: .log)
You can however, have multiple sets of labels / ticks / grid lines by adding multiple AxisMarks
. They will share the same scale, but might be useful for things like displaying both C
and F
on the same chart
Is there a way to have multiple scales per chart? ♦️
Full Question: Is there a way to have multiple scales per chart? Let’s say I’m doing weather and I want one line with cloud cover (a 0-100% scale) and rainfall per hour (in, say, mm). The x axis on both of those would be time, but the y axes are different.
Answer: An axis always has one scale. So x and y have one scale. You can have multiple axes to display e.g. temperature in C and F. If you want to show multiple measures, I would recommend multiple charts with aligned x-axes. You can even hide the axis of the upper chart to create a more compact view.
Is there a method to modify or specify the label style of a chart? ♦️
Full Question: Is there a method to modify or specify the label style of a chart? For instance, my chart includes price averages for home heating oil. I want to clearly display on the label that the Double is in dollar format on the Y axis.
At this time, when I attempted a brief demo app it simply made the Y axis label as an Int.
Answer: Yes, there are a few options here. You can use AxisMarks(format: FormatStyle)
or AxisValueLabel(format: FormatStyle)
or AxisValueLabel { Text(\(..., format: ...)) }
If your units are more verbose, a design alternative is to clarify them in the title or headline of your chart, to save horizontal space on the y axis.
Does Charts support polar coordinates?♦️
It does not, but feedback is appreciated!!
How can I add a text or SF Symbol at the top of a vertical BarMark? ♦️
You can use the annotation
modifier to add an annotation on top of the bar, where the content of the annotation is a SF Symbol image. Here’s an example:
BarMark(...)
.annotation(position: .top) {
Image(systemName: "sfsymbol_name")
}
Note that you can also use Text
or other views as the content of the annotation.
Can we transform a mark when the user presses and releases it? ♦️
Full Question: Can we transform a mark when the user presses and releases it, like we could do with a ButtonStyle
. If not, we can probably use an overlay, but can we change the size of marks without changing their values (for example with a scale effect)?
Answer: Currently we don’t have a scaleEffect
modifier. You can use something like width: .fixed(hovering ? 100 : 80)
in BarMark
constructor. This will make the bar wider when hovering
is true.
What’s the best way to that we can use Swift Charts in a predominately UIKit app? ♦️
Sara has a session on this where she adds a Swift Chart to a UIKit app: Use SwiftUI with UIKit
Controls
Is it possible to provide custom form styles? ♦️
There is a new FormStyle
protocol you can conform to to create your own styles. Please file feedbacks if you find anything you’d like to do with it, but can’t.
Does the grouped formStyle work in Mac Catalyst SwiftUI? ♦️
That style is available in Catalyst, like iOS — but the design hasn’t changed in Catalyst and looks the same as it does previously on iOS.
Tip: Accessibility Quick Actions on watchOS 💡 ♦️
Accessibility Quick Actions on watchOS is super cool. Instead of having AssistiveTouch on all the time, you can have it on just to do quick things in your app with a gesture.
For example, you can enable QuickActions to answer a phone call with just one hand — clench your fist, and it performs the action.
Using .accessibilityQuickAction is an easy way to add that functionality to your app.
Tip: Avoiding tap gestures with button-like elements. 💡 ♦️
Avoid using tap gestures for button-like things. Custom button styles will provide a better user experience, both in interaction handling and accessibility.
Same goes for toggles! If you’re using a tap gesture to mutate some boolean state back and forth, consider a toggle style!
Is there any difference in terms of performance about using Table instead of List on iOS? ♦️⭐️
There shouldn’t be. Tables default to plain lists on iPhone.
Is it possible to set default focus on TextField when it appeared first time? ♦️
Yeah, checkout the new defaultFocus() modifier! 😄
What is the best way to resign (text field) focus from some distant view in the view hierarchy? ⭐️
Full Question: What is the best way to resign (text field) focus from some distant view in the view hierarchy. scrollDismissesKeyboard
is a great step in the direction I need, but I’d like to programmatically trigger that same behavior, for example, on some button tap.
For example, looking at ways to replace this code:
UIApplication.shared
.sendAction(#selector(UIResponder.resignFirstResponder),
to: nil, from: nil, for: nil)
// as an action to perform on a view:
extension View {
func resignFocus() {
UIApplication.shared.sendAction(...)
}
}
Answer:
You can do this with the Focus State API: https://developer.apple.com/documentation/swiftui/focusstate
You want to bind a focus state property to your text field using View.focused(_:equals:)
, and then set the binding’s value to nil
/false
from your button action as a way to programmatically resign focus and dismiss the keyboard when the bound text field has focus.
Making the action available to distant views is a matter of arranging your app’s data flow appropriately. There’s no single answer, but for example, you could declare your focus state property on your scene’s root view and pass a reset action down to any descendant views that need it. Or if the action is created by a leaf view, you can use the preferences system to make the action available to ancestors.
Can the new multiple date picker be used to choose date ranges? ♦️
Full Question: Hello! Talking about the Date Picker with multiple dates, can they allow to choose date ranges (like when you book a flight), so with just two dates possible and the highlight spanning over all of the days in between?
Answer: Currently, MultiDatePicker
only supports non-contiguous date selection. So range selection is not supported at the moment.
Is there a way to use a capacity `Gauge` outside of the lock screen? ♦️
Yes! Gauge is usable outside of the LockScreen, and also has a whole host of new styles!
I would take a look at the linearCapacity
, accessoryLinearCapacity
, and accessoryCircularCapacity
styles. Please note though that gauge styles prefixed with “accessory” are really intended to appear in contexts like the Lock Screen / widgets, or some similar design context within your app proper, so please give careful consideration to where you use them.
Advantages of .buttonStyle(MyCustomStyle()) vs. MyCustomButton(…) ⭐️
Full Question:Using .buttonStyle(MyCustonStyle())
instead of MyCustomButton(...)
is more SwiftUI-y. But why should I prefer one over the other (considering MyCustomButton
uses MyCustonStyle
under the hood)?
Answer: We strongly recommend separating semantic controls and styles as it provides a lot more flexibility.
For example, using the button style modifier will apply to the whole hierarchy, so if you decide you want specific parts of your app to use a different button style, you can just apply it to that specific hierarchy without changing the control itself.
With this approach you also get multi-platform behaviors for free.
How can I control how the focus moves around the controls of my view when I press “tab”? ♦️
Full Question: SwiftUI macOS: How can I control how the focus moves around the controls of my view when I press “tab”? Currently it jumps from top left to right, then down. But I have two columns in my window and would prefer it go down first on the left side and then again up to the right. Is there some functionality to pre-define the order of the controls?
Answer: You can use View.focusSection()
for this, which is newly available on macOS 13.0. Marking a view as a focus section causes the Tab loop to cycle through all of the section’s focusable content as a group before moving on to the next thing in layout order. So, something like this should get you the sort of column-wise navigation you’re after:
struct ContentView: View {
var body: some View {
HStack {
VStack {
FocusableView()
FocusableView()
}
.focusSection()
VStack {
FocusableView()
FocusableView()
}
.focusSection()
}
}
}
Also, a plug for my colleague Tanu’s WWDC21 talk: Direct and reflect focus in SwiftUI
Is it possible to make Form controls look more native on a macOS app made with Catalyst? ♦️ ⭐️
Full Question: Hi, is there any recommended way of making controls inside a SwiftUI’s Form
look more native on macOS in an app made with Catalyst? Would I have to have an AppKit bundle/plugin that renders those SwiftUI views?
Answer: To enable macOS-native UI in your Catalyst app, go to your target’s settings: General → Deployment Info → Mac Catalyst Interface, and switch from “Scaled to Match iPad” to “Optimize for Mac”.
Controls in SwiftUI will automatically adapt to be more Mac-like in that mode. Also check out the new Form styles, to have the controls arranged in a traditional Mac columnar layout.
User Reply: From what I understand, this will put Mac controls into the iOS style Form – it will not enable the macOS style Form. Unless this is changing this year, there’s no way to get native macOS SwiftUI in Catalyst.
Answer: This year, there is the new FormStyle
support, with FormStyle.columns
available on all platforms, including Catalyst. That creates a Form layout like the default on native macOS with trailing aligned labels next to the leading aligned controls
Form {
Picker("Notify Me About:", selection: $notifyMeAbout) {
Text("Direct Messages").tag(NotifyMeAboutType.directMessages)
Text("Mentions").tag(NotifyMeAboutType.mentions)
Text("Anything").tag(NotifyMeAboutType.anything)
}
Toggle("Play notification sounds", isOn: $playNotificationSounds)
Toggle("Send read receipts", isOn: $sendReadReceipts)
Picker("Profile Image Size:", selection: $profileImageSize) {
Text("Large").tag(ProfileImageSize.large)
Text("Medium").tag(ProfileImageSize.medium)
Text("Small").tag(ProfileImageSize.small)
}
.pickerStyle(.inline)
}
.formStyle(.columns)
https://developer.apple.com/documentation/swiftui/formstyle/columns
Data Modeling
How to implement invalidation when conforming to DynamicProperty? ⭐️
Full Question: What is the correct way to implement invalidation when conforming some type to DynamicProperty
? Should hosting another builtin DynamicProperty
enough? Are these wrapped properties like @State
or @ObservedObject
guaranteed to work as expected in DynamicProperty
?
Answer: Dynamic properties are fully composable, so any other dynamic properties you use inside of their declarations will be picked up on and invalidated properly. That means supporting invalidation as you would expect is often just a matter of utilizing the support already built into other dynamic properties, like @State
Is it possible to prevent DynamicProperty invalidation? ⭐️
Full Question: Is there a better way to prevent DynamicProperty
from invalidating a view’s body based on different criteria? I am currently doing this with a private ObservableObject
backing that manages its objectWillSend
calls, which seems to works well but also feels like I am doing some backflips (the context is being able to scope in on specific changes on an ObservableObject
model for performance reasons).
Answer: There is no direct way to prevent DynamicProperty from updating.
What you are doing is a way to do it. If the purpose is to manage ObservableObject
invalidation I would suggest consider refactoring your model into multiple model.
Keep in mind that you can also implement your own objectWillChange
Publisher.
What is the use of DynamicProperty’s update method? ⭐️
Full Question: What’s the intended use for DynamicProperty
‘s update()
method? It seems most of the time the DynamicProperty
‘s members are @State
ful objects managing their own state and using update()
with them requires a dispatch async (to the next runloop cycle).
Answer: You’re definitely correct that the state management dynamic properties do is usually handled using their own sub-dynamic properties, and custom logic! The update method is called directly before the corresponding view’s body
is called, and is more a place for any logic that needs to happen before body runs to occur.
User Reply: So, for things that I assume are not stateful in nature, just derivates of the state?
Answer: That tends to be the case. That said, there may be some examples of property wrappers that use information about when the view is rendered to drive stateful systems. When debugging, I’ll sometimes use a DynamicProperty
with an Int wrappedValue
that increments whenever the surrounding view’s body is drawn, for example.
Extending @State and @Environment ⭐️
Full Question: Using @State
and @Environment
works e.g. in ButtonStyle
even though it’s not a View
. I’m looking to implement a similar API technique in my framework to let users style my custom components. However, I can’t get @State
and @Environment
to automatically work in my custom style structs. What is the recommended way to replicate this behavior so that I can provide such natural-feeling styling API for my custom framework components?
Apple Engineer: We don’t currently support extending our property wrapper APIs like @State
and @Environment
outside of how they are used in SwiftUI.
User #1: I have a child view which has a @StateObject var MyModel but the initialiser to MyModel accepts a parameter passed from the parent view. How do I create MyModel in the initialiser of the child view or is there an alternate way?
User #2: My memory on this is poor but does SwiftUI synthetize an init to pass in stateObject params from the parent?
User #3: To the original poster: not an Apple engineer, but I’ve build something similar in the past by making the struct
conform to DynamicProperty
. SwiftUI will pass through all DynamicProperty
members recursively and inject dependencies (i.e. @Environment
and @State
) before calling body
.
OP: I’m working on a closed-source framework (PSPDFKit) that aims to provide SwiftUI (and UIKit) APIs that feel like first-class citizens, and the “view style” technique that SwiftUI utilizes would be extremely valuable for me to let the users of our framework customize specific parts of components, much like e.g. using ToogleStyle
.
Apple Engineer: @User#3 note that custom types conforming to DynamicProperty
will only work if values of those types are stored within views or other SwiftUI types that generally support dynamic properties like @State
and @Environment
.
User #3: Ah, yes of course, I assumed that was given, outside of it would make little sense. Sorry for bringing a bit of confusion to the table!
User #4: @OP, I’ve done something similar for other closed source things. The technique I am using revolves around creating a protocol/configuration (similar to what you’d find in ButtonStyle
definition) and then injecting it into the EnvironmentValues
via a custom value type conforming to EnvironmentKey
. Then the component (e.g. Button
for the purposes of this description) would internally grab the environment set (via @Environment(\.buttonStyle)private var buttonStyle
)
You’d then just pass the configuration you build into the make*
functions you create as needed.
Hope that makes sense. I’ve really enjoyed this technique in the components I’ve built.
User #1: Since StateObject(wrappedValue:)
is not supposed to be called directly, the workaround i have found is every time my StateObject
tends to depend on some parameter from parent view, I have used an ObservedObject
(instead of a StateObject
) and passed the ObservedObject
from the parent view (edited)
Apple Engineer: @User#4’s technique is an effective way to implement a similar pattern.
OP: @User3 I got more or less as far as you described but I had troubles nesting DynamicProperty
and controlling where exactly do they inherit the Environment from. @User4, thanks for sharing this! I have a similar infrastructure in place but my question was about using dynamic properties like @State
directly in the structs conforming to MyCustomStyle
protocol.
User #4: and to dive a little more down for the State/Environment part of it, I would just pass those through the configuration struct if a style would need it (do this for passing things like isEnabled down).
similar to how ButtonStyleConfiguration
has isPressed
as a value.
OP: Yes, this would come in the Configuration parameter of a makeBody(configuration:) -> some View
function.
What are best practices to avoid state changes affecting unnecessary views?
Full Question: FWIK it’s a good practice to have state changes the deeper in the view hierarchy as you can get, in order to avoid redundant redraws. Are there any best practices to achieve that besides breaking views down to tiny elements and composing them together?
Engineer #1: The right way to structure your state varies by app. I’m interested in understanding your specific use case. Are you asking this when using @State
or ObservableObject
?
OP: Both actually. My use case involves a ViewModel which has @Published
variables, together with diff element which is kind of a half drawer that involves @State
for it’s open/closed state.
Engineer #1: For State, I would generally recommend defining it at the level where you are going to use it.
For your ViewModel it could be useful to break your model into multiple ObservableObject
that are specific to a specific functionality (like a cell, or a single screen).
But I would avoid prematurely breaking you model into very small pieces.
SwiftUI does diff your view and will only re-render if it detects that something has changed.
Engineer #2: Everything suggested in the Demystify SwiftUI Session from a year or two back is definitely useful.
Keeping Identifier
s separate from values is recommended generally (but not a hard and fast rule), especially in Navigation APIs.
Why would anyone use observable object instead of state object?
You might want to use @ObservedObject
if the lifecycle of your model is not owned by your view.
An example is if you are using UIHostingConfiguration
and you have your model owned by UIKit in a UIViewController
and just passed to a SwiftUI cell.
Also, in general you want to use @StateObject
in a parent view, and if you pass the same ObservableObject
instance to child views you can use just @ObservedObject
.
Are there any changes to Core Data/SwiftUI integration this year? ♦️
Full Question: Curious if there are any changes to Core Data/SwiftUI integration this year. Using ManagedObjects with SwiftUI seems to results in View that know a lot about Models or Models that have a mix of business logic and view logic which can be confusing. Any suggested best practices for using Managed Objects with SwiftUI? I tried writing value type wrappers to do MVVM but that breaks down with relationships.
Answer: Great question. There were no additions to SwiftUI in regards to Core Data. However, we do have our CoreData Earthquakes sample app and CloudKit sample apps all showing best practices with SwiftUI and background contexts.
The former performs batch inserts and merges changes on a background thread, then publishes the changes to SwiftUI on the main thread.
This notion of edit propagation is described in the Landmarks app in the SwiftUI Tutorials
SceneStorage not working on simulator ♦️ ⭐️
Full Question: I watched Curt’s Cooking/Recipe section and am trying to understand state restoration. I thought Curt said adding SceneStorage
and the Task
to check if navigationDetail
was nil would do it but it doesn’t seem to work. Do you still need to use the onContinueUserActivity
and then have each view in the stack hierarchy have a .userActivity
to create/describe a NSUserActivity
for that view?
Answer: What I shared in the talk and in the Copy Code associated with it should work. Or check out the sample project here: https://developer.apple.com/documentation/swiftui/bringing_robust_navigation_structure_to_your_swiftui_app
You shouldn’t need to use user activity directly at all for this.
User Reply: Hmm. I tried running it in simulator. I select Apple Pie, select Fruit Pie Filling, press Shift-Cmd-H. Press the stop button in Xcode, and then relaunch the app and it starts at the initial Categories screen. Am I not testing it correctly?
Answer: Ah! Yeah, Xcode kills background storage unless you hold it “just right”. The pattern when working with Xcode, is to (1) background the app in the simulator or on device, (2) wait 10 seconds for the system to do its backgrounding work, (3) kill the app using the Stop button in Xcode. Then when you run the app again, it should use the saved value.
How does objectWillChange coordinates with view updates? ⭐️
Full Question: My question is about some code in the SwiftUI Cookbook for Navigation session. Around 23 minutes in, Curt uses the task
modifier to run an async for
loop over the model’s objectWillChange
publisher. In the loop, he then accesses an @Published
property of the model. But a @Published
property fires the objectWillChange
property before it updates its wrappedValue
. Doesn’t that mean that when the code accesses navModel.jsonData
, it will get the out-of-date model values? Or is there some async magic that guarantees the loop body will not run until later after the objectWillChange
publisher has finished publishing and the @Published
property has updated its wrappedValue
?
Answer: Great question! Task schedules its body on the main actor, which is also where the view update happens. So objectWillChange
enqueues the body of the for loop, but it’s enqueued behind the update of the data model.
is there any way to get my offscreen nested StateObjects to not be deallocated when scrolled offscreen within a Lazy*Stack? ⭐️
Full Question: Hi there, is there any way to get my offscreen nested StateObjects to not be deallocated when scrolled offscreen within a LazyStack? The only solution I’ve found is to “hoist” them to the top level of the LazyStack, e.g. within the Content of a ForEach, but that feels kinda clunky to have to do in every scenario—wondering if there’s another option.
Answer: I think “hoisting” your state into an ancestor is your best bet here. As an aside, StateObject
is more commonly used with model objects and you generally don’t want to tie your model object’s lifecycle to the view.
In the case of a model object it might make sense for an ancestor model object to be maintaining references to these children model objects.
Clearing up controversy on ObservedObject(wrappedValue:) ⭐️
Full Question: There has been a lot of controversy regarding the ObservedObject(wrappedValue: ) initializer. Is it safe (and encouraged) to use it, or do we have an alternative for it this year?
Engineer: This initializer is fine to use! In fact, the ObservedObject(wrappedValue:)
initializer is invoked every time you construct an ObservedObject
, even if you don’t explicitly write it yourself. When you write: @ObservedObject var myObservedObject = myModel
, The Swift compiler converts that standard property wrapper syntax to a call that looks something like: var _myObservedObject = ObservedObject(wrappedValue: myModel)
.
The controversy I think you’re referring to is using that initializer explicitly in the initializer for one of your views. Typically, we see people doing this to allow observed objects to be instantiated with specific information. That is also something which we think is fine to do. We recognize that the ergonomics of that case is not ideal (since you have to access the de-sugared property wrapped (in the example I gave, _myObservedObject
), but it’s not at all harmful.
User #2 Reply: What about the State initializer?
Engineer: The state initializer worries me a bit more. Not because it’s dangerous — it’s totally fine to use it yourself (as I mentioned, the normal syntax is just sugar for the fully spelled out case) — but because I can’t think of as many cases where you need that syntax for @State that aren’t dangerous.
Remember that @State
is initialized once per lifetime of the whole view, not once per time a view’s initializer is called The views representation will be recreated on demand. That means that if you’re re-initializing the state every time the views init is called, you’re going to be clobbering your own state.
So that’s fine to do, but make sure that you’re only using it to set the initial value of a state, and that you’re not resetting your state depending on some initializer value.
User #3: I believe same applies for @Binding and co. too?
Engineer: Yup!
User #3: But for @Binding
is it still harmful? Because source of truth is already on a parent view or somewhere up there?
Engineer: Yeah, binding is also one of the cases that would sound alarm bells for me ⚠️
User #4: I think the confusion comes from this comment:
/// Don't call this initializer directly. Instead, declare a property
/// with the ``State`` attribute, and provide an initial value:
Engineer: As a final note: regarding the “don’t call this initializer directly,” that’s mostly because as I mentioned, the cases where you “need” the underlying initializer (for @State
and @Binding
especially) are few and far between. Most of the time, you’d want to be using the standard property wrapper syntax, so we want to make sure people reading the docs look there first.
Is using @EnvironmentObject for dependency injection of entities not directly related to the view state, considered bad practice? ⭐️
Full Question: Is using @EnvironmentObject
for dependency injection of entities not directly related to the view state, like a Service (to fetch values from network) or a telemetry logger, considered bad practice? Thinking on a MVVM architecture context.
Answer: I wouldn’t consider it a bad practice. Be mindful when using plain @Environment
, that if you’re passing a struct, any change in value will invalidate any views reading that value from the environment. But if you’re using @EnvironmentObject
with a class that’s effectively immutable that shouldn’t be a problem.
With the new “any” keyboard, will be still need AnyView? ♦️ ⭐️
Full Question: with the new any
keyword, will we still need AnyView
to type erase views, or can we declare a collection with any View
now? will this also have the same performance implications of using type erased views?
Answer: Ideally you should not use AnyView
and over the year ViewBuilder
has improved enough that you should be able to eliminate most, if not all, its usage.
That said, yes you will still need to use AnyView
because you need to actually instantiate that type, and not just use View
as a type. any View
is just defining an (existential container) box.
User Reply: is it possible to use view builders as function params or property types without Generics? We are using AnyView
to present content. there are different types of content each represented by an enum
with associated value. This forced us to use AnyView
to have concrete types. What would be your suggestion to move away from AnyView
in this use case?
Answer: If you have finite number of views you are displaying described by an enum could you switch over that enum in body
of a view? ViewBuilder
does support switch
statements.
It really depends on your use case. But most of the time, if you’re in the situation where you think you need [AnyView]
or [any View]
, what you should likely do is invert the view dependency flow and have [AnyDataModel
or [any DataModel]
instead, then create your views based on the type of data provided at runtime.
User #2 Reply: I have similar use case. Sometimes I would like to not couple the view which other views it present, but instead use other Flow/Factory logic that shows whats needed depending on the business logic. It forces me to use AnyView
. E.g.:
struct MainView: View {
@ObservedObject var viewModel: MainViewModel
var channelsViewFactory: (Int?) -> AnyView
var body: some View {
Text("ABC")
channelsViewFactory(viewModel.selectedId)
}
}
...
extension AppDependencies: MainViewFactory {
func makeMainView() -> MainView {
return MainView(
channelsViewFactory: { id in
if loggedIn {
self.makeChannelsView(for: id)
} else {
self.makeSignInView()
}
}
}
}
Answer: In this case, you could introduce generics into MainView (e.g., MainView: View) and then create a container view above MainView to encapsulate the generic constraint(s).
What’s the recommended way to repeatedly fetch data in a SwiftUI app? ⭐️
Full Question: Hi there! What’s the recommended way to repeatedly fetch data in a SwiftUI app, (so that we don’t push updates from a different thread)?
Answer: In general, I would suggest to factor out the logic that fetches the data into its own type. You always want to execute this kind of side effect not on the main thread and the hop back onto the main thread to set the data on the model.
Swift’s actors are a great tool to encapsulate this kind of logic. So for example you could the model that is offered to the view be an ObservableObject
that is bound to the main actor, and have a separate actor (hence running on a separate thread) that takes care of the fetching and report back new data to your observable object.
How can I tune performance, if my whole app state is in the state object of my app struct? ⭐️
Full Question: Assume that I keep the whole app state in the state object in my app struct? This way I can make sure that all the views are in a consistent state and have a single source of truth. How can I tune performance because in this case, SwiftUI starts diffing the whole app view hierarchy on every single change of the app state?
Answer: You’re completely right that with a single observable object, you’ll end up invalidating large parts of your view hierarchy whenever a change occurs. Invalidating even large amounts of the SwiftUI view hierarchy should be an inexpensive operation, as view descriptions are rather lightweight, but if you are running into performance issues here, there are a few things you can do to help. The first recommendation I have is to split out some of the values which are only relevant to a certain subset of views into their own observable object. This is likely going to get you the most performance win of any of these suggestions, but if you don’t want to make that architectural change, there are still some things you can do:
- Avoid marking non-published values of your
ObservableObject
published - (Assuming you’re using
EnvironmentObject
to make sure your singleStateObject
can be accessed throughout your view hierarchy) ensuring you only declare dependencies on theEnvironmentObject
in places it’s needed - And if you still need to optimize further, writing a custom
objectWillChange
implementation for yourStateObject
s which only does invalidation when changes that should actually affect the UI occur (in cases, for example, where published values have multiple different representations that should display in the same manner).
Do ids in a ForEach just need to be unique within that ForEach or globally? ⭐️
Full Question: Do ids in a ForEach
just need to be unique within that ForEach
or globally? For instance using the new Charts, all my data is keyed by date and there are several places where I have adjacent ForEaches that include mostly the same dates.
Answer: The IDs must be unique within the container, so for example if you have a List with two ForEach’s inside it, they must generate unique IDs within that List. (edited)
Making sense of _printChanges() output ⭐️
Full Question: When using Self._printChanges() in the body of Views, what should we pay close attention to? Is @Self printed often harmful? What are the numbers @44 we see when using FetchRequest in the View.
Answer: @Self
self is not inherently harmful. The purpose of Self._printChanges()
is not to indicate that something is wrong but rather as a tool to match your expectation with what is happening at runtime.What you want to be on the look out for is that you don’t do unnecessary invalidation which might cause performance issue.If you see @
followed by any number it means that we couldn’t find the field (property) name, so printed the byte offset of the swift view struct field instead.
Concurrency and SwiftUI ⭐️
Full Question: When it comes to concurrency and SwiftUI, how are you managing creation/injections of your actor objects/business layer so that your views have access to them or do you have any specific recommendations based on the newer Swift concurrency model. I’ve been reviewing through some of the 2021 videos in conjunction with the new discussions this year and I tend to see .shared
in the sample code, which would mean a singleton which I’d frankly rather avoid. We been injecting interactors through the @Environment or @Environment object but this has issues because using @StateObjects we don’t necessarily have access to the @Environment object during init when we’ve been trying to binding subscriptions to our @Published vars. If we use .task {} would we have access to @Enviroment at the time the task closure is run? In this way we could probably set access to our common actors through the @Environment or @EnvironmentObject and then set up flows that just bind to @State via an async Sequence. Is there a way to know when @Environment is actual set for a view so we can respond to in within a @StateObject to set up subscription bindings either through publishers or async sequences?
Answer: For pretty much any property wrapper in SwiftUI its value become available just before the body of the view is called. If you have ever implemented a custom DynamicProperty
that is exactly when update()
is called.
Yes, by the time the .task
closure run your environment is available and you can capture its value.
I just want to remind you that you should not conform an actor to ObservableObject
because the expectation is that all the ObservableObject
instance are isolated to the main thread.
User Reply: Yah, of course the ObservableObject would not be an actor. The actor is that business layer object that we need to call in to perform operations and that’s what I’m trying to figure out how the best way for a view to reach out to them would be. I don’t like making actors singletons as I’d rather have them injected or passed to the View so we can have a test version, etc.
I’ve found that because there is some unknown amount of time between init() of a view or especially the init() of the @StateObject of view and when the environment is set that it’s hard to know when to run configuration code related to this injection.
Answer: It’s only safe to access any of the property wrapper from within body.
Is it bad to break down views? ⭐️
Full Question: For complex views, I often define subviews inside computed vars to keep my body
block more readable. Especially for components that don’t need to be reused elsewhere in the app, so they don’t seem to warrant a reusable struct. Example:
struct MyView: View {
var body: some View {
someText
someButton
}
private var someText: some View {
Text("Hello")
}
private var someButton: some View {
Button("Press") {}
}
}
I’ve heard that this can be bad for performance – is that true? And does using @ViewBuilder on some computed vars have any impact?
Answer: SwiftUI’s traversal of your view tree isn’t impacted at all by whether you chose to use new structs, or computed properties, for small pieces of views, etc. So this is totally reasonable. Using @ViewBuilder here also shouldn’t have a performance impact. I would highly recommend doing so!
The only thing that you should pay attention to is your mechanisms for invalidating your views will be a bit less fine-grained, as you’re making a larger part of your view hierarchy dependent on the sources of truth specified by MyView
. Make sure you’re paying attention to what pieces of your view depend on which pieces of data, and when you see computed properties that have completely disparate dependencies from the rest of the view, you consider breaking those out.
What is the difference between onChange() and onReceive? ⭐️
Full Question: What is the difference between .onChange
and .onReceive
modifier.? .onReceive
is like a combination of .onAppear
and .onChange
, is this the complete and accurate picture?
Answer: onReceive
is specifically to subscribe to Combine’s Publisher types and produce a side effect. onChange
is used to produce a side effect when a property of your view changes. For example you can use that to produce a side effect when the scene phase in the environment changes.
User Reply: Okay, thank you for the explanation! If I use both .onReceive and .onChange on a Published property, will there be a difference in behaviour and is one recommended over the other in this case?
Answer: It really depends on your use case: Is the value equatable? Is it a lightweight event? Does your view have constraints to adhere to, such as de-duping, debouncing, exponential backoff, etc.
Graphics and Animations
Tip: Power of shadows ♦️ 💡⭐️ 💬
Something we couldn’t talk about in the talk (for brevity) was how complex these new shadows are. Not only do they composite on top of each other as you’d expect, but if you apply a shadow on a translucent shape, the shadow will not show through the translucency.
SwiftUI-Lab Comment: Here’s a small example of combined shadows. This example uses two shadows, but you can combine more.
struct ContentView: View {
var body: some View {
let s1 = ShadowStyle.drop(color: .black, radius: 3, x: 3, y: 3)
let s2 = ShadowStyle.inner(color: .red, radius: 15, x: 0, y: 0)
Circle()
.stroke(lineWidth: 30)
.foregroundStyle(.shadow(s1).shadow(s2))
.foregroundColor(.yellow)
.frame(width: 100, height: 100)
}
}
What elements of font animations interpolate? ♦️ ⭐️
Full Question: Terrific updates all around! When animating the Font
on a Text
, when can we expect the font to smoothly interpolate instead of crossfade?
Answer: Generally changing weights and sizes of the same font will interpolate, but not changing fonts or going from one font family to another. For example, italic to non-italic will not animate.
What is best? Storing a View or a closure? ⭐️
Full Question: Not sure if this was already asked before since it’s such a common question. But, what’s the recommended way to use a @ViewBuilder
for custom components: calling it right away in the init()
and storing the view, or calling it later inside the body
and storing the view builder itself?
Engineer #1: We’d generally recommend resolving it right away and storing the view.
Engineer #2: Storing the view will have better performance than storing a closure (potentially avoiding allocations).
Of course, if you need to dynamically resolve a view from a closure (such as if it takes parameters), then storing the closure is also fine!
You can attach @ViewBuilder
to properties! You can actually support it on both view and closure properties, depending on whichever you need:
struct MyView<Content: View>: View {
@ViewBuilder var content: Content
// or
@ViewBuilder var content: (Int) -> Content
}
User Reply: Is this the same for Layout? The example code for AnyLayout shows creating a layout in the body instead of in the View.
Engineer #2: Apologies, not sure I fully understand the question. Just in case: creating views in body is fine, we’re specifically talking about stored properties, and storing views versus closures for creating views.
How do I actually apply Variable Color to an SF Symbol along with the percentage I want highlighted vs dimmed?
You can make a variable color symbol with:
Image(systemName: "wifi", variableValue: signalStrength)
It’s part of initializing the image, not a separate modifier you apply.
What is the advantage of using custom Symbols rather than SVG with `template` rendering mode? ⭐️
In addition to the great color support, using a custom symbol also helps it better fit with text by growing with fonts, respecting bold text, and matching baselines.
Is it possible to create an infinite page collection view? ⭐️
Full Question: Would it be possible to create an “infinite” paged collection view in SwiftUI? Similar to the week view in the calendar app where you can swipe through weeks endlessly. Thanks!
Answer: On way to achieve that would be to have onAppear
on a the individual views that the List
is scrolling over and use that load in more data.
User Reply: What if you want the @State
data to not grow as the user continues to page? Removing items from the array would offset the data and causes a jarring affect.
Answer: One way you can solve the problem is by keeping only identifiers in the @State
so that the storage doesn’t grow too much, and load on the demand the actual data as view are coming on screen.
Will .foregroundColor(.white.shadow(…)) work with custom PNGs/SVGs as well? ♦️ ⭐️
foregroundStyle
s apply only to shapes, image masks, and text. If the Image
is configured as a template, then foreground styles should be applied. If the image is non-template, they won’t.
Is there a way of disabling scrolling on a List? ♦️
Use the new scrollDisabled
modifier. https://developer.apple.com/documentation/swiftui/menu/scrolldisabled(_:)
Navigation: Split Views
Is NavigationView deprecated? ♦️
Soft-deprecated. We recommend against it in new code, but it would not be a compile time warning.
Can an object class (such as an NSManagedObject) be used for the navigation destination data? ♦️
Full Question: Am I understanding right that the navigation destination data cannot be a class object (such as an NSManagedObject) and I would need to use something like a string to represent the object?
Answer: Object instances should work fine.
Can a NavigationLink activation be detected without access to the stack path? ♦️
Full Question: Is there a way to detect that a NavigationLink was activated by the user without having access to the stack path? It was formerly possible by observing the Binding, but I’m not seeing how to do this right now.
Answer: Check out the SwiftUI Navigation Cookbook talk, there is a lot of flexibility that you can get from having full control cover the path.
The path
of a NavigationStack
doesn’t necessarily have to live in the same View
as the stack — it can be higher up in your app’s architecture.
One approach is to bind a path
to the surrounding NavigationStack
. Then use an onChange
modifier to observe the path.
User reply: My concern was to work in a leaf view without any path knowledge, while still being able to locally perform logic when some NavigationLink
is activated. The objective is to avoid leaking the parent model into the child, but still have some logic around navigation being performed by the child.
Answer: Alternatively, you could have a button that appends to the path. To get the data separation you’re looking for, you might need to pass an action in the environment or as a parameter that would do the appending.
User reply: My concern with NavigationLink
with value is that the values are eagerly evaluated.
Answer: Only for links that are on-screen or in an eager container.
Can NavigationStack work with custom matchedGeometryEffect animations? ♦️
Full Question: I’m just wondering whether matchedGeometryEffect
can work when navigating between views using the NavigationStack
? So, for example, imagine tapping on a card on a ScrollView
to open a fullscreen view of the card (i.e. like the App Store) using NavigationStack
/matchedGeometryEffect
?
Answer: NavigationStack
uses the system-standard push-pop animations. You might try creating a custom overlay for this use case, so the card would not be part of the path.
Is it possible enumerate a NavigationPath or replace certain elements? ♦️ ⭐️
Answer: It’s not currently possible to enumerate a NavigationPath
. Because it’s type-erased, the elements could only be exposed as any Hashable
so aren’t directly useful.
Generally, if the set of things that can be added to a path form a closed set, which would be the case if you can usefully enumerate it, I’d recommend wrapping your presented values in an enum with associated types.
User Reply: I’m wondering if having a mutable collection of enum values might be generally more useful than using a NavigationPath.
Answer: Then use an array of that enum instead of NavigationPath.
How to hide the disclosure indicator of a NavigationLink
Full Question: How can I hide the disclosure indicator for a NavigationLink
within a List
? For example, if I was wanting to implement the built-in Reminders app with the grid icons in the List header.
Answer: The disclosure indicator isn’t currently customizable. With the new NavigationStack
and path binding, you can use a regular Button
and append your value directly to the path.
Can we set the NavigationPath from onContinueUserActivity and onOpenURL? ♦️
Hi – great question! This is certainly something you can do, and is a good example of how you can support something like deep linking, for example, by parsing the URL provided to onOpenURL
and using it to construct your navigation path.
Is there a way to show a preview of a view when the user taps and holds on a NavigationLink? ♦️
Full Question: Is there a way to show a preview of a view when the user taps and holds on a NavigationLink
(similar to how a website is previewed when holding on a link in Safari)? I tried using the contextMenu
modifier, but it only seems to render Button
views.
Answer: Yes, use the new contextMenu overload that accepts a preview view.
NavigationLink( ... )
.contextMenu {
Button { ... }
} preview: {
ViewIWantToPreview()
}
Here’s some more detail on that new modifier: https://developer.apple.com/documentation/swiftui/view/contextmenu(menuitems:preview:)
User Reply: Can I have the user tap the preview to open another view (the “full” view) as well? I’m playing around with it in Xcode previews and it seems like tapping the preview just closes the context menu.
Answer: No, it’s not currently supported— please file a feedback request.
Is it possible to navigate from SwiftUI view to UIViewController? ⭐️ ♦️
Full Question: I am curious is it possible to navigate from SwiftUI view to UIViewController
. Is it possible to pop out from the SwiftUI view back to UIViewController
?
Answer: You can push a UIViewRepresentable
onto a NavigationStack
, and use that to wrap a UIView
.
That works with the view-destination NavigationLink
s inside a NavigationView
as well for previous releases.
Are deep links possible? Do they work well? Are there limitations compared to what is possible with UIKit? ♦️ ⭐️
Check out the SwiftUI Cookbook for Navigation session and the What’s New in SwiftUI session for 2 examples of deep links.
They are indeed possible, and we think they work pretty well Of course, there’s a lot of routing to consider with any app — for instance if your deep link is behind some authentication token, you’ll need to do the extra work there.
The general idea is that a deep link is certain destinations presented — in order — on a navigation stack, and with this years’ new APIs you can have full control over what is on the navigation stack.
When using NavigationView, is there a way to start the app with a the sidebar already collapsed (or do it programmatically)? ♦️
Hi – thanks for your question. The new NavigationSplitView
API provides an initializer which takes a binding indicating the visibility of the columns. This can be used on macOS to indicate that the sidebar should be initially collapsed.
There is an example in the documentation here:
https://developer.apple.com/documentation/swiftui/navigationsplitview/
Navigation: Stack Views
Can split view panels have different priorities? ♦️
Full Question: Can split views have priorities set for each panel? For example in a 3 panel configuration, I want the middle view to expand when you resize a window?
Answer: Check out the .navigationSplitViewColumnWidth
modifier.
Does NavigationSplitView support the sidebar on the right? ♦️
Right-hand sidebars are not supported by NavigationSplitView. I’d love Feedback with specifics for right-hand sidebar use cases.
NavigationView issues when switching from split style to stack style. Does the new API fix it? ♦️
Full Question: I remember before from the NavigationView API, that when SwiftUI automatically switches from a split navigation style to the stack navigation style, for example when switching from portrait to landscape on iPhone 12 Pro Max, the selection state wasn’t preserved and some UI glitches happen. Was that all solved with the new APIs?
Answer: It should be easier with the new API, but it’s also possible that the selection issues were caused by how the app modeled the data. I guess I’m saying it’s still possible to write bugs 🙂.
Preserving state when switching to a new destination ♦️
Full Question: Does the new NavigationSplitView preserve state when we switch to a new navigation destination like tab bar in UIKit, or do we need to roll our state restoration, like what we had to deal with using the old TabView?
Answer: If you want to switch to a TabView
in narrow size classes, you’ll want to design a navigation model type that provides selection information for the NavigationSplitView
in regular size classes and a different projection of the same information to a TabView
that’s shown in narrow size classes. I’d love Feedback with specific use cases. I’m very interested in making this easier.
Is there anything different visually, to the user, between a NavigationView and a NavigationStack? ♦️ ⭐️
They’re both structural components, but they do behave a bit differently. NavigationView
renders differently depending on how many views you pass to it and what navigationViewStyle
you set on it. NavigationStack
takes a single root view and always renders as a stack.
Is it possible to switch between two-column and three column layout depending on sidebar selection? ♦️
Full Question: On iPadOS in a NavigationView
, Is it possible to switch between two column and three column layout depending on what is selected in my sidebar?
Answer: It isn’t possible to do that directly. Often those style of UIs can be built by swapping the view in the detail area between a single root view and an HStack with two-elements. That is, instead of conceiving of it as a two or three column NavigationSplitView
, think of it as a two-column one, where the detail column itself has one or two columns.
Is there a modifier to prevent a ForEach list collapsing? ♦️
Full Question: Previously in NavigationView
on iPad, the detail view would be displayed with the ForEach
list being collapsed in a sidebar. With the new NavigationSplitView
, can I use a modifier to not collapse the ForEach
list?
Answer: Thanks for the question. NavigationSplitView
takes a columnVisibility
binding. You can set it to .all
to reveal all the columns programmatically!
What is recommended way to style navigation bar in SwiftUI? ♦️
Answer: Check out the .toolbarBackground
modifier, new in iOS 16 and aligned releases.
User Reply: Does .toolbarBackground
apply to only that view or any view pushed into navigation stack as well?
Answer: I believe it applies to the current view, so you can change the behavior at different levels of the hierarchy.
There’s also toolbarColorScheme
if you need to control the text appearance.
Finally, you can use a toolbar item in the principal
placement to replace the default navigation bar title with an arbitrary SwiftUI view.
Is it ok to pass NavigationPath down to child views as a @Binding or @EnvironmentObject? ♦️ ⭐️
Full Question: With NavigationPath
, I’ve already felt the need to pass it down to child views as a @Binding
or @EnvironmentObject
so that they can influence the stack programatically. Is that a reasonable approach or am I overlooking something?
Answer: Passing the binding or passing a navigation model as an environment object are both reasonable approaches with the API we have available today.
I hope we can make this more ergonomic. I’d love a Feedback with your use case!
I personally like the navigation model approach, since I can easily do other work when the navigation state changes without putting that code in my Views. But either approach works fine.
User Reply: So, a ViewModel (probably observableobject) that dictates navigation?
Answer: Yeah. That works for the way I think about navigation.
Is it possible with SwiftUIs new NavigationStack to hide the tab bar of a TabView when the destination view appears? ♦️
Take a look at the new toolbar visibility accepting modifier. This is new in SwiftUI and allows configuring the hiding or showing of different bars like the navigation bar, or the tab bar.
ContentView()
.toolbar(.hidden, in: .tabBar)
See https://developer.apple.com/documentation/swiftui/presentedwindowcontent/toolbar(_:in:)
New Grids
Can Grid cells span rows and columns? ♦️
They can span columns. See .gridCellColumns(_:)
Does Grid replace LazyVGrid? ♦️ ⭐️
LazyVGrid
and LazyHGrid
arrange their children lazily and so are great fits for large amounts of content within a scroll view.
Grid
requires all of its children be loaded up front and because of that has some powerful features that the lazy grids do not.
Are there any special considerations needed when putting a Grid inside a ScrollView? ♦️
Grid is eager, so putting it in a ScrollView can create performance issues, unless you’re certain the number of items is small.
Can a cell span partial columns in a Grid? ♦️
Full Question: Can a column span partial columns in Grid
? i.e. If I have 8 items in a three column Grid
, I want the last row to have two cells that are 1½ columns each.
Answer: The column spanning uses an integer, but you could try doubling the number of columns you’re using
New Layout Protocol
When should we use custom layouts vs native elements. When to use each? ♦️
Full Question: With graphics and custom layouts being added, when should we use native elements vs custom styling? How far should custom designs go?
Answer: It entirely depends on your use case! We ultimately trust your sense of design on this one since only you know what’s right for your app. To give some general guidance though: Take native elements as far as you can, and if you find you need further customization beyond that to finely polish the experience, then don’t be afraid to use it!
One quick disclaimer: It can be easy to drop to custom layouts prematurely. Stacks and Grids are incredibly powerful already, so be sure you can’t get what you need with them before you use the new layout protocol.
Does the new Layout protocol animate between VStack and HStack? ♦️ ⭐️ 💬 ⚠️
Full Question: I saw that VStack
and HStack
conform to Layout. Does that mean using an if-else to switch between the two with the same content will now animate the change?
Answer: Yes. To animate between layouts you’ll need to switch between the layouts using AnyLayout
, for example:
let layout = isVertical ? AnyLayout(VStack()) : AnyLayout(HStack())
layout {
Text("Hi")
Text("World")
}
Check out the Compose custom layouts with SwiftUI talk for more details!
⚠️ SwiftUI-Lab Comment: In beta 3, HStack
and VStack
no longer conform to Layout. In beta 4, new Layout types have been added: HStackLayout
, VStackLayout
, ZStackLayout
and GridLayout
.
Here’s a full example:
struct ContentView: View {
@State var horizontal: Bool = false
var body: some View {
let layout = horizontal ? AnyLayout(VStackLayout()) : AnyLayout(HStackLayout())
layout {
Text("Hi")
Text("World")
}
Toggle("Horizontal", isOn: $horizontal.animation(.easeInOut))
}
}
How does ViewThatFits decide if a view fits? ♦️ ⭐️
Full Question: How does ViewThatFits
decide if a view fits? I find it a bit strange that Text with a line limit of 1 still “fits” even if it’s truncated for example.
ViewThatFits
consults the view’s ideal frame size for both dimensions.
The latter behavior isn’t intended. A feedback # and keeping an eye on later seeds is a good strategy here.
User Reply: Adding a fixedSize(horizontal: true, vertical: false)
seems to fix the issue but also seems unnecessary. I’ll file a feedback.
How can i get something similar to autolayout priorities? ♦️ ⭐️
Full Question: how can i get something similar to auto-layout priorities, of being able to have 2 views in a stack equal but only up to a certain width?
Engineer #1: You can see the rendered version of that text in the docs under the heading “Access Layout Values”: https://developer.apple.com/documentation/swiftui/layout
Engineer #2: You can pass any kind of information you like to an adopter of Layout
. So that “certain width” could be passed to the instance of the Layout
itself. And the layout priority can be provided via the subviews proxy of Layout
.
In the swiftinterface file there is discussion of this:
/// Views have layout values that you set with view modifiers.
/// Layout containers can choose to condition their behavior accordingly.
/// For example, a built-in ``HStack`` allocates space to its subviews based
/// in part on the priorities that you set with the ``View/layoutPriority(_:)``
/// view modifier. Your layout container accesses this value for a subview by
/// reading the proxy's ``LayoutSubview/priority`` property.
Thinking about this more….
Reasoning about this in terms of autolayout might make things tricker than they need to be, especially for 2 views. For instance you could instead use a custom LayoutValueKey
to say that View A should take 70% of the proposed size, and View B should take 30%.
Rough sample code:
MyCustomLayout(widthThreshold: 300.0) {
viewA.oversizedWidthPercentage(0.7)
viewB.oversizedWidthPercentage(0.3)
}
Alignment guides and the new Layout protocol ♦️
Full Question: When using the Layout protocol, is it possible to resolve an alignment guide for the layout container, not a subview, to align the subviews relative to the container correctly? For instance, a Layout that proposes size (100, 50) would resolve HorizontalAlignment.center to 50 (if unmodified). If not, what is the correct way to do this?
Engineer #1: You may be able to use the alignmentGuide
modifier to define how an alignment guide for a view will resolve.
User Reply: My goal is to get the CGFloat
value from the container view for its own alignment guides in the placeSubviews
function. (Specifically, to do a layout similar to VStack where the children align in the left middle or right)
Engineer #2: Well, you get the bounds of the region that you are laying out inside of, so you can know the midpoint in each dimension, for example.
You can also set explicit alignment guides for the layout container by implementing either or both of the explicit alignment methods:
What happens to a subview if you don’t call .place() on it in placeSubviews in a Layout? What happens if you call it twice? ♦️ ⭐️
Answer: If you call the method more than once for a subview, the last call takes precedence. If you don’t call this method for a subview, the subview appears at the center of its layout container and uses the layout container’s size proposal.
User Reply: I found this function a bit strange as I was expecting it to return an array of placements instead of running a place function. Is it more efficient the way it’s set currently set up? Actually, one more follow-up. Is there a way to simply not place a view or have it hidden? Imagine a coverflow style view where you only want to show a small set of subviews.
Answer: You interact with the subviews from within the layout protocol via the subview proxies that you get as input. This lets you do things like propose a size or place the view, but not perform arbitrary operations on the view, or apply arbitrary modifiers. If there’s something specific that you’d like to do but can’t, please do file a feedback with your use case.
When is Layout calculated? ♦️
Full Question: When Layout will be calculated? From UIKit experience, sizeThatFits
fail to calculate size on viewDidLoad
.
Answer: Layout participates in SwiftUI’s general update system. It will be computed when any state affecting the layout changes.
Does Layout support lazy-loading its subviews? Would it be a good replacement for UICollectionView with a custom layout? ♦️
Answer: Layout is eager.
User Reply: is eager the opposite of lazy?
Answer: Yes, sorry. Eager means computed in advance. Lazy means computed only when needed.
Can I use custom layout to create a hierarchical tree map/view of rectangular boxes of data items connected with lines?
Layout
could readily handle positioning the boxes. The lines would be the interesting part. You might experiment with using the layout values to route information.
You could also experiment with using Canvas
to do the drawing, though that’s a low-level approach that wouldn’t give you accessibility out of the box. Check out the accessibilityRepresentation
modifier for that.
Do LayoutValueKey values travel up through nested Layouts? Also, how are they different from PreferenceKey? ♦️ ⭐️
LayoutValueKeys
only travel up to the nearest Layout container. One reason for this is for Layout to facilitate building of encapsulations that can be composed without having unanticipated effects on the other views that surround it
Do all system stacks implement the Layout protocol for use in AnyLayout? Is this documented anywhere? ♦️ 💬 ⚠️
The goal is for as many system provided layouts to conform to the Layout protocol as possible, including the stacks. You can find what protocols a type conforms to in the developer documentation.
Yup, scroll down on this page: https://developer.apple.com/documentation/swiftui/hstack
⚠️ SwiftUI-Lab Comment: Until beta 2, Grid
, HStack
and VStack
conformed to Layout
. That is no longer the case. However, starting with beta 4, new layout types exist: HStackLayout
, VStackLayout
, ZStackLayout
and GridLayout
.
How does Layout interact with other view modifiers or constructs that can provide layout data? ♦️ ⭐️
Full Question: How does Layout
interact with other view modifiers or constructs that can provide layout data, like offset, geometry reader, anchor preferences, etc?
Answer: A layout is only able to position its direct subviews.
It can be superior to GeometryReader
because it’s able to do that while also computing a sizeThatFits
.
If you’re trying to arrange things that are crossing parent-child boundaries, though, anchor preferences will likely still be necessary.
Should Layout work fine with UIKit-representable views? ♦️ ⭐️
Yes! To get more control over how UIViewControllerRepresentables
size themselves in SwiftUI layout you may want to implement their new sizeThatFits method.
Is it possible to use @State, et. al from within a custom Layout? ♦️ ⭐️
Full Question: Is it possible to use @State
, et. al from within a custom Layout
? Or is there a different way we should parameterize our layouts? I noticed that the protocol doesn’t inherit from View
, so I wasn’t sure if using the property wrappers would work.
Answer: You can add input parameters to your layout, and you can attach values to particular views using the LayoutValueKey
protocol. To store data between calls into your layout, you can use the cache, but that’s only to avoid repeated calculations, and you should be prepared for the cache to go away at any time.
User Reply: Okay, so anything stateful should live in the parent view (and be passed as init args) or in a child view (and be read through a LayoutValueKey
)?
Answer: Yes.
Is it possible to compose some HStack and VStack within my custom Layout? ♦️ ⭐️
Full Question: When creating a custom Layout
, is it OK to use another Layout
within the func like sizeThatFits
or placeSubviews
, including the built-in Layout (like HStack
)? More specifically, is it possible to compose some HStack
and VStack
within my custom Layout
? In that case I have to create a LayoutSubview
by myself, is it possible?
Engineer #1: Yes this is an explicit use case the Layout protocol was designed to support.
Engineer #2: Yes, that should be fine, although you’ll need to manage the cache(s) of any intermediate layouts you use. (E.g. store them in your own layout’s cache storage, and invalidate them as needed.)
You can also create subsets of the subviews collection, to pass to the inner layouts… use the LayoutSubviews
subscript(bounds:)
operator for that.
Previews
Tip: Xcode Previews and Tabs ♦️ 💡
We’ve been getting some questions regarding the new previews experience where each preview is given its own tab, and folks are wondering if there is a way to instead have multiple previews in the same tab.
Each view in the PreviewProvider
now shows in its own tab. If the individual views are differently configured rows for a list or can be combined in a VStack
then a single preview could be used for seeing the different configurations. We’d love feedback if you have a unique case that you don’t think is covered by the current solutions!
Tip: New Previews experience with the previewLayout modifier ♦️ 💡
We’ve been seeing some confusion regarding the behavior of the .previewLayout
modifier in the new previews experience which we wanted to address.
the .previewLayout
modifier does indeed still work, but like previous releases it does not affect the live preview which means you’d need to switch to the new Selectable mode to see its effect.
Any tips to make Previews run faster? ⭐️
Full Question: My Previews take generally a long time to render, often failing (timeout) and sometimes it’s quicker to just launch a run on the simulator. Also, it takes some huge place on disk (40GB+). Any hint to optimise all this and make the Previews quick, light and reactive?
Answer: If you’re experiencing long times to render there might be something specific about your setup. Filing a feedback with a sysdiagnose taken while the preview is taking the long time to render would be very helpful for us to diagnose this.
As far as general tips to keep things fast, it helps to break up you views into smaller components where possible. And if your app project itself takes a long time to build, then it could be very helpful to split your Swift UI views out into a separate framework target so they could be built without the burden of the rest of the project.
I think the general theme is trying to focus on building as little as possible. If you can build out those new SwiftUI views in a framework / package, you can opt to build just that package rather than all of the app.
User Reply: Yes I was just thinking about this last advice — although perhaps with a Package rather than a framework.
Answer: Yup, packages would be fine, too. It’s even easier to use them in Xcode now. The larger point is to break out the parts that need Swift UI into their own targets that can be built independently. It might even help to have a separate “previewing” app target that you link your Swift UI views into instead of your main app. It all depends on your setup, of course. The tl;dr is that Previews needs to build some target to get its work done. If your whole app target is large and slow to build then it will be the bottleneck for previews that you can investigate.
What’s the best practice for working with Core Data and SwiftUI previews, such that we don’t end up interacting with the actual store data? ⭐️
Core Data supports pointing to in-memory storage which won’t need the file system and would work great in any situation where you don’t want to persist the changes permanently, even in Previews.
Is there a fix for Previews failing with views in SPM modules? ⭐️
Full Question: Currently Xcode Previews fail to find the .module
bundle (and associated resources) for views in SPM modules. This has a workaround (manually locating the bundle), but adds additional boilerplate code.
I’d like to know it’s a known issue. I’ve seen that Previews got a substantial update in Xcode 14 and I was surprised to see this was not addressed.
Answer: Thank you for the feedback. Yes, we know this is a common issue and are still finding the best path forward to solve it. Thanks for the patience!
How do I see multiple previews at the same time with Xcode 14? ♦️
Each view in the PreviewProvider
now shows in its own tab, and there are two ways to see multiple configurations in one tab.
First, you can see multiple previews in the same tab for variations in device settings by using the new variants mode, accessible from the bottom bar of the canvas.
Second, for multiple previews of the same view but with different inputs, you can make a single preview of a List
or VStack
that contains those different configurations. This is particularly helpful with rows of a List
as the List
will also show those views with the correct list styling and metrics.
Is it possible to print to the console while in SwiftUI previews?
Engineer #1: This is a known issue we’re actively investigating.
Engineer #2: (Also not necessarily the best solution, but sometimes I end up just adding a debug label attached to state and use that as a print…)
FWIW, os_log
and Logger
do work fine in previews and then you can look for those with the Console app like any other syslog output.
Sharing
SwiftUI + UIFrameworks
What would be the recommended way to update cell’s height when used with DiffableDataSource? ♦️ ⭐️
Full Question: Once again, here goes the question about SwiftUI views in Table/Collection cell. 😁 If we’d like to tinker with our own UIHostingConfiguration
to support older OS versions, what would be the recommended way to update cell’s height when used with DiffableDataSource
? Currently, the most common scenario I see is a pair of tableView.beginUpdates()
tableView.endUpdates()
It would be nice to recap on our available options
Answer: On iOS 15 and earlier, you request an update to the size of self-sizing cells in UICollectionView and UITableView
by:
- If using diffable data source, re-applying the diffable data source’s current snapshot with
animatingDifferences: true
- If not using diffable data source, performing empty batch updates on the collection or table view directly (that’s the same as the begin/end updates you mentioned)
Is there a way to coordinate between SwiftUI DragGesture with their UIKit counterparts?
Full Question: Is there a way to coordinate between SwiftUI DragGesture with their UIKit counterparts in the UIGestureRecognizerDelegate like shouldBegin
or shouldRequireFailure
?
Answer: Sorry, no, there’s no interop between SwiftUI gestures and UIKit gestures.
User Reply: How about multiple SwiftUI DragGestures
?
Answer: You can ensure exclusivity with this:https://developer.apple.com/documentation/swiftui/gesture/exclusively(before:)
User Reply: Does it work if there are two DragGestures applying to different views?
Answer: No, the exclusivity is only for a single gesture application.
In SwiftUI, is there a way to listen for navigation events or would that have to be custom from UIKit delegates?
Answer: With the new iOS 16 NavigationStack
, you can bind a path to the stack. Then you can use the .onChange()
modifier to watch for changes to the path.
User Reply: What about non navigation events like dismissal of sheets or full screen modals? Is there a stateful way to be informed of when these events are done so we can say present a follow up confirmation or pop the navigation stack back?
Answer: Those dismiss events will reset the binding that caused the modal presentation to appear. So the same recommendation works there. You can use onChange()
to observe that binding too.
User Reply: What if you’re programmatically changing those bindings? If I set an isPresented var to false to dismiss a modal, how would I be able to know when the modal is actually gone? If I change the navigation backing state right away to pop a view or try and set a sheet as not to display , I’m usually presented with inconsistent UI behavior or a message saying that I’m trying to present / dismiss in an invalid state. I’m usually stuck doing something like asyncAwait after and then playing with the timeframe and that’s, well, gross.
Answer: Ah, I see. The delay hack is one approach. Another approach people seem to have some success with is using a common sheet
modifier for a variety of presentations. Then you can drive the sheet with an optional enum state. Set it to nil
to dismiss, or switch between different non-nil values to present different sheets.
We’re also made some improvements in iOS 16 to make the invalid state issue much less common. For example, we try to delay the presentation of the next sheet until the previous one is gone. So you might find that things just work now.
Is there an analog to AppKit’s flagsChanged notification? ⭐️
Full Question: Is there an analog to AppKit’s flagsChanged notification, to change the appearance of SwiftUI controls based on keyboard modifiers? I’m currently polling on a timer to do this, but that is unsatisfying. (FB9601140)
Answer: You could try using something like this:
import AppKit
import SwiftUI
class KeyboardModifierMonitor: ObservableObject {
@Published var eventModifiers = EventModifiers()
var localEventMonitor: Any?
var globalEventMonitor: Any?
init() {
localEventMonitor = NSEvent.addLocalMonitorForEvents(
matching: [.flagsChanged],
handler: { [weak self] event in
self?.eventModifiers = EventModifiers(event.modifierFlags)
return event
}
)
globalEventMonitor = NSEvent.addGlobalMonitorForEvents(
matching: [.flagsChanged],
handler: { [weak self] event in
self?.eventModifiers = EventModifiers(event.modifierFlags)
}
)
}
deinit {
localEventMonitor.map { NSEvent.removeMonitor($0) }
globalEventMonitor.map { NSEvent.removeMonitor($0) }
}
}
extension EventModifiers {
init(_ flags: NSEvent.ModifierFlags) {
self.init()
if flags.contains(.capsLock) { insert(.capsLock) }
if flags.contains(.shift) { insert(.shift) }
if flags.contains(.control) { insert(.control) }
if flags.contains(.option) { insert(.option) }
if flags.contains(.command) { insert(.command) }
if flags.contains(.numericPad) { insert(.numericPad) }
if flags.contains(.function) { insert(.function) }
}
}
which you would then use in your controls like so:
public struct MyAmazingButton: View {
@StateObject fileprivate var eventMonitor = KeyboardModifierMonitor()
public var body: some View {
let modifiers = eventMonitor.eventModifiers
let isOptionHeld = modifiers.contains(.option)
let isShiftHeld = modifiers.contains(.shift)
User Reply: Nice! Will do. Oh darn! I did try exactly that a year ago. The problem is that while the macOS menu is down, the message is not delivered. The behavior in AppKit of opening a menu, then trying the modifier keys to see what changes while the menu is down is lost.
Answer: EventMonitors
don’t fire during tracking loops, and while a menu is open, AppKit is tracking that via an event tracking loop. You can submit a feedback request to get modifier changes during event tracking. But what it really sounds like is that you want a way to provide alternate menu items via SwiftUI.
Windows, Scenes, Sheets, Popovers and MenuBarExtra
Are windows available for iOS and iPadOS or only macOS? ♦️ ⭐️
For iOS support of Window, please check release notes for future changes.
Using the new Window scene for splash screens ♦️
Full Question: Can Window
be used for splash screen finally (such as What’s New? or News startup) . I’ve had a lot of issues making Scenes
work for Document-based (and opted out on iOS) because it starts an app with a Nondescript Files.
Answer: Window
is likely not the best expression for these types of onboarding experiences. A feedback for this type of functionality would definitely be appreciated though!
Creating an onboarding experience with SwiftUI ♦️
Full Question: Pages and Numbers are both document-based apps which have a beautiful onboarding experience. How do I build such an onboarding experience for my document-based app using SwiftUI?
Answer: Hi – this is a great question! While we do not currently have a high-level concept in SwiftUI for this type of flow, it may be possible to combine the new Window
scene together with the new OpenDocumentAction
and NewDocumentAction
to create it. You would need to define the Window as the primary (first) scene in your app, however. It’s possible there may be some drawbacks to this approach, so I’d love a feedback for an enhancement in this area.
Can a presented view switch between popover and sheet style, depending on size class?
Full Question: Is it possible to have a presented view that switches between the popover/sheet presentation styles depending on the size class (i.e., sheet in compact, popover in regular)?
Answer: Yes! You can do this by creating a custom view modifier that uses looks at the UserInterfaceSizeClass
. Custom modifiers are great for conditional combinations of other modifiers, views, etc. and this is a perfect use case.
We have an example of this use case in the BookClub sample app, in ProgressEditor.swift
.
Is it possible to control a MenuBarExtra window width and height? ♦️ ⭐️
Full Question: When using the new MenuBarExtra
in window style is it possible to control the width and maximum height of the content window?
Answer: Hi – great question. The window size should be derived from the content provided to it, though we do impose a minimum and maximum on the resulting size. If you are seeing unexpected behavior in this area, please do file a feedback with a sample to reproduce it, and we can take a look.
Is it possible to create a windowless app with MenuBarExtra? ♦️ ⭐️
Full Question: When creating a MenuBarExtra
, is it possible to create something that runs on its own, separate from the parent app? I have an app that users can save data to by means of various app extensions. If the app is closed, however, those changes don’t get synced to CloudKit. I was thinking of having a menu bar app that would essentially be my workaround: always listening, always active. Can a MenuBarExtra do that for me?
Answer: Hi – you can certainly define a SwiftUI App
with only a MenuBarExtra
Scene
. If your app should not show a Dock icon, that will require some changes to the Info.plist
file, which should be noted in the documentation for MenuBarExtra
.
Posting that bit of documentation here as well:
For apps that only show in the menu bar, a common behavior is for the app to not display its icon in either the Dock or the application switcher. To enable this behavior, set the LSUIElement flag in your app’s Info.plist file to true.
See See https://developer.apple.com/documentation/swiftui/menubarextra
How to make MenuBarExtra persistent after app quits? ♦️
Full Question: With the new MenuBarExtra
API on the Mac, if I want the menu item to be persistent after my app quits, do I need to do any extra work (e.g. adding a login item) or does the system automagically make sure it appears after reboot/logout?
Answer: Hi – this is a great question. You can certainly use MenuBarExtra
to create a standalone menu bar app, though you may want to look into the Info.plist
changes noted in the documentation:
For apps that only show in the menu bar, a common behavior is for the app to not display its icon in either the Dock or the application switcher. To enable this behavior, set theLSUIElement
flag in your app’sInfo.plist
file totrue
.
If the menu bar app is used in conjunction with a main app, then you’ll need to package it together (ie, the “helper app” model). Adding a login item sounds like the way to go if you want it to show at login regardless of the user’s preference for restoring state.
We’d also certainly welcome any feedbacks for enhancement requests in this area as well.
Other Questions
Any updates related to UI testing in SwiftUI apps? ⭐️
Full Question: Curious if there’s updates related to UI testing in SwiftUI apps? Or should I be thinking more in terms of testing the model layer that drives the declarative UI?
Answer: Our recommendation is still to thoroughly test your model layer. In my experience, the best SwiftUI apps move their logic into model code, using SwiftUI as a declarative mapping from that model state to views. Then your tests of the model layer are effectively tests of the resulting UI as well
Is it possible to reorder items and insert into an outline group?
Answer: SwiftUI doesn’t have much in the way of automatic support for that, I’m afraid. I’d love a Feedback with details of your specific use case. We did expose public API for disclosureGroupStyle
this year, which might be worth investigating.
User Reply: I was thinking this could be a normal list, but there a limits with cross-section item moves that I end up tripping over.
Answer: Makes sense. With the current API, I’d use a view model that vends the items as if they are a flat array, then style the items based on their apparent depth. That would let you use onMove
or the awesome new editing support on List
to update your view model.
When is onAppear/onDisappear called? How do they compare to the UIKit equivalents of did/willAppear? ⭐️
hi, the framework makes no guarantees on the specific timeframes on when these methods are called, but you can be sure that onAppear will always be called before the view is visible to the user and onDisappear will never be called while the view is on screen.
We have recently updated the documentation for these methods online to clarify these details; see
https://developer.apple.com/documentation/SwiftUI/AnyView/onAppear(perform:)
https://developer.apple.com/documentation/SwiftUI/AnyView/onDisappear(perform:)
in terms of a comparison to UIKit, there is not a direct parallel. The UIKit methods are invoked specifically around when views become visible/not visible to the user, whereas the SwiftUI calls are tied more to when the views are constructed/torn down, rather than visually presented.
Difference between ViewModifier and custom Views
Full Question: ViewModifier
question: What’s the difference between a custom ViewModifier
(without DynamicProperty
) that uses some built-in modifiers in body(content:)
, and a custom View
extension func that just use those built-in modifiers?
Similarly, what’s the difference between a custom ViewModifier
with some DynamicProperty
and a custom View
with some DynamicProperty
(also has a @ViewBuilder
content property to receive content to modify) ? I think two have the same render result and behavior.
Answer: Because of the way a ViewModifier
is expressed, the engine knows it’s not changing the content
passed in and can apply performance optimizations (compared to just an extension on View
).
When using .contextMenu on a List , how can I find out what was actually selected? ♦️
Full Question: A macOS question. When using .contextMenu
on a List
, how can I find out what was actually selected? When looking at Finder, the selected item (i.e., item on which the action should be performed on) is either the selection (1 or more) OR it is the item that has been right clicked on directly. How can I replicate this behavior using SwiftUI?
Answer: Check out the new context menu API that accepts a forSelectionType
parameter. This passes the value of the selection into the modifier for you to act on.
List(selection: $selection) {
...
}
.contextMenu(forSelectionType: MySelection.self) { selection in
// check selection
}
Be sure to match the type of your lists selection to the type you provide to the context menu modifier.
https://developer.apple.com/documentation/swiftui/view/contextaction(forselectiontype:action:)
Is there a value of avg. duration that no View’s body should go above for avoiding performance issues?
Full Question: Question regarding the SwiftUI Instrument: Is there a value of avg. duration that no View’s body should go above when rendering for avoiding performance issues/hitches ?
Answer: View’s body properties should be cheap and avoid any potentially expensive work. If there are things you need in your views body, its best to pre-compute that into a piece of state that you can pass your views.
Concretely, each frame has a hard deadline of generally 1/60s or 1/120s (depending on the refresh rate of the display) to render its content. Thats 8ms in the fastest case.
Thats for the entire screen to render its content though, not just your single view . So if any one view is taking more than a few ms to finish calling body, you’ll run into issues.
If you’re using Swift concurrency to move work off the main thread, consider watching this session: Visualize and optimize Swift concurrency. This is all mostly related to scrolling performance. Other kinds of things like app launch or navigation pushes will typically take longer than multiple frames to render, but those situations have different expectations around frame deadlines.
Are there any tools to help implement good design? (e.g., Sketch export tools to generate code for components/symbols)
Full Question: Having implemented several SwiftUI apps, I still consider its constraints concerning custom designs a challenge to communicate to designers. Apart from HIG or trying out, do you know any resources/tooling which helps to implement good design? Like are their Sketch export tools to generate code for components/symbols?
Answer: I know there are Figma (and potentially Sketch) plugins for generating SwiftUI code from designs, however I haven’t personally used them so I can’t vouch for how well they work
Apple also provides design tool plugins, which make it easier for designers to create mock ups that use stock system components and styles: https://developer.apple.com/design/resources/
Of course, nothing is going to be as high fidelity as the designer jumping into SwiftUI directly. Many designers have found SwiftUI to be surprisingly approachable and there are a number of SwiftUI resources available on the internet targeted at teaching designers to code SwiftUI views.
Can onTapGesture be used more than once on a single view? Like single-click to select, double-click to open in a window?
onTapGesture
can indeed be used more than once but you need to get them in the right order, so that the double-tap fails before the single-tap succeeds. Something like this:
ItemView()
.onTapGesture(count: 2) {
...
}
.onTapGesture {
...
}
Tip: Modifying the background of a SwiftUI List ⭐️ ♦️ 💡 💬 ⚠️
Engineer: We’ve gotten a few questions about modifying the background of a SwiftUI List.
In general, you should not rely on implementation details of a framework (including SwiftUI) to achieve specific behaviors in your apps. The release notes did call out the change in implementation of List since we were aware of developers using UIKit methods to style their Lists in the past. This is a known limitation, but we realize this is a popular request, so rest assured we have noted the feedback here and in the SwiftUI lounge!
⚠️ SwiftUI-Lab Comment: Good news! Starting with beta 3, a new modifier exists that will make it possible to work with the background of views with scroll views: scrollContentBackground(_ visibility: Visibility)
By hiding the standard system background, you may put your own:
List {
Text("One")
Text("Two")
Text("Three")
}
.scrollContentBackground(.hidden)
.background(.green.gradient)
Summary
Congratulations for reaching the end of this long post. I hope you enjoyed the SwiftUI Digital Lounge content.
Please feel free to comment below, and follow me on twitter if you would like to be notified when new articles come out. Until next time!
This is fantastic, thank you for doing this again this year!
Thanks so much for capturing and sharing this! I’ve learned more about SwiftUI from this site and SwiftUI Companion than everywhere else combined 🙂