2

I had this implementation with Swift 2.0 and the Xcode suggestion is not only baffling but causes compilation error as well. it's a library where users are passing callfunc closures.

Before

protocol MyProtocol {

}

class Main

private static var t: dispatch_once_t = 0
private static var singleton: MyProtocol?
public class func getSingleton(callfunc: () -> MyProtocol) -> MyProtocol {
    dispatch_once(&self.t) {
        self.singleton = callfunc()
    }
    return singleton!
}

After

    private static var __once: () = {
        MyProtocol.singleton = callfunc()
    }()

    open class func getSingleton(_ callfunc: () -> MyProtocol) -> MyProtocol {
        singleton = MyProtocol.__once()
        return singleton!
    }

I basically need to pass parameter to __once function.

USER:

class Test: MyProtocol {

}

Main.getSingleton({Test()});

It's not a duplicate of Using a dispatch_once singleton model in Swift, a closure is being passed, it's a .framework and closure is passed in by the user of the library.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
user2727195
  • 7,122
  • 17
  • 70
  • 118
  • 3
    You don't need to do any of this; if you use `static let` for a property in Swift, you get a singleton. – TwoStraws Sep 22 '16 at 00:13
  • TwoStraws, the object to be created as singleton is passed by the user of the library, `static let` won't work as framework has no idea what classes could be there, that's why it accepting a `protocol` based closure. – user2727195 Sep 22 '16 at 02:22
  • See my answer in: [Singleton with properties in Swift 3](http://stackoverflow.com/questions/37953317/singleton-with-properties-in-swift-3/41825525#41825525) – Mehul Sojitra Jan 27 '17 at 05:07
  • check this ans: https://stackoverflow.com/questions/37953317/singleton-with-properties-in-swift-3 – Rohit Sisodia Jun 16 '17 at 10:57

6 Answers6

17

I usually like this pattern:

final class MyClass { static let shared = MyClass() }

Then you can call MyClass.shared to get your singleton.

Patrick Tescher
  • 3,387
  • 1
  • 18
  • 31
  • 11
    I'd also suggest declaring `init` to be `private`, to avoid another instance from accidentally being instantiated. – Rob Sep 22 '16 at 01:05
  • what about the closure parameter? it's a framework code and closure is passed by the user of the framework, i hope it's clear – user2727195 Sep 22 '16 at 02:18
  • 2
    Proof that this is the right way to do it now http://krakendev.io/blog/the-right-way-to-write-a-singleton – Chris Morse Nov 04 '16 at 02:01
  • Its also mentioned in [Apple Docs](https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/AdoptingCocoaDesignPatterns.html) that this way is the right way to use Singleton – JustADev Jan 13 '17 at 13:37
  • out of context answer, it's a library and singleton is user class singleton, user class singleton is not known to library at compilation time, and is provided to at runtime via closure, makes sense? – user2727195 Jan 29 '17 at 09:14
3
public class MyClass {

    static let sharedInstance = MyClass()

    private init() {
        print("MyClass Initialized")
    }

}
Draken
  • 3,134
  • 13
  • 34
  • 54
Basil Mariano
  • 2,437
  • 1
  • 20
  • 13
1

This works (as in it won't call callfunc twice), if you don't mind the function becomes @escaping:

class Main {
    private static var CALLER: (() -> MyProtocol)?
    private static let GETTER: MyProtocol = CALLER!()

    public class func getSingleton(_ callfunc: @escaping () -> MyProtocol) -> MyProtocol {
        CALLER = callfunc
        return GETTER
    }
}

Note that this does not address thread safety (CALLER can be changed before reaching GETTER), and the CALLER will be overwritten every time getSingleton is used which may impose some performance penalty.

Graham
  • 7,431
  • 18
  • 59
  • 84
kennytm
  • 510,854
  • 105
  • 1,084
  • 1,005
  • Thanks Kenny, can you please explain main the usage of @escaping and it's meaning, I'm new to this. and also is it guaranteed that closure is called only once for this static GETTER? – user2727195 Sep 22 '16 at 08:34
  • @user2727195: Escaping -> http://stackoverflow.com/questions/39504180/escaping-closures-in-swift/39504347#39504347; and yes the CALLER will only be called once, but it will be reasigned everytime `getSingleton` is used. – kennytm Sep 22 '16 at 08:45
  • and what about thread safety, it doesn't seem like thread safe – user2727195 Sep 22 '16 at 09:45
  • @user2727195 Retrieving the GETTER is thread-safe, but that's all about safety. It is possible that another thread sneaks in a different caller between the CALLER and GETTER statements. You will still get a valid MyProtocol anyway. – kennytm Sep 22 '16 at 09:49
  • ok, and I can't use `__once` and other built in dispatch.sync related methods to achieve what I want here, i.e. Main class maintaining singleton references to user's class (i hope i got the intent right) – user2727195 Sep 22 '16 at 09:55
  • @user2727195 You *could* use `Dispatch.sync` (check if `singleton` is nil or filled before calling the callfunc), but that would not be as optimized as `dispatch_once`. – kennytm Sep 22 '16 at 10:58
  • I liked the `dispatch_once` style I had before, not sure why it didn't like it, do I've an option using `dispatch_once` or `__once`? – user2727195 Sep 22 '16 at 16:04
  • @user2727195 Yes, you could write your function using Objective-C. – kennytm Sep 22 '16 at 16:05
  • no, I mean i wanna stick to swift, just using `dispatch` style and I believe it's part of swift – user2727195 Sep 22 '16 at 16:06
  • @user2727195 I mean, since Swift does not allow you to use `dispatch_once` directly, you have to bridge to Objective-C in order to use that function. – kennytm Sep 22 '16 at 16:08
  • I see what you mean, no problem I'm good with dispatch style, and I had the bridge in place, this new `__once`??? is it a new style? – user2727195 Sep 22 '16 at 16:10
1

The answers to date are great, but I like to be able to test my classes that use singleton with unit tests. The existing solutions require that you get a real singleton, not one that you can Fake, Mock, and Stub. In order to make testing easier, I use something like this:

protocol ScheduleManaging {
    func add(_ schedule: TimerSchedule)
}

class ScheduleManager: ScheduleManaging {
    var schedule: TimerSchedule?

    static var sharedInstance: ScheduleManaging = {
        return ScheduleManager()
    }()

    private init() {
    }
}

typealias ScheduleManagingMethods = ScheduleManager
extension ScheduleManagingMethods {
    func add(_ schedule: TimerSchedule) {
        self.schedule = schedule
    }
}

In classes under test that use the singleton, I can do something like this:

class OtherTests: XCTestCase {

    class FakeSharedManager: ScheduleManaging {
        var addMethodCalled = false
        func add(_ schedule: TimerSchedule) {
            addMethodCalled = true
        }
    }

    func test_SomeMethodThatShouldCallAdd_CallsAdd() {
        ScheduleManager.sharedInstance = FakeSharedManager()
        // Test that some class that uses the ScheduleManager
        // calls the "add" method of the ScheduleManager
        let someClass = ManagerUsingClass()
        someClass.someMethodThatShouldCallAdd()
        XCTAssertTrue(ScheduleManager.sharedInstance. addMethodCalled)
    }
}
Paul Waldo
  • 1,131
  • 10
  • 26
1

Create a singleTon class as follows :

     class mySingleTon{
        
        //1. gives you SingleTon object which is created only once.
        static let sharedInstance = mySingleTon() 
        
        //2. make init private so it  prevents others from using the default '()' initializer for this class.    
        private init(){
        print("i am born")
        }
    
        func foo()
        {
        print("hello")
        }
    }

Example usage :

class A{
func bar()
{
  mySingleTon.sharedInstance.foo()

//3. Uncomment to get error : initializer is inaccessible due to 'private' protection level.
// let newSharedInstance = mySingleTon()
}

let a = A()
let b = A()
a.bar()
b.bar()

Output :

i am born

hello

hello

As you noticed Initializer is only called once. As per apple docs :

“The lazy initializer for a global variable (also for static members of structs and enums) is run the first time that global is accessed, and is launched as dispatch_once to make sure that the initialization is atomic. This enables a cool way to use dispatch_once in your code: just declare a global variable with an initializer and mark it private.”

Explaining Comments :

  1. Static member of class implicitly calls "dispatch_once" hence it is thread safe.

  2. Making init private prevents initialisation again.

  3. Test code line to prove initialisation is private.

Community
  • 1
  • 1
Rahul Serodia
  • 419
  • 6
  • 13
0

Another way, for me which is good enough using init private

final class SingletonClass {

    // reachable from other classes
    static let sharedInstance: SingletonClass = SingletonClass()

    // properties
    var stringArray : [String] = []

    // not reachable from other classes
    private init() { }      

}
abdullahselek
  • 7,893
  • 3
  • 50
  • 40