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
Related
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)
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.
How should I correctly create such a class which can be used from any .swift-file of my project and does not need to be initialized with a variable?
I mean that I want not to have to write something like
someVar = myClass()
in each file where I want this class to be usable. I just want this class to have a global public variables and change them from a .swift-file of my project by doing something like
myClass.myVar = value
I'm thinking about making this class a separate file. What's a correct way to do this?
You can create a static property inside a class or struct and call it anywhere. E.g:
//Config.swift
struct Config
{
static var field1 : String = "Field_Value"
static var field2 : String = "Field_Value"
}
You can use the property calling StructName.propertyName.
E.g:
//MyCode.swift
print(Config.field1)
Config.field1 = "New Value"
You can create a new class, make a variable off that class outside any class.
Class Awesome{
}
let awesomeness = Awesome()
you can than use 'awesomeness' as class instance in every other swift file
I want to add functionality and another property to ALL of my classes.
So I wrote a category:
#implementation NSObject (MyCategory)
And I declared a static property in it:
static MyObj myObj;
And I created 2 class methods to get and set it:
+ (MyObj) getMyObj {
return myObj;
}
+ (void) setMyObj:(MyObj)obj {
myObj = obj;
}
Now I imported NSObject+MyCategory.h in my .pch file, so all classes will be effected by this. Indeed all classes now have the new functionality and state:
#import "NSObject+MyCategory.h"
The problem is that when I set myObj, it changes myObj on all classes. All classes share 1 myObj.
I want each class to have its own myObj that is added using the category. I don't want one myObj, rather I want as many myObj's as classes. Each class should have its own myObj.
Thanks,
Nur
You can not add properties instance variables to a class in categories. Either subclass NSObject or use associated objects.
Your solution adds a single static variable (not "property", in Objective-C that means something else), there is no way using categories to add a static variable per class.
However your idea is close to what will work for you; if you can only have one variable and want to store many values what can you use? A dictionary.
static NSMutableDictionary *References;
+ (void) load
{
// load is called exactly once when this category is first loaded,
// setup the dictionary
References = [NSMutableDictionary new];
}
+ (void) setMyObj:(MyObj)reference
{
// NSMutableDictionary will take any object reference as a key,
// for a class method self is a reference to the unique class object,
// so use self as the key and store the reference
[References setObject:reference forKey:self];
}
+ (MyObj) getMyObj
{
// returns previously stored reference or nil if there isn't one for this class
return [References objectForKey:self];
}
HTH
I'm trying to figure out how to centralize a method that I use in a few of my ViewControllers. I already had a singleton that I was using for some variables. I called the singleton class Shared.
I moved my method to the Shared class and tried calling it like so:
m.createdAt = [Shared getUTCFormateDate:[messageObject objectForKey:#"created_at"]];
It's giving me an exception saying that the selector doesn't exist when it tries to call it.
I have already imported Shared.h. Any other thoughts would be appreciated.
If your class is named "Shared" then it looks like you are trying to call a class method rather than an instance method. So, you need to declare the method with + instead of -.
here is the correct pattern for creating a Singleton in objective-c: (Ill use an example of a User object.. taken from code I have open in front of me). also, please note that there is a difference between Singleton classes and Static Class methods, as discussed here.. Difference between static class and singleton pattern?
in the .h file, declare a static method that returns an instance of your class.
+(User *) currentUser;
in the .m file, create a static variable that holds your instance
static User * _user;
then, in your .m class, create your "public" static accesor GET that returns or instantiates and returns your static variable
+ (User *) currentUser
{
if (!_user)
{
_user =[[User alloc]init];
// ... init the singleton user properties and what not
// ...
}
return _user;
}
then, when you want to call on your Singleton class you just make sure that User.h is imported and call [[User currentUser] someMethodorProperty];
enjoy