Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift Run Code Right After Singleton Initialization

Tags:

ios

swift

I have some Swift code that I want to run right after my singleton gets initialized. The reason this code needs to run after my singleton is initialized is because the code relies on that singleton existing.

I have the following example code of what I'm aiming to do. But it looks like this code isn't working because the refreshData is trying to access the singleton before it's fully been created.

class MyObject: NSObject {
    static let shared: MyObject = MyObject()

    let otherManager: OtherStuff = OtherStuff()

    var number: Int = 1

    override init() {
        super.init()
        setup()
    }

    func setup() {
        print("Setup code goes here")
        otherManager.refreshData()
    }
}

class OtherStuff {
    func refreshData() {
        MyObject.shared.number += 1
        print(MyObject.shared.number)
    }
}

On repl.it it prints Setup code goes here and just kinda hangs forever. The example I'm working with is a lot more complicated, but I believe this small example accurately reproduces the issue I'm having.

How can I fix this so that I wait for the singleton to be setup and everything before running that code?

like image 630
Charlie Fish Avatar asked Feb 28 '26 16:02

Charlie Fish


2 Answers

Swifty way if you can change OtherManager's refresh function signature, i.e. func refreshData() to func refreshData(for: MyObject):

static let shared: MyObject = {
    let object = MyObject()
    otherManager.refreshData(for: object)
    return object
}()

Or, although a bit bulky (and Objective C style), I would use a private shared instance and call setup in its property observer.

static var shared: MyObject {
    get {
        if _shared == nil {
            _shared = MyObject()
        }
        return _shared
    }
}

private static var _shared: MyObject! = nil {
    didSet {
        shared.setup()
    }
}

Edit: Why DispatchQueue.main.async { self.setup() } is not the right solution:

class Test: NSObject {

    var value: Int!

    override init() {
        super.init()
        DispatchQueue.main.async {
            self.setup()
        }
    }

    func setup() {
        value = 1
    }
}

let test = Test()

// error: Execution was interrupted, reason: EXC_BAD_INSTRUCTION...
let value: Int = test.value

The last line of code let value: Int = test.value was called before the object had a chance to call setup, unsurprisingly as it was async call.

like image 140
Lukas Avatar answered Mar 03 '26 05:03

Lukas


Here is an example not to call infinite shared object, but use the existing singleton to do the rest work.

class MyObject: NSObject {
static let shared: MyObject = MyObject()

let otherManager: OtherStuff = OtherStuff()

var number: Int = 1

override init() {
    super.init()
   setup()
}

func setup() {
    print("Setup code goes here")
    otherManager.refreshData(self)
}
}

class OtherStuff {
  func refreshData(_ myObject: MyObject) {
    myObject.number += 1
    print(myObject.number)
 }
}
like image 39
E.Coms Avatar answered Mar 03 '26 05:03

E.Coms