I've been looking into applying the Model View Presenter architecture to a new iOS project. After some reading, I found that this post had the best example. Link to raw gist of code here.
The bottom of the example has the assembly code:
// Assembling of MVP
let model = Person(firstName: "David", lastName: "Blaine")
let view = GreetingViewController()
let presenter = GreetingPresenter(view: view, person: model)
view.presenter = presenter
Furthermore, the author states:
Since we don’t want the View to know about the Model, it is not right to perform assembly in presenting view controller (which is the View), thus we have to do it somewhere else. For example, we can make the app-wide Router service which will be responsible for performing assembly and the View-to-View presentation.
My question:
Where should I put the assembly code?
Where can I find more example of an app-wide router?
So it's not just me who was wondering about this ;)
Let me share with you my other finding regarding to MVP in iOS:
Screencast by David Gadd - It's a bit long (1h 20min) and a bit old (December 2012) but for sure worth seeing. You will see there how MVP is implemented (in AppCode) for a very small app (alongside with a pretty good description on how to write unit tests). The router in app David is creating is called ServiceLocator. Unfortunately I couldn't find the code to download for this screencast (but it is possible to create your own version of this app while watching it).
Perhaps there are other ways to create router, but this screencast helped me to understand it a bit more. I'm pretty new to MVP concept and I didn't use it in a bigger app (bigger than one-screen-let's-see-how-it's-done-app). It would be great to see how MVP was implemented in a real life app...
[EDIT]
I've just realised I didn't answer your first questions.
According to app in screencast router is a class with one class method:
+ (id)resolve:(PresenterTypeEnum)type;
In implementation of this method you will find simple switch. Basing on type send in a parameter method will return proper instance of a presenter.
This method is called in viewDidLoad. Once you have instance of a presenter you just have to set the view of a presenter with self.
I hope this explanation is clear. Anyway, I highly recommend watching screen cast, then it should be clear as a crystal ;)
Related
As expected, I normally have difficulties when following TDD (i.e. writing the tests first) with the view layer.
Namely, in order to observe or trigger certain visible changes (layout or styling) I would need to make the view's internals public. This breaks encapsulation and allows for client code do some thing like myView.label.text = "User".
To avoid this, I either add getter methods to UIView class:
var userName: String{ return label.text }
Or do some extensions that are added only to the test framework:
extension MyView{
//avoids making a getter just for the sake of testing, while keeping it private and interchangeable
var userName : String{
return (viewWithTag(someLabelTage) as! UILabel).text
}
A different approach is to skip the TDD work flow (i.e. test manually after the feature is done) and add snapshot testing (see https://github.com/pointfreeco/swift-snapshot-testing) to the increase coverage and have a safety net when refactoring.
Considering all this, I was wondering if there are any other patterns or approaches, that I can use to be more efficient while keeping my confidence in the code.
Not an expert in Swift, but regardless of the language/framework, something tells me you're actually making your task harder.
I normally have difficulties when following TDD with the view layer.
This is expected. The View support to be simple and have no behavior (i.e domain logic) at all. It should be simple user interaction, and binding data to the controls in the view. So you don't need TDD or to be more precise Unit Tests around the View in my opinion. You're not going to get much value out of trying to write Unit tests for the View.
You View can be tested more effectively using a UI test framework, such as Selenium, or your own UI test framework, which tests User interactions. This way you more return in investment (ROI) than attempting to TDD the View layer.
I don't have much to add to Spock's answer. Tests should be written to help development and maintenance of the code in the future. If they get in the way then either the code is not architected properly or it's code for which the ROI is low.
A pattern that is worth mentioning is the humble view. There are a few ways to approach it, each with different trade offs, but the gist is to have data structures defining how the view should look like, and then let the view read these data structure and set its properties from them.
This allows you to test that the data structure driving the view is generated properly, without having to test the view itself, because it's nothing but an humble object that reads and sets values.
First of all i know MVC well and have been using it in project but when it comes to organizing classes and there role i am bit not sure of there proper implementation. Lets take a scenario to proceed with:
A sample which will display All Employee and Department. Data will be fetched from Web Services(Json) and will be stored as offline(Core Data).
So MVC pattern would be:
View will be my storyboard with Employee and Department UIViewController.
Controller will be EmployeeViewController.swift and DepartmentViewController.swift
Model will be Employee.swift and Department.swift
class Employee: NSObject {
var name: String?
}
class Department: NSObject {
var departmentName: String?
}
ServiceManager which will make calls to the web service.
ParseData which will parse the web service response and convert it into Employee and Department objects
CoreDataManager is singleton class to manage CRUD operation on offline DB.
Here are series of question on the above scenario which i have:
Is my understanding correct? Is the structure which i am trying to build follows proper MVC?
How the controller will interact with these components (Service Manager, ParseData, CoreDataManager). Should there be another class which will facilitate the communication between controller and data management(if controller does this then it will a tightly-coupled structure and massive as well).
Should Model be having any code other then property and initialization method as most of the model which i have seen only have property declaration?
Should there be separate UIView classes instead of storyboard to create a proper MVC structure?
Is my understanding correct? Is the structure which i am trying to
build follows proper MVC?
First I will say that "proper" MVC will depend on who you're asking. Its origin is commonly attributed to Trygve Reenskaug when he introduced this into Smalltalk in the 70's. However, his type of MVC was widely different from the bloated versions most commonly used today. The modern way of thinking about MVC is
Model = mostly a dumb class which primarily encapsulates data
View = whatever we present on the screen
Controller = the big lump of code that does almost everything,
sometimes offloaded by a manager class or two of some sort
Reenskaug, however, would have a model and a view and a controller for a button. For a label. For a field. I'm not saying that is what we should strive for, but there should be better ways to structure a project than using the Massive ViewController pattern (as it is jokingly referred to in the iOS community).
Luckily, there are.
Uncle Bob is preaching Clean Architecture. There are several implementations of this, and various people have made their own implementations of this for iOS, like VIPER and Clean Swift.
How the controller will interact with these components (Service
Manager, ParseData, CoreDataManager). Should there be another class
which will facilitate the communication between controller and data
management(if controller does this then it will a tightly-coupled
structure and massive as well).
Following the principles of Clean Architecture, you should encapsulate these functionalities into layers, in a way that enables you not just to split the code into multiple components, but also enables you to replace them with other components when that is needed. (But yes, at the very least avoid putting all of this in your controller!)
Should Model be having any code other then property and initialization
method as most of the model which i have seen only have property
declaration?
Again, there is not a single answer here. Some proponents of "real" OOP will say that each object should be self-served (i.e. a model object should know how to persist itself), while others extract the knowledge of such operations into "managers". Putting code to persist an object into the object could mean littering persistence functionality into many objects, or require you to rely on subclassing or other solutions to avoid this coupling.
Should there be separate UIView classes instead of storyboard to
create a proper MVC structure?
Storyboard or not does not determine whether you're using "proper" MVC. Also, what kind of class you're choosing (UIView or UIViewController) to represent the View is also not important. Your ViewController can be dumbed down to such a degree that it contains no logic (forwarding the logic that it DOES have to another class, i.e. the Presenter in VIPER).
I would recommend reading about the Clean Architecture and maybe watch a video of Uncle Bob explaining it, read other people's reports on implementing it, and then consider whether MVC is the correct pattern for your iOS project.
I'm new to BDD and I'm trying to figure out how I could write a test for pressing the back button in my app, or whether BDD requires me to write a test at all.
Here's a few example scenarios with functionality:
What: tapBackButton
Scenario: formNotSaved
Result: showAlertNotifiyingTheUser
What: tapBackButton
Scenario: formIsSaved
Result: goes1ScreenBack
I have no idea how to write unit tests for this! I have added the OCMock framework but it doesn't seem like you're allowed to change a uiviewcontroller's navigation controller since its read-only.
I really want to change my development process to write a failing test first and then code, but this makes it difficult.
Thanks for you time!
I'm not aware of Apple development, but I can comment on standard TDD stuff and that should get you in the right direction - I hope :)
What I'd do for this is have your "Alert" code in a seperate class with an interface, say IAlerter (unless Apple framework already has an interface for "messageboxes/alerts" that you can leverage).
Inject the interface into the constructor of your UI class.. Then in the test, you mock out the IAlerter in the test.
so basically.. (in C#, Moq (mocking library) style, Pseudocode sorry ;))
//ARRANGE
var alerterMock = new Moq.Mock<IAlerter>();//fake the alerter - cuz you really don't want it to happen, you just want to do a "Verify" that it "was" called.
var ui = new UI(alerterMock.Object);
//ACT
ui.StartDataEntry();
ui.GoBack();
//ASSERT
alerterMock.Verify(a => a.ShowAlert(Moq.It.IsAny<string>()));//verify ShowAlert was called with any string - or at least do a lil ".Contains" to make sure it was calling code that shows the text you expect.
HTH :)
The new Google Analytics SDK introduces a new subclass to UIViewController from which you have to inherit: GAITrackedViewController.
More info here: https://developers.google.com/analytics/devguides/collection/ios/v2/screens
This obviously clashes with MvxBindingTouchViewController, as you can only inherit from one class in C#.
What's the recommended way in getting this working?
MvvmCross's MvxTouchViewController's is special...
MvvmCross's MvxBindingTouchViewController is even more special...
But these are only special in that they inherit from standard UIViewControllers (UIVIewController, UITableViewController, UITabBarController, etc) and then they add functionality for:
ViewModel construction and the ViewModel property at the Cirrious.MvvmCross layer
construction and storage of Bindings at the Cirrious.MvvmCross.Binding layer
Take a look at some examples:
https://github.com/slodge/MvvmCross/blob/vnext/Cirrious/Cirrious.MvvmCross.Touch/Views/MvxTouchViewController.cs and https://github.com/slodge/MvvmCross/blob/vnext/Cirrious/Cirrious.MvvmCross.Binding.Touch/Views/MvxBindingTouchViewController.cs
https://github.com/slodge/MvvmCross/blob/vnext/Cirrious/Cirrious.MvvmCross.Touch/Views/MvxTouchTableViewController.cs and https://github.com/slodge/MvvmCross/blob/vnext/Cirrious/Cirrious.MvvmCross.Binding.Touch/Views/MvxBindingTouchTableViewController.cs
https://github.com/slodge/MvvmCross/blob/vnext/Cirrious/Cirrious.MvvmCross.Touch/Views/MvxTouchCollectionViewController.cs and https://github.com/slodge/MvvmCross/blob/vnext/Cirrious/Cirrious.MvvmCross.Binding.Touch/Views/MvxBindingTouchCollectionViewController.cs
In these you can hopefully see this involves a fair amount of cut and paste of code - although we do try to minimise it using extension methods. (If C# had multiple inheritance or mixins, we wouldn't need to do that - I'd love to have Mixins... but don't want multiple inhertitance ever!)
So.... if you want to add your own MvxXXXXXBindingViewController, then:
take your base XXXXX class,
inherit from it and add 'the stuff' to make an MvxXXXXViewController,
then take your MvxXXXXXViewController and inherit from it again to make your MvxBindingXXXXXViewController
publish to your blog and to a new GitHub repo so everyone else can piggyback off your hard work
job done
Advanced notes:
If you want to see the same thing in Droid, see Insert a Monogame view inside MvvmCross monodroid Activity
The TabBarController is also interesting - it's got some additional methods
At some point 'soon' (first half of this year) we will create VeeThree and this will switch the MvxViewController's to a non generic format - this is because MonoTouch now recommends against using Generics on iOS base classes - Rolf says it's safe most of the time, but when it causes bugs they are 'heisenbugs'.
There is also some dead old-iOS code in the current classes (ViewDidUnload) - this code will be culled in VeeThree too.
I recently followed Stephen Walther through creating a generic repository for your data models using the Entity Framework with the following link, http://bit.ly/7BoMjT
In this blog he briefly talks about creating a generic repository and why it's suggested to do so (to be clear of friction). The blog itself doesn't go into great detail on how to inject the GenericRepository into your project for that you'll need to download his source code of Common Code. However, once I finally understood the importance of the Repository pattern, and how it makes a difference in the data models I create in ASP.Net MVC I was wondering if I could do something similar to my Controllers and Views?
Can I create a ControllerRepository or ControllerFactory(as I've Bing'd it) and create a generic controller with 5 ActionResults and depending on what I inject into my GenericRepository datamodel (i.e. I have DellXPSComputers, GateWayComputers, HPComputers as a single db datamodel)
And actually have only one controller besides the Generic one I create that will go and grab the right datamodel, and view?
If so, what is the best way to implement this?
You could create a generic controller factory, but I don't see many scenarios why you'd ever want to. Except in your tests and redirects, you'd never be calling a controller method directly (vs. a repository method which you're calling in many places).
Yes! You absolutely can!
I've done it in the past with great success. The result is that you end up with a web application layer surfacing your repos with almost no code (just what's necessary to provide CRUD services for your entities).
Ultimately, you'll end up with something like this in your implementation of CreateController:
Type controllerType = controllerbase.MakeGenericType(entityType, datacontextType);
var controller = Activator.CreateInstance(controllerType) as IController;
return controller;
Wiser men than me would use a IOC framework to inject the types, I'm using plain old reflection and reading the type names out of the route values in URLs like:
http://computer/repo/entityname/by/fieldname/value.html
Good luck!