Convert Swift 3 code without using dispatch_once_t

Before upgrading to Swift 3, I had the following code:

//Set up singleton object for the tracker class func setup(tid: String) -> WatchGATracker { struct Static { static var onceToken: dispatch_once_t = 0 } dispatch_once(&Static.onceToken) { _analyticsTracker = WatchGATracker(tid: tid) } return _analyticsTracker } 

I get the following error:

 'dispatch_once_t' is unavailable in Swift: Use lazily initialized globals instead 

Apparently, the conversion tool converted the code into this:

  class func setup(_ tid: String) -> WatchGATracker { struct Static { static var onceToken: Int = 0 } _ = WatchGATracker.__once return _analyticsTracker } 

And at the top of my class, he added the following:

 private static var __once: () = { _analyticsTracker = WatchGATracker(tid: tid) }() 

But I still get the error message:

 Instance member 'tid' cannot be used on type 'WatchGATracker' 

tid is declared as:

 fileprivate var tid: String 

It was declared as:

 private var tid: String 

I can't figure out how to fix my code, does anyone have any suggestions?

0
ios xcode swift swift3 watchkit
Sep 19 '16 at 15:29
source share
3 answers

It is not clear what you expect from this code. What happens if someone calls WatchGATracker.setup(tid: "abc") and then calls WatchGATracker.setup(tid: "123") ? It seems the last teed is quietly ignored. I do not believe that this is the correct singleton.

You need to be clearer about what the right behavior is. For example, if a programming error does not cause setup before using the tracker, you want something like this:

 class WatchGATracker { static private(set) var instance: WatchGATracker! class func setup(tid: String) { precondition(instance == nil) instance = WatchGATracker(tid: tid) } init(tid: String) { . . . } } 

If different parts of the program can call with different values, you need to do something more:

 static private var instances: [String: WatchGATracker] = [:] class func setup(tid: String) -> WatchGATracker { if let instance = instances[tid] { return instance } let instance = WatchGATracker(tid: tid) instances[tid] = instance return instance } 

(As noted in the comments, if you want to ensure thread safety for this sample, you need to add a dispatch queue at the class level to manage it the same way you would add an instance send queue at the instance level to ensure thread safety at that level. You don't get automatic thread protection if a parameter is required for initialization.)

0
Sep 19 '16 at 15:42
source share

When the error says "Use lazily initialized globals instead," it suggests something like:

 class WatchGATracker { private var tid: String static let shared = WatchGATracker(tid: "foo") private init(tid: String) { ... } ... } 

Or, if you really want to give the caller the opportunity to set tid (this is a curious pattern for a singleton), change init to not accept the parameter, make tid implicitly expanded, but not private and declare shared as:

 class WatchGATracker { var tid: String! static let shared = WatchGATracker() private init() { ... } ... } 

and then you can do

 WatchGATracker.shared.tid = "foo" 



By the way, with regard to fileprivate , this suggests that it simply because it now translates the old private . But if you have any reason, it should be fileprivate , I would most likely return it to private (now this makes it closed to the lexical domain).

0
Sep 19 '16 at 15:48
source share

We are all used to overly complex singleton patterns ... It's actually very simple:

 class WatchGATracker { static let sharedInstance = WatchGATracker() private override init() {} } 

Source: http://krakendev.io/blog/the-right-way-to-write-a-singleton

Regarding the setup() function, I agree with @Rob Napier above in his answer, but I will take a step further. If you are trying to reconfigure a singleton, you are doing it wrong. If you have the required setting, which varies from one use to another, you must create separate instances.

Maybe there is some kind of connectivity or another function that you are trying to reuse, which leads you along a singleton path, but if in this case you have to extract this particular function for your own class and allow these custom instances to share this singleton.

 /// Watch Connection singleton class WatchConnection: NSObject { static let sharedInstance = WatchConnection() private override init() {} func doSomething() {} } //Watch tracker class for each instance of a watch class WatchGATracker { init(tid: String) { //do something useful } let connection = { WatchConnection.sharedInstance }() } let one = WatchGATracker(tid: "one") one.connection.doSomething() 
0
Nov 04 '16 at 1:59
source share



All Articles