2

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?

rmaddy
  • 314,917
  • 42
  • 532
  • 579
Lewis Black
  • 957
  • 2
  • 8
  • 22
  • It depends upon, what do you want to download. Do you want to download files and store them in an app that can be accessible from any view of app? – Krunal Feb 22 '17 at 14:00
  • is there concurrent use of downloadVC's data ? – Nikunj Damani Feb 22 '17 at 14:02
  • Depending on the entity of "data" the answer changes. Is the data you are downloading big in terms of size? Do you need to get this downloaded data later no without downloading it again? We need more context – Giuseppe Lanza Feb 22 '17 at 14:04
  • @Lewis Black User defaults are not for storing large size data and you should not keep any sensitive information in user Default.So you can try using other database like core data etc. – Tushar Sharma Feb 22 '17 at 14:07
  • This question is a little unclear but it's a good question. – Fattie Feb 22 '17 at 14:36

4 Answers4

2

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.

Duncan C
  • 128,072
  • 22
  • 173
  • 272
1

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.)

Community
  • 1
  • 1
Fattie
  • 27,874
  • 70
  • 431
  • 719
  • While using a singleton is simple, it's rarely the correct solution. It causes all kinds of potential issues. – rmaddy Feb 22 '17 at 14:56
  • I'm not as anti-singleton as some, but I also don't believe that a singleton is always the answer. Likewise your categorical rejection of Core Data is an overstatement. Core Data is a powerful framework with lots of advantages. It's true that it's not available outside of Apple's ecosystem but that's not always the deciding factor on what framework to use. – Duncan C Feb 22 '17 at 15:03
  • Hey DC. You literally have to have a singleton as the "point" of touching your data in an app. As certainly as say the "screen or compass" is a singleton in devices. – Fattie Feb 22 '17 at 16:59
  • I completely respect you @rmaddy - but it would be difficult to make a more pure and perfect example, in every way from theoretically to practically, than "data point" for something that "has to be a singleton". I'd be interested to hear as a practical matter what you do, maybe you can put a short answer in. Cheers – Fattie Feb 22 '17 at 19:30
1

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.

user3581248
  • 964
  • 10
  • 22
  • @JoeBlow maybe, though I did mention the AppDelegate, I didn't state it clear enough that I am aware of the fact, that singletons are important in the iOS development (I won't be writing about Android since I have 0 experience in that field). However, when it comes to designing a good and sustainable code, they really shouldn't be overused. Also, I think that the simplicity of usage is the main thing that makes them hated - simply because lazy or inexperienced developers often treat them as a magic solution to every architectural challenge. – user3581248 Feb 22 '17 at 19:39
0

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.

Nicholas Smith
  • 11,642
  • 6
  • 37
  • 55