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.
Related
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
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)
I have a problem trying to solve this circular reference.
First I have two NSManagedObject ACoreData and BCoreData
I want to separate the model from the DataBase layer to the UI Model.
So, I create A and B classes which those will be in the UI.
I have created a protocol (Convertible) that ACoreData and BCoreData will implement to convert to the ui objects.
So far so good, but now I have a problem. Each time I call convert from ACoreData it will create a new A and it will assign the BCoreData converted, but then the BCoreData object will call convert again for the A object. I will end up with a loop calling convert() each other.
This is the code:
protocol Convertible{
associatedtype T
func convert() -> T
}
class ACoreData: Convertible{
var b: BCoreData?
func convert() -> A {
var a = A()
a.b = self.b?.convert()
return a
}
}
class BCoreData: Convertible{
var a: ACoreData?
func convert() -> B {
var b = B()
b.a = self.a?.convert()
return b
}
}
class A{
var b: B?
}
class B{
var a: A?
}
Do you know how can I solve this problem to avoid the loop in this circle reference?
Thanks in advance.
I suggest that the best solution is don't do this. You're adding a lot of complexity to your code for no real advantage. Weigh the benefits you see for keeping managed objects away from the UI against the additional complexity of needing to convert to/from managed objects all the time, copying values from one to the other any time one of them changes, the memory hit of keeping duplicate copies of data around, and probably other stuff I haven't thought of yet. "Clean" architecture is going to cost you a lot in terms of maintainability and performance.
If you don't want the UI to know about managed objects, define a protocol that your managed objects adopt. Make the UI work with "things that implement the protocol" instead of managed objects. Keeping the UI unaware of the details of the data store does not require duplicate data models.
I have a class with an array which values comes from a text file. I would like to read this values once and store them in a shared variable, making possible other classes access that values.
How can I do that in Swift?
UPDATE:
Suppose I have three classes of animals and which of them can be found in a set of places which is load from differents tables (each animal have yours and the structure is different for each one). I would like to be able to use them linking to specific class:
clBirds.Places
clDogs.Places
clCats.Places
Note that I need to load data once. If I dont´t have a shared variable and need to put it outside the class, I need to have different names to the methods, just like:
BirdsPlaces
DogsPlaces
CatsPlaces
And we don´t have heritage in this case
Declare the variable at the top level of a file (outside any classes).
NOTE: variables at the top level of a file are initialized lazily! So you can set the default value for your variable to be the result of reading the file, and the file won't actually be read until your code first asks for the variable's value. Cool!
Similarly, you can declare an enum or struct at the top level of a file (outside any classes), and now it is global. Here's an example from my own code:
struct Sizes {
static let Easy = "Easy"
static let Normal = "Normal"
static let Hard = "Hard"
static func sizes () -> String[] {
return [Easy, Normal, Hard]
}
static func boardSize (s:String) -> (Int,Int) {
let d = [
Easy:(12,7),
Normal:(14,8),
Hard:(16,9)
]
return d[s]!
}
}
Now any class can refer to Sizes.Easy or Sizes.boardSize(Sizes.Easy), and so on. Using this technique, I've removed all the "magic numbers" and "magic strings" (such as NSUserDefault keys) from my code. This is much easier than it was in Objective-C, and it is much cleaner because (as the example shows) the struct (or enum) gives you a kind of miniature namespace.
I have a design issue which has proven to bee too much for my current design skills.
I hope my request is not too trivial or too stupid for the incredibly skilled people I saw in these forums over time.
Basically, this is what I need:
to be able to reference a specific class instantiation by means of another class static or constant declaration (hope it makes as much sense to you as it does to me, hah).
The 'enum' behavior would be particularly useful for its 'ease of access' and for its standard methods.
//simple class with a constructor
public class myclass {
int myint = 0;
string mystring = "";
public myclass(int localint, string localstring) {
myint = localint;
mystring = localstring;
}
}
//the core of the issue.
public enum myenum : myclass {
enum1 = new myclass(9,"abr"),
enum2 = new myclass(99,"acad"),
enum3 = new myclass(999,"abra")
}
So that elsewhere, when I need 'abra', instead of manually instantiating it, and having countless duplicates all over the code, I just
myenum mylocalenum;
mylocalenum = enum3; //no mistake, the underlying class variables are predefined
The purpose is to have a selectable, pre-set 'myenum' which basically encapsulates another data structure which I predefine in the declaration phase.
This is because I have several data pre-sets by design, and I need to interact with them as with an enum (get their number, their descriptions, and basically associate them with predefined values).
If you have a solution, or even a resembling alternative, please let me know.