I'm Use structures by default when possible,
what's the best practice to update the original struct instance a copy of it was passed to another variable.
let's say I have a Post struct
struct Post {
let title: String
let likes: Int
let viewsCount:Int
var comments:[Comment]
}
and we have a simple Master-Details Screens
when pushing the details screen we are copying the post to the details scene, and there the data may be changed as likes increments, added comments and so on,
so when pop This ViewController, to the new data model to update the original model in the Master ViewController.
what are the possible solution and the best practices for it?
assuming we are using MVC or MVP
You could create a common shared model class that is responsible for holding the Post data and that both view controllers could use and observe.
This really has nothing to do with structs; it is simply the usual issue of passing data into a view controller on creation and passing data back from that view controller on destruction. It happens that in your case you are positing that this is the "same" data — i.e. you pass a Post to the detail view controller and you pass a Post back from the detail view controller — but that is just a contingent fact.
So, to answer the question as formulated, you would use any of the usual techniques. The detail view controller would need to pass the modified Post back to the master view controller. It could use a Notification or (more directly) you could use the standard protocol-and-delegate architecture.
On the other hand, if Post is the basis of your app's data, it would not be unreasonable to argue that the premise itself is flawed: this should have been a class all along, not a struct, exactly so that the data can be maintained in a central place and references to it can be maintained in different places.
Indeed, if things are more complex, you might have the app's data (including the Post) live in some third location off in model-data-space, and have all view controllers send a notification up to the data when they change it and have the data send a notification down to all view controllers in response (that is what Ralf Ebert's answer quite reasonably suggests). That sort of thing is a lot easier nowadays because (in iOS 13) we have observable objects and the Combine framework.
I would create helper functions and make them mutating. e.g.
mutating func incrementLikes() {
likes += 1
}
Make sure your properties are var.
Ideally classes are best for the role you described as object maintain identity.
Each post has an identity.
According to apple docs "use classes when you need to control the identity of the data you're modeling"
When you share a class instance across your app, changes you make to that instance are visible to every part of your code that holds a reference to that instance. Use classes when you need your instances to have this kind of identity.
Classes vs strut
Related
I have a UIPageViewController containing multiple view controllers each with questions for the user to respond to. Once these questions are answered I want to save the values somewhere locally so I can later create a PDF populated with these values. What is the best way to do this? Would I use NSUserDefaults?
It depends on how you want the data to persist. If you want the data to persist the life of the user, even if the app is deleted from the device, you would obviously need to store it away from the client such as in a database (i.e. Realm, CoreData). If you want the data to persist the life of the app itself, data that you would continue to use to shape the user's experience, you could use UserDefaults but that doesn't seem like what you're trying to do. And if you just want the data to persist the life of that instance of the app, store it in regular properties.
I suspect, for whatever reason, the third option would be your choice. If so, I would suggest creating a model object that's as an instance property of the parent view controller (that all page view controllers can reference)...
class SurveyAnswers {
var userName: String?
var userAge: Int?
var favoriteSauce: PizzaSauce?
var livesWithCats: Bool?
}
...and simply use the protocol/delegate pattern to pass data from the child to the parent which would update properties in that object (so long as they are updating the same instance of that object). Then, when you're ready to prepare the PDF file, simply ask the parent for that object, whose properties have been set by the child view controllers.
And if you're OK with it, you could use the shared AppDelegate as a way to store and retrieve this data. I would just caution against making this a habit but depending on your application as a whole, this may be a perfectly acceptable option.
General rule of thumb: set properties in destination objects to pass data forward and use delegates to pass data backward.
I've been working with mvc design pattern to develop iOS, and I was wondering if I'm doing this right. I've read a lot about MVC, and, in my concept, all information that will be displayed or will change view's characteristics will be stored at the model (as it is the 'knowledge' of my vc). So, in my way designing MVCs, I usually create a mvc to each view that users interact, and I have model objects that manages all 'shared' information across the App, and one singleton to persist some data, the 'UserSession' class. For example:
In my HomeViewController I have different subviews depending the status of my objects, so in this MVC (home) I have a 'presenter' as a model that controls my homeSubview type and their data. When I need to retrieve information from another model object (like user, or whatever object) is my 'presenter model' who do the trick (in this case), and the same happens in another mvc inside the project. My ways is something like:
Controller [ask to] Model (that communicate with others model if needed) which ['respond' to] Controller then [update the] View
So, that looks good to me, but I fell like I'm not using the mvc pattern correctly. Can someone give a opinion?
In advance: sorry about the english. And I appreciate any suggestion/help, Thank you!
I don't see anything wrong with what you are suggesting, though it sounds more like a MVVM(C) pattern rather than MVC.
So in iOS we are pretty much forced to use view controllers, so we need the extra (C) here, but essentially:
M (model), these should represent your data model.
class Model {
var firstName: String
var lastName: String
....
}
V (view), this should handle presentation, drawing to the screen, and displaying data
class View: UIView {
let fullNameLabel = UILabel()
}
VM (view model), this is where you adapt the data in your model to fit what your view needs. This is good because if you have multiple views that display data from the same model class you need only add a new view model, to adapt the data that it wants, rather than clutter up your model with lots of helper methods
class ViewModel {
let model: Model
func fullName() -> String {
return "(model.firstName) (model.lastName)"
}
}
And obviously we also have out controller that orchestrates the whole thing :)
It feels to me like this is more what you are doing, where your presenter is the view model.
This helps to encapsulate the functionality that doesn't really belong in either your view or model. The full name is not technically part of the data model, nor todo with rendering/presentation, so we move it to a adapter/presenter/view model (whatever you want to call it).
Maybe this is of some help to you? Feel free to include code examples for any other info if you want anymore feedback/have any more questions 👨💻
I have a problem to understand how to properly download data from a database to my model object according to MVC architecture in my app.
Here is how the app looks like:
/ NavigationController -> ViewController -> ...
TabBarController - NavigationController -> ViewController -> ...
\ NavigationController -> ViewController -> ...
So TabBarController is initial controller with 3 tabs. Under each tab there is a chain of UIViewController embedded in a NavigationController. Each View controlled by particular UIViewController is displaying data from database. These data are updated quite often so I need to download them from database periodically.
Then I have a model object. In my app I have only this one model object so all UIViewController have access to the same model object (I know each UIViewController should have its own model object but I have reasons to do that in this way).
As I understand MVC, the communication should be like this:
when a View must be updated, UIViewController sends a request for data to a model object
model object gets the data from a source
when the data are updated, model object sends information to a UIViewController
UIViewController updates data from a model object to a View
This should be OK. But I am not sure where should be placed a DatabaseController which connects to a database and sends requests to it. This should be separated controller and model object must have a reference to it because he needs to tell him that he needs update its data.
I have found some blog with information that the DatabaseController should be created inside the model object and separated from model object code using a category. My opinion is that this is bad idea because it breakes MVC architecture.
What do you think? What is the best solution for placing DatabaseController in the app? Should I put it into the object model or outside...
I've never used Objective-C before, but in general OOP, the code to access the DB should definitely be placed in a single, separate place - among other things, this makes it mush easier to maintain and modify the code to access the DB in the future, and makes code clearer in general.
A well-known and widely used approach is the Data Access Object (DAO) pattern, which allows you to abstract the persistence details of your application from the rest of the logic.
The Repository pattern is also used very often for this purpose.
In your case, if you have a model class called Thing, you'd create a ThingDAO, with methods such as getAllThings(), getThingById(id), updateThing(thing), deleteThing(id), etc.
Then you can call this object from your Controller, or better in my opinion, from your model class(es).
You may think that, as we add another layer, "it breakes MVC architecture" - in my opinion, actually the DAO layer is a part of the model. I think your concept of model is probably not quite right - bear in mind that the model is not one class, but a group of (potentially thousands of) classes. In a big app, your model would contain things like Entities, DAOs, EventListeners, Handlers, Managers, Commands, Transformers, Helpers, and many other things...
Data access, and more, like business logic should be part of the model, and I'll try to explain why:
putting more and more of the application logic into the model component leads to the fat models, skinny controllers paradigm
fat model, skinny paradigm leads to unit-testable code, as controllers are not very suitable for unit testing
this approach also increases the reusability of your components, as it's unlikely you'll reuse controllers (most of the controllers are application-targeted)
Of course this applies to MVC, however as this pattern is heavily used in the iOS development, it makes sense to primary focus on it.
My suggestion would be to move towards MVOC architecture where O represents Others. Others would be a layer that does not fit in either of M, V, or C. Here O would be your network layer. A singleton object that your controller calls to fetch data and gets a filled model object in return.
I am using a simple Master Detail scenario where the master uses a fetched results controller to populate its tableview. When a row is selected, the respective NSManagedObject is passed to the detail view controller where it is used to populate a few UILabels. I added an EditorViewController to allow some of the fields to be updated by the user. On save, I dismiss the EditorViewController and go back to the Detail view controller. The save uses the context assigned to the NSManagedObject.
What is the best way to know that the object in question changed so I can update the UI in the Detail VC? Currently, if I try to update the UILabels in the Detail VC, I don't see any of the new values for the object's properties. I know a delegate/protocol scenario would likely work but I am curious if there something already in place that I can use – a notification I can look for to update the object or if I should have a fetched results controller on the details screen? Not sure the proper path to take here.
NSFetchedResultsController is a most common solution to this problem. It listens for changes in the context and updates the table view's content accordingly. Your question is rather general, so I'll leave a link to a very good article showing how to use it properly.
You can use NSFetchedResultsController and register fetch request for that object (I think it is good solution). Alternatively you can listen for notifications like NSManagedObjectContextDidSaveNotification, but I would not recommend it.
Or you can pass an object to your EditorViewController and update properties there directly.
It's hard to say without seeing any code what you are going for, but the other common pattern for working with CoreData makes use of the Model-View-Controller(MVC) paradigm to stage info from your data store in a class variable, then your interface consumes from that class variable. You worry about saving, updating and refreshing the data between CoreData and your class variable separately from your interface consuming that data.
A typical workflow would then be:
//User does something that creates new info
//Store that info permanently in CoreData
//Refresh the class Variable by loading from CoreData (you can also skip this by updating the class variable simultaneously while saving to CoreData, but this is really sloppy and problematic. Better to save then re-fetch)
//Update your interface from the class Variable
Alternately, you could try Key Value Observing? but I'm not sure it's right for your situation.
I have a question regarding keeping the controller and view separate. It seems to me that the controller should only pass a model to the view, and the view decides how to display the model. This way, the controller and model stay separate and can be independently developed. However, a lot of tutorials I see online and even in the book Pro ASP.NET MVC Framework, I see a lot of examples using either ViewData["string"] or TempData["string"].
Doesn't this introduce two problems? The first one is that the view is now somewhat coupled to the controller in that it has to know the name of the strings that the controller is setting in ViewData/TempData. The second is that these are loosely-typed, meaning there's no Intellisense. If I was developing the controller, I can't just tell another developer working on the view to just use Intellisense for the model, I'd have to give him the name of the strings, and if I ever change the string names, I'd also have to change it in the view.
I guess ultimately what I'm asking is, is this right? Or am I not understanding something?
View Data is one of the a way to pass information between the view and the controller but, as you said, there's no intellisence and it increase coupling. Instead, you should consider using a ViewModel. See Scott Gu NerdDinner example (freely available) about the way to use ViewModel and about the pros and cons of ViewDate vs ViewModel.
I hope it will help.
I think that the tutorials and book are using this method to try and make things a little easier to start out. However, I my opinion is that this might be starting some bad habits as you suggest.
The way I do it roughly based upon this article by Jimmy Bogard:
All views are strongly-typed
One specific ViewModel class per View
The View determines what data is in the ViewModel
I don't use the Auto-Mapper as he does though. I usually use a utility method to convert my Model object to the associated ViewModel object.
Yes you are right, it's considered best practice to create a class often called ViewModel that is being sent to/used in the View. The ViewModel usually contains the model as well as any other data the view might need, such as page number for a paginated list view, or values for a list for the view to display.