I'm working in Xcode using Spritekit and I'm struggling to build a separate class to display high scores.
Level 1 has a property called highscore(#synthesize highscor = highscore;) as its name suggests, records the highest scores, but I can't figure out how to send it to the high score class.
JKGLevel1 *class1Instance = [[JKGLevel1 alloc] init];
int x = class1Instance.highscor;
That's how I tried it, but when I leave level 1 and go to the high score class, it keeps telling me that the high score is 0.
First of all you do not need to #sythesize anything anymore, but thats just an aside. What you will need to do is create a singleton class, i.e.: a class with properties that are only instantiated once and can be accessed ANYWHERE in you code where you please.
For example I use this for storing account information :
In the .m file
+ (id)sharedManager {
static AppManager *sharedMyManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedMyManager = [[self alloc] init];
});
return sharedMyManager;
}
- (id)init {
return [super init];
}
in the .h file
#import <Foundation/Foundation.h>
#import <Accounts/Accounts.h>
#interface AppManager : NSObject
#property (nonatomic, retain) ACAccount *twitterAccount;
#property (nonatomic, retain) NSMutableArray *arrayOfAccounts;
+ (id)sharedManager;
#end
**Then in any file I want to use this : **
AppManager *sharedManager;
sharedManager = [AppManager sharedManager];
NSString *usrName = sharedManager.twitterAccount.username;
Hope this actually helps you. There are also other ways you can do this by just creating a class that you manage carefully without this singleton nonsense, but this is a cleaner way of doing it.
Do something like this:
In your HighScoreClass make a property named lavel1HighScore. (if you don't want to make property for each level then you can do it by an array. :) )
When your level 1 is complete pass the highScore of level1 to that property of HighScoreClass.
Like:
JKGLevel1 *class1Instance = [[JKGLevel1 alloc] init];
HighScoreClass *highScore = [[HighScoreClass alloc] init];
highScore.lavel1HighScore = class1Instance.highscor;
Hope this helps.. :)
You could put the scores in a plist to be read in viewdidload or if there is a reason to pass a live score object from level to level -
I would suggest that you put in your scores in a separate class (just an ordinary class with a header and method (.h & .m) and just make the class available to all your levels (class) this can easily be done via the following code:
... levelx.h
#import <UIKit/UIKit.h>
#class Class_scores;
#interface LevelxViewController : UIViewController <> {
}
#property (nonatomic, strong) Class_scores *highscore_class;
needless to say you will need to define a function in the (high_score) class to return the high score and call it from your level class.
Related
I am trying to access an Objective C singleton from Swift, however I only seem to get the initial value created in the init function of the singleton. The flightControllerState object exposed is updated in a delegate function and I can see that the value is properly updated on the Objective C side.
I have followed a few different posts here on SO and also this article on how to call the shared object from Swift. (I should also mention this is running inside a react native project if that may have any impact?)
EDIT updated swift code - I added the wrong line to the init method to grab shared instance - issue is still the same
Objective-C Singleton
#import DJISDK;
#interface RCTBridgeDJIFlightController : RCTEventEmitter<DJIFlightControllerDelegate> {
DJIFlightControllerState *flightControllerState;
}
#property(nonatomic, readonly) DJIFlightControllerState *flightControllerState;
+ (id)sharedFlightController;
#end
#implementation RCTBridgeDJIFlightController
DJIFlightControllerState *flightControllerState;
#synthesize flightControllerState;
+ (id)sharedFlightController {
static RCTBridgeDJIFlightController *sharedFlightControllerInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedFlightControllerInstance = [[self alloc] init];
});
return sharedFlightControllerInstance;
}
- (id)init {
// I also tried this to make sure the shared instance was returned but no luck
//if (sharedFlightControllerInstance != nil) {
// return sharedFlightControllerInstance;
//}
if (self = [super init]) {
flightControllerState = nil;
}
return self;
}
-(void)flightController:(DJIFlightController *)fc didUpdateState:(DJIFlightControllerState *)state {
flightControllerState = state;
}
#end
Swift class calling singleton and accessing values
class VirtualStickController {
var flightControllerSharedInstance: RCTBridgeDJIFlightController
override init() {
self.flightControllerSharedInstance = RCTBridgeDJIFlightController.sharedFlightController()
}
func getFlightControllerState() {
if let state = flightControllerSharedInstance.flightControllerState {
print("FLIGHT CONTROLLER STATE: \(state)") // always null
} else {
print ("NULL")
}
}
DJIFlightControllerState *flightControllerState;
#synthesize flightControllerState;
There is no need to use #synthesize for properties in (modern) Objective-C except in special circumstance.
The property flightControllerState is an instance property and will be synthesised (with or without the #synthesize) using a hidden instance variable for its storage.
The variable flightControllerState is a global variable, it happens to have the same name as the property but has no connection whatsoever with it.
At a guess you are changing the global variable in Objective-C and expecting to see the result in Swift via the property, you won't.
Remove the global variable and then check the rest of your code.
Apart from that your code produces a valid shared instance which can be shared between Objective-C and Swift and changes made in one language will be visible in the other.
HTH
Regarding the titular question about how to access an Objective C singleton from Swift, I would recommend an alternative. Modern convention is to declare your sharedFlightController as a class property and declare init as NS_UNAVAILABLE:
#interface RCTBridgeDJIFlightController : NSObject
...
#property (nonatomic, readonly, class) RCTBridgeDJIFlightController *sharedFlightController;
- (instancetype)init NS_UNAVAILABLE;
#end
The implementation would implement a getter for this class property:
#implementation RCTBridgeDJIFlightController
+ (instancetype)sharedFlightController {
static RCTBridgeDJIFlightController *sharedFlightControllerInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedFlightControllerInstance = [[self alloc] init];
});
return sharedFlightControllerInstance;
}
...
#end
Now, your Swift code can reference RCTBridgeDJIFlightController.shared, as is the convention with Swift singletons.
Regarding why you are receiving a nil for the status, there are one of two possible problems:
You Objective-C code has confusing combination of explicitly defined ivars, manual synthesis, and global variables. (See below.)
I would also suggest that you confirm whether flightController:didUpdateState: is ever getting called at all. (I don't see you ever setting the delegate of the flight controller.) Add a breakpoint or NSLog statement in that method and confirm.
On the first issue, above, I would suggest:
You should not use those commented lines in your init method. If you want to make sure that your singleton object is used, then declare init as NS_UNAVAILABLE.
Given that all your init method is doing is updating flightControllerState to nil, you can remove it entirely. In ARC, properties are initialized to nil for you.
You should not declare explicit ivar in your #interface. Let the compiler synthesize this automatically for you.
You should not #synthesize the ivar in your #implementation. The compiler will now automatically synthesize for you (and will use an appropriate name for the ivar, adding an underscore to the property name.
You should not declare that global in your #implementation.
If you want to use this sharedFlightController from Swift, you should define it to be a class property, not a class method. I know that that article suggested using a class method, but that really is not best practice.
Thus:
// RCTBridgeDJIFlightController.h
#import <Foundation/Foundation.h>
// dji imports here
NS_ASSUME_NONNULL_BEGIN
#interface RCTBridgeDJIFlightController : NSObject
#property (nonatomic, readonly, nullable) DJIFlightControllerState *flightControllerState;
#property (nonatomic, readonly, class) RCTBridgeDJIFlightController *sharedFlightController;
- (instancetype)init NS_UNAVAILABLE;
#end
NS_ASSUME_NONNULL_END
And
// RCTBridgeDJIFlightController.m
#import "RCTBridgeDJIFlightController.h"
#interface RCTBridgeDJIFlightController ()
#property (nonatomic, nullable) DJIFlightControllerState *flightControllerState;
#end
#implementation RCTBridgeDJIFlightController
+ (instancetype)sharedFlightController {
static RCTBridgeDJIFlightController *sharedFlightControllerInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedFlightControllerInstance = [[self alloc] init];
});
return sharedFlightControllerInstance;
}
- (void)flightController:(DJIFlightController *)fc didUpdateState:(DJIFlightControllerState *)state {
NSLog(#"State updated");
self.flightControllerState = state;
}
#end
The end result is that you can now use it like so:
class VirtualStickController {
func getFlightControllerState() {
if let state = RCTBridgeDJIFlightController.shared.flightControllerState {
print("FLIGHT CONTROLLER STATE: \(state)")
} else {
print("NULL")
}
}
}
Note, because the sharedFlightController is now a class property, Swift/ObjC interoperability is smart enough so the Swift code can just reference shared, as shown above.
A very common, generic question but it seems that everybody has it's own opinion about accessing variables that belong in another class. What I want is to use boolean variable in class2 to perform a statement. Example of what I want is:
Class1.h
#Interface Class1{
bool boolean;
}
#Property (nonatomic, retain) bool boolean;
Class1.m
#synthesize boolean;
Class2.m
if(class1.boolean == YES){
Do Something
}
The if statement is class2 doesn't seem to work, as I tried to print the boolean value in class2 and all it returns is false. I want to get the current value of class1 boolean variable and use it in class 2 without initialising it.
Looking at your question, it seems you want to create an instance of 'Class1' in another class, get the properties value to be presented there.
In that case, whenever you instantiate 'Class1', it comes with the initial values. That means the values will be 'null' for sure. If you want to get the changed value, you need to create 'Class1' as Singleton class, where, this class will be instantiated once in the whole application. Means change the value of 'boolean1' in any class, and get the same value in another, whenever or wherever you want.
But again, it totally depends on how you want to use the whole thing.
Singleton example:
// Class1.h
#interface Class1 : NSObject
/**
* A boolean property
*/
#property (nonatomic, strong) BOOL *boolean;
// Class1.m
#implementation Class1
// This is actual initialisation of the class instance.
+ (Class1 *)sharedInstance {
static Class1 *sharedInstance = nil; //static property, so that it can hold the changed value
// Check if the class instance is nil.
if (!sharedInstance) {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
// If nil, create the instance, using dispatch_once, so that this instance never be created again. So, if needed, app will use the existing instance.
sharedInstance = [[super allocWithZone:NULL] init];
// custom initialisation if needed
});
}
return sharedInstance;
}
// So that, if somebody uses alloc and init, a new instance not created always.
// Rather use existing instance
+ (id)allocWithZone:(NSZone *)zone {
return [self sharedInstance];
}
// So that, if somebody uses alloc and init, a new instance not created always.
// Rather use existing instance
- (id)copyWithZone:(NSZone *)zone {
return self;
}
#end
Now updating and using the value.
//Class2.m
#import "Class1.h"
Class1 *myinstance = [[Class1 alloc] init];
myinstance.boolean = YES;
Getting the value on another class
//Class3.m
#import "Class1.h"
Class1 *myinstance = [[Class1 alloc] init];
if(myinstance.boolean == YES){
Do Something
}
This question already has answers here:
Passing data between view controllers
(45 answers)
Closed 9 years ago.
I need to make one instance of an array that can be access by multiple view controllers. It will contain objects that will be displayed in a table view and created by a modular view and then displayed in the table view. I can't figure out how to access it once I make a #property for it in the main view controller or the AppDelegate class.
You should make a singleton and the recommended way to do that in objective-c is to create a class and add a method that looks something like:
+ (YourClass *)sharedYourClass
{
static dispatch_once_t onceToken;
static YourClass *sharedInstance;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
Put the array as a property in your class.
//YourClass.h
#interface YourClass : NSObject
#property(nonatomic, strong)NSArray *yourArray;
+(YourClass *)sharedYourClass;
#end
And in every class you want to use your singleton start by importing YourClass.h and then use it like:
NSArray *arr = [YourClass sharedYourClass].yourArray;
[YourArray sharedYourClass].yourArray = [[NSArray alloc] init];
etc..
What I do is put the data I want shared, in your instance the array, in the AppDelegate. Then I define a protocol that the app delegate conforms to. This lets me access the data anywhere. For example, say I have an array I want everywhere:
First define a protocol:
#protocol ApplicationState<NSObject>
#property(nonatomic, strong) NSArray* data;
#end
Then make your app delegate conform to it:
#interface AppDelegate : UIResponder <UIApplicationDelegate, ApplicationState>
#property (strong, nonatomic) UIWindow *window;
#property(nonatomic, strong) NSArray* data;
#end
Then reading and writing this shared object is simple:
id<ApplicationState> appState = (id<ApplicationState>) [UIApplication sharedApplication].delegate;
appState.data = [[NSArray alloc] init];
I am going to assume you can make the class of the array inherit from NSObject, and then pass it to the View Controller from there...
You have 2 ways to do this:
1.- Instantiate 1 arra on the main class and pass the reference to each viewcontroller what you need.
2.- Make a singleton class to hold the array an use this in your project.
First create a class like this
//GlobalDataClass.h
#interface GlobalDataClass : NSObject
#property(nonatomic,retain)NSArray *myArray;
+(GlobalDataClass*)getInstance;
#end
#import "GlobalDataClass.h"
//GlobalDataClass.m
#implementation GlobalDataClass
#synthesize myArray;
static GlobalDataClass *instance =nil;
+(GlobalDataClass *)getInstance
{
#synchronized(self)
{
if(instance==nil)
{
instance = [GlobalDataClass new];
}
}
return instance;
}
#end
Then you can use it in your viewControllers like this:
-(void)viewDidLoad{
[super viewDidLoad];
self.dataObj = [GlobalDataClass getInstance];
NSLog(#"%#",self.dataObj.myArray);
}
Hope it helps!
I would like to set up my global constant values within a Constants Singleton class in my iOS app such that any class importing the constants can use those values.
However, after playing around for a few hours with this idea, I am still unable to make it work.
In my Constants.m file
#interface Constants()
{
#private
int _NumBackgroundNetworkTasks;
NSDateFormatter *_formatter;
}
#end
#implementation Constants
static Constants *constantSingleton = nil;
//Categories of entries
typedef enum
{
mapViewAccessoryButton = 999
} UIBUTTON_TAG;
+(id)getSingleton
{
.....
}
I have another class MapViewController where I have a reference to the Constants singleton and Im trying to access the enums like this
myDetailButton.tag = self.constSingleton.UIBUTTON_TAG.mapViewAccessoryButton;
However, this is not working. Im not able to access the UIBUTTON_TAG inside the mapviewcontroller
ANybody have any suggestions?
Thanks
If you want the enum available throughout the app, put the enum definition in the .h file, not the .m file.
Update:
Objective-C doesn't support namespaces and it doesn't support class level constants or enums.
The line:
myDetailButton.tag = self.constSingleton.UIBUTTON_TAG.mapViewAccessoryButton;
should be:
myDetailButton.tag = mapViewAccessoryButton;
assuming you define the UIBUTTON_TAG enum in some .h file.
When you compile an Objective-C app, all values of all enum must have unique names. This is a result of Objetive-C being based on C.
Update 2:
There is one way to get what you want but not with enums. Something like this should work:
Constants.h:
#interface UIBUTTON_TAG_ENUM : NSObject
#property (nonatomic, readonly) int mapViewAccessoryButton;
// define any other "enum values" as additional properties
#end
#interface Constants : NSObject
#property (nonatomic, readonly) UIBUTTON_TAG_ENUM *UIBUTTON_TAG;
+ (id)getSingleton;
// anything else you want in Constants
#end
Constants.m
#implementation UIBUTTON_TAG_ENUM
- (int)mapViewAccessoryButton {
return 999;
}
#end
#implementation Constants {
int _NumBackgroundNetworkTasks;
NSDateFormatter *_formatter;
UIBUTTON_TAG_ENUM *_uiButtonTag;
}
#synthesize UIBUTTON_TAG = _uiButtonTag;
- (id)init {
self = [super init];
if (self) {
_uiButtonTag = [[UIBUTTON_TAG_ENUM alloc] init];
}
return self;
}
// all of your other code for Constants
#end
Now you can do:
myDetailButton.tag = self.constSingleton.UIBUTTON_TAG.mapViewAccessoryButton;
I'm not sure if there is a point to this though.
One way to do this is simply stick it in your precompiled header (.pch) if you aren't going to be changing the enum a lot.
I am somewhat new to iOS 5 singletons and am using a singleton as documented here:
iOS 5 Singletons
Something like this:
MyManager.h
#import <Foundation/Foundation.h>
#interface MyManager : NSObject
//Data for section 1
#property(nonatomic,copy) NSString * section1a;
#property(nonatomic, assign) NSUInteger section1b;
//Data for section 2
#property(nonatomic,copy) NSString * section2a;
#property(nonatomic, assign) NSUInteger section2b;
+ (id)sharedInstance;
#end
MyManager.m
#implementation MyManager
#synthesize section1a, section1b, section2a; , section2b;
+ (id)sharedInstance
{
static dispatch_once_t pred = 0;
__strong static id _sharedObject = nil;
dispatch_once(&pred, ^{
_sharedObject = [[self alloc] init]; // or some other init method
});
return _sharedObject;
}
#end
So I use it as follows:
MyManager * myManager = [MyManager sharedInstance];
myManager.data = self.data
Is this how you generally use the singleton? Am I missing anything?
Sorry for the basic questions I just want to make sure I am doing it right.
Thanks
You're doing it right. This is (currently :) the way to go and works fine with ARC enabled, multiple threads etc.
It isn't strictly a singleton, as you can alloc more instances of the class, but I don't think that's relevant in your case.
This is not a singleton because you can create multiple instances of this class with alloc/init methods.
A right answer is here
I usually make my singleton as such (suppose we are making a singleton for Manager):
static (Manager *) instance;
+ (Manager *) getInstance
{
if(!instance) instance = [[Manager alloc] init];
return instance;
}
This is a very basic singleton (it doesn't take into account multithreading or other advanced features) but this is the basic format.
Refer to this: Singleton in iOS 5? You should override allowWithZone to prevent rogue code from creating a new instance.