Any Reason Not Use Use a Singleton "Variable" in Swift

Any reason not use use a singleton variable in Swift?

Functionally, these are very similar, but I'd advise using the Model.shared syntax because that makes it absolutely clear, wherever you use it, that you're dealing with a singleton, whereas if you just have that model global floating out there, it's not clear what you're dealing with.

Also, with globals (esp with simple name like "model"), you risk of having some future class that has similarly named variables and accidentally reference the wrong one.

For a discussion about the general considerations regarding globals v singletons v other patterns, see Global Variables Are Bad which, despite the fairly leading title, presents a sober discussion, has some interesting links and presents alternatives.


By the way, for your "OCD friends" (within which I guess I must count myself, because I think it's best practice), not only would declare init to be private, but you'd probably declare the whole class to be final, to avoid subclassing (at which point it becomes ambiguous to what shared references).

Why not use a struct-based singleton in Swift

You can't use a struct fora singleton because struct is a value type so when you assign it to a variable you get a copy. This can be easily shown

struct Singleton {
static var shared = Singleton()
var value: Int

private init() {
value = 0
}
}

Singleton.shared.value = 1
var otherSingleton = Singleton.shared
otherSingleton.value = 2

Now if we print the value of both

print(Singleton.shared.value, otherSingleton.value)

we get

1 2

So otherSingleton is clearly a separate instance so now we have 2 singletons :)

But if we simply change the type of Singleton to class that is a reference type and then run the same code the result of the print is

2 2

since it is the same instance we have changed the value property for.

Swift - Using a singleton to share variables - Not passing over

Answer

Are you sure you are reading the value after writing it?

Please check it adding a breakpoint.

It this is the problem you could move the reading from viewDidLoad to viewDidAppear.

Less is more

Your Singleton is working

// from my Playground
CardName.sharedInstance.returnedCardName = "123"
CardName.sharedInstance.returnedCardName // "123"

However is does contain lots of useless and redundant code :D

This is a better version

class CardName {
static var sharedInstance = CardName()
private init() {}

var cardName: String?
}

Please just use

CardName.sharedInstance.cardName = "something"

to save a value.

And

CardName.sharedInstance.cardName

to read that value.

Optional property

The new property, as you can see, is declared as Optional String. It means it can contain nil. This is a better way to represent the absence of value than the empty String you were using.

Why is using a Global Variables class (singleton) bad practise?

A lot of objects in UIKit are singletons. The UIApplication object is a singleton. NSUserDefaults has the standardUserDefaults singleton. UIDevice currentDevice returns a singleton.

Just because it's a singleton doesn't mean it's bad. What is bad is when you start tying functionality in other classes to your singleton object, with it so deeply ingrained that you can't alter the singleton or the affected object easily.

I use a singleton to store my non-CoreData object structures. I also define some helper methods for getting the library directory, encoding data, and keyed archiving. So I can reference a master array object wherever I need it, or easily access methods that would otherwise just be copy and pastes.

Why does the suggested Swift singleton implementation use a struct?

with your implementation, the 'sharedInstance' is not a singleton cause each time it gets called, it creates new instance of MySingleton. And, to create a static variable, you have to put it in struct o enums, otherwise, you will get compiler error

Why we should use struct and class function in singleton pattern?

Actually it is recommended to use your second code because of the improvements in the versions of swift.One more thing that you should consider is to declare your singleton object using static let and also make the initializer private

class Bar{

private init(){
// initialization
}

static let shared = Bar()

}

Singleton in Swift

The standard singleton pattern is:

final class Manager {
static let shared = Manager()

private init() { ... }

func foo() { ... }
}

And you'd use it like so:

Manager.shared.foo()

Credit to appzYourLife for pointing out that one should declare it final to make sure it's not accidentally subclassed as well as the use of the private access modifier for the initializer, to ensure you don't accidentally instantiate another instance. See https://stackoverflow.com/a/38793747/1271826.

So, returning to your image cache question, you would use this singleton pattern:

final class ImageCache {

static let shared = ImageCache()

/// Private image cache.

private var cache = [String: UIImage]()

// Note, this is `private` to avoid subclassing this; singletons shouldn't be subclassed.

private init() { }

/// Subscript operator to retrieve and update cache

subscript(key: String) -> UIImage? {
get {
return cache[key]
}

set (newValue) {
cache[key] = newValue
}
}
}

Then you can:

ImageCache.shared["photo1"] = image
let image2 = ImageCache.shared["photo2"])

Or

let cache = ImageCache.shared
cache["photo1"] = image
let image2 = cache["photo2"]

Having shown a simplistic singleton cache implementation above, we should note that you probably want to (a) make it thread safe by using NSCache; and (b) respond to memory pressure. So, the actual implementation is something like the following in Swift 3:

final class ImageCache: NSCache<AnyObject, UIImage> {

static let shared = ImageCache()

/// Observer for `UIApplicationDidReceiveMemoryWarningNotification`.

private var memoryWarningObserver: NSObjectProtocol!

/// Note, this is `private` to avoid subclassing this; singletons shouldn't be subclassed.
///
/// Add observer to purge cache upon memory pressure.

private override init() {
super.init()

memoryWarningObserver = NotificationCenter.default.addObserver(forName: .UIApplicationDidReceiveMemoryWarning, object: nil, queue: nil) { [weak self] notification in
self?.removeAllObjects()
}
}

/// The singleton will never be deallocated, but as a matter of defensive programming (in case this is
/// later refactored to not be a singleton), let's remove the observer if deallocated.

deinit {
NotificationCenter.default.removeObserver(memoryWarningObserver)
}

/// Subscript operation to retrieve and update

subscript(key: String) -> UIImage? {
get {
return object(forKey: key as AnyObject)
}

set (newValue) {
if let object = newValue {
setObject(object, forKey: key as AnyObject)
} else {
removeObject(forKey: key as AnyObject)
}
}
}

}

And you'd use it as follows:

ImageCache.shared["foo"] = image

And

let image = ImageCache.shared["foo"]

For Swift 2.3 example, see previous revision of this answer.



Related Topics



Leave a reply



Submit