I have a general design question for dealing with data. I currently am working on an app that stores about 100+ different unique properties (mostly integers, some strings) and am currently using a single object of a custom class to manage all of them.
I need the data to be "persistent" throughout the app so I currently pass the object via segues. I have managed to build 20+ views, most with their own view controller. I am very new to Objective-C and iOS development and have a feeling this is a bad practice.
I do not understand that much about core data and am not sure if it would be a better solution for me. I have also read about singletons and have heard mixed things about using them for this sort of solution.
In the future, I will need to permanently store the data that is held temporarily in the custom class I have written.
What is the best way to deal with this situation? Is it standard practice to pass the object around over many different views?
If the data needs to permeate through the entire app then a singleton might be a good way to go.
In game dev there is a pattern called the chalkboard pattern that allows any part of the game to read and write to the chalkboard. This can be used for health points, scores, etc...
This would suit you well too. Rather than pushing your data model around all the time just use the singleton to access each bit/ If the data needs to be updated then store the updates to the singleton.
The thing to avoid is using the singleton just because its there. If a bit of data needs to get from one place to another then don't just use the singleton if it isn't necessary.
Related
I've looked through a lot of answers on stack overflow but haven't found anything that answers this question.
I have an app where you download data in a downloadVC (there are many other VC's). I want to be able to access the currentUser and the downloaded data in the downloadVC whenever I go to the downloadVC without re-downloading the data.
The options I've looked at so far are:
Making a singleton with data that I could access at all points of the app (feels like the easiest way but people have warned me singletons aren't good).
Making a class var of download VC. (This is the solution I understand the least, what is the scope of a class var? Is it just the same as making a singleton?)
Passing it around (This wouldn't work for me as the app is too big to have it passed between every VC)
Saving it to user defaults, (how quick is accessing something user defaults?)
Please tell me if this questions doesn't fit the stack overflow rules?
Singletons are usually implemented using a static variable, so your first and second options are quite similar.
There is a "singletons are evil" sect that's very vocal in the development community lately.
Personally, I think they have their place and can sometimes clean up your design. I recently worked on a project that had been designed by a member of the "Singletons are evil" cult who then went to absurd lengths to pass a data manager object around to every other object in the project, resulting in a lot of overhead and more than a few bugs when the object got dropped.
There is no one answer. You need to weigh the pros and cons of different approaches for your app.
I would caution against over-using UserDefaults though. That's intended for saving small bits of data like user preference settings, not big data objects.
Basically, adding any networking logic inside the view controller is a first big mistake that you could make. So move that to another class that is responsible only for sending network requests and handling the responses.
Then, when you have the data downloaded, you'll probably need something to manage the cache - no matter whether it is a set of big files or a couple of small strings, the logic would be the same - to have all this cache controlled by one object. This cache manager could save the objects to local files, use CoreData or simply keep those objects in memory - this is for you to decide, depending on what kind of app are you creating.
Now, the cache manager could basically be your endpoint for view controllers, since it will either download the data and return it to view controller after the request's success, or return it immediately. The view controllers don't really have to know about any networking at all.
But, how the view controllers will find out about the cache manager?
You could either pass around the reference to it, but you've already said that this is impossible in your app. So basically another solution would be to use the hated singleton pattern. I too personally think this is a bad pattern, however you cannot create any app without it, since you always must have at least one singleton, which is AppDelegate.
Anyway, a good idea might be to create a singleton class (or maybe even use the AppDelegate for that purpose), that would be responsible for handling the dependencies between the classes responsible for networking, the cache manager, and any other classes that you might need to perform some logic behind your app. This is actually a pattern called Dependency Injection Container. It would allow you to easily access your model classes through this container, without neither having a ton of singletons that will eventually make you confused, nor creating some ridiculous logic of passing around the model objects.
The simple answer is, yes, you use a singleton.
That's exactly correct.
Note that whether you use
SQLite.swift,
core data,
realm.io,
write to a text file,
a baas such as Firebase or Back4app (aka Parse),
or literally just "keep it in an array" ("in memory"),
Yes, the answer to your question is you'll have singleton which is where you "keep - access" that stuff. Exactly correct.
That seems to be what you are asking here.
Given that ...
... if you are then additionally asking "what's the easiest / best / most modern way ("in my data singleton") to store data locally in my app", the real-world answer in 2017 is
realm.io
Previously you used Apple's core data. Which is (a) spectacular (b) extremely difficult. Importantly though: realm.io and SQLite are identically available on both Android and iOS; in very many cases, on real world projects today, this eliminates Core Data from consideration.
However. All of this is somewhat moot. Don't forget ...
We now live in a "baas world"
You can't eg. "get a job programming iOS or Android" any more. You get a job because of how good you are / your specific expertise areas in either Firebase, Parse, PubNub, Cloudbase, or whatever. For better or worse, being able to move buttons and tables around in Xcode, Studio, is not really a thing anymore. (There are a minuscule number of hyper-specialities, like GPU programming, dynamic mesh, or the like, that are exceptions to this.) Similarly, say you're a hobbyist making your own app or startup: again in that case, it's just entirely and totally about what backend you work with. (And that applies equally to games or business/social apps.) There are simply no "local, static" apps any more. (Again, applies equally to games or business/social apps.)
(Note indeed that realm.io, which is "the" simple, obvious way to keep data on apps these days - indeed, those guys have/are moving to becoming, indeed, a OCC cloud baas-paas-whatever service themselves. We'll probably all just use realm.io instead of Firebase in a year.)
So in a sense the answer to your question is something like Firebase or back4app. But: then within your app, you centralize that to a singleton, indeed (the first part of your question).
But note....................
it is extremely unlikely, at the beginner level, that any of this will be relevant: Just keep the data in an array! That's all there is to it. OK, once in a billion years when a user happens to literally restart the device or your app, the app will reload data. So what?
Note that a common name for your "get data here" Singleton is
Local
So,
import Foundation
often .. import SQLite, Realm, Firebase or whatever
public let local = Local.shared
open class Local {
static let shared = Local()
fileprivate init() {
print("'Local' singleton running AOK")
}
// could be this simple..
var clients:[String:[Blah]] = [:]
var stock:[String:String] = [:]
func loadStockInfoFromWWW() { ... }
func calculateQuantityInfoOrWhatever() { ... }
// or it could be more like this..
func reloadClientsFromParse() { ... }
func sendNewDataToFirebaseParse() { ... }
.... etc
you then just access it from anywhere in your app like
local.updateFromWeb()
height = local.stock["ladders"][idNumber].size.h
and so on.
That's all there is to it.
(A word on singleton code style.)
It depends on file complexity & size. If you don't need to parse the contents, or they're simple to parse, but it's large in size then I'd recommend using the Documents folder and retrieving it from there, if it's small or involves complex processing then a singleton acting as a manager would be my approach as it feels appropriate.
There is no shortage of tutorials on coredata, or questions here on SO about how to get started with coredata, or how to use specific parts.
My question is higher level - how should a larger project be architected with coredata?
Should the project keep most of the functions that deal with managed
objects in a single class?
Should the functions that deal with the
methods be static (I suppose they are called 'class methods') or
instance methods?
Is it ok to pass managed objects into class
methods from different threads? what about if I also supply a
context to the method?
Should I only be doing a single fetch for each entity when the app starts, then doing all of my searches and inserts against the context, or grabbing smaller sets of data from a fetch request as I need it?
As far as the blogosphere goes, it seems like the coredata architectures are the wild west - every man for themselves. Any good design patterns here to follow?
True, although some of you questions are really based on personal preference. I for one will never use a sigleton.
NO and YES. Yes on class to keep the connection to the context and no every controller will request its own data.
Using class methods requires you to either pass the context around or store it in a static. Using a static can cause problems if you need to change the context.
No, each thread should have it's now context. Core Data is not thread save.
To save memory only fetch what is needed, no sense in fetching everything at once.
I would suggest using NSFecthResultsController for filling things like table views.
If you are fetching data and storing it in core data I can really suggest using a separate context for inserting. Cocoanetics has a great article about multiple context setup.
It seems to satisfy the three requirements here: On Design Patterns: When to use the Singleton?
I need it to exist only once.
I need to access it from all over the source base.
It handles concurrent access, i.e. Locks for writes, but can handle concurrent reads.
Hi all,
I've been reading a lot of no doubt intelligent educated and wise gems of advice that Singletons are 'Evil' and singletons are anti patterns or just plain bad news.
With the argument that logging makes sense but not much else.
Just interested to know if the case of essentially a persistent data store context makes sense for a singleton, i.e. Reading/Writing from disk to memory and referencing an object graph.
And if not, how do people normally tackle this issue, I don't currently have any problem with it, I know it's created only once, it's fast and the accessor logic is in one place. Meaning it takes me one line of code to do anything data model related.
Which leaves the only argument that it's bad for testing, in that it's a hard coded production implementation to data, but couldn't I just write a method swizzle through a category or in the test code to create the test version of the singleton?
And the final argument from DI testers, is that it is a hard coded implementation, rather than simply an interface to something, which I do get but I don't really have a major drive to use a DI framework given that I can use protocols for implementation, and use separate init methods for setting up an objects state in testing. There's only ever going to be two types of state for the singleton, or realistically one type...production.
Making it (in my opinion) much easier to read and faster to develop.
Change my view SO?
Yup, for some singletons are Evil. For the new developers who has little MRC knowledge and more ARC it sounds scary because they need to mess with memory,volatile,synchronize etc.
However it is not against to use them, they indeed has their own purpose to use with some are below.
when sharing large data models like arrays and dictionaries etc between multiple screens (VC's) we can't prefer storing them in UserDefaults (because userdefaults is permanent storage and storing such large entries make app start lazily) instead singletons are best since they stay only current app context and restarting app creates new one.
when we need a stable db connection to be accessible allover the app without messing up with connecting and closing in every business classes we can go for it.
when we wanted an ability to app for theming itself dynamically we would need to create a singleton class which holds all the color,image instances etc. and use that instance in application VC/Views etc so no code duplication and re-processing theme occurs in all places.
You dont have to change your view but tweak it a bit to get some positive intention towards singletons.
Hoping this clears it out, thanks
I have a simple game made, still working on some of the additional portions of the game. I am not sure how/where to store my game data (scores, settings, etc)
I've been using a singleton with all of the data that I'll use in the program synthesized for ease of access, but I don't know if this is a good method? It seems to me it would make settings and such quite easy, because I can just share the singleton among the menu view and the game view, without having to copy the data.. Is there another way? Every book I've read so far seems to skip over the model and just combine it with the controller portion of the MVC.
Singletons while appealing for their simplicity generally make the code base less flexible to changes. They also do not encourage a clear separation of functionality in the model.
An alternative is to use core data for the model. You would have a managed object for game,scores, settings etc and pass around references to the objects. With Core Data you get persistence for free along with fast/efficient mechanisms for sorting and presenting data.
So I have an app with a bunch (like 25+) view controllers. And these vc's use a lot of data, most of which I could either store locally in the app, or store it on my server. I would rather store this data on my server to minimize app size, so in order to do that, I read somewhere that the best practice for doing that is to have a data class, which would be a special class that gets all the data from the internet at one time in one place, rather than just getting the individual data as needed, in like viewDidLoad or somewhere similar. So my question is, is it really better to make a central data source within my app and every class draw from it. And if so, could you provide an example of how I should set up that class. For example, how to make it run in the background (I guess a different thread) and how to pass NSDictionarys and NSArrays, etc. from the data source to individual class as needed. Thanks in advance. I can clarify if necessary.
You are talking about two different topics:
Accessing data from a main source from all the view controllers.
Accessing data from a server.
The best solution for you would be to create a new class that would hold variables that are used by some different classes. This tutorial explains how to do that - I think this would be one of the best solutions that you'll find on the internet.
For solving the second question, you should also create a class that execute SQLite statements and gets information from a database. You should use SQLite in order to do that, and if you won't like it, you could try to use Core Data, although SQLite in my opinion is much simpler.
This tutorial describes how to do that.
After you've done both of the things you can create an instance of the class that handles the database services for you in the class that shares information between the view controllers, and access that instance from different view controllers.
In the end, you could try to perform tasks like that on different threads. You should try doing it using NSOperationQueue objects - they will do most of the job for you. I suggest reading about them in the developer documentation provided by Apple in order to understand how it works.
Based on your replies in the comments, I would recommend this solution:
Transferring data from your server to the device - After downloading
your data from your server, store it into an SQLite database in the
device.
Accessing your data from your many view controllers - Use Core Data
to access the downloaded data in your database from any of your view
controllers.
If you don't know much about database programming, Core Data is an easy way to start. It encapsulates your tables into classes that you access like simple objective-c classes, so you don't have to learn SQL. You can also call them from any and all of your view controllers, whenever you need them, so you don't have to make a special class to handle your data. It is also better performance-wise and cleaner design-wise than passing your data through your view controller hierarchy. See the guide here.