1

For the user model in my iOS app, I use a singleton. I want to save the user object with NSCoding.

No problem to save it but how assign the decoded user to the shared instance of my singleton?

And another question, is it a good idea to use singleton for user object?

Rob
  • 415,655
  • 72
  • 787
  • 1,044
  • "Is it a good idea to use singleton for user object?" That's a matter of opinion and debate (discouraged on Stack Overflow), but there are plenty of thoughts on that topic here: http://stackoverflow.com/questions/137975/what-is-so-bad-about-singletons – Rob May 22 '16 at 21:31

2 Answers2

1

What you need to do is an unorthodox approach to the singleton pattern implementation. Usually in swift you do this:

class MySingleton {
static let sharedInstance = MySingleton()
    init() {
       println("BBB")
    }
}

You cannot use it this way because all the setup work is deferred to the initialiser whereas you want to do some conditioning before you actually create the instance. The easiest way to do what you need and preserve thread-safety is to use dispatch_once from the depths of apple's C library:

class MySingleton : NSObject {
    class var sharedInstance: MySingleton {
        struct Statics {
            static var instance: MySingleton? = nil
            static var dispatchOnceToken: dispatch_once_t = 0
        }

        dispatch_once(&Statics.dispatchOnceToken) {
           if let data: NSData = .... (get your data here) {
               let decodedInstance = NSKeyedUnarchiver.unarchiveObjectWithData(data: data) as! MySingleton
           Statics.instance = decodedInstance
           } else {
              Statics.instance = MySingletion()
           }
        }
        return Statics.instance!
    }
}

As a side note - what I usually do is that I use Core Data object for modelling user and therefore save myself lots of pain with persistence. I keep user as a field on some singleton class dedicated to keeping general application state, access tokens, etc. The class initialiser recovers user object from persistence and assigns it to it's field.

And last but not least - always think twice before introducing any singleton into the app, as this pattern can so easily be abused!

Terminus
  • 925
  • 10
  • 23
-1

Meh. In principle, no, this is possibly bad form, but in practice I've had very good success with it, and have had to go to great contortions in code bases where I didn't do it this way. Singletons are a venerable part of Cocoa. There's a broad movement (myself included) trying to move away from them, but it's not always clear that the pain is worth it. I'm not going to make much noise about whether you should or shouldn't here, let's talk about how.

The answer is to remember that Cocoa singletons are not Singletons. They're "shared singletons." They're just some instance. So set it.

struct User {
    static var sharedUser = User()
}

func decodeSomething() {
    User.sharedUser = ... some decoded user ...
}

This form isn't thread safe, which can be fine. If you need it to be thread-safe you can do that in the usual ways (dispatch_sync, dispatch_barrier_async). And you can add a didSet handler if you want to post a notification or notify observers some other way that the user has changed. But this whole approach works fine.

As you have more experience, you will probably decide that you hate this for some reason that you can't exactly articulate, vaguely retailed to "testability" (many of us do), and you'll come up with other solutions. But really this is fine.

Rob Napier
  • 286,113
  • 34
  • 456
  • 610