6

I have a swift UIHostingController which renders a SwiftUI. I call a function in view did appear which builds fine but doesn't create the intended output.

class LoginView: UIHostingController<LoginViewComponent> {
    required init?(coder: NSCoder) {
        super.init(coder: coder, rootView: LoginViewComponent())
    }

    override func viewDidAppear(_ animated: Bool) {
        sessionHandler()
    }

    func sessionHandler(){
        let user = User()
        if user.isLoggedIn(){
            view.isUserInteractionEnabled = false
            print("Authenticating Session")
            self.rootView.loginState(state: "success")
        }else{
            view.isUserInteractionEnabled = true
            print("Needs Logging in")
        }
    }

}

The function ("loginState(state: "success")") works when called within the SwiftUI view class, however when called from the hosting controller it doesn't work.

Any help would be greatly appreciated.

Rs Graphics
  • 183
  • 3
  • 14
  • Any reason why you can't just do all of this in the LoginViewComponent? As @Asperi mentioned, you can use a model object to track the User state and then use SwiftUI's .disabled(_:) modifier on the whole view. The value of disabled's argument would come from your state object. – smr Dec 07 '19 at 22:47

1 Answers1

1

SwiftUI is reactive state-base machine, actually, and all views are struct values, so you need to change concept, instead of imperatively send messages specify state dependency and reactions on those states...

Thus, keeping your custom-host-controller it could be set up like the following

import SwiftUI
import UIKit
import Combine

// model that keeps login state
class LoginState: ObservableObject {
    @Published var state: String = ""
}

struct LoginViewComponent: View {
    @EnvironmentObject var loginState: LoginState // state to be observed

    ...
   // somewhere in body you use loginState.state
   // and view will be refreshed depending on state changes
}

class LoginView: UIHostingController<AnyView> {
    let loginState = LoginState() // here it is created

    required init?(coder: NSCoder) {
        super.init(coder: coder, rootView: AnyView(LoginViewComponent().environmentObject(self.loginState))) // here the ref injected
    }

    override func viewDidAppear(_ animated: Bool) {
        sessionHandler()
    }

    func sessionHandler(){
        let user = User()
        if user.isLoggedIn(){
            view.isUserInteractionEnabled = false
            print("Authenticating Session")
            self.loginState.state = "success" // here state changed
        }else{
            view.isUserInteractionEnabled = true
            print("Needs Logging in")
        }
    }

}
Asperi
  • 228,894
  • 20
  • 464
  • 690
  • Hello, Thank you for the reply, when trying to set the environment object to the loginview component I am getting the following error: "Cannot convert value of type 'some View' to expected argument type 'LoginViewComponent'" – Rs Graphics Dec 07 '19 at 18:22
  • Yes, you're right... looks like type-erasing is needed in this case... not very elegant. Inheritance is not for SwiftUI. – Asperi Dec 07 '19 at 20:39