View Source Modifiers

There are many ViewModifiers that can be applied to a View, such as foregroundStyle, background, alert, etc.

These modifiers are defined as functions on the View protocol in SwiftUI.

A modifier function in SwiftUI is represented as a struct in LiveView Native.

// SwiftUI
extension View {
    func bold(_ isActive: Bool) -> some View
}

// LiveView Native
@ParseableExpression
struct _boldModifier: ViewModifier {
    static let name = "bold"

    let isActive: Bool

    init(_ isActive: Bool) {
        self.isActive = isActive
    }

    func body(content: Content) -> some View {
        content.bold(isActive)
    }
}

The @ParseableExpression macro generates a parser based on the init definitions in the struct.

Code Generation

Due to the large number of modifiers in SwiftUI, code generation is used to create modifier types from the functions in SwiftUI.

Swift generates a .swiftinterface file for all frameworks in iOS. It contains all of the public symbols in the framework. For SwiftUI, you can find the .swiftinterface file inside of Xcode at this path:

/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/SwiftUI.framework/Modules/SwiftUI.swiftmodule/arm64-apple-ios.swiftinterface

The code generator parses this file and converts any modifier functions into structs. You can run the generator with the following command.

# in the top level of this repo
swift run ModifierGenerator "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/SwiftUI.framework/Modules/SwiftUI.swiftmodule/arm64-apple-ios.swiftinterface" > Sources/LiveViewNative/_GeneratedModifiers.swift

This will run the generator with SwiftUI's .swiftinterface file as input, and write the generated modifiers to the path Sources/LiveViewNative/_GeneratedModifiers.swift.

The source code for the generator can be found at Sources/ModifierGenerator. In ModifierGenerator.swift, you will find a denylist. To enable a new modifier in the output, remove it from the denylist.

Supporting Types

Modifiers use a variety of types as arguments, such as Color, some ShapeStyle, Angle, etc.

These supporting types can be found at Sources/LiveViewNative/Stylesheets/ParseableTypes.

When the generator encounters a generic argument, it will prefix the protocol name with Any. For example, some ShapeStyle becomes AnyShapeStyle.

If this type exists in SwiftUI, extend it to conform to ParseableModifierValue. Otherwise, create an eraser type that conforms to both protocols.