0

I'm just getting started with Combine. I have these questions for this situation:

  1. Is it accepted to have a tuple as a PassthroughSubject output?
  2. Is it accepted to have a completion handler as part of the PassthroughSubject output?

Example situation:

A SwiftUI log in view where I hand off logging in to another class and expect a result back:

struct LogInView: View {
    var loginSubject = PassthroughSubject<(username: String, password: String, completion: (Error?) -> Void), Never>()         

    var body: some View {
        Button {
            loginSubject.send((username: "Jim", password: "qwerty123", completion: { error in
                if let error = error {
                    // handle error
                } else {
                    // navigate to app
                }
            }))
        } label: {
            Text("Log in")
        }
    }
}

I would like to know other possible solutions to this scenario (I'm not able to use the 'login helper' class directly in the SwiftUI view due to 'LogInView' being in a package and the 'log in helper' being in the main app) and/or if this would be generally accepted as a solution.

Simon
  • 1,354
  • 1
  • 14
  • 18
  • Of course you can use a tuple type; that's common. The stuff about the completion handler is meaningless; the whole point of Combine is that it _replaces_ the use of completion handlers: "straightening out" asynchronous code is exactly what it's about. If you have a completion-handler type method that you don't own but you want to use it with Combine, that's a Future. – matt Sep 13 '21 at 23:35
  • In your scenario, where is the subject observed? Is there a reason to do this in a view and not a view model? – jnpdx Sep 13 '21 at 23:43

1 Answers1

1
  1. Yes, it's very common.
  2. No, using a completion handler like this is working against Combine's inbuilt completion handling. Rather than using a passthrough, you should instead have a function that takes a username and a password and produces a publisher, and subscribing to that publisher will give you your completion/output handling. Such a function would usually live in an ObservableObject that you inject into your view hierarchy as an @EnvironmentObject.

Further reading: https://www.hackingwithswift.com/quick-start/swiftui/how-to-use-environmentobject-to-share-data-between-views

Charles Maria
  • 2,165
  • 15
  • 15
  • 1
    How does the environment object compare to simply injecting the class through the views init? Is there a reason to have it as an Observable Object? Thanks for the help! – Simon Sep 14 '21 at 08:33
  • EnvironmentObject is what you’ll want to use if info about the login is required in multiple places across the app (almost all apps eventually have this requirement). – Charles Maria Sep 14 '21 at 08:43
  • EnvironmentObject allows you to instantiate a single object of a given type and it’s kept alive across all views below the view it was injected on (so most use cases involve injecting on the root view). If you have a view model that is only required in a single view, rather than injecting it, the inbuilt method best used would be StateObject, which will instantiate once and be kept alive across view refreshes. – Charles Maria Sep 14 '21 at 08:45