SwiftUI tips for organizing multiplatform projects
source link: https://www.jessesquires.com/blog/2021/11/19/swiftui-tips-for-organizing-multiplatform-projects/
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
SwiftUI tips for organizing multiplatform projects
19 November 2021
If you are working on a multiplatform SwiftUI project, you will start accumulating #if os()
checks and #if canImport()
checks. Overtime, these start to accumulate and โ in addition to being unsightly โ they make your code much more difficult to read. When possible, I have started to encapsulate these preprocessor directives to improve code organization and readability.
Consider the following example, where a view is constructed with additional Spacer
views only on macOS.
struct ExampleView: View {
var body: some View {
Text("One")
#if os(macOS)
Spacer()
#endif
Text("Two")
#if os(macOS)
Spacer()
#endif
Text("Three")
#if os(macOS)
Spacer()
#endif
}
}
This is overwhelming and repetitive. Instead of repeatedly checking #if os(macOS)
, we can encapsulate the spacer view code to make this more readable.
struct SpacerMacOSOnly: View {
var body: some View {
#if os(macOS)
Spacer()
#endif
}
}
Using this new view results in a dramatic improvement.
struct ExampleView: View {
var body: some View {
Text("One")
SpacerMacOSOnly()
Text("Two")
SpacerMacOSOnly()
Text("Three")
SpacerMacOSOnly()
}
}
We can also use this approach for view modifiers that only exist on one platform. Consider building a List
view where we want to hide the row separators, which only exist on iOS.
List {
Text("Option 1")
#if os(iOS)
.listRowSeparator(.hidden)
#endif
Text("Option 2")
#if os(iOS)
.listRowSeparator(.hidden)
#endif
Text("Option 3")
#if os(iOS)
.listRowSeparator(.hidden)
#endif
}
We can create a new view modifier and extension to encapsulate hiding the separators.
struct HideRowSeparator: ViewModifier {
func body(content: Content) -> some View {
#if os(iOS)
content.listRowSeparator(.hidden)
#else
content
#endif
}
}
extension View {
func hideRowSeparator() -> some View {
modifier(HideRowSeparator())
}
}
And then the original code can be simplified. Unfortunately, you currently cannot apply .listRowSeparator(.hidden)
to the entire List
.
List {
Text("Option 1").hideRowSeparator()
Text("Option 2").hideRowSeparator()
Text("Option 3").hideRowSeparator()
}
Finally, whatโs better than encapsulating these various preprocessor directives into their own components? Avoiding them entirely, of course. You can achieve this by placing files in only the target for the specific platform in which they are needed. For example, if you need to provide a UIApplicationDelegate
, you can include that in only your iOS target. When you separate files into their respective targets, you donโt need any checks at all. However, it is not always possible to structure your code in this way, such as the examples above.
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK