Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support binds against @Published properties #4

Open
helje5 opened this issue Apr 29, 2022 · 3 comments
Open

Support binds against @Published properties #4

helje5 opened this issue Apr 29, 2022 · 3 comments
Labels
enhancement New feature or request

Comments

@helje5
Copy link
Member

helje5 commented Apr 29, 2022

This is a funny side effect when using the inline view accessor like so:

class CowsOverview: ViewController {
  @Published var search = ""
  var view: some View {
    TextField("Search it", $search) // <= doesn't work
  }

This does work when using the ViewController via the environment, e.g.:

class CowsOverview: ViewController {
  @Published var search = ""
  struct ContentView: View {
    @EnvironmentObject var viewController : CowsOverview
    TextField("Search it", $viewController.search)
  }
}

This is because the $ accesses the projectedValue of the property wrapper - @Published in the first case, and @EnvironmentObject in the second. And while @EnvironmentObject gives you a Binding (and then that goes on traversing keypathes), the @Published gives you the Combine Publisher for the property.

There are a few ways to solve this, either using a trampoline providing the Bindings, maybe like:

var `$`: Trampoline<Self, ... key path stuff>

or maybe just adding a .bind() to @Published, like:

extension Published {
  var bind : Binding<Value> { ... }
}

Maybe someone has additional ideas. Generally it is not a huge deal, as one often ends up w/ real view structs anyways.

@helje5 helje5 added the enhancement New feature or request label Apr 29, 2022
@helje5
Copy link
Member Author

helje5 commented Apr 29, 2022

BTW:

  var `$`     : String { "Hello" } // works
  var `$self` : String { "Hello" } // errors out

helje5 added a commit that referenced this issue May 6, 2022
An `@Published` like property wrapper, which has a `Binding`
as its `projectedValue`.
This in itself seems to work OK and addresses issue #4.

However, I'd also like to override `@State`, so that this
works:
```swift
class Page: ViewController {
  @State var title = "Hello"

  var view: some View {
    TextField("Title", $title)
  }
}
```

Using a simple:
```swift
extension ViewController {
  public typealias State = BindablePublished
}
```

But this fails, the compiler gets confused in finding the
right `@State` subscripts I presume.
@helje5
Copy link
Member Author

helje5 commented May 6, 2022

The feature/bindable-published branch has an implementation of a BindablePublished property wrapper, which seems to work OK. What doesn't work OK is aliasing it to @State from within the framework:

class Page: ViewController {
  #if true // works
    @BindablePublished var title = "Hello"
  #elseif true // works
    typealias State = BindablePublished
    @State var title = "Hello"
  #elseif false // fails, if alias is in ViewController protocol
    @State var title = "Hello"
  #endif

  var view: some View {
    TextField("Title", $title)
  }
}

@helje5
Copy link
Member Author

helje5 commented May 6, 2022

This is another, less recognisable, variant:

TextField("Title", bind.title)

extension ViewController {
  
  var bind : Binder<Self> { Binder(self) }

  // Works, but also requires backticks on the callsite
  var `$` : Binder<Self> { Binder(self) }
}

@dynamicMemberLookup
struct Binder<O: ObservableObject> {
  
  let instance : O
  
  init(_ instance: O) { self.instance = instance }
  
  public subscript<Subject>(dynamicMember keyPath: ReferenceWritableKeyPath<O, Subject>)
         -> Binding<Subject>
  {
    Binding<Subject>(
      get: { instance[keyPath:keyPath] },
      set: { value in instance[keyPath: keyPath] = value }
    )
  }
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

1 participant