You know how SwiftUI views are customized via "view modifiers" like
Rectangle()
.foregroundColor(.appleRainbow)
right? These are methods which return a new view with the modifier applied, so you can chain them with other modifiers etc.
You may also know that SwiftUI is great for building cross-platform apps with minimal OS-specific changes.
Though, if I want to make something that works differently on iOS and macOS, I have to either write 2 different views with a lot of code duplication and cordon off the OS-specific code in #if os(...)
blocks, even just to use 1 OS-specific modifier (such as .onCommand(_:perform:)
for macOS menus or .onPlayPauseCommand(perform:)
for the Apple TV remote.)
So I just thought, wouldn't it be great if there were view modifiers like
Rectangle()
.iOS(fill(.unicornPuke))
.macOS(foregroundColor(.aquaBeige))
.tvOS(focusable())
and so on. So I fired up a New File and started crackin:
import SwiftUI
public extension View {
func iOS(_ modifier: ?) -> Self {
#if os(iOS)
return self.modifier(...) // ?
#else
return self
#endif
}
}
That's about as far as I got. To do this, we need to
- Pass one of the methods available on
self
as an argument. - Include any arguments for that method.
- Run that method on
self
I have a feeling it might involve key paths, @dynamicMemberLookup
and/or @dynamicCallable
but I cannot at the moment figure out how to put these together.
Is something like this even possible?
Note the syntax should be simply .OS(someModifier(someArgs: etc))
and not .OS { $0.someModifier(someArgs: etc) }
Update: If anyone's interested, I managed to implement it the not-as-elegant way:
@inlinable
func iOS <ModifiedViewType: View> (modifier: (Self) -> ModifiedViewType) -> some View {
#if os(iOS)
return modifier(self)
#else
return self
#endif
}