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.
Related
I'm looking for examples of patterns in existing iOS apps (Swift preferred, but I guess it doesn't matter) to get some ideas on how to handle models when working with Firebase (or any NoSQL DB). The complication is due to 1) the way in which Firebase listens for changes to data, and 2) keeping the db request logic out of the models.
So let's say I have something like this:
struct User {
let name: String
let checkin: Checkin
}
struct Checkin {
let name: String
let coordinates: Double
}
Now when I decide I want data in my view controller, I'll do something like Database.database()..reference()... and have a method on my model parse the JSON and insert it into the model. Now what happens when it gets to a relationship property (in this case, checkin when creating an instance of User)? If I want the full user model with all child relationships, I'm only going to get back something like { checkin: someRandomCheckinId } from Firebase. I could make another call to get the checkin by ID, but I'd have to do that logic in my model when parsing the data received from Firebase, which doesn't seem ideal - it seems that logic best belongs on the VC (or a separate data manager class called by the VC). Is there a better way?
Additionally, I want to be able to listen for changes given it's a main feature of Firebase. I can't imagine polluting my model with a bunch of listeners is a good thing. 🤔
Any examples of repos with a good pattern for this would be great to help wrap my head around an appropriate solution. Thanks in advance!
There are two parts to this question. The first is how to structure data in a NoSQL document store. The second is how to structure an application in a modern OO language. Both expand well past the reach of Firebase and Swift.
For the first part, the advice I would give you to start with is that you should keep related data together for fast access. There is normally a space penalty when working with these type of databases as data is duplicated. This is ok as space is relatively cheap compared to time. In your example, if you often want to get all the 'Checkins' when you get a 'User' then store the 'Checkins' for a user in that user. If you are used to working with RDBs then you need to remember that with this type of storage, you can store layers of data and an attribute can have multiple values. Make sure you read the Firebase documentation on how data is accessed as they have some recommendations for how data should be stored to avoid wasting time and space. The main idea is to keep data flatter rather than too many layers deep and remember that getting a parent also gets all its children. This usually involves some data duplication.
For the second part of the question, you are going to have to dig a little deeper. Start by reading up on the software engineering principles. The SOLID principles are the most important but there are many others that you should abide by. One principle states that you should keep related data and behaviour together. Another states that each module(class) should have a single responsibility(reason to change). Most iOS developers follow the very bad development practice of loading the ViewControllers with all the application logic which violates many principles. Model classes are not just for storing state, they should also contain behaviour related to that state. A ViewController should not be accessing a database and should not know anything about the database. This should happen somewhere in your application model. If data is accessed in a specific way for each type then store that logic with the type. If much of it is the same for all types then abstract that part out.
When I first began working on a long iOS project, my first, I had need for a small class that was readily accessible throughout the project. At first I passed it around as a property and it became way too much of a headache.
So, even though I've seen posts here advising against it, I created that class in the AppDelegate and access it as needed through a pointer to the AppDelegate. Then, I would revisit it later.
So, it is now later, I'm approaching release of the project, and I want to deal with this issue. It works just fine as it is but if it is lousy practice I want to fix it before release.
I don't understand why it is lousy practice. Sometimes you just need a class that is readily available and these seems like as good a way as any to get it. But there could be some downside I'm not understanding.
Assuming I need this class instance (it is very lightweight but heavily used) to be accessible throughout the project (probably about 50 VCs, total), what would be a good alternative to just referencing it via the App Delegate?
TIA for comments. I hope it doesn't start a war.
There's no hard-fast rule for for this and hundreds of opinions to be had. Here's my take on it.
I don't understand why it is lousy practice. Sometimes you just need a class that is readily available and these seems like as good a way as any to get it. But there could be some downside I'm not understanding.
You're right to feel this way and yes sometimes you need a class readily available with your data. It's lousy because it is not how AppDelegate should be used. In an ideal world it shouldn't contain lots of unrelated state-data and should serve a singular purpose: delegating system calls to your app. The trick is where do you put your model so that everyone can have access to it?
Assuming I need this class instance (it is very lightweight but heavily used) to be accessible throughout the project (probably about 50 VCs, total), what would be a good alternative to just referencing it via the App Delegate?
Put your state in a class using the singleton pattern. It will ensure that only one copy of it is ever created and all your classes should be able to access it. This is a well accepted pattern in the iOS SDK (NSUserDefaults, UIApplication, etc..). Just beware of this class growing too large or doing too many things. Try your best to keep it simple and focused and the Object-Oriented Police will leave you alone.
Other resources and opinions worth considering:
http://www.cocoawithlove.com/2008/11/singletons-appdelegates-and-top-level.html
Is it a bad practice to use your AppDelegate as your Singleton?
Is it good practice to use AppDelegate for data manipulation and Handling?
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 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.
I commonly create apps that require storage of data, and this data is used across the entire program. It's not much, though, so I usually use NSUserDefaults to load/save this data. However, the saving/loading code, along with packing/unpacking the data, takes up space, and I thought moving this code to reusable methods inside a global singleton would be a good idea. It seems to have worked well.
Even so, I've read a lot lately on the evils of singletons and global objects, and I've started to have second thoughts. People often say that the use of singletons is almost always an indications of poor design. For the most part, I'd disagree (I think simple uses like this are a good design pattern), but I'm certainly no expert on the matter.
So, is using singletons even in a simple way like this bad? If so, what's the better alternative?
I definitely don't agree that singletons are evil. They are sometimes overused perhaps but on some occasions are just perfect for the job. In some applications it makes sense to have some kind of general data manager. The singleton pattern is used extensively in the SDK itself (app delegates, shared managers, default centers and so on). Most often these are not "pure" singletons, as in you can access a shared instance but can also create new instances if necessary.
The question you need to ask yourself is whether it would be useful to have access to a single instance of a data manager from anywhere at anytime, if it isn't then a singleton is probably not needed. If you are going to be using singletons in multi-threaded environments however, you do need to worry about race conditions (can one thread modify a resource while another is accessing it), the documentation has good explanations on how best to achieve this in Cocoa.
Let me try to explain with an example - Am using some code from a game I wrote. Let's say you have a GameMap class and a Tile class. The GameMap represents a 2 dimension grid of Tile objects.
GameMap *gameMap = [[GameMap alloc] init];
NSArray *theTiles = gameMap.tiles;
The instance of the GameMap owns the grid of tiles, and creates the tiles when the game map is created. No singleton needed.
You may say "but I only have one GameMap at a time, what's the big deal?". What about loading saved games, or loading new levels? Well that becomes as easy as:
// In whatever class object owns the game map
self.gameMap = [[GameMap alloc] initWithSaveData:saveData];
In conclusion, create an instance of a class that has code to manage other instances of things. Keep as little global as possible and your code will be more scalable and maintainable.