I've extended AppDelegate with my category. The goal is initiate server request and show notification, when user entering some geofence with terminated app. This all done in class GeoNotificationManager. All I need to do - instantiate this class once app launched from location event.
Everything works, except the PROBLEM: - when #property (strong) GeoNotificationManager* geof; is inside category, code inside GeoNotificationManager doesn't work (no server requests, no notifications). And when it's inside AppDelegate itself, everything works as expected.
Also I tried without having property in catergory, it doesn`t work too.
GeoNotificationManager* manager = [GeoNotificationManager new];
[manager sendTestServerRequest:#"test"]; // REQUEST NOT SENDING, WHY???
//AppDelegate+Geofence.h
#import "AppDelegate.h"
#class GeoNotificationManager;
#interface AppDelegate (Geofence)
#property (strong) GeoNotificationManager* geof;
#end
NSString * const kNewPropertyKey = #"kNewPropertyKey";
#class GeoNotificationManager;
#implementation AppDelegate(Geofence)
-(BOOL)application:(UIApplication *)application willFinishLaunchingWithOptions:
(NSDictionary<UIApplicationLaunchOptionsKey,id> *)launchOptions {
if ([launchOptions objectForKey:UIApplicationLaunchOptionsLocationKey])
{
self.geof = [GeoNotificationManager new];
[self.geof sendTestServerRequest:#"test"];
}
return true;
}
// I don't understand what is that below, but seems it's required for having property in category
- (void)setGeof:(id)aObject
{
objc_setAssociatedObject(self, (__bridge const void * _Nonnull)(kNewPropertyKey),
aObject, OBJC_ASSOCIATION_ASSIGN);
}
- (id)geof
{
return objc_getAssociatedObject(self, (__bridge const void * _Nonnull)(kNewPropertyKey));
}
#objc class GeoNotificationManager : NSObject, CLLocationManagerDelegate {
let locationManager = CLLocationManager()
override init() {
super.init()
locationManager.delegate = self
locationManager.desiredAccuracy = kCLLocationAccuracyBest
locationManager.allowsBackgroundLocationUpdates = true
locationManager.showsBackgroundLocationIndicator = true
}
// OTHER CODE, -didEnterRegion, server request etc
OBJC_ASSOCIATION_ASSIGN just should be replaced with OBJC_ASSOCIATION_RETAIN, and it works!
But if anyone knows how to do this with local variable, please share
Related
I'm using 'WCSession' for the connection between my app and apple watch. I preferred singleton approach. So, I made a shared session:
static Shared_WCSession *sharedInstance = nil;
+(Shared_WCSession*)getSharedInstance {
#synchronized(self) {
// If the class variable holding the reference to the single ContentManager object is empty create it.
if(sharedInstance == nil) {
sharedInstance = [[Shared_WCSession alloc] init];
}
}
return sharedInstance;
}
Then in start session I set up the delegate for the session:
-(void)startSession {
if ([WCSession isSupported]) {
self.session = [WCSession defaultSession];
self.session.delegate = self;
[self.session activateSession];
LOG(#"WCSession is supported");
}
}
What is the proper way to deallocate the delegate?
According to apple's docs I can do it in the following methods:
sessionDidBecomeInactive(_:)
sessionDidDeactivate(_:)
If I set the delegate to nil there will this interfere with the performance of my applications?
First I want to know that self.session is following arc, and as delegate always contains weak reference then no need to set it to nil.
Is it causing any issue? if you are not setting it nil manually ? If yes then you can set it to nil in sessionDidDeactivate as it says:Called after all data from the previous session has been delivered and communication with the Apple Watch has ended. and you can set new session like
func sessionDidDeactivate(session: WCSession) {
// Begin the activation process for the new Apple Watch.
WCSession.defaultSession().activateSession()
}
WCSession.delegate won't leak: it is a weak reference
NS_CLASS_AVAILABLE_IOS(9.0)
#interface WCSession : NSObject
// ...
/** A delegate must exist before the session will allow sends. */
#property (nonatomic, weak, nullable) id <WCSessionDelegate> delegate;
// ...
If you're using ARC and your delegate is still being held on memory, it is not because of WCSession.delegate
I have Location Tracking class and another class with listing. I need to check if locationService is running when entering listView. In the locationService class I've added boolean to track service start and stop. But I can't track this boolean variable from listView class.
here is my code how I am doing now, but it takes only initial value of the locationService class
//listView.m
#property (strong,nonatomic) LocationTracker * trackerClass;
- (void)viewWillAppear:(BOOL)animated{
self.trackerClass = [[LocationTracker alloc] init];
if([self.trackerClass getTrackerStatus]==1)
NSLog(#"LOCATION SERVICE STATUS IS RUNNING");
else
NSLog(#"LOCATION SERVICE STATUS IS STOPPED");
}
//locationService.h
#property (nonatomic, readwrite) int trackerStatus;
- (int)getTrackerStatus;
//locationService.m
- (void)stopLocationTracking {_trackerStatus=0;}
- (void)startLocationTracking {_trackerStatus=1;}
- (int)getTrackerStatus{
return _trackerStatus;
}
thank you in advance!
Try to set this boolean variable as a local variable and create a simple static getter to get this bool. your LocationTracker will change the value of the variable and you can get it from anywhere.
#implementation LocationTracker
BOOL isStart;
//Dont forget to delclear this methode in the .h file
+(BOOL)isStart{
return isStart;
}
your code...
Any where in your app just import your calss and wirte:
BOOL isStart = [LocationTracker isStart];
//The getting vaiable should be the updated bool in you LocationTracker class
Try deleting
- (int)getTrackerStatus{
return _trackerStatus;
}
As my user changes location, I need to compare his location with an array of locations I pull from the web. However, in my appDelegate, I'm not sure where exactly to place my code as I'm not sure what methods are called or not called when the app is terminated, but the CLLocationManager still works.
Specifically, I need to input this code where it will actually be called when the app is still terminated:
// alloc and init the various (Mutable)Array properties
self.locations = [[NSMutableArray alloc] init];
// Create new HomeModel object and assign it to _homeModel variable
_homeModel = [[DealsModel alloc] init];
// Set this view controller object as the delegate for the home model object
_homeModel.delegate = self;
// Call the download items method of the home model object
[_homeModel downloadItems];
The _homeModel will then call this method:
-(void)itemsDownloaded:(NSArray *)items
{
// This delegate method will get called when the items are finished downloading
// Set the downloaded items to the array
_locations = [items copy];
}
Which I will further edit to compare the user's location to the array of locations.
The thing is, this array of locations only changes once a week. Does the app really have to pull it from the web every time the user's location changes? Or is there a way to cache this and only pull it when self.locations has been deallocated?
This is what I have now, but I feel there must be a better way:
#interface AppDelegate () <CLLocationManagerDelegate>
{
DealsModel *_homeModel;
}
#property BOOL didRunBefore;
#property CLLocationManager *locationManager;
#property NSMutableArray *deals;
#end
#implementation AppDelegate
-(void)itemsDownloaded:(NSArray *)items
{
// This delegate method will get called when the items are finished downloading
// Set the downloaded items to the array
_deals = [items copy];
[self compareSponsorLocations:_deals toUserLocation:[self.locationManager location]];
}
- (void)locationManager:(CLLocationManager *)manager
didUpdateLocations:(NSArray *)locations {
if (!self.deals) {
// alloc and init the various (Mutable)Array properties
self.deals = [[NSMutableArray alloc] init];
// Create new HomeModel object and assign it to _homeModel variable
_homeModel = [[DealsModel alloc] init];
// Set this view controller object as the delegate for the home model object
_homeModel.delegate = self;
// Call the download items method of the home model object
[_homeModel downloadItems];
} else {
[self compareSponsorLocations:self.deals toUserLocation:[locations lastObject]];
}
}
- (void) compareSponsorLocations: (NSArray *) array toUserLocation: (CLLocation *) location
{
for (Deal *deal in array) {
NSLog(#"%#", deal.name);
}
NSLog(#"%#", location.description);
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
...
To handle location updates when you app is in background or even terminated, you can use "Significant Location Changes". It will trigger application to start in background mode when location has been changed significantly. Then you can start a background task to perform the operations on your need.
Documentation:
https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/LocationAwarenessPG/CoreLocation/CoreLocation.html#//apple_ref/doc/uid/TP40009497-CH2-SW8
Easy way to simulate it and debug:
XCode / iOS simulator: Trigger significant location change manually
In appdelegate.h
#property (nonatomic,assign) bool isPhone;
In appdelegate.m
if ( IDIOM == IPAD ) {
self.isPhone = NO;
objDrawing = [[DEDrawingPage alloc]initWithNibName:#"DEDrawingPageiPad" bundle:nil];
} else {
self.isPhone = YES;
objDrawing = [[DEDrawingPage alloc]initWithNibName:#"DEDrawingPageiPhone" bundle:nil];
}
+(DEAppDelegate *)getDelegate
{
return (DEAppDelegate *)[[UIApplication sharedApplication] delegate];
}
In viewController.m
if([DEAppDelegate getDelegate].isPhone) // bad access here
{
objSettingPage = [[DESettings alloc]initWithNibName:#"DESettingsiPhone" bundle:nil];
}
Accessing isPhone property is giving bad access in IF condition. This is happening with BOOL type property only. I have tried taking NSNumber or NSString. They are working fine. Why BOOL is giving bad access. I have also tried with (lowercase) bool, that too is useless.
Can you clean the build and re-check the issue. I have followed the above same steps, didn't get issue.
Also, have you defined below in delegate.h
+(DEAppDelegate *)getDelegate;
Why don't use macro of appDelegate object which is given below to use in all your application define in your project's .pch file :
#define appDelegateObject ((AppDelegate *)[UIApplication sharedApplication].delegate)
Note : change AppDelegate to your Application Delegate instance
Use this macro like this :
if(appDelegateObject.isPhone) //Here isPhone is property of appDelegate
{
//anything here
}
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
}
}