Recreate a singleton - ios

I've come to the unfortunate point where I will need to recreate a singleton.
It looks like this
class Myplayer: AVPlayer {
static let sharedInstance: Myplayer = {
let instance = Myplayer()
return instance
}()
As you can see from this, the reason for this is that AVPlayer can sometimes come into a failed state (.status == .failed) and the documentation specifically mentions that the instance itself will need to be recreated in order to work any further.
I have wound up my app with this singleton pattern and have a few months of live traction with it so changing this to a non-singleton pattern would likely take too much work at this point.
So my question is, how can I recreate this singleton in a pretty way?

I use this pattern whenever I need a singleton that is resettable during (for example) unit tests:
class MyManager {
private static var _sharedInstance: MyManager? = nil
static var sharedInstance: MyManager {
if _sharedInstance == nil {
_sharedInstance = MyManager()
}
return _sharedInstance!
}
static func resetSharedInstance() {
_sharedInstance = nil
}
}

You're on the right track. The key is to create a computed property that checks whether the status of the AVPlayer is .failed, and if so, recreates the instance. A second private static let contains a reference to your singleton and gets returned after the check:
import AVKit
class MyPlayer {
private static var avPlayer = AVPlayer()
private static let _sharedInstance = MyPlayer()
public static var sharedInstance: MyPlayer {
if self.avPlayer.status == .failed {
self.avPlayer = AVPlayer()
}
return self._sharedInstance
}
private init () {}
}
There is no need to recreate the singleton.

You can have a public / internal sharedInstance as a computed property which shares the object from a private _sharedInstance. This way, you can not assign a new instance from outside the class. Also add a static function that recreates the instance by assigning the private _sharedInstance a new Myplayer object.
class Myplayer: AVPlayer {
private static var _sharedInstance = Myplayer()
static var sharedInstance: Myplayer {
return Myplayer._sharedInstance
}
static func recreateInstance() {
Myplayer._sharedInstance = Myplayer()
}
}

As posted in comment, you should never recreate a singltone!
By definition:
In software engineering, the singleton pattern is a software design
pattern that restricts the instantiation of a class to one "single"
instance
Maybe it's a bad news for you, but if AVPlayer isn't designed as a singleton, then you will never be sure, that there is only one instance of a class at the moment. So you or somebody else can create at another place another instance of AVPlayer.
Can recreation destroy a singleton pattern? If programming language does support references to the objects, then definitely YES! (At this point I have to say, that I'm not from ios/swift world ;) )
Just consider such scenario: You have a singleton object and somewhere in code there is a variable SomeVariable_A_OfTypeSingleton, let's say static, which points to the singleton object. Then at some point you do recreate a singleton, so that singleton access method does return new object, BUT the variable SomeVariable_A_OfTypeSingleton still have an old reference to the old object.
So you will have two instances of a class and it will not be a singleton anymore.
The only way I see is to reset the state of singleton(that is probably not possible in your case).

Related

Why Can Singleton classes be used as regular classes

I was under the impression that the main reason for using singletons was to make sure that only one instance could be created in a program. I thought that the compiler wouldn't let you create instances of a singleton as if it would be a regular class.
In the following code I have a singleton where I'm creating multiple instances of it and it behaves as a regular class, but for some reason I was expecting an error.
What makes a singleton different than a regular class if it lets you create multiple instances?
// singleton class
class Car {
static let sharedCar = Car()
func run(){
print("Running")
}
}
// use
Car.sharedCar.run()
// other instances- I was expecting an error here
var jetta = Car()
jetta.run()
var cobalt = Car()
cobalt.run()
What am I missing here, can someone explain singletons?
I thought that the compiler wouldn't let you create instances of a singleton as if it would be a regular class.
There is no language feature called "singleton", it is an idiomatic pattern. If you leave your implementation of singleton open for instantiations from outside, there is nothing the compiler can do about that.
In the following code I have a singleton where I'm creating multiple instances of it and it behaves as a regular class, but for some reason I was expecting an error.
You should add a private init to make sure there are no external instantiations:
class Car {
static let sharedCar = Car()
func run(){
print("Running")
}
private init() {
}
}
Now you are the only one who can instantiate your class. Users of Car class outside of your code are forced to rely on sharedCar instance that you create for them.

Ios swift class without instantiate

How do you (if possible) create a class that anything that references it gets the same object?
So, if I had a LoginClass
and I dont want to create a new instance in each file but rather be able to just do
LoginClass.userID
in any file without first creating an instance of it?
It possible. Use singleton:
Stackoverflow question
Tutorial by Ray Wenderlich
You are looking for a Singleton
This is the code
class Login {
static let sharedInstance = Login()
var userID: String?
private init() {}
}
This is how you retrieve the same instance
Login.sharedInstance
And this is how you use it
Login.sharedInstance.userID = "123"
In a different point of your code...
print(Login.sharedInstance.userID) // 123
Creating one instance per application life cycle means you want to implement Singleton pattern. You can implement singleton like this
class LoginManager {
static let sharedInstance = LoginManager()
var userId:String?
var name:String?
}
And now you can use this like
LoginManager.sharedInstance.userId

iOS: Subclass singleton in swift

I have a framework where I have a singleton class, let's say Singleton. This class is used by other classes in the framework.
In the app project I want to subclass this singleton class, e.g. AppSingleton: Singleton. Is it possible? What is the right solution?
I provide a solution but it may be a little hacky.
Class A {
open class var shared: A {
return A.privateShared
}
private static let privateShared = A()
}
Class B {
open class var shared: B {
return A.privateShared
}
private static let privateShared = B()
}
I must clarify, this ways isn't perfect since it actually create 2 instance! So, it will technically not a singleton any more.
However, you can override the class B's property or method to call A.shared method or property instead. You must know what you are doing and consider use the other way to fix the problem you want to solve.

Singletons In Swift

The first time I learned how to Implement Singleton Pattern in Swift is in this Book Pro Design Patterns in Swift.
The way I started implementing the Singleton Pattern is in the example below:
class Singleton {
class var sharedInstance: Singleton {
struct Wrapper {
static let singleton = Singleton()
}
return Wrapper.singleton
}
private init() {
}
}
But then I found this implementation while reading about Cocoa Design Patterns
class Singleton {
static let sharedInstance = Singleton()
private init() {
}
}
So my question is, what's the difference between the two implementations ?
Back in Swift 1 days, static let wasn't implemented yet. The workaround was to create a wrapper struct. With Swift 2, this is not needed anymore.

Trouble mocking singleton for unit testing in Swift

Hello I am trying to mock one of the singletons I use to test that various view controllers actually call properly it's methods.
I have the singleton declared as such
public class ModelsManager {
static let sharedInstance = ModelsManager()
private init() {}
[...]
}
In the view controllers that use the singleton, it is set to a lazy computed property as such:
class MyViewController: UIViewController {
lazy var Models = {
return ModelsManager.sharedInstance
}()
[...]
}
I am trying to mock the ModelsManager singleton in my XCTestCase as such:
[...]
func testSomething() {
let vc = MyViewController(nibName: "MyView", bundle: nil)
var mockModelsManager = ModelsManagerMock.sharedInstance
vc.Models = mockModelsManager
[... do something that calls a function in ModelsManager...]
expect(mockModelsManager.flag) == true // Using Nimble here
}
class ModelsManagerMock: ModelsManager {
var flag = false
override func test() {
flag = true
}
}
In the expect() assertion I am getting Value of type 'ModelsManager' has no member 'flag'
What am I missing here?
EDIT
It appears that what I was missing was ModelsManagerMock.sharedInstance still returns IRModelsManager() from the superclass. Due to the fact that static can't be overwritten by subclasses, how do I get around this?
The correct solution must involve not subclassing your singleton. Creating a singleton with a private init method prohibits you from subclassing this method.
If the goal is to test the current functionality of the singleton, why do you want to add additional functionality to it? The key point of a singleton is that there should only ever be one. If you want to support more than one, you shouldn't make it a singleton, even if it's just for testing.

Resources