0

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!

timbre timbre
  • 12,648
  • 10
  • 46
  • 77
Matthew So
  • 75
  • 1
  • 2
  • 8

1 Answers1

1

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)

Sandeep Bhandari
  • 19,999
  • 5
  • 45
  • 78
  • Thank you! I am still studying but want to ask follow up question. As said I will have to input 'id' from json to the view controller's 'constructor', in the SceneDelegate, to instantiate the view controller. Your sample code look great and inspiring but I still don't know how to get this done. (I am trying to use RxSwift and I am a very beginner to this. So whether or not with RxSwift, the priority is to achieve the goal, RxSwift is an option.) – Matthew So Jan 20 '21 at 22:21
  • @matthew-so: RxSwift is a FRP (functional Reactive Programming paradigm) just like Object Oriented Programming or Protocol Oriented Programming Reactive Programming is just another programming paradigm. Which programming paradigm you choose shouldnt affect the final goal. They are just different ways to achieve the final goal similarly that shouldnt affect the design pattern decision as well. Though one coding paradigm might favor one design pattern more than other inherently but it wont in anyway block you from choosing any design pattern you wanna use – Sandeep Bhandari Jan 21 '21 at 04:38
  • I think you are little confused and taken up too many concepts at once, rather get familiarize yourself with one concept at a time see its real implementation and then once you have proper idea you can decide which programming paradigm to choose n why ? which design pattern to choose and why? etc etc – Sandeep Bhandari Jan 21 '21 at 04:39
  • I am not little confused. I am very confused because of the "receive id as an input parameter" :( But your explanation is clear and navigational. Many thanks! I want to have a time to think about WHY but the assignment has a due date and am struggling. Hope to figure out on time. Thank you very much again for your guide. This is impressive. – Matthew So Jan 21 '21 at 05:15
  • @matthew-so: RxSwift has a bit exponential learning curve, if you are not familiar with any kind of Ractive Programming paradigm (if you have never worked on any such framework before) go with Swift (which is plain Protocol Oriented programming) and simply stick to whatever the better coding practices you are aware of mainly sticking with value Symantec over reference Symantec, using protocols over inhertiance, concern separation, depenency injection stuff like that – Sandeep Bhandari Jan 21 '21 at 05:20
  • @matthew-so: Get done with the work before due date, learn Rx in your own time (this will help you get grip over apple's combine framework as well) come back make a small changes to better utilize concepts you learn with Rx later, if you follow all the best practices it will be easy to make changes to use RxSwift :) all the best :) Remember Design patterns are guidelines not rules :) everyone will have their own perception of how to implement specific design pattern and also every one will have their own take on various coding paradigm :) Am biased towards FRP – Sandeep Bhandari Jan 21 '21 at 05:21