Swift - How to share a class (as a singleton) accross windows in a menubar app? - swift

I have a NSViewController that has a class variable as follows:
class ServerAdminViewController: NSViewController, NSTextFieldDelegate, MyTableViewDelegate {
var myClass = MyClass()
func showAdmin(window: NSWindow, myClass: MyClass) {
self.myClass = myClass
window.makeKeyAndOrderFront(self)
NSApp.activate(ignoringOtherApps: true)
}
An instance of this class is instantiated in the App initialization. Later when user clicks on a menu item, I want to show a window and use that value as a singleton for the app, during the time this window is open, also being able to modify myClass. However, the line where the instance variable is declared is being executed multiple times and after the self.myClass = myClassassignment, resetting the object (this is a menubar app that opens a window).
Tried
Several things, ex. removing the parameterless constructor from myClass, declaring that variable private and with a "!", adding init methods to the ViewController (without success) and some other failed attempts.
Question
How to make myClass a singleton and share it accross the application (i.e. make it available to this window) without reinitializing it?

Soo there use to be a bunch of stuff to do this with dispatchOnce and things like that. But now the way I do this is by declaring a shared static variable on my class. This will not handle persistence but it will solve your problem creating your class many times. The shared reference with only be recreated on use after being released. Or if it has never been used before. The class variable is also handy for retrieving the class for many controllers.
class MyClass {
static let shared = MyClass()
}
class MyViewController: UIViewController {
let myClass = MyClass.shared
}

Related

ViewController passed as parameter is not being deinitialized (Swift)

Setup:
I have a ViewController ProblemView and class A. I pass ProblemView to class A, so I can work on it. It looks like this (simplified):
class ProblemView: UIViewController{
var instanceOfA = A()
instanceOfA.passView(passedVC: self)
}
class A{
var workOn = ProblemView()
func passView(passedVC: ProblemView){
workOn = passedVC
// I noticed, if I declare a varible locally like var workOn2 = passedVC, my problem is solved -
// but I need the variable globally, because I don't want to pass it around within this class
}
func doSth(){
// here I interact with variables of the passed ViewController
}
}
Problem: Whenever I restart this process within the app the memory increases every single time until I get memory error.
What I tried: I added deinit to both classes. class A is always deinitialized but class ProblemView is not (this might be the problem?).
I also found out, that when I don't declare workOn globally but within the passView function, then it works just fine. But I need have the variable globally, because I use it within many different functions of A. What could be a solution or workaround to this problem?
Strong references to each other.
Try to change class A:
weak var workOn: ProblemView?
func passView(passedVC: ProblemView){
workOn = passedVC
// I noticed, if I declare a varible locally like var workOn2 = passedVC, my problem is solved -
// but I need the variable globally, because I don't want to pass it around within this class
}
func doSth(){
// here I interact with variables of the passed ViewController
}

What design patten it is that the class variable has the class itself

A class variable that contains the class itself, for example, the main property of the class DispatchQueue
Question 1
What kind of design pattern it is? Is this related to Singleton?
Question 2
How does the object initialized when referring this property?
Yes, it is the singleton pattern. It is all over swift like UserDefaults.standard, NotificationCenter.default, FileManager.default, ... For instance, URLSession.shared is defined in the apple docs as:
The shared singleton session object.
The singleton pattern makes sure that only one instance of the class is created. The class lazily creates its sole instance the first time it is requested and thereafter ensures that no other instance can be created.
Here is some sample code:
class MyClass {
static let shared = MyClass()
//Make the initializer private
private init(){}
//Here is a sample method
func doSomething() {
print("Doing something")
}
}
Making the initializer prevents the possibility of creating many instances, and thus the following line would yield an error:
let instance = MyClass() //MyClass initializer is inaccessible due to private protection level
To access the singleton use MyClass.shared:
let instance = MyClass.shared
instance.doSomething() //Prints: Doing something
For more details on the singleton pattern in Swift, you may look here and here.

Is final necessary for a singleton class in Swift?

To create a singleton class, I wrote something like this:
class SingletonEx{
var name = ""
private init(){}
static let sharedInstance = SingletonEx()
func instanceMethod(){
}
static func classMethod(){
}
}
Some tutorials say final is necessary while others just ignore final keyword. After I tried subclassing SingletonEx, I got the following results.
It seems I can't write an initializer for subclass, which means I can't use an override instance method in a subclass.
As far as I know, singleton definition is all about single instantiation and accessing instance methods through the only instance. So I don't think it is necessary to use final in the singleton definition. But both my teachers and some online tutorials say it is necessary.
I got confused, since you can't create a subclass instance anyway, even you override the instance methods, you can't use it or access it, what's the point to say final is necessary for a singleton class?
If I am wrong, please point out.
Super Class
First of all you need to know the properties and methods that are marked with private are just known to the Super class and Sub classes won't access them!
A class can inherit methods, properties, and other characteristics from another class. When one class inherits from another, the inheriting class is known as a subclass, and the class it inherits from is known as its superclass. Inheritance is a fundamental behavior that differentiates classes from other types in Swift.
Classes in Swift can call and access methods, properties, and subscripts belonging to their superclass and can provide their own overriding versions of those methods, properties, and subscripts to refine or modify their behavior. Swift helps to ensure your overrides are correct by checking that the override definition has a matching superclass definition.
In your case in SingletonEx class you market init with private which means that you can create object just in the body of the class! that means no one, no where, can't create an object of SingletonEx!
If you want to a method end up in Super Class and you don't want to Sub classes overide that method you need to mark that method with final which means it's not private but its available only from Super class!
Sub Class
When class Y Inheritance from SingletonEx which means that cant create an object outside of the class ! because Super class initializer is unavailable during init() method from class Y ! While you need to call the super.init() if you want to initialize an object from Y class !
if you remove private from private init() {} from SingletonEx class you be able to create object from SingletonEx class and also from Y class !
your code should looks like this :
Swift 4 :
class SingletonEx{
var name = ""
init(){}
static let sharedInstance = SingletonEx()
func instanceMethod(){
}
static func classMethod(){
}
}
class Y : SingletonEx {
private var yName = "Y name is : "
init(name:String) {
super.init()
self.name = self.yName + name
}
}
Usage :
override func viewDidLoad() {
super.viewDidLoad()
let yObject = Y.init(name: "badGirl :D ")
print(yObject)
// --> Output : Y name is : badGirl :D
}

CLKComplicationServer initializes an already initialized singleton class CLKComplicationDataSource, although init() is private

To update complications on the watch, I use a singleton class ComplicationController (irrelevant code has been omitted below):
final class ComplicationController: NSObject, CLKComplicationDataSource {
static let shared = ComplicationController() // Instantiate the singleton
private override init() {
super.init()
print("====self: \(self): init")
} // Others can't init the singleton
}
The singleton is created on the watch by the extension delegate:
class ExtensionDelegate: NSObject, WKExtensionDelegate {
override init() {
super.init()
_ = ComplicationController.shared
}
}
When I launch the watch extension with a breakpoint at the print statement above, the execution breaks and the stack trace is:
When I then execute the print statement in a single step, the debugger shows:
====self: <Watch_Extension.ComplicationController: 0x7bf35f20>: init
When I then continue, the execution breaks again at the same breakpoint, and the stack trace is:
After another single step, the debugger shows:
====self: <Watch_Extension.ComplicationController: 0x7d3211d0>: init
Obviously, the CLKComplicationServer has created another instance of the singleton.
My question is: Did I something wrong or is this a bug? If it is a bug, is there a workaround?
PS: It does not help not to initialize ComplicationController in the ExtensionDelegate. In this case, the 2nd instance is created as soon as ComplicationController.shared is used anywhere in the code.
I found a workaround:
Do not instantiate the singleton in the app, i.e. do not use ComplicationController.shared anywhere.
If you have to call functions in ComplicationController.shared, send a notification, e.g. to the default notification center.
The ComplicationController singleton had to have registered for such notifications, and when it receives one, it has to execute the required function.
This did work for me.

Understanding Singleton in Swift

I am trying out to create a singleton in SWIFT and this is what I have done so far
class Global {
class var sharedInstance:Global {
struct singleton {
static let instance:Global = Global()
}
return singleton.instance
}
}
var a = Global.sharedInstance
var b = Global()
if a === b {
println("Pointing to Same Instance")
}
else {
println("Pointing to different instance")
}
I have used computed type property to create a singleton (learnt that from another stackoverflow question).As of now the output is "Pointing to different instance".
What I am looking for is "a" and "b" in above example points to different instance of GLOBAL class and this breaks the point of singleton. How to make "a" and "b" in above example to point to the same instance of the class.
Thank you
This pattern does not guarantee there will only ever be one instance of the Global class. It just allows for anyone to access a single common instance of Global via its sharedinstance property.
So Global() declares a new instance of the Global class. But Global.sharedinstance does not create a new instance of Global, just fetches a pre-created one (that is created the first time anyone accesses it).
(If you alter your declaration of b to read var b = Global.sharedinstance you’ll see it confirms that a and b are pointing to the same instance.)
If you want to ban the creation of further instances of Global, make its init private:
private init() { }
But bear in mind you’ll still be able to create other Globals from within the file in which it’s declared, so if you’re doing the above in a playground or single-file test project, you won’t see any effect.
Class instance once in App life cycle.
class AccountManager {
static var sharedInstance = AccountManager()
var userInfo = (ID:"Arjun",Password:"123")
private init(){
print("allocate AccountManager")
}
}
here we set Private because :
Private access restricts the use of an entity to the enclosing declaration, and to extensions of that declaration that are in the same file. Use private access to hide the implementation details of a specific piece of functionality when those details are used only within a single declaration.
also set static property of sharedInstance
because if you need to access class property without instance of class you must have to declare "Static".