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
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)
}
}
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 :
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 :
Static member of class implicitly calls "dispatch_once" hence it is thread safe.
Making init private prevents initialisation again.
Test code line to prove initialisation is private.
I usually like this pattern:
final class MyClass {
static let shared = MyClass()
}
Then you can call MyClass.shared to get your singleton.
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.
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() { }
}
public class MyClass {
static let sharedInstance = MyClass()
private init() {
print("MyClass Initialized")
}
}