Menu

State

Skip support for SwiftUI State on Android. Consult the SkipUI module for a complete list of supported SwiftUI.

The following example screens and source code is from SkipUI’s Showcase sample app StatePlayground.swift

Android screenshot for State component (light mode) iPhone screenshot for State component (light mode) iPhone screenshot for State component (dark mode) Android screenshot for State component (dark mode)
import SwiftUI

class TapCountObservable: ObservableObject {
    @Published var tapCount = 0
}

struct TapCountStruct : Identifiable {
    var id = 0
    var tapCount = 0
}

class TapCountRepository: ObservableObject {
    @Published var items: [TapCountStruct] = []

    func add() {
        items.append(TapCountStruct(id: items.count))
    }

    func increment() {
        if !items.isEmpty {
            items[items.count - 1].tapCount += 1
        }
    }
}

struct StatePlayground: View {
    @State var tapCount = 0
    @State var hasStateTapped: Bool? // Test optional vars
    @StateObject var tapCountObservable: TapCountObservable
    @State var tapCountStruct: TapCountStruct
    @StateObject var tapCountRepository = TapCountRepository() // Test ForEach observable

    init() {
        // Test that we can initialze state property wrappers
        _tapCountObservable = StateObject(wrappedValue: TapCountObservable())
        _tapCountStruct = State(wrappedValue: TapCountStruct())
    }

    var body: some View {
        List {
            Section {
                NavigationLink("Push another", value: PlaygroundType.state)
                NavigationLink("Push binding view") {
                    StatePlaygroundDestinationBindingView(tapCount: $tapCount)
                }
            }
            Section {
                if hasStateTapped == true {
                    Text("State tap count: \(tapCount)")
                } else {
                    Text("Tap below to increment state tap count")
                }
                Button("State") {
                    tapCount += 1
                    hasStateTapped = true
                }
                StatePlaygroundBindingView(tapCount: $tapCount)
            }
            Section {
                Text("Observable tap count: \(tapCountObservable.tapCount)")
                Button("Observable") {
                    tapCountObservable.tapCount += 1
                }
                StatePlaygroundBindingView(tapCount: $tapCountObservable.tapCount)
            }
            Section {
                Text("Struct tap count: \(tapCountStruct.tapCount)")
                Button("Struct") {
                    tapCountStruct.tapCount += 1
                }
                StatePlaygroundStructBindingView(tapCountStruct: $tapCountStruct)
            }
            Section {
                Text("Struct tap count (field): \(tapCountStruct.tapCount)")
                Button("Struct (field)") {
                    tapCountStruct.tapCount += 1
                }
                StatePlaygroundBindingView(tapCount: $tapCountStruct.tapCount)
            }
            Section {
                ForEach(tapCountRepository.items) { item in
                    Text("Repository item tap count: \(item.tapCount)")
                }
                Button("Add item") { tapCountRepository.add() }
                Button("Increment last") { tapCountRepository.increment() }
            }
        }
        .onChange(of: tapCount) {
            logger.log("onChange(of: tapCount): \($0)")
        }
        .onChange(of: tapCountObservable.tapCount) {
            logger.log("onChange(of: tapCountObservable.tapCount): \($0)")
        }
        .onChange(of: tapCountStruct.tapCount) {
            logger.log("onChange(of: tapCountStruct.tapCount): \($0)")
        }
        .toolbar {
            PlaygroundSourceLink(file: "StatePlayground.swift")
        }
    }
}

struct StatePlaygroundBindingView: View {
    @Binding var tapCount: Int
    var body: some View {
        Button("Binding") {
            tapCount += 1
        }
    }
}

struct StatePlaygroundStructBindingView: View {
    @Binding var tapCountStruct: TapCountStruct
    var body: some View {
        Button("Binding") {
            tapCountStruct.tapCount += 1
        }
    }
}

struct StatePlaygroundDestinationBindingView: View {
    @Binding var tapCount: Int

    var body: some View {
        VStack {
            SubView1(tapCount: _tapCount)
            SubView2(tapCount: _tapCount)
        }
        .navigationTitle("Binding View")
    }

    struct SubView1: View {
        @Binding var tapCount: Int

        var body: some View {
            Button("Bound tap count 1: \(tapCount)") {
                tapCount += 1
            }
            .buttonStyle(.bordered)
        }
    }

    struct SubView2: View {
        @Binding var tapCount: Int

        var body: some View {
            Button("Bound tap count 2: \(tapCount)") {
                tapCount += 1
            }
            .buttonStyle(.bordered)
        }
    }
}