Let's say I have a Car object with standard attributes like color, model, brand, and number of seats.
Each view controller in my app would be responsible for getting each of those attributes until the end:
Color View Controller --> Model View Controller --> Brand View Controller --> Seats Controller --> Result Controller
The way I pass data between each controller right now is through a Singleton, but I've read online that is is an anti-pattern and creates a lot of coupling later. Question is then: what is the best way for me to funnel all of the data to Result Controller?
Try to modify your singletons to services.
For example: your singleton StateManager has two method: set(state: SomeState) and getState() -> State.
First, use protocols:
protocol StateManager: class {
func set(state: SomeState)
func getStatus() -> SomeState
}
Second, your app modules shouldn't know about witch StateManager they use:
class MyAppController: UIViewController {
var stateManager: StateManager?
...
}
And when you init your VC:
let vc = MyAppController()
vc.stateManager = ConcreteStateManager.sharedInstance
//present vc
If you have a centralized place for create VC, you can hold instance of ConcreteStateManager in it, without calling sharedInstance.
Also, read about Dependency injection in swift.
Related
Do you guys know if it's possible to use a Swift nested class as UIViewController in Storyboard?
I'm looking into different ways to structure my projects, one of which is grouping related classes by nesting them in the same main class.
Let me explain with an example:
Say I have a screen for displaying my app's settings which needs a UIViewController and a model.
I could create 2 classes: SettingsController and SettingsModel, but I'm wondering if it wouldn't be nice to have it structured otherwise by having a main class Settings containing nested classes Controller and Model like so
class Settings {
class Controller: UIViewController {
...
}
class Model {
...
}
}
That way I could use them by doing Settings.Controller and Settings.Model which I think would look pretty nice.
Now this works fine expect when using storyboard. In Storyboard when I select a view controller to set the custom class, if I type in Setting.Controller and hit enter, the field does not validate and is cleared.
So my question is do you guys know what I'm doing wrong or if this is simply not possible?
Of course, it's after having posted the question that I found a solution so I'm sharing it for posterity.
So there was 1 problem to the way I was trying to do it:
Nested classes are references by using the dot
notation: Settings.Controller but Interface Builder does not
see that as a valid class name
The solution was simple, give the controller it's own Objc name:
class Settings {
#objc(SettingsController)
class Controller: UIViewController {
}
...
}
By doing this you give the nested controller an ObjC name thereby exposing it the Interface Builder. Now you can reference the controller by filling in SettingsController.
I don't think storyboards supports that (yet), so in the meantime you need to use some workarounds.
If all you want is the syntax Settings.Controller, you can do this: (Inspired by this answer)
Declare your Settings.Controller class not as a nested class, but as a standalone class SettingsController:
class SettingsController : UIViewController { ... }
Then in Settings, add a typealias:
class Settings {
typelias Controller = SettingsController
class Model { ... }
}
Now you can use SettingsController in the storyboard, but Settings.Controller in code.
Working sample available at: https://gitlab.com/nipunrd_git/mvvmarch
I am in process of migrating an app from MVC to MVVM. As far as I have read, you need to make your viewController independent of the Model, there by making a viewModel to glue the model and the controller together.
If we consider a simple screenA which has a refresh button to get some data via a web service and then fill the data in the view:
ScreenAViewController.swift: Will contain the code to hit the web service via a singleton network helper class.
ScreenA -> NetworkHandler -> fetchData
In order to make ScreenA, use the fetched data, we should now make a ViewModel, so I made ScreenAViewModel. Let's say our model was named "Design":
struct Design{
var pattern: String
}
Now our ViewModel would look like this:
class ScreenAViewModel{
private let design: Design
public let pattern: String
init (design: Design, pattern: String){
self.design = design
self.pattern = design.pattern
}
}
When we get the data from the web service, we should first create a Design model and then initialize our ViewModel with that. This ViewModel will now be used by our view controller to fetch and display data from. Any Business logic will be written in this only.
Is there a way to shift #3 to the view model itself? I think by having this conversion from model to viewModel in the controller itself is beating the purpose of hiding model from controller.
I have a class that takes care of all network request. So my confusion is where should i call this network request class either from viewcontroller or model class. Suppose i have 'User' model class with properties id,name,phone. In order to access login API, should i create a function 'loginwithphone' within this model class and call it from viewcontroller or should i call network request class directly from Viewcontroller. Is the network request class considered as a model in itself? If so, calling it from viewcontroller will still make my code architecture as MVC. Doesnt it?
You should take a look to Observer pattern which defines MVC architecture, to define any way to manage your request through callbacks.
Model layer of MVC architecture defines your POJOS, so 'loginwithphone' function should be in a controller class, for example 'logincontroller'.
To sum up, your network request class should be considered as a class inside a resources layer. and should call your controller class.
Hope it helps!
In MVC you should call the network request through a networking class. Preferably you will first run a class or enum that will build the request for you. After that you will get the model object returned to you that you will have stored in your viewController and send to all the views you want to populate.
Off-topic: I like MVVM as you then call another class (viewModel) which holds all the models and the requests, and all you have in your viewController are views and callbacks waiting for the viewModel to finish it's requests.
Example:
//ViewController
self.viewModel.fetchData()
self.viewModel.dataFetched = {
view.populate(withData: self.viewModel.datas)
}
//ViewModel
var dataFetched: (() -> ())?
var datas: [Data] = []
func fetchData() {
let request = networkManager.buildRequest()
sendRequest( success: { datas in
self.datas = datas
dataFetched?()
})
}
This is a very vague description of how its done. But i like this way
I have a simple model with menu and menuItem which both inherit from an abstract class.
I have created an adminController which can do CRUD operations.
which I've stuck here is that should create controller for menu and menuItem?
because both menu and menuItem are both inherit from a same type.
meanwhile their corresonding views for a method like Edit() can have different appearance.
I mean I have to check in views to know I'm trying to Edit Menu or MenuItem
but if I implement controller for each entity, I have duplicate code.
so what is the best approach for designing controller for this kind of operations?
For what it's worth, controllers are just classes, which means they can be inherited from. So, you can simply do:
public class MenuController : Controller
{
// all the actions here
}
public class MenuItemController : MenuController
{
}
Based on the naming conventions, then, you can apply different views to the actions per controller simply by putting them in their respective view directories: "Menu" and "MenuItem", respectively.
If you need to change a particular action method in the subclassed controller, just make the action virtual in MenuController and then override it in MenuItemController:
public class MenuController : Controller
{
public virtual ActionResult SomeActionToOverride() { ... }
}
public class MenuItemController : MenuController
{
public override ActionResult SomeActionToOverride() { ... }
}
This is all just basic OOP.
The answer, unfortunately, is "It Depends".
You could dispatch to different views based on the object type, from the same Menu controller, with the common operations on the controller, with the views calling the same HttpPost actions with a type-discriminating property. This is a design which exposes the OO architecture to the public API surface.
Or, as these are separate domain entities, you could expose two controllers with their own suite of CRUD operations and have a more REST like API surface.
Duplicate code to provide independent endpoints or security policy differentiation at what amounts to a public API surface is less of a smell than elsewhere.
How can I provide a user object to every view in ASP.NET MVC without having to create a ViewModel for absolutely every view I have?
When using ASP.NET Membership, I just get a Profile variable in the views with the profile information, but when rolling my own I don't see a way to export that information.
Inherit your controllers from base controller. In base controller override OnActionExecuting and set ViewData["UserObject"] here. Something like this:
public class YourBaseController : Controller
{
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
filterContext.Controller.ViewData["UserObject"] = ...
}
}
Or create custom filter with the same OnActionExecuting method if you want only certain controllers providing user object to View.
UPDATED:
Create custom Html helper if you dont want to cast ViewData["UserObject"] every time:
public static object RenderUserObject(this HtmlHelper html)
{
return ((html.ViewData["UserObject"] as UserObject) ?? new UserObject()).ToString();
}
Inherit your ViewModel classes from a master ViewModel with a User property.
Alternatively, you can pass the object in ViewData collection and use an extension method to make it easy to access the object in the view.
One alternative to having a base ViewModel class, and so having to define a ViewModel class for every view, is to create a generic ViewModel<T> class which exposes a property T InnerModel, or something similar. Then you can pass a ViewModel<Foo> rather than having to explicitly create a FooViewModel class.
Of course if you also need more bespoke ViewModels in places, you can keep the ViewModel base class, and have ViewModel<T> and your bespoke ViewModels extend it.