user6354073
user6354073

Reputation:

View Model Dependency Injection

ItemDetailViewModel is initialized with the following signature:

init(item: Item, didPressButton: Observable<Void>, api: FirebaseAPI)

It is initialized in ItemDetailViewController, which gets an Item from segue of source controller. Im realizing that ItemDetailViewController is technically the View, so it should not have Item as a stored property. How else do I transfer Item to ItemDetailViewModel? This also means that ItemListViewController shouldn't have a selectedItem stored property to use in prepareFor(segue:).

Potential Solution

So when a CollectionView Cell is tapped in ItemListViewController (source controller), it should trigger ItemListViewModel through binding to store Item, and then perform segue. Meanwhile, in prepareForSegue I use ItemListViewModel to initialize ItemDetailViewModel with Item. Ok.. That could work.

How do I handle the button taps, which changes its image on each tap? through a function?

What is the proper way to initialize view models and inject dependencies?

Upvotes: 2

Views: 1527

Answers (1)

Daniel T.
Daniel T.

Reputation: 33967

You have an object that needs three pieces of data to be constructed, but one of the pieces of data comes from a different source than the other two. This is a good time to use higher-order functions.

struct ItemDetailViewModel {
    static func factory(item: Item, api: FirebaseAPI) -> (_ action: Observable<Void>) -> ItemDetailViewModel {
        return { action in 
            return ItemDetailViewModel(item: item, didPressButton: action, api: api)
        }
    }
}

Your view controller would accept the result of this function:

class ItemDetailViewController: UIViewController {
    var viewModelFactory: (Observable<Void>) -> ItemDetailViewController = { _ in fatalError("factory called before provided.") }

    override func viewDidLoad() {
        super.viewDidLoad()
        let viewModel = viewModelFactory(myButton.rx.tap.asObservable())
        // bind output to view model
    }
}

Then in your previous view controller's prepare for segue:

if let controller = segue.destinationViewController as? ItemDetailViewController {
    controller.viewModelFactory = ItemDetailViewModel.factory(item: anItem, api: api)
}

Upvotes: 2

Related Questions