swiftmvvminitinput-parameters

Swift - receive json object as input parameter to view controller?


I am working on an assignment for iOS project, and one of business logic rules is to let view controller receive id as an input parameter, and I don't get what it means.

The id is a JSON object field like below:

{
"id": 1213213,
"anotherKey:12322123,
"andAnotherKey: {
"keyInKey":123121
}
,
...
}

And my guess is open a view controller with custom init like below:

    init(id: String) {
        
        super.init(nibName: nil, bundle: nil)
    }
    
    required init?(coder: NSCoder) {
        fatalError("init(coder:) has not been implemented")
    }
    
    
    override func viewDidLoad() {
        super.viewDidLoad()
        setupUI()
    }

Is my understanding likely what the assignment intends? If so, how do I instantiate this view controller from SceneDelegate? I've adopted MVVM pattern and the scene delegate doesn't look like a great place to leave view model. Any advice would be grateful!


Solution

  • Is my understanding likely what the assignment intends?

    No. If you are using MVVM, I would rather set the string value to viewModel, and inject the fully configured viewModel as a dependency to ViewController (called principle of inversion of control (IoC)).

    If you aren't using any third party dependency injector, you can always use init This is called Dependency injection via Constructor

    There are multiple ways to introduce Dependency injection, discussion of which is not in the scope of this answer.

    Answer below assumes you are not using any third party Dependency Injector and using Constructor based injection.

    how do I instantiate this view controller from SceneDelegate?

    step 1: Have a viewModel class

    class ViewModelA {
        let someParam: String
    
        init(with param: String) {
            self.someParam = param
            //rest of code
        }
        //rest of code whatever makes sense here
        //modification of string any buisness logic
        //or hold other data models
    }
    

    Step 2: Inject ViewModel as constructor Dependency to ViewController

    class ViewControllerA: UIViewController {
        let viewModel: ViewModelA
    
        init(with viewModel: ViewModelA) {
            self.viewModel = viewModel
            super.init(nibName: nil, bundle: nil)
        }
    
        required init?(coder: NSCoder) {
            fatalError("init(coder:) has not been implemented")
        }
    
        //rest of code to set up, update UI and view delegates
    }
    

    Step3: Set your view controller as root view controller of window in willConnectTo session method of SceneDelegate

        func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
            // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
            // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
            // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
            guard let windowScene = (scene as? UIWindowScene) else { return }
            let window = UIWindow(windowScene: windowScene)
            let viewModelA = ViewModelA(with: "abcd")
            let viewControllerA = ViewControllerA(with: viewModelA)
            window.rootViewController = viewControllerA
            self.window = window
            window.makeKeyAndVisible()
        }
    

    I've adopted MVVM pattern and the scene delegate doesn't look like a great place to leave view model.

    You have to set rootView controller somewhere, dont you think? Earlier we used to set rootView controller in AppDelegate ( conceptually speaking creating a ViewModel and ViewController instance in AppDelegate is also somewhat flaky). Even if you use storyboard and set some view controller as initial viewController, even iOS does the same thing under the hood. Its because you wanna follow MVVM and not MVC you have to intercept and instantiate ViewController with viewModel as its dependency manually.

    I personally prefer to work with MVVM with router, that way creating and injecting dependency to ViewController can be separated out (better concern separation) and can be reused (better code reusability)