Background
I have a singleton class in my app, declared according following the one line singleton (with a private init()) in this blog post. Specifically, it looks like this:
#objc class Singleton {
static let Singleton sharedInstance = Singleton()
#objc dynamic var aProperty = false
private init() {
}
}
I would like to bind the state of aProperty to whether a menu item is hidden.
How I tried to solve the problem
Here are the steps I followed to do this:
Go to the Object Library in Interface Builder and add a generic "Object" to my Application scene. In the Identity inspector, configure "Class" to Singleton.
Create a referencing outlet in my App Delegate by Ctrl-dragging from the singleton object in Interface Builder to my App Delegate code. It ends up looking like this:
#IBOutlet weak var singleton: Singleton!
Go to the Bindings inspector for the menu item, choose "Hidden" under "Availability", check "Bind to", select "Singleton" in the combo box in front of it, and type aProperty under "Model Key Path".
The issue
Unfortunately, this doesn't work: changing the property has no effect on the menu item in question.
Investigating the cause
The issue appears to be that, despite declaring init() as private, Interface Builder is managing to create another instance of my singleton. To prove this, I added NSLog("singleton init") to the private init() method as well as the following code to applicationDidFinishLaunching() in my app delegate:
NSLog("sharedInstance = \(Singleton.sharedInstance) singleton = \(singleton)")
When I run the app, this is output in the logs:
singleton init
singleton init
sharedInstance = <MyModule.Singleton: 0x600000c616b0> singleton = Optional(<MyModule.Singleton: 0x600000c07330>)
Therefore, there are indeed two different instances. I also added this code somewhere else in my app delegate:
NSLog("aProperty: [\(singleton!.aProperty),\(String(describing:singleton!.value(forKey: "aProperty"))),\(Singleton.sharedInstance.singleton),\(String(describing:Singleton.sharedInstance.value(forKey: "aProperty")))] hidden: \(myMenuItem.isHidden)")
At one point, this produces the following output:
aProperty: [false,Optional(0),true,Optional(1)] hidden: false
Obviously, being a singleton, all values should match, yet singleton produces one output and Singleton.sharedInstance produces a different one. As can be seen, the calls to value(forKey:) match their respective objects, so KVC shouldn't be an issue.
The question
How do I declare a singleton class in Swift and wire it up with Interface Builder to avoid it being instantiated twice?
If that's not possible, how else would I go about solving the problem of binding a global property to a control in Interface Builder?
Is an MCVE necessary?
I hope the description was detailed enough, but if anyone feels an MCVE is necessary, leave a comment and I'll create one and upload to GitHub.
I just want to start my answer by stating that singletons should not be used for sharing global state. While they might seem easier to use in the beginning, they tend to generate lots of headaches later on, since they can be changed virtually from any place, making your program unpredictable some times.
That being said, it's not impossible to achieve what you need, but with a little bit of ceremony:
#objc class Singleton: NSObject {
// using this class behind the scenes, this is the actual singleton
class SingletonStorage: NSObject {
#objc dynamic var aProperty = false
}
private static var storage = SingletonStorage()
// making sure all instances use the same storage, regardless how
// they were created
#objc dynamic var storage = Singleton.storage
// we need to tell to KVO which changes in related properties affect
// the ones we're interested into
override class func keyPathsForValuesAffectingValue(forKey key: String) -> Set<String> {
switch key {
case "aProperty":
return ["storage.aProperty"]
default: return super.keyPathsForValuesAffectingValue(forKey: key)
}
}
// and simply convert it to a computed property
#objc dynamic var aProperty: Bool {
get { return Singleton.storage.aProperty }
set { Singleton.storage.aProperty = newValue }
}
}
Unfortunately you can’t return a different instance from init in Swift.
Here are some possible workarounds:
Make an outlet for an instance of your class in Interface Builder and then only reference that instance throughout your code. (Not a singleton per se, but you could add some runtime checks to make sure it’s only instantiated from a nib file and not from code).
Create a helper class for use in Interface Builder and expose your singleton as its property. I.e. any instance of that helper class will always return a single instance of your singleton.
Make an Objective-C subclass of your Swift singleton class and make its init's always return a shared Swift singleton instance.
There is a way around the problem in my particular case.
Recall from the question that I only wanted to hide and unhide a menu according to the state of aProperty in this singleton. While I was attempting to avoid write as much code as possible, by doing everything in Interface Builder, it seems in this case it's much less hassle to just write the binding programmatically:
menuItem.bind(NSBindingName.hidden, to: Singleton.sharedInstance, withKeyPath: "aProperty", options: nil)
Related
I have a subclass whose inheritance chain breaks down to look like this:
InputAccessoryEnabledTextField : UITextField : UIControl : UIView : UIResponder
InputAccessoryEnabledTextField provides an override:
private var myInputAccessoryController: UIInputViewController?
override var inputAccessoryViewController: UIInputViewController? {
get { myInputAccessoryController }
set { myInputAccessoryController = newValue }
}
The code above, working as the solution I was seeking, is from the accepted answer (#Sweeper) to a question I just asked on S.O. It is overriding an instance property of UIResponder.
However, it doesn't make sense to me. How can/does it work?
How is it possible that UITextField, superclass to my subclass, honors an override provided my subclass (InputAccessoryEnabledTextField)?
Doesn't that violate the inheritance hierarchy? Shouldn't only subclasses of InputAccessoryEnabledTextField be able to see its override, not superclasses?
Or do overrides apply to the whole object, such that every inherited superclass sees the state of some arbitrary outermost subclass? Or, is it that the iOS text subsystem is doing some really convoluted stuff?
Maybe this is too abstract a question for S.O. and I don't mind closing or deleting it, Just posting this to avoid a 'dialog' in the comments that the bot complains about.
Note: I don't find much clarity about it in Inheritence chapter of Swift 5 documentation *
In short
This is indeed the overriding of properties. Swift deals with properties by generating code that is equivalent to accessing properties via a via getters and setters. This allows to override a property by overriding the getter and the setter.
More explanations
Your snippet is overriding of a property
In your code, InputAccessoryEnabledTextField indirectly inherits from UIResponder, which has an existing property inputAccessoryViewController.
Your code snippet defines a new private property myInputAccessoryController, and uses it in the overriding of the inherited property inputAccessoryViewController, and more precisely, the overriding of its getter and setter.
Purpose in the case of your snippet
In fact, the purpose of this overriding is even explained in the documentation of inputAccessoryViewController:
The value of this read-only property is nil.
But what's the use of a property that is real only and returns only nil?
If you want to attach custom controls to a system-supplied input view controller (such as the system keyboard) or to a custom input view (...), redeclare this property as read-write in a UIResponder subclass.
How can property overriding even work?
While property overriding may seem weird at the first sight, we realize that this is just the normal overriding mechanism once we have understood that:
The stored or computed nature of an inherited property isn’t known by a subclass—it only knows that the inherited property has a certain name and type. You must always state both the name and the type of the property you are overriding, to enable the compiler to check that your override matches a superclass property with the same name and type.
Here we see the power of Swift's properties. You can make any property public, and still benefit from encapsulation and specialization, overriding it like functions. The explanation is that a property has two faces:
the class-internal implementation details: is the property stored or computed ?
the implicit class interface for the external world, including for subclasses: the outside world use the getter and the setter. These can be overridden.
the same principle works for property observers such as didSet: you can override them even if the base class didn't define any special behavior for them.
Here a small unrelated but extreme toy example to illustrate this feature (I would not recommend its design ;-) ):
class Lense {
init (opticalZoom: Int) {
magnifyingFactor = opticalZoom
}
// stored property
// And has implicitly a getter and a setter
var magnifyingFactor : Int = 2
}
class ElectronicLense : Lense {
// overriden property
// it overrides getter and setter, and uses the propery of the super-class to store the value
override var magnifyingFactor: Int {
get { super.magnifyingFactor * 5 }
set { super.magnifyingFactor = newValue / 5 }
}
// pass through property
var opticalFactor : Int {
get {super.magnifyingFactor}
}
}
var le = ElectronicLense(opticalZoom: 3)
print ("Zoom: \(le.magnifyingFactor) with an optical factor \(le.opticalFactor)")
The following example code demonstrates that a Swift superclass experiences its own properties through outermost subclass overrides!
(The example below proves #RobNapier correct, which I initially confirmed by successfully overriding UIResponder.inputAccessoryViewController and observing my viewController activated when the keyboard pops up for my subclassed UITextView : UIResponder)
The Good:
Swift overrides as explained by #RobNaipier in comments, make sense, at least from certain points of view. And can obviously be exploited for its interesting flexibility
The Bad:
However, it isn't what I assumed, and I was somewhat stunned that inheritance works that way, because intuitively I realized that letting subclasses tamper with superclasses` view of themselves is potentially risky, especially if one doesn't know superclass implementation details (as is the case with UIKits proprietary implementation code Apple doesn't release the source to the public).
The Ugly:
So while Swift inheritance lets the inheritors achieve tweak things for interesting or useful effect, and could be very handy in some cases, in practical use, for example with UIKit, it does leads to anticipated problems and confusion.
The coup de grâce, which I'm grateful that Rob pointed out, is that, due to the anticipated downsides, class inheritance with UIKit is increasingly discouraged by Apple and struct+protocol has been adopted by SwiftUI.
class TheSuperclass {
var x = 5
init() {
print("How superclass sees it before subclass initialized: x = \(x)")
}
func howSuperclassSeesItselfAfterSubclassInit() {
print("How superclass sees it after subclass initialized: x = \(x)")
}
}
class TheSubclass : TheSuperclass {
override var x : Int {
get { super.x + 10 }
set { super.x = newValue }
}
override init() {
super.init()
print("How subclass sees it after superclass" +
"initialized: x = \(x), super.x = \(super.x)")
}
}
TheSubclass().howSuperclassSeesItselfAfterSubclassInit()
The above code when run in Playground displays the following:
How superclass sees it before subclass initialized: x = 5
How subclass sees it after superclass initialized: x = 15, super.x = 5
How superclass sees it after subclass initialized: x = 15
I would like to hide some property setters and initializers on my Swift model objects. These are reference data that the server provides, and under no circumstances should they be created or modified by the application. This is simple enough in Swift.
However, there is application in my project (a separate target) that needs to break this rule. It is a tool I use to populate the data in bulk, so of course needs to be able to initialize new model objects and set their properties.
What are my options for accomplishing this? I would rather not use a completely new project since it will mean a lot of code duplication. Is there some language-level way to keep this mutability hidden from one application but available to another?
If you declare a property with the let keyword. It can then only be set in the init of the type.
You can also declare a private setter to make the property readonly from the caller of the type but read/write inside the type
struct Foo {
private(set) var bar: Bool = true
func toggle() {
bar.toggle()
}
}
var foo = Foo()
let barState = foo.bar // This works
foo.toggle() // This works too
foo.bar.toggle() // This will make a compile time error
In swift, is there anyway I can create a shared class? That is, say there is a class called Value:
class Value{
var a = 0
}
I want to use this class to be shared among different object instances.
For example,
class Main{
func main(){
print(Value.a)
Value.a++
}
}
class OtherClass{
func otherMain(){
print(Value.a)
Value.a++
}
}
let main = Main()
let other = OtherClass()
//I want this call to print 0
main.main()
//I want this call to print 1
other.otherMain()
I tried static on var a, but then it won't let me do arithmetics such as addition... But it is working if I change the Value into static instead of class. I thought only difference between struct and class was either variable type or reference type...
I'll appreciate to any help. Thanks
The best way to go about what you want to do, unfortunately, is not to do it at all. It seems that the architecture you're designing is quite fragile and two independent classes shouldn't depend on a shared object state and if they do, they should be related via class inheritance or protocols.
A better solution would be:
class MyClass {
static var a = 0
}
class SubclassA: MyClass {
func someFunc() {
print(self.dynamicType.a)
self.dynamicType.a += 1
}
}
class SubclassB: MyClass {
func otherFunc() {
print(self.dynamicType.a)
}
}
There's a few reasons you should do it this way, over your previous solution:
Your shared state can stay private to those two classes. No one outside of those classes can or should modify that value.
Singleton patterns while sometimes useful, have a bunch of problems. This article explains it better than I can.
You have to think of your application and your code architecture as a state machine, moving from one state to another. The goal is to write code that's reusable, modular, generic, and has as little state as possible. The more stateless your code, the better. That's the whole idea behind platforms like ReactJS and ReactiveCocoa; they help simplify that application state.
What you might be looking for is data persistence, something like Realm or CoreData, where you persist data to disk and then you can read it back at a later time as needed. Imagine for example that instead of an Int your shared state was an array of UIImages. With the singleton pattern, you're keeping those images in memory at all times. What if the list grows to be 200 or 300 in length? Then you have memory issues.
I've found similar questions but none of the solutions seem to work.
This is my problem:
I have a class, GeneralPostAreaController: UIViewController
It has a property, "queryObject" which is defined as "ParseQueryer()" (custom class I created to get data objects)
essentially "queryObject" stores data to create a post
I have another class (which is also a separate file) , PostTableViewCell: UITableViewCell
I want to reference "queryObject" inside PostTableViewCell so I can do actions with the data inside "queryObject"
Code below:
class GeneralPostAreaController: UIViewController {
var queryObject = ParseQueryer()
...Other code here
In another file:
class PostTableViewCell: UITableViewCell {
//reference the queryObject here
Thank you! BTW I am also really new to programming and Swift.
Quick and dirty solution is let queryObject be a static variable.
class GeneralPostAreaController: UIViewController {
static var queryObject = ParseQueryer()
}
Then, you can access queryObject anywhere in your application like this:
GeneralPostAreaController.queryObject
I am not sure is this is correct behaviour or if its unintended. I have setup StealthFighter so that it returns a class type computed property variable called ammunition.
func globalTests() {
println("globalTests")
println("AMMUNITION: \(StealthFighter.ammunition)")
var myStealthFighter = StealthFighter()
println("MISSILES: \(myStealthFighter.missiles)")
println("AMMUNITION: \(myStealthFighter.ammunition)") // ERROR
}
class StealthFighter {
class var ammunition:Int {
return 500;
}
var missiles: Int = 5
}
When directly accessing the class StealthFighter this works fine and returns 500 as expected. But if I create and instance myStealthFighter and then try and access the class property on the instance I get the error: 'StealthFighter' does not have a member named 'ammunition' I can't find any mention of this, I am assuming from this that class properties are accessible only via the class? and not on any instances created from it? I just want to make sure I am understanding this correctly ...
EDIT:
So I have probably worded the type variable name wrong as it should probably be maxAmmunition to signify that StealthFighters can only take 500 rounds. I can see the point, if you want the maxAmmunition for the class then you ask the class.
As #Kreiri and #0x7fffffff points out it does seem that you can ask the instance what the class ammunition (or maxAmmunition) is by using dynamicType.
println("CLASS - AMMUNITION: \(StealthFighter.ammunition)")
var myStealthFighter = StealthFighter()
println("INSTA - AMMUNITION: \(myStealthFighter.dynamicType.ammunition)")
.
// OUTPUT
// CLASS - AMMUNITION: 500
// INSTA - AMMUNITION: 500
Your assumption is correct. Type variables are only meant to be accessed directly from the class. If you want to get at them from an instance, you can do so by accessing the dynamicType property on your instance, like so.
let theFighter = StealthFighter()
let missiles = theFighter.dynamicType.missiles
println(missiles)
However, I don't think that this is the correct approach for you to be taking here. Assuming that you want to have one class "StealthFighter", and possibly multiple instances of that class, each with the ability to have its own number of missiles independent of the others, you should probably make this an instance variable by simply ditching the class keyword.
dynamicType allows access instance’s runtime type as a value, so accessing class property from instance would look like this:
var myStealthFighter = StealthFighter()
myStealthFighter.dynamicType.ammunition
Works in playground, at least.
These properties are known as Type properties in swift. It should be called on its type ie class name, not on instance. Type properties holds same value across all the instances of the class just like static constant in C.
Querying and Setting Type Properties
Type properties are queried and set with dot syntax, just like instance properties. However, type properties are queried and set on the type, not on an instance of that type
Excerpt from : swift programming language
Swift 4:
var myStealthFighter = StealthFighter()
type(of: myStealthFighter).ammunition
Yes. This is a correct behaviour. These Type Properties can only be accessed over the Type and are not available on the instance itself.
In the Swift Book from Apple it is described in the section "Type Properties" (Page 205).
Swift Type Properties
“Unlike stored instance properties, you must always give stored type properties a default value. This is because the type itself does not have an initializer that can assign a value to a stored type property at initialization time"