Skip to content

Latest commit

History

History
164 lines (142 loc) 路 7.89 KB

MigrationGuide.md

File metadata and controls

164 lines (142 loc) 路 7.89 KB

Action Status MIT License

SwiftTheming 馃帹 is a handy light-weight handy theme manager which handles multiple themes based on system-wide appearances - light and dark appearances and overrides the system appearance for the application.

馃憖 What's been changed in Version 2?

As Version 2.0.0 is intended to provide a better developer experience, the architecture of the API has been changed as below.

Declaration of themes

In Version 2, the API comes with Theme structure instead of letting developers create their own theme enumeration and inject it as generic type into ThemeProvider. To declare multiple themes, it is mandatory to conform to Themeable protocol and then extend Theme to list down desired themes with unique identifiers.

extension Theme: Themeable {
    static let bluoTheme = Theme(key: "bluoTheme")
    static let jadoTheme = Theme(key: "jadoTheme")
    
    public func themed() -> Themed {
        switch self {
        case .bluoTheme: return BluoTheme()
        case .jadoTheme: return JadoTheme()
        default: fatalError("You are accessing undefined theme.")
        }
    }
}

Declaration of assets

While all different assets are declared in a single value type conforming to Assetable in Version 1, those are now declared separately by conforming to specific asset protocols (ColorAssetable, ImageAssetable, etc).

enum ColorAsset: ColorAssetable {
    case backgroundColor
    case accentColor
    case borderColor
    case contentColor
    case fontColor
}

enum FontAsset: FontAssetable {
    case titleFont
    case staticFont
}

Defining default theme and appearance

Although the default theme and appearance are injected through themeProviding(defaultTheme:defaultAppearance) in Version 1, it can be done by extending DefaultTheming structure with the conformance of Defaultable protocol in Version 2.

extension DefaultTheming: Defaultable {
    public func defaultTheme() -> Theme {
        .bluoTheme
    }
    
    public func defaultAppearance() -> PreferredAppearance {
        .system
    }
}

It is mandatory to declaring those default values. If this declaration is missed in code implementation, the app will crash when running.

Removal of generic types

Previously, the API mostly relies on generic type passing throughout the call side and it makes developers feel tedious to use. In Version 2, the API no longer relies on those generic types so that ThemeProvider, ThemeProviding and Themed can be used freely without passing generic types.

// ThemeProviding and ThemeProvider
struct ContentView: View {
    @ThemeProviding var themeProvider
    var body: some View { /* some stuff */ }
}

// Themed
class JadoTheme: Themed, Assetable { 
    // some stuff
}

Injecting assets into themes

As the generic type on Themed has been removed, when theme class is created, it is mandatory to conform to Assetable protocol and provide required type alias while inheriting Themed class.

class BluoTheme: Themed, Assetable {
    typealias _ColorAsset = ColorAsset
    typealias _FontAsset = EmptyAsset
    typealias _GradientAsset = EmptyAsset
    typealias _ImageAsset = ImageAsset

    func colorSet(for asset: ColorAsset) -> ColorSet { /* some stuff */ }
    
    func imageSet(for asset: ImageAsset) -> ImageSet { /* some stuff */ }
}

Easy access to interface elements

With the revamp of the API, it is no longer complicated and tedious to access interface elements - Color, Image, Font and Gradient. The API comes with a handy initializer to create interface element based on asset key.

struct ContentView: View {
    var body: some View {
        Color(ColorAsset.backgroundColor)
    }
}

Available initializers

Interface Element Description
Color init(_:appearance:theme:)
- asset: asset for color
- appearance: preferred appearance to override current appearance (optional)
- theme: preferred theme to override current theme (optional)
Font init(_:appearance:theme:)
- asset: asset for font
- appearance: preferred appearance to override current appearance (optional)
- theme: preferred theme to override current theme (optional)
Gradient init(_:appearance:theme:)
- asset: asset for gradient
- appearance: preferred appearance to override current appearance (optional)
- theme: preferred theme to override current theme (optional)
Image init(_:appearance:theme:)
- asset: asset for image
- appearance: preferred appearance to override current appearance (optional)
- theme: preferred theme to override current theme (optional)

Utilizing modifiers

Moreover, there are some modifiers which work the same as SwiftUI modifiers are provided in the purpose of ease.

Available modifiers

Modifier Description
foregroundColor foregroundColor(_:appearance:theme:)
- asset: asset for color
- appearance: preferred appearance to override current appearance (optional)
- theme: preferred theme to override current theme (optional)
background background(_:appearance:theme:)
- asset: asset for color
- appearance: preferred appearance to override current appearance (optional)
- theme: preferred theme to override current theme (optional)
font font(_:appearance:theme:)
- asset: asset for font
- appearance: preferred appearance to override current appearance (optional)
- theme: preferred theme to override current theme (optional)

Migrating UserDefaults from Version 1

As the API has been completely changed, it is required to migrate theme data stored in UserDefaults when shifting from Version 1 to Version 2. In this regard, the API doesn't provide any method for migration process. However, lucky enough, it can be done by a small piece of code. Here is the sample code for migration process.

@main
struct MainApp: App {
    // Performing migration process.
    init() {
        let key = UserDefaults.Key.theme
        guard let value = UserDefaults.get(OldTheme.self, key: key) else { return }
        UserDefaults.set(Theme(key: value.key), key: key)
    }
    var body: some Scene { /* some stuff */ }
}

// It is required to store your old themes in your code. 
enum OldTheme: Codable {
    case bluoTheme
    case jadoTheme
    
    var key: String {
        switch self {
        case .bluoTheme: return "bluoTheme"
        case .jadoTheme: return "jadoTheme"
        }
    }
}

馃攷 Exploration

To explore more about SwiftTheming 馃帹, you can check out the documentation or dig around the source code.