How to create referencing variables? - ios

I'm making an app for practice. This app shares with a simple model through AppDelegate. To manipulate the model, I got an NSDictionary object from the model and allocate it to a viewController property. but It seems too verbose.
// viewController.h
#property (nonatomic, strong) NSMutableDictionary *bookDetail;
#property (nonatomic, strong) bookModel *modelBook;
// viewController.m
- (void)setLabel {
self.label_name.text = self.bookDetail[#"name"];
self.label_author.text = self.bookDetail[#"author"];
}
- (void)viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
id appDelegate = [[UIApplication sharedApplication] delegate];
self.modelBook = [appDelegate modelBook];
self.bookDetail = self.modelBook.bookList[self.modelBook.selectedId];
[self setLabel];
self.editMod = NO;
}
- (IBAction)editSave:(id)sender {
if (self.editMod == NO) {
....
[self.nameField setText:self.bookDetail[#"name"]];
[self.authorField setText:self.bookDetail[#"author"]];
....
} else {
self.bookDetail = [#{#"name" : self.nameField.text,
#"author" : self.authorField.text} mutableCopy];
[self setLabel];
....
}
}
#end
*bookDetail work like a copy of self.modelBook.bookList[self.modelBook.selectedId] not a reference. Using self.modelBook.bookList[self.modelBook.selectedId] works well, but I don't want to. How Can I simplify this code?

*bookDetail work like a copy of self.modelBook.bookList[self.modelBook.selectedId] not a reference. Using self.modelBook.bookList[self.modelBook.selectedId] works well, but I don't want to.
Your question is not clear to me so this might be wrong, but hopefully it helps.
bookDetail is not a "copy" in the usual sense, rather it is a reference to the same dictionary that self.modelBook.bookList[self.modelBook.selectedId] references at the time the assignment to bookDetail is made.
Given that you say that using the latter "works well" is sounds as though self.modelBook.selectedId is changing and you expected bookDetail to automatically track that change and now refer to a different dictionary. That is not how assignment works.
How Can I simplify this code?
You could add a property to your modelBook class[1], say currentBook, which returns back bookList[selectedID] so each time it is called you get the current book. In your code above you then use self.modelBook.currentBook instead of self.bookDetail and can remove the property bookDetail as unused (and incorrect).
HTH
[1] Note: this should be called ModelBook to follow naming conventions. Have you noticed the syntax coloring is incorrect? That is because you haven't followed the convention.

Create the shared instance of BookModel then you can access it anywhere:
Write this in bookModel:
+ (instancetype)sharedInstance
{
static bookModel *sharedInstance = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[bookModel alloc] init];
// Do any other initialisation stuff here
});
return sharedInstance;
}
Then you can access this like bookModel.sharedInstance.bookList

Related

How add NSDictionary to EKEvent

i would like to add extra data to EKEvent, i tried NSDictionary (there is a lot of data to add) but it doesn't work..
sample code:
NSMutableDictionary *dictionary = [[NSMutableDictionary alloc]init];
[eventStore setValue:dictionary forKey:MAIN_DICTIONARY];
any ideas?
You're using setValue:forKey: in a wrong way. That a look here. There are different options to achieve what you want: category, subclassing or create a class that contains the EKEvent and the NSMutableDictionary. It depends on how you need to use the EKEvent.
You cannot do it this way, because even with key-value coding you can only set (declared or non declared) properties known by the instance. Basically the accessors (setter, getter) are executed. But there is no property MAIN_THREAD,no setter setMAIN_THREAD: in EKEvent.
If you want to extend instances of a foreign class that are created by the system (the instances, not the class), there are to ways to add data:
You create an own class, let's say MyEvent and give them a reference to the system instance (EKEvent) as a property plus the properties you need. When you get an instance of EKEvent you look-up your list of MyEventss using the identifier. With that you have the full access to your data.
You use associated objects. But you have to take care that they are not handled by the instance, i. e. while copying.
The first solution is better by far. Simple sample code:
#interface MyEvent : NSObject
#property (readonly) EKEvent* systemEvent;
#property id customProperty;
- (instancetype)eventForSystemEvent:(EKEvent*)systemEvent;
#end
#implemenation MyEvent
// Look-Up
NSMutableDictionary *eventLookUp;
+ (void)initialize
{
if( self == [MyEvent class])
{
eventLookUp = [NSMutableDictionary new];
}
}
- (instancetype)eventForSystemEvent:(EKEvent*)systemEvent
{
return eventLookUp[systemEvent.calendarItemIdentifier];
}
// Instance creation
- (instancetype)initWithSystemEvent:(EKEvent*)systemEvent
{
// Usual initializer
…
eventLookUp[systemEvent.calendarItemIdentifier] = systemEvent;
return self;
}
+ (instancetype)newEventWithSystemEvent:(EKEvent*)systemEvent
{
return [[self alloc] initWithSystemEvent:systemEvent];
}
#end
Typped in Safari

Singleton UIViewController

I'm trying to add some data from a random class to my viewController,
So to keep always the same data, i did a singleton on my UIViewController, but it doesnt work i never get the data on my tableview.
this what i added to my UIViewController :
+(id)sharedMBVC {
static MBViewController *sharedMBVC ;
#synchronized(self) {
if (!sharedMBVC)
sharedMBVC = [[MBViewController alloc] init];
return sharedMBVC;
}
}
and from my class i call it by doing this :
MBViewController *vc = [MBViewController sharedMBVC];
Do i have to set somewhere the content of my NSArrays that they are declared in my viewDidLoad of the viewcontroller ? or there is something else to do.
PS : i was doing in my class before vc = (MBViewController *)[[[[UIApplication sharedApplication] delegate] window] rootViewController]; but now my uiviewcontroller its not a rootview anymore, thats why im trying to find other way to access to it, and i guess the best solution is to do a singleton
Can u help me guys
OK, so the problem you have is that the viewController that displays the arrays also "owns" the arrays. This means that (with your current setup) to be able to change the arrays you need to get hold of the viewController to be able to access the arrays.
You need to change this by removing the arrays from that viewController.
You can still do this with a singleton (if you prefer) but create a brand new class called something like ArrayManager.
This will contain the arrays and ALL the methods for updating the arrays.
So for instance if your viewController has a method called - (void)addObjectToArray:(id)object; then move this method to the ArrayManager singleton class.
Now in your displaying viewController you can do...
[[ArrayManager sharedInstance] getSomeDataFromTheArray];
And in the place that has to update the array you can do...
[[ArrayManager sharedInstance] addObjectToArray:someObject];
Now you don't need to worry about passing the viewController around at all.
This can be improved with various things. For instance, you maybe don't need a singleton at all and can just take this ArrayManager class and inject it into the places that need it by setting a property etc...
Also, you could possibly use CoreData to store the information.
Also, your singleton method is not correct. The way recommended by Apple is to use...
+ (ArrayManager *)sharedInstance
{
static dispatch_once_t once;
static ArrayManager *arrayManager;
dispatch_once(&once, ^{
arrayManager = [[ArrayManager alloc] init];
});
return arrayManager;
}
Rewriting your singleton...
.h file
#interface PTVData : NSObject
+ (PTVData *)sharedInstance;
- (void)addSensor:(NSString *)sensorName;
- (NSInteger)numberOfSensors;
- (NSString *)sensorAtIndex:(NSUInteger)index;
#end
.m file
#interface PTVData ()
#property (nonatomic, strong) NSMutableArray *sensors;
#end
#implementation PTVData
+ (PTVData)sharedInstance
{
static PTVData *sharedPTVData;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedPTVData = [[PTVData alloc] init];
});
return sharedPTVData;
}
- (id)init
{
if (self = [super init]) {
_sensors = [[NSMutableArray alloc] initWithObject:#"None"];
}
return self;
}
- (void)addSensor:(NSString *)sensorName
{
if (sensorName
&& ![self.sensors containsObject:sensorName]) {
[self.sensors addObject:sensorName];
}
}
- (NSInteger)numberOfSensors
{
return self.sensors.count;
}
- (NSString *)sensorAtIndex:(NSUInteger)index
{
return self.sensors[index];
}
#end
By doing this you hide the actual array of sensors. It is only directly accessible through the PTVData class.
Now in your tableview methods you can do...
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section
{
return [[PTVData sharedInstance] numberOfSensors];
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
UITableViewCell *cell = ...
cell.textLabel.text = [[PTVData sharedInstance] sensorAtIndex:indexPath.row];
return cell;
}
Well, I think, these arrays don't belong to the MBViewController from architectural point of view. I would separate them to a data layer (DataSource class of some sort, for instance) and keep a reference to the DataSource everywhere you need. Have a look at the Second iOS App tutorial by Apple. It contains a simple example of data layer implementation.
UPDATE:
Also, check out Fogmeister's answer. He explains a possible implementation of such object rather well :)
As for why singleton didn't work as you expected in this case, I believe, the reason could be the following:
If you get to the MBViewController via a segue (which, I think, you are), then a new instance of MBViewController is created every time. If you access your arrays from MBViewController using self.myArray, then you access this new MBViewController's myArray. While sharedMBVC keeps a reference to the shared instance, it's just ignored by the segue.
in my ViewController.h
#property PTVData *ptvdata;
ViewController.m
ViewDidload
ptvdata = [PTVData sharedPTVData];
_sensorsCollection = ptvdata.sensorsCollection;
then i have a method in my ViewController.m
- (void) addSensorToCollection:( NSString *)sensorName{
[[PTVData sharedPTVData] addSensorToCollection:sensorName];
_sensorsCollection = ptvdata.sensorsCollection;
[ self.tableView reloadData];
}
}
My PTVData.h
#property (nonatomic,retain) NSMutableArray *sensorsCollection;
+(id)sharedPTVData;
-(id) init;
- (void) addSensorToCollection:( NSString *)sensorName;
#end
my PTVData.m
#synthesize sensorsCollection = _sensorsCollection;
+ (id)sharedPTVData {
static PTVData *sharedPTVData = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedPTVData = [[self alloc] init];
});
return sharedPTVData;
}
- (id)init {
if (self = [super init]) {
_sensorsCollection = [[NSMutableArray alloc]initWithObjects:#"None", nil];
}
return self;
}
- (void) addSensorToCollection:( NSString *)sensorName{
if (![_sensorsCollection containsObject:sensorName]&& sensorName!= nil) {
[_sensorsCollection addObject:sensorName];
}
}
Instead of initializing your arrays in viewDidLoad, do it in sharedMBVC function. This will ensure that arrays are not re-initialized every time the view loads.

Pass BOOL value

I am working on an app and I got stuck at the point where I can't seem to retrieve the value of a BOOL set in a class.
I spent too much time already on it, been through all the questions I found that seem to cover the matter.
The bad thing here is that I get something, but not what I need (I get a 0, which means, I guess, that the value wasn't retrieved correctly as it should be 1).
The things I tried are :
pass a pointer to my first class and access to my BOOL like this:
//in some method
self.pointerFirstClass.myBOOL;
NSLog(#"%d", firstClass.myBOOL); => This gives 0!
by declaring it (talking of the pointer) as a property in my second class (and importing the h. file from my first class, where my BOOL is declared as property too):
#property FirstClass *pointerFirstClass;
But I got 0 using this.
The other shot I gave was add my BOOL in the first class and create an instance of the class in my second class
//in some method
FirstClass *firstClass = [[FirstClass alloc] init];
if (firstClass.myBOOL){
//Do something
}
NSLog(#"%d", firstClass.myBOOL); => This gives 0!
But I got 0 too.
As Booleans are primitive types, like in C, I get a bit confused since I am new to object-oriented programming, I don't know how I could like create a getter for this, for example.
I also tried to do a - (BOOL)getBOOLValue method in my first class, and call this method in my second class and assign it to a BOOL in that second class.
But the result wasn't better.
Am I missing something?
Is there a way to get my value that I didn't think of or didn't know about yet?
I am running low on thoughts on how to get around this, it shouldn't be that hard IMO so I hope it is something simple that I just left aside.
EDIT :
Some actual code. I am working between 2 files called AppDelegate (yes, the actual one) and WelcomeViewController (so a VC).
AppDelegate.h
#interface AppDelegate : UIResponder <UIApplicationDelegate>
{
BOOL inRegion; //thought of this making my BOOL as a property of AppDelegate
}
#property (strong, nonatomic) UIWindow *window;
#property BOOL inRegion; //Declaring my BOOL here to make it accessible for another class
- (BOOL)getBOOLValue; //An attempt to pass my BOOL value
AppDelegate.m
- (void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region
{
if (state == CLRegionStateInside)
{
self.inRegion = YES; //Set my BOOL to TRUE
}
else if (state == CLRegionStateOutside)
{
self.inRegion = NO; //Else set it to False
}
- (BOOL)getBOOLValue
{
return inRegion; //Tried to create a custome "getter"
}
WelcomeViewControler.m (I changed nothing in the .h file)
I said I tried many things, right now, this is the last version of my code.
//Simply trying to do a Segue on a condition...
- (IBAction)onClick:(id)sender {
AppDelegate *appDelegate = [[AppDelegate alloc] init];
if (appDelegate.inRegion) {
[self performSegueWithIdentifier:#"WelcomeToDetection" sender:self];
}
else
{
//Do Nothing
}
}
As said, I want to retrieve the BOOL value of the AppDelegate.
Thank you.
This code doesn't make sense:
self.pointerFirstClass.myBOOL;
NSLog(#"%d", firstClass.myBOOL); => This gives 0!
The first line doesn't do anything. You're not assigning anything to the property, and you're not doing anything with the value. Furthermore, the second line doesn't relate to the first line in any way that we can see from the code you've provided. Try this instead:
self.pointerFirstClass = [[FirstClass alloc] init];
self.pointerFirstClass.myBOOL = YES;
NSLog(#"myBOOL = %d", self.pointerFirstClass.myBOOL);
In other words, you need to be sure that self.pointerFirstClass points to a valid object. And then you need to make sure that you've assigned the value you want to the myBOOL property of that object.
Update: This looks like a case where you're talking to the wrong object. Look at this:
- (IBAction)onClick:(id)sender {
AppDelegate *appDelegate = [[AppDelegate alloc] init];
This is surely not what you really want. The application object is a single object -- a real singleton, in fact, meaning that there is and can be only one application object. That object has a delegate object, and that's a specific instance of your AppDelegate class. In this code, though, you're creating a new instance of AppDelegate, one that's different from the one that the application is using. Any changes that are made to the actual application delegate in response to messages from the application will not be reflected in the new object that you've created.
What I think you want is to get the actual application delegate object, and you can do that using:
[[UIApplication sharedApplication] delegate];
So, change your code to look like this:
- (IBAction)onClick:(id)sender {
AppDelegate *appDelegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];;
if (appDelegate.inRegion) {
[self performSegueWithIdentifier:#"WelcomeToDetection" sender:self];
}
// note: you don't need an else clause if it doesn't do anything
}
That way, you'll be talking to the same object that the app uses, which is the one that has the inRegion property set in response to the location manager call.
UPDATE - Now we can see your code the problem is obvious, you are trying to access the appDelegate by creating a new one...
- (IBAction)onClick:(id)sender {
AppDelegate *appDelegate = [[AppDelegate alloc] init];
Instead you should be doing this....
- (IBAction)onClick:(id)sender {
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate]
--
Not sure if you are posting your actual code? but the first example you give...
self.pointerFirstClass.myBOOL;
NSLog(#"%d", firstClass.myBOOL); => This gives 0!
Shouldn't the second line be
NSLog(#"%d", self.pointerFirstClass.myBOOL);
Also this property...
#property FirstClass *pointerFirstClass;
Won't retain it once you've set it, it needs to be
#property (nonatomic,strong) FirstClass *pointerFirstClass;
In the second example...
FirstClass *firstClass = [[FirstClass alloc] init];
if (firstClass.myBOOL){
//Do something
}
NSLog(#"%d", firstClass.myBOOL); => This gives 0!
You allocate and initialise a new FirstClass object and then check the property straight away, if you are not setting this to YES in the init then it will be false
Like I say, maybe you're not posting your actual code?
I guess what you want is initializing myBOOL to 1.
If so, you need do something as following
#implement FirstClass
- (id)init
{
self = [super init];
if(self) {
_myBOOL = 1;
}
return self;
}
// Other methods
#end
EDIT:
The comments is why you get 0.
- (IBAction)onClick:(id)sender {
AppDelegate *appDelegate = [[AppDelegate alloc] init]; // this is the problem.
// you create a new appdelegate,
// and never call locationManager:didDetermineState:forRegion:
if (appDelegate.inRegion) {
[self performSegueWithIdentifier:#"WelcomeToDetection" sender:self];
}
else
{
//Do Nothing
}
}
rewrite your code as following:
- (IBAction)onClick:(id)sender {
AppDelegate *appDelegate = [UIApplication sharedApplication].delegate;
if (appDelegate.inRegion) {
[self performSegueWithIdentifier:#"WelcomeToDetection" sender:self];
}
else
{
//Do Nothing
}
}

Copying of NSMutableArray from one viewcontroller to another viewcontroller?

I have one NSMutableArray in FirstViewController declared as firstArray.
I want to copy the secondArray into firstArray.
In the SecondViewController,
Self.FirstViewController.firstArray = self.secondArray;
When I attempt to NSLog the firstArray.count from the FirstViewController, it display 0. It should have two objects in the array
Anyone can advise on this?
You can choose one of this solutions:
Singleton
Passing Data between ViewControllers
Delegation
You can find all the info you need right here: https://stackoverflow.com/a/9736559/1578927
Singleton example:
static MySingleton *sharedSingleton;
+ (void)initialize
{
static BOOL initialized = NO;
if(!initialized)
{
initialized = YES;
sharedSingleton = [[MySingleton alloc] init];
}
}
It looks like either the second array has already been deallocated when passing the reference to the first view controller, or the first view controller itself has already been nilled out. If the first is true, then you may need a different model object to hold your data rather than persisting it in the controller layer of your app. If that is not the case, then you may want to consider a direct copy. The easiest way of doing this is to declare the firstArray property as the keyword copy rather than strong in your interface file.
If you do need to persist the data in the model layer of your app, a singleton pattern object would indeed be one way of achieving this as EXEC_BAD_ACCESS (nice name!) points out. A slightly more modern (though functionally equivalent) way of writing a singleton is as follows.
#interface MySingleton : NSObject
#property (strong, readwrite) id myData;
+ (id)sharedSingleton
#end
#implementation MySingleton
+ (id)sharedSingleton
{
static MySingleton *singleton = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
singleton = [[MySingleton alloc] init];
// Do other setup code here.
});
return singleton;
}
#end
Note the use of dispatch_once - this makes certain that the static singleton can only be created once (whereas technically, you can invoke +[NSObject initialize] as many times as you feel like manually, though I'd never advise doing so).
You may also take advantage of NSNotificationCenter
SecondViewController.m
[[NSNotificationCenter defaultCenter] postNotificationName:#"arrayFromSecondVC" object:secondArray];
FirstViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(populateArray:) name:#"arrayFromSecondVC" object:nil];
}
-(void)populateArray:(NSNotification *)notif
{
self.firstArray = [notif object];
}
And remove the notification when the viewUnload or didRecieveMemoryWarning method.
Hope it helps.

How to pass object between several views in storyboard, iOS Dev

I have searched for this but the answer is too simple to satisfy me :(
I want to pass a NSManagedObjectContext from TabBarController to almost every view controllers in my app. In detail, my storyboard structure is kind of like this:
TabBarController >>> several tab items views ( this is easy to pass, just pass one time in according view controllers). But I have another relation: TabBarController(the one mentioned above) >>> NavigationController >>> TableViewControllerOne >>> TableViewControllerTwo,
now it's disaster. Because basically I have to transfer the NSManagedObjectContext instance from the NavigationController to TableViewControllerOne and to TableViewControllerTwo....that could involve a lot of prepareSegue:sender and it's not easy to manage.
So my question is: in iOS development, is there a way that I can create a "global" object that I can easily access in my entire app? From apple's official template for master-detail view using core data, it instantiates the NSManagedObjectContext in app delegate, passing it to master view controller. What if I have to use this object after several view pass? Wow, that's a lot of code to do.
Hope I made myself clear and someone could help :)
Thanks a lot.
I would seriously recommend using the MagicalRecord library for your CoreData stack requirements.
You can then do things like:
[NSManagedObjectContext MR_defaultContext];
or
[NSManagedObjectContext MR_contextForCurrentThread];
And setting up your CoreData in the first place can be as simple as:
[MagicalRecord setupCoreDataStackWithAutoMigratingSqliteStoreNamed:#"myDatabase.sqlite"];
Personally I like to use a singleton object of a ModelController class where I can put Core Data stack-related objects (Context, Model and Store Coordinator) and helper methods all in one place, that I can then access from anywhere in my view controllers. Something like this:
#implementation ModelController
- (NSManagedObjectModel *)managedObjectModel
{
// ...
return _managedObjectModel;
}
- (NSPersistentStoreCoordinator *)persistentStoreCoordinator
{
// ...
return _persistentStoreCoordinator;
}
- (NSManagedObjectContext *)contextForCurrentThread
{
NSManagedObjectContext *context = [[NSThread currentThread] threadDictionary][#"threadManagedObjectContext"];
if (context == nil)
{
context = [[NSManagedObjectContext alloc] init];
[context setPersistentStoreCoordinator:self.persistentStoreCoordinator];
[[NSThread currentThread] threadDictionary][#"threadManagedObjectContext"] = context;
}
return context;
}
- (void)resetCoreDataStack
{
[[[NSThread currentThread] threadDictionary] removeObjectForKey:#"threadManagedObjectContext"];
self.persistentStoreCoordinator = nil;
self.managedObjectModel = nil;
}
+ (ModelController *)sharedModelController
{
static ModelController *modelController;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
modelController = [[ModelController alloc] init];
});
return modelController;
}
#end
I use the following shared store idea, which creates a singleton. This has been fairly flexible for me in storing different things globally (The code is adapted from iOS Programming The Big Nerd Ranch Guide).
In the code below I have a PartStore class which will always return the same PartStore instance.
To get access to this instance just include PartStore.h in the class which needs access to the shared store and retrieve the instance using the class method:
[PartStore sharedStore];
You then have access to any of the properties contained within the instance of that class.
// PartStore.h
#import "PartData.h"
#interface PartStore : NSObject
#property (nonatomic, strong) PartData *scannedData;
+ (PartStore *)sharedStore;
#end
// PartStore.m
#implementation PartStore
+ (PartStore *)sharedStore
{
static PartStore *sharedStore = nil;
if (!sharedStore) {
sharedStore = [[super allocWithZone:nil] init];
}
return sharedStore;
}
+ (id)allocWithZone:(NSZone *)zone
{
return [self sharedStore];
}
#end

Resources