For our iOS programming class we must make a framework for Swift iOS. We had the idea of a framework simplifying CoreData manipulation. I began by creating a class where you put the NSManagedObjectContext created in AppDelegate at the beginning, so you don't have to write this long (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext anymore.
open class SimpleCoreData {
var context: NSManagedObjectContext
init(context: NSManagedObjectContext) {
self.context = context
}
func delete(entity: NSManagedObject) /*-> Bool*/ {
// some code
}
func getAll(entityClass: NSManagedObject.Type) throws -> [NSManagedObject]? {
// some code
}
func create(entityDescr: NSManagedObject.Type) -> NSManagedObject? {
// some code
}
But I would like it to be accessible from everywhere in the application, and this simplification would be useless if you have to instantiate this each time.
I was first thinking about a singleton, but I recently learned it wasn't a good practice.
So do you know any solution to make this accessible from everywhere in the client app? Or maybe singleton is okay in this case?
Keeping Rob Napier's excellent comments in mind, if you decide to avoid a singleton in this case, a common approach would be
Create an instance of your SimpleCoreData class in the app delegate when the app launches.
Have the app delegate pass this to your initial view controller. That view controller would have a SimpleCoreData property but would not create the instance-- it would expect one to be assigned by whichever code creates it, which here is the app delegate.
Repeat this pattern everywhere you need a SimpleCoreData. That is, when you create an object that needs a SimpleCoreData, make sure it has a property with that type, and assign a value when you create it. For view controllers, a good place to do this is in prepare(for:sender:), if you're using segues.
It's not necessary to create the SimpleCoreData in the app delegate, though. You could create it at the first point in the app hierarchy where it's needed, and pass it along from there. So if it's only needed in the second view controller in the hierarchy and in other objects loaded from there, create it in that view controller.
This doesn't make your SimpleCoreData instance available everywhere automatically, it means that you're creating one and then passing it around. Often that works fine, but as Rob notes it's not always the best approach. It can lead to passing an object along to an object that doesn't need it, because some other object that gets created later on does. For example if you need SimpleCoreData in your initial view controller but then not again until five levels down the hierarchy, you still need to pass it along every step of the way. That's an example of when a shared instance can be useful. It's not a singleton since other instances are allowed, but it's a default instance that can be used as needed.
I finally learned thanks to you that singletons aren't so evil, they can be used in some case, including this one, and it seemed in my case that it was a good choice. Maybe I will change it for a shared instance pattern.
So the singleton works well. Thank you everybody for your advices, I learned a lot in design patterns.
Related
I will try to make this question as understandable as possible. I am implementing core data in my app, and I need to access the NSManagedObjectContext from around 10,000 different instances of a class (this class extends UIView). The Core Data stores what is displayed on these instances and the class builds it.
Everything that I have found so far uses View Controllers, of which you only have one instance, so you can just alloc init the VC in AppDelegate, set an #property for NSManagedObjectContext and be on your way. This does not work for my program.
What I want to do is have many instances of my CoreDataHelper class (which I will alloc init in the class that I have around 10,000 instances of, which all have a property pointing to the same NSManagedObjectContext. Is this a possible way to do it or will I have to make my program less flexible by moving all of the code to create the 10,000 different objects to the View Controller?
Sure, just put your NSManagedObjectContext in a singleton and all your instances can access the single class.
It does not matter if you get your managed object context from a singleton or from your app delegate (where presumably you the core data stack is set up by default).
To follow the pattern suggested by Apple with view controllers, do the exact same thing with your views: give them a #property of type NSManagedObjectContext and set it during initialization. Seems straight forward enough.
The advantage of the singleton is that you do not even need the property on your view but can call the singleton instead. But why go there? From your comments I understand that you do not really know how a singleton works. You don't need it. Go with the class property solution.
One more caveat: with your setup, you are seriously braking the MVC architecture by giving the views access to your data. Instead, you should indeed have a view controller do this and then populate your views with the retrieved data. I do not think that there is a compelling reason to deviate from this principle.
I have a theoretical-practical question. I can't understand how I must do. I have a class let's call them DataManager that manage all plist writing-reading things and I need to get access to plist (i.e. work with that DataManager class) from different UIViewControllers.
I also have one class, I call it ModelManager, that is work with all kind of "utilities classes", include my DataManager. ModelManager works only with one complex UIViewController right now, let's call it MainUIViewController for clearness. And for now, I thought that all calls from UIViewControllers will be comes to ModelManager and from it to end-call classes. But now I'm confused.
Here is an illustration of my architecture:
I'm see different approaches and don't know how to decide and if there is some rules or guides for that. So, here is my choices:
1) I add some interface to ModelManager and from my another UIViewController (not a MainUIViewController) allocate and initialise it.
2) I add some interface to ModelManager and create a property with reference to ModelManager in another UIViewController and when segues performs set this property from MainUIViewController.
3) Work with DataManager itself and allocate and initialise it from another UIViewController
4) Work with DataManager itself and create a property with reference to DataManager in another UIViewController and when segues performs set this property from MainUIViewController.
Which approach is correct?
I know that this is some kind of depends from developer which approach to choose, but I never read and didn't find any tutorial or guide of how to develop multi-class architecture.
Ask me about any circumstance that you want to know.
You can use a singleton or you can instantiate one instance of the class in your app delegate and pass it around to all your view controllers via #propertys on each controller. There's no right answer, it's mostly a matter of preference. I prefer to make my ModelManager/DataManager type classes singletons, but a lot of people are rabidly opposed to singletons. However, if you work with Cocoa for any length of time you'll find that it's full of them (NSUserDefaults, NSFileManager, UIDevice, probably some others I'm forgetting).
Here's a good example on how to create singletons: http://www.galloway.me.uk/tutorials/singleton-classes/
BTW: Once you have your singleton, learn how to use KVO to make your view controllers respond to changes in the model. It's pretty fantastic once you get the hang of it. http://nshipster.com/key-value-observing/
I've got a test project to use the private data object on several view controller.
(I've downloaded it from web & git-hub)
- (ExampleAppDataObject*) theAppDataObject;
{
id<AppDelegateProtocol> theDelegate = (id<AppDelegateProtocol>) [UIApplication sharedApplication].delegate;
ExampleAppDataObject* theDataObject;
theDataObject = (ExampleAppDataObject*) theDelegate.theAppDataObject;
return theDataObject;
}
First question is, theDelegate was casted with AppDelegateProtocol, even this applications UIApplication delegate name was ViewControllerDataSharingAppDelegate, and there's no warning. I can't under stand why, maybe it's because that was a id type?
(AppDelegateProtocol is a custom delegate protocol he declared in the AppDelegate.)
Second, it shows this kind of code on every view controller, and it seems like just a single-ton pattern.
I don't think this is not the best way to transfer data between view controller.
Which is the best way to transfer object data type?
Thanks.
Creating a protocol decouples the code somewhat from the specific implementation. You could conceivably have several applications, each of which uses its own custom class as an app delegate, but all implementations conform to the AppDelegateProtocol.
I used to use the app delegate to hold global data and methods a lot when I first started in iOS.
However, that fills your app delegate with app-specific code.
I've shifted away from that approach recently, and use a data container singleton (and perhaps a utility methods singleton as well.) As is typical for a singleton, I define a class method that lets me fetch the singleton. I add properties to my singleton as needed to store data, and then just use the class method to get a pointer to the singleton. I write my class method to lazy load the singleton.
Its also easy to make your data container singleton persistent by making it conform to NSCoding. Then every time you get moved to the background, just save your singleton somewhere. On app launch, read it in.
So I'm getting myself into a confusion over where my data's going and where it's stored in my application. It's not a specific question so hopefully someone can provide a generalised answer.
I need to pass some data around between a few UIViewController instances, and I'm currently doing that with a singleton object called my dataManager. This class has one method, a class method, called + (LCDataManager *) sharedDataManager, and that method basically checks if whether the sharedDataManager already exists, if so, return it, if not, create it and set up its variables. This means that I can refer to that class anywhere I like, access and modify its variables anywhere I like, from across multiple classes.
First question: is this the correct / best / most appropriate means of passing data around like this? I'm hoping it obeys MVC, it feels like it does, and I hope I'm right.
Second question: what if I want to put an instance method in that class, and call it from within the class method? Let's say my sharedDataManager needs to call a method to grab some objects one of its variables (an array), and put them in another array, then send that back out again. I can't do that, can I? What's the way around that? If I make an instance of that class (rather than using the shared instance), I lose the ability to use that instance across multiple viewControllers.
I'm hideously confused, and it seems like it's not the problem I'm making it. Appreciate any guidance, and preferably not that "Read the Apple documentation" stuff – they write as if you already know what you're doing, and frankly I don't yet.
First question: is this the correct / best / most appropriate means of passing data around like this? I'm hoping it obeys MVC, it feels like it does, and I hope I'm right.
Your design is perfectly MVC compliant.
Second question: what if I want to put an instance method in that class, and call it from within the class method?
you can surely define an instance method and call it like this:
[[MyModelClass sharedModel] myInstanceMethod];
indeed, [MyModelClass sharedModel] will give you an instance of MyModelClass (which should be guaranted to be unique being it a singleton).
If you want to call the instance method from the sharedModel class method, you could also do that, because sharedModel owns a reference to your singleton, so it can send messages to it.
is this the correct / best / most appropriate means of passing data around like this?
There's nothing wrong with only having a single instance of LCDataManager, but using the Singleton pattern has potential problems. An alternative is to just initialize one LCDataManger and to pass it around to wherever it's needed.
what if I want to put an instance method in that class, and call it from within the class method?
The accessor + (LCDataManager *) sharedDataManager should only return the instance. I guess what you want is something like
+ (LCDataManager *)preparedDataManager {
LCDataManager *shared = [self sharedDataManager];
[shared doSomeInstanceMagic];
return shared;
}
- (void)doSomeInstanceMagic {
// magic!
// grab some objects one of its variables (an array),
// and put them in another array
}
Matthijs Hollemans has an excellent three-part tutorial on his blog about the correct way to make your view controllers talk to each other:
Part 1
Part 2
Part 3
there is no problem with this development architecture, and it is the must used (I think) in the iOS development. In the book IOS Programming: The Big Nerd Ranch Guide they call it Model View Controller Store.
Regarding your second question, yes, you can declare instance methods and call then from your sharedDataManager. What is not usual is creating other instances of a singleton class, but it is possible.
Within an application it's possible to have different UIViewControllers that need to share the same NSManagedObject. I'm usually do the following:
#interface CustomController : UIViewController
#property (nonatomic, retain) ProductNSManagedObject* productManaged;
#end
Then when I istantiate CustomController I inject it like the following:
customController.productManaged = ....
once done, CustomController is responsible to release it.
This approach works well (I don't know if is it correct), but what to do when a controller need that object but it's not a direct child of the controller that has that object? e.g.
MainController -> ChildController -> SubChildController -> ....
where MainController has the managed object.
Do I have to create a lot of intermediary properties or do I need to execute a fresh NSFetchRequest or something else?
The same aspect could be applied to the NSManagedObjectContext. Searching around I've found that the context can be grabbed from the application delegate that posseses it (if any). But this approach lacks of flexibility as Marcus Zarra wrote in passing-around-a-nsmanagedobjectcontext-on-the-iphone.
Any suggestions? Thank you in advance.
I create a singleton object that contains the managed object context that will be used throughout the application. I put any supporting code related to the data (e.g., persistent store coordinator) inside this singleton and keep all of the view and controller information separated from it.
In one case, I need a managed object context for another thread. It became apparent that it would be useful to refactor and put that context inside the same singleton. Then merging between the two contexts can be done inside the singleton.
This has helped me manage my code. You might consider it.
This is a very common question (see here and here for related ones). As I wrote in the answers for the related questions, you should stay away from singletons and create a separate object that will take care of object instantiation, of creating the object graph for your application. This separate object can hold references to all shared objects and supply them to the objects being built, so that none of your regular objects has to keep a reference to something just to pass it as a dependency to other objects. See this blog post for more rationale against singleton misuse and for further pointers, especially the articles by Miško Hevery.
I have created a sample Xcode project that shows how to wire an app without singletons, keeping the coupling low and resolving other singleton issues. It’s very simple at the moment, I will add more common use cases later.