I have a concept question about MVVM. I'm learning this pattern in swift 3.
This is my situation:
I have a screen with a list of users. The UserViewModel has a property that is a list of users, but the controller shouldn't know about the User model. I read that controller shouldn't know absolutely nothing about the models.
If the controller requests data from a user of this list, this data must be a dictionary of data or can be a User model?
Then, the UserViewModel must contain also the same properties than the User model?
Thank you for your help.
If the controller requests data from a user of this list, this data must be a dictionary of data or can be a User model?
Data can be the UserModel.
Then, the UserViewModel must contain also the same properties than the User model?
Hhm. Of course not. Why UserViewModel should have same properties that UserModel have? There is no any reason for that.
For purpose when you need to return some userModel.name property to controller, you can and should get this only from property of UserModel, i.e. get the right UserModel from ViewModel and than get the property:
func controllerFunc() {
let userModel = viewModel.getUserModelFromList()
print(userModel.name)
}
But in case when you need to prepare value for controller, you can create property in ViewModel, that contains some sort of modification for UserModel's property. And controller should get property from ViewModel, not from UserModel.
I read that controller shouldn't know absolutely nothing about the models.
Well, ideally - yes. Also, should be mentioned that ViewModel shouldn't know anything about UI components, i.e. ViewModel shouldn't have import UIKIt statement.
For example, if you need an UIImage, you should return name (String type) of image from ViewModel and create UIImage object in controller.
Related
I was watching a lecture about MVVM in Swift and how it works, and I basically understood that the Model will contain data and logic, ViewModel passes that data to the View, maybe cleans it up a bit and the View can also call intent functions in the ViewModel to notify the Model of some things that need to be modified.
Now I know I don't really have much context but there are a bunch of lectures here and there's no way for me to really explain everything for now but basically we're making a memory card game and now we are now changing it to have a MVVM design pattern(It didn't have one before). The model currently contains a Card struct and a choose function to choose a card and stuff like that, but for some reason the lecturer puts an array of emojis(The content of the cards in this game) in the ViewModel and not the Model.
I thought that the Model should be the one that stores the data and not the ViewModel? Could anyone maybe try to explain why this was done?
ViewModel:
import SwiftUI
class EmojiMemoryGame //this is the ViewModel
{
static let emojis = ["floaf","taco","george","chicken","squeaky","cat","dollar","a","b","c","d","e","f","g","h"] // these are supposed to be the emojis I just used some words instead.
static func createMemoryGame() -> MemoryGame<String>
{
MemoryGame<String>(numbersOfPairsOfCards: 4) { pairIndex in emojis[pairIndex]}
}
private var model : MemoryGame<String> = createMemoryGame()
var cards: Array<MemoryGame<String>.Card>
{
model.cards
}
}
Model:
import Foundation
struct MemoryGame<CardContent> //MemoryGame is the model for the MVVM pattern
{
private(set) var cards : Array<Card>
func choose(_ card: Card)
{
}
init(numbersOfPairsOfCards: Int, createCardContent: (Int) -> CardContent)
{
cards = Array<Card>()
//add numbersOfPairsOfCards*2 to cards array.
for pairIndex in 0..<numbersOfPairsOfCards
{
let content = createCardContent(pairIndex)
cards.append(Card(content: content))
cards.append(Card(content: content))
}
}
struct Card
{
var isFaceUp: Bool = false
var isMatched: Bool = false
var content: CardContent
}
}
It's entirely subjective, MVVM is just a design pattern and does not need to be rigorously adhered to.
Note that in MVVM a given view is typically "backed" by a single ViewModel and that a VM might interact with many different models. If the data is to be shared across views, it probably belongs on some shared model. If the data just used on a single view, it's likely fine on the VM.
I knew and understood which lecture you are referring too.
Model is for storing the data, but VM is the middleman who can uses the Model to compute data or update the View without giving the View a direct access to the Model. ViewModel also updates the Model whenever it receives any specific interaction from the View or User. You do not process or compute any data inside a Model to update the View. You must use the ViewModel.
Also, in this case the "emojis" array is not really the main model. It is just an array of String not "Card". emojis is used to store the data for initializing the "Model". Think of it like an input from the user, you need this to pass to your Model, otherwise you don't need a Model if you don't have any data to store or initialize.
The main model is still "Card", and "Card" is still completely abstract in this context.
ViewModel's job is to compute the data like a middleman and then either passing it to the Model or Update the View. Also, it is fully possible for a ViewModel to have its own implemented variables for helping the process between VM and Model or VM and View.
All in all, Model does not do any process, compute, direct interact with the View. Model only stores the data. ViewModel does all the compute, process, initialize.
ViewModel is a common incorrect interpretation of the underlying concept, stemming from the lack of spaces that we have in programming languages obfuscating intent. What you’re actually working with is a View Model, expressible in Swift via View.Model or Model.View…
struct CardView: View {
struct Model: DynamicProperty {
extension MemoryGame.Card {
struct View: SwiftUI.View
…or, when it’s a model for several views instead of one, that’s called a Store:
final class Store: ObservableObject {
Even though MVVM is a cross-platform term that Apple has largely rejected, every view has a model, regardless of if you abstract it into a separate type. The view model is anything the view needs to draw itself. Anything outside of that is a model for something else and doesn’t belong in the type.
I'm new to SwiftUI architecture especially when using MVVM. Currently I've got the following situation that I try to solve using MVVM. I struggle to understand the boundaries of a view model and how to solve the data handling part for it.
Let's give an example: the properties of the following domain model should be changed throughout a sequence of different NavigationViews, think of it as a wizard.
The struct representing this domain model looks like this:
class BusModel: Identifiable {
let id: UUID
var name: String
var drivers: [DriverModel]
}
class DriverModel: Identifiable {
let id: UUID
var firstName: String
var lastName: String
var toursToDrive: [TourToDriveModel]
}
class TourToDriveModel: Identifiable {
let id: UUID
var dayOfTheWeek: Int
var tour: TourModel
}
class TourModel: Identifiable {
let id: UUID
let label: String
}
As you see, the domain model has some sort of hierarchy: BusModel contains a list of DriverModel contains a list of ToursToDriveModel contains a TourModel
The purpose of the first NavigationView is to make changing the BusModel.name field possible. After clicking the next button, the list of drivers getting displayed, represented using NavigationLinks. There it should be possible to add a new driver or update the existing ones. So clicking on a list item navigates the user to the next view, presenting the data of those DriverModel. There it should be possible to change the drivers firstName and lastName and should show it's toursToDrive presented in a list as well. Changing a tourToDrive follows the same navigation principle as described for the driver.
So my question is: after all every view changes some properties of the underlying BusModel object and I don't know what a view model in this context would look like. Should the view model be related to all those single views or should every view has it's own dedicated view model?
Currently my view model spans the whole set of views, where every view changes to related properties of the view model. But I don't think that's correct because the view model holds every logic of every single view and if the relationship would be 1:1 of view and view model, those logics would be scoped to the distinct view they belong to.
So I guess, putting the domain model into an environment object which can be used within every distinct view model would be a better choice?
Another problem I have right now is, that the BusModel fields are exposed as #Published properties, so it looks something like this:
class AlterBusViewModel: ObservableObject {
#Published var name: String
#Published var drivers: [DriverModel]
}
But if DriverModel.firstname gets updated, the view model doesn't propagate the changes to ancestor views as well, because it's some sort of nested property I guess? Even if the view model would be divided into view related view models, this problem persists from my point of view. How could this be solved as well?
Finally the domain model and data handling gets replaced by core data later on, but for now I would like to solve it by using a class representing the domain model at runtime which should be updated subsequently.
If there are any questions, feel free to ask, I will provide more info as soon as possible!
Many thanks in advice.
Being able to share data between multiple view controllers and doing that in a way that makes use of recommended patterns such as MVC seems to be essential to create good apps, but my problem is that these things aren't clear at all for me.
I am conscient that this question is really dense, but for things to be clear I think you really need to understand the whole thing.
First of all we need to be sure of what Model, View and Controller are doing, here is how I would describe them, please tell me if I'm right about that:
Model : a class that's responsible for managing data, and only that (for example, a class that will go on the web to retrieve information, such as weather forecast).
View : a view is an object that's displayed to the user, who can often interact with it, that's the objects that you can drag and drop in Interface Builder (for example a button) and you might also create one from scratch, or custom an already existing one by subclassing it.
Controller : a controller is responsible for managing a view and its subviews, it receives events (such as viewDidLoad, or even when the user taps a button) and can react to it, for example, it might change the text of a label.
Now about the way they are interacting between each other, I'd say that the controller is between the view and the model, it's managing the view and might ask for data to the model. In addition to receiving events from the view, it might also receive events from the model, for example, if the controller asks to the model for a specific data on the web (let's say if it asks weather for a specific city) the data won't be available immediately, instead, the model will notify the controller so that it can update the view with the data it received. Am I right?
One of the first thing I'm wondering is if an object could be considered as a model if it isn't here to retrieve data, but to do other things that are simply not related to the view, for example, could an object that's responsible for communicating and managing a bluetooth accessory considered as a model ? Could an object that sends data to a cloud considered as a model ? And what about a Tic Tac Toe AI ?
Then, singleton instances, I often heard of them when an app had to share data between multiple views, but first of all, I never really understood why it was necessary to use them in this case ?
Then, here is a singleton that I found in an article of the We Heart Swift website.
class Singleton {
struct Static {
static let instance = Singleton()
}
class var sharedInstance: Singleton {
return Static.instance
}
}
Singleton.sharedInstance
The problem if that I have had difficulties to find anywhere more details about why it's written in this way, and most of all, can a singleton have an initializer that takes arguments? How to add properties and methods to a singleton like this one? What are exactly the Static structure and the sharedInstance?
My last question is about why, technically, does a singleton makes it possible to get an access to things we have defined somewhere else? What I mean is that if I create an instance of let's say, a Dog class in my AppDelegate, and if I want to access to this specific instance in a view controller, then it wouldn't be possible, so how does singleton makes that possible under the hood?
EDIT : Oh, and, is the use of singletons recommended by Apple?
Thank you.
It has to do with the static in the struct. Static is essentially a class variable that persists for every instance of that class, so when you make the shared instance static, every time you access it, even from another instance of Singleton.instance it is the same variable because it is static. It persists amongst instances. However, Swift does not support class variables yet, so when it does, that should quickly replace the Struct syntax that is common of singletons. It is very similar to static variables in java.
For example:
class Singleton {
var someVar = 0
struct Static {
static let instance = Singleton()
}
}
to create a singleton with a variable and the following to access it:
let foo = Singleton.Static.instance
foo.someVar = 11
let bar = Singleton.Static.instance
println(bar.someVar) // Prints 11
As you can see, bar.someVar was never set, and that is because the variable for the shared instance was set, so it prints 11.
First note that I am not referring to any specific framework or technology like XAML.
The question is how to implement the MvvM pattern using ICommand for selection of an item in a list (=clicking a row)?
I have a view model (pseudo code):
class ListViewModel
{
// Items in the list.
public ObservableCollection<T> Items {};
// Command for item selection.
public ICommand ItemSelectedCommand
{
...
}
// Select an item in the list.
public void SelectItem(int index)
{
...
}
// The current selected item.
public T SelectedItem
{
get { ... };
}
}
How would I now connect my UI to that view model "manually"? Say, for instance in an iOS application.
I would probably have a UITableViewController, get an instance of the view model and populate the UITableView contents from it. Then I would trigger the ICommand from the RowSelected() method.
And here comes the thing I don't understand: how does the view model now know which item index was selected? I don't want to call SelectItem() because then I would not need the loosely coupled ICommand at all.
And maybe here we have to look how it is solved in XAML to understand the trick?
Coming from XAML and WPF, there are two options to forward selection changes from the UI to the ViewModel (as I understand your question, you're not asking about the other way around - feedbacking changes in the ViewModel to the UI - here):
Command with payload
The ICommands Execute method has a payload parameter. Executing a command without a payload can be done passing null:
SomeCommand.Execute(null);
In your case, it would make sense to pass the selected item as the parameter in the event handler:
vm.ItemSelectedCommand.Execute(eventArgs.SelectedItem);
or
vm.ItemSelectedCommand.Execute(myList.SelectedItem);
In the command's execution method, you can handle the parameter. Note that your ViewModel property SelectedItem is not directly involved here. If you need the selected index explicitly (which is not the case, usually), I would check the selected item's index in the Items collection.
Binding selected item of list to a ViewModel property
Option B is to 'bind' the selected item of the list to a distinct property on the ViewModel, in your case the SelectedItem property in the event handler of the list:
vm.SelectedItem = myList.SelectedItem;
The command is kind of redundant then, although you could invoke it without a payload after setting SelectedItem on the ViewModel. I would rather handle the change of the selected item in the set accessor of the property on the ViewModel.
Note: XAML and WPF come with quite a lot of infrastructure code out of the box. MVVM doesn't make sense without a proper framework to actually take care of binding UI and ViewModels in a loosely coupled way. You quickly end up with a lot of extra work and little benefit, because you're still maintaining tight dependencies. Bottom line: I recommend getting or writing a proper MVVM framework, before actually implementing it.
I'm pushing myself to make the applications I write simpler, and I've taken some steps to do that, but I'm left with an interesting problem that doesn't at all feel like it would be unique to me. I'm wondering what I'm doing wrong.
I have a ViewModel that keeps a collection of model objects. The view is a ListView that displays all of the objects in the collection. The model objects have all the logic in them to manipulate them. Inside the ListView row for each item I have a button, and that button needs to be wired to call a method on the model object.
To get this to work I need to add a command binding, but to the parent window data context, that passes a parameter of the model object in the row, all so that model object can be used inside the ViewModel (the parent window data context) to call the method on the model object that's being passed in.
This seems really much more complex than it needs to be. I'm willing to throw out anything I've done already, there are no sacred cows, I just want this to be done in a simpler method that will be easy to look back on in a year and figure out what I was doing.
{Binding RelativeSource={RelativeSource FindAncestor, AncestorType={x:Type Window}},
Path=DataContext.MyCommand}
Create a presenter class in your ViewModel for the model objects and have a collection of those. You can then put the ICommand property on those instead and pass a reference to the method you want to call in the parent datacontext.
Perhaps something like the following:
public class ModelPresenter : INotifyPropertyChanged
{
private Model _model;
public ModelPresenter(Model model, Action<Model> parentAction)
{
_model = model
_action = parentAction;
}
public ICommand MyAction
{
get { return new RelayCommand(() => _parentAction(_model)); }
}
...
}
It also sounds like you might be binding to Properties of your model your view. You shouldn't do this as it can cause a memory leak if your models aren't implementing INotifyPropertyChanged (see: http://support.microsoft.com/kb/938416/en-us).