1

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?

Jonny
  • 15,955
  • 18
  • 111
  • 232
  • 1
    Don't recreate the singleton. If you recreate a instance it can never be singleton. Rather write a method in singleton class which will reset it to clean state. Thats what you get when you recreate instance also. Simply clear all the properties and fallback to their initial values :) You have reset instance rather than recreated instance :D – Sandeep Bhandari Oct 04 '17 at 10:37
  • Not possible with AVPlayer unless you have some other info. – Jonny Oct 04 '17 at 10:38

4 Answers4

3

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
    }
}
Nicolas Miari
  • 16,006
  • 8
  • 81
  • 189
1

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()
    }
}
Yannick
  • 3,210
  • 1
  • 21
  • 30
  • This kind of looks good, but I wonder if as soon as you do something like this, is this still the singleton pattern... – Jonny Oct 04 '17 at 09:29
  • If you think that this does not follow the singleton pattern, then it is because of your use case. – Yannick Oct 04 '17 at 09:35
1

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.

Dennis Vennink
  • 1,083
  • 1
  • 7
  • 23
  • Nice idea, but wouldn't it be better to have the `avPlayer` be a property on the singleton? – Jonny Oct 04 '17 at 15:19
  • It's not possible to access non-`static` instance variables from a `static` member; so `avPlayer` has to be `static`. [This](https://softwareengineering.stackexchange.com/questions/211137/why-can-static-methods-only-use-static-data) explains it nicely. – Dennis Vennink Oct 04 '17 at 15:36
  • With several useful answers, this is the one I ended up using for now. I did however use `avPlayer` as an member variable / property on the MyPlayer instance (it works well, just remove `static` from `avPlayer`). It's possible that it doesn't make a difference in this case but. The point is to have MyPlayer a singleton and then being able to recreate the related AVPlayer when it fails. – Jonny Oct 05 '17 at 00:46
  • For everyone taking notes, and to make things clear, I renamed MyPlayer to something else, because it's not really a player no more and this should avoid confusion. Xcode 9 has excellent refactor/renaming support for Swift these days. – Jonny Oct 05 '17 at 00:49
0

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

Rekshino
  • 6,954
  • 2
  • 19
  • 44