Skip to content

Add Binder for swiftui#63

Closed
sobabear wants to merge 1 commit into
DevYeom:mainfrom
sobabear:main
Closed

Add Binder for swiftui#63
sobabear wants to merge 1 commit into
DevYeom:mainfrom
sobabear:main

Conversation

@sobabear
Copy link
Copy Markdown
Contributor

@sobabear sobabear commented Jan 5, 2024

Related Issues 💭

I want to make sugar for just setter value on State for ViewStore when i use SwiftUI. Thus I suggest binder using keypath to setter for oneway

Description 📝

Lets suppose i use simple textfield app

import Foundation
import SwiftUI
import OneWay


struct OneWayView1: View {
    @StateObject var store: ViewStore<OneWayReducer1> = .init(reducer: .init(), state: .init())
    var body: some View {
        VStack {
            
            TextField("TextField", text: $store.state.searchText)
                .task {
                    
                    for await searchText in store.states.searchText {
                        // Problem line1
                        print("🥶\(#file) \(#line) \(searchText)")
                    }
                }
            
            Button("buttonbutton") {
                store.send(.tapButtonForDebug)
            }
        }
    }
}

class OneWayReducer1: Reducer {
    enum Action {
        case setSearchText(String)
        case tapButtonForDebug
        
    }
    
    struct State: Equatable {
        var searchText: String = ""
    }
        
    func reduce(state: inout State, action: Action) -> AnyEffect<Action> {
        switch action {
        case let .setSearchText(value):
            state.searchText = value
            return .none
            
        case .tapButtonForDebug:
            // Problem line2
            print("🥶\(#file) \(#line) \(state.searchText)")
            return .none
        }
    }
}

I marked two line which both prints out. And when i types like "Hello OneWay", Problem Line1 would be print
" H", "He".... "Hello OneWay". However when i clicked, button which leads to Action.tapButtonForDebug prints out nothing but "" <- which is state.searchText.

So on this problem, i mostly used to handle this problem like adding Binder like

extension OneWayView1 {
    var searchTextBinder: Binding<String> {
        .init {
            store.state.searchText
        } set: { newText in
            store.send(.setSearchText(newText))
        }
    }
}

You know whenever it is getting more and more properties on State, this binder makes code much larger, thats why i open this PR.

Expected result would be better than above code.

TextField("TextField", text: store.binder(\.searchText))

Additional Notes 📚

Checklist ✅

  • If the changes affect existing functionality, please verify whether the above information has been appropriately described.

@sobabear
Copy link
Copy Markdown
Contributor Author

sobabear commented Jan 5, 2024

I wrote none of annotation for documentation of usage, unless this function would be suitable. if it is ok, i will do with your opnion

@DevYeom
Copy link
Copy Markdown
Owner

DevYeom commented Jan 6, 2024

@sobabear

Thanks!

When passing a binding, it is correct to directly call the store's send() as you suggested.

TextField(
    "TextField",
    text: Binding(
        get: { store.state.searchText },
        set: { store.send(.setSearchText($0)) }
    )
)

The syntax sugar you suggested looks very nice. However, OneWay avoids having UI-related dependencies to maintain a lightweight library. It seems better to create a separate module for UI Extension or to include it in your own project.

// In your own UI extension module or wrapping module
@_exported import OneWay

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants