iBeacon didEnterRegion not firing when in background and locked - ibeacon

I have an iBeacon configured as:
NSUUID *proximityUUID = [[NSUUID alloc] initWithUUIDString:kUUID];
self.beaconRegion = [[CLBeaconRegion alloc] initWithProximityUUID:proximityUUID identifier:kIdentifier];
self.beaconRegion.notifyEntryStateOnDisplay = NO;
self.beaconRegion.notifyOnEntry = YES;
self.beaconRegion.notifyOnExit = YES;
When my app is closed and the device is locked, didEnterRegion is never fired:
locationManager:(CLLocationManager *)manager didEnterRegion:(CLRegion *)region{
[self sendLocalNotificationForBeaconRegionHello:(CLBeaconRegion *)region];
}
- (void)sendLocalNotificationForBeaconRegionHello:(CLBeaconRegion *)region
{
UILocalNotification *notification = [UILocalNotification new];
notification.alertBody = [NSString stringWithFormat:#"Welcome - %#", region.identifier];
notification.alertAction = NSLocalizedString(#"View", nil);
notification.soundName = UILocalNotificationDefaultSoundName;
notification.fireDate = nil;
notification.hasAction = false;
[[UIApplication sharedApplication] presentLocalNotificationNow:notification];
}
The didExitRegion does get called even if the app is closed and the phone is locked. I only get a notification when entering a region when unlocking the phone.
Any ideas what might be the problem?
Thanks

I suspect the method is getting called but the notification swallowed for some reason (although the code looks OK). Try adding a method like below to help figure out what is going on:
- (void)locationManager:(CLLocationManager *)manager didDetermineState:(CLRegionState)state forRegion:(CLRegion *)region
{
if(state == CLRegionStateInside) {
NSLog(#"locationManager didDetermineState INSIDE for %#", region.identifier);
}
else if(state == CLRegionStateOutside) {
NSLog(#"locationManager didDetermineState OUTSIDE for %#", region.identifier);
}
else {
NSLog(#"locationManager didDetermineState OTHER for %#", region.identifier);
}
}
It would also be useful to see your code in didExitRegion for comparison, and hear details about how you are testing enter/exit conditions (including wait times.)

Related

objective c WatchKit WKInterfaceController openParentApplication call blocks indefinitely

I'm using the following code to "simply" determine the application state of the parent application from my watch app:
WatchKit Extension:
[WKInterfaceController openParentApplication:[NSDictionary dictionary] reply:^(NSDictionary *replyInfo, NSError *error)
{
UIApplicationState appState = UIApplicationStateBackground;
if(nil != replyInfo)
appState = (UIApplicationState)[((NSNumber*)[replyInfo objectForKey:kAppStateKey]) integerValue];
//handle app state
}];
Main App:
- (void)application:(UIApplication *)application handleWatchKitExtensionRequest:(NSDictionary *)userInfo reply:(void (^)(NSDictionary *replyInfo))reply
{
__block UIBackgroundTaskIdentifier realBackgroundTask;
realBackgroundTask = [[UIApplication sharedApplication] beginBackgroundTaskWithExpirationHandler:^{
reply([NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:[[UIApplication sharedApplication] applicationState]], kAppStateKey, nil]);
[[UIApplication sharedApplication] endBackgroundTask:realBackgroundTask];
}];
reply([NSDictionary dictionaryWithObjectsAndKeys:[NSNumber numberWithInt:[[UIApplication sharedApplication] applicationState]], kAppStateKey, nil]);
[[UIApplication sharedApplication] endBackgroundTask:realBackgroundTask];
}
When the app is in the foreground this works 100% of the time. When the app is "minimized" or "terminated" this maybe works 50% of the time (maybe less). When it doesn't work it appears to be blocking indefinitely. If after 1 minute, for example, I launch the parent app, the call (openParentApplication) immediately returns with the state "UIApplicationStateBackground" (the state it was before I launched the app as clearly the app isn't in the background state if I launched it).
BTW: I'm testing with real hardware.
What am I doing wrong? Why is iOS putting my main app to sleep immediately after receiving the call even though I create a background task? This is a complete show-stopper.
Any thoughts or suggestions would be greatly appreciated!!
After some research it looks to be a known issue. For example, the following link identifies this issue and provides a solution:
http://www.fiveminutewatchkit.com/blog/2015/3/11/one-weird-trick-to-fix-openparentapplicationreply
However, this solution did not work for me. As a result I implemented the following solution (its a little sloppy, but this is intentional to help condense the solution):
//start the timeout timer
timeoutTimer = [NSTimer scheduledTimerWithTimeInterval:kTimeOutTime target:self selector:#selector(onTimeout) userInfo:nil repeats:NO];
//make the call
messageSent = [WKInterfaceController openParentApplication:[NSDictionary dictionary] reply:^(NSDictionary *replyInfo, NSError *error)
{
if(nil != _stateDelegate)
{
UIApplicationState appState = UIApplicationStateBackground;
if(nil != replyInfo)
appState = (UIApplicationState)[((NSNumber*)[replyInfo objectForKey:kAppStateKey]) integerValue];
[_stateDelegate onOperationComplete:self timeout:false applicationState:appState];
_stateDelegate = nil;
}
}];
//if the message wasn't sent, then this ends now
if(!messageSent)
{
if(nil != _stateDelegate)
{
//just report that the main application is inactive
[_stateDelegate onOperationComplete:self timeout:false applicationState:UIApplicationStateInactive];
}
_stateDelegate = nil;
}
-(void)onTimeout
{
timeoutTimer = nil;
if(nil != _stateDelegate)
{
[_stateDelegate onOperationComplete:self timeout:true applicationState:UIApplicationStateInactive];
}
_stateDelegate = nil;
}
In a nutshell, if the timer fires before I hear back from the main app I will basically assume that the main app has been put to sleep. Keep in mind that all pending calls will succeed at some point (e.g. app state is restored to active) and, thus, you will need to handle this scenario (if necessary).

CKSMSComposeRemoteViewController timed out waiting for fence barrier from com.apple.mobilesms.compose

Ok, so sendSMS worked fine before on ios7 and below. However, on ios8 the sendSMS function just fails with the error in the title of the question. I am getting a warning here (after trying to resolve by changing NSArray to NSString using other stack overflow questions): Incompatible pointer types assigning to 'NSArray *' from 'NSString *' for controller.recipients = recipients; It is returning a result of MessageComposeResultCancelled.
if (ABMultiValueGetCount(phoneNumbers) > 0) {
phone = (__bridge_transfer NSString*)
ABMultiValueCopyValueAtIndex(phoneNumbers, 0);
[self sendSMS:#"Play me on PokerBuddies.
Download the app at: https://itunes.apple.com/us/app /poker-buddies/id404168013?mt=8"
recipientList:[NSString stringWithFormat:phone, nil]];
} else {
phone = #"[None]";
}
- (void)sendSMS:(NSString *)bodyOfMessage recipientList:(NSString *)recipients{
MFMessageComposeViewController *controller = [[MFMessageComposeViewController alloc] init];
if([MFMessageComposeViewController canSendText]){
controller.body = bodyOfMessage;
controller.recipients = recipients;
controller.messageComposeDelegate = self;
[self presentViewController:controller animated:YES completion:nil];
NSLog(#"Send SMS");
}
}
I have same problem like MessageComposeController timeout issue .
I solved it by doing this.
You have to create instance variable of MFMessageComposeViewController and when you are going to present message controller you have to check if instance object is already created then do it nil and initialize that object again.So this error "CKSMSComposeRemoteViewController timed out waiting for fence barrier from com.apple.mobilesms.compose" will not come and controller open exactly.
if ([MFMessageComposeViewController canSendText]) {
if (messageComposer) {
messageComposer = nil;
messageComposer = [[MFMessageComposeViewController alloc]init];
}
messageComposer.recipients = arrPhoneNumber;
messageComposer.messageComposeDelegate = self;
messageComposer.body = #"Your text";
isMessageComposeAppear = 1;
[self presentViewController:messageComposer animated:YES completion:nil];
}

ManagedObjectContexts with threads (dispatch queues) gets into a deadlock on iOS7

I know there are many threads about NSManagedObjectContexts and threads but my problem seems to be only specific to iOS7. (Or at least not visible in OS6)
I have an app that makes use of dispatch_queue_ and runs multiple threads to fetch data from the server and update the UI. The app was working fine on iOS6 but on iOS7 it seems to get into deadlocks(mutex wait). See below the stack trace -
The "wait" happens in different methods usually when executing a fetch request and saving a (different) context. The commit Method is as follows :
-(void)commit:(BOOL) shouldUndoIfError forMoc:(NSManagedObjectContext*)moc {
#try {
// shouldUndoIfError = NO;
// get the moc for this thread
NSManagedObjectContext *moc = [self safeManagedObjectContext];
NSThread *thread = [NSThread currentThread];
NSLog(#"got login");
if ([thread isMainThread] == NO) {
// only observe notifications other than the main thread
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(contextDidSave:)
name:NSManagedObjectContextDidSaveNotification
object:moc];
NSLog(#"not main thread");
}
NSError *error;
if (![moc save:&error]) {
// fail
NSLog(#"ERROR: SAVE OPERATION FAILED %#", error);
if(shouldUndoIfError) {
[moc undo];
}
}
if ([thread isMainThread] == NO) {
[[NSNotificationCenter defaultCenter] removeObserver:self
name:NSManagedObjectContextDidSaveNotification
object:moc];
}
}
#catch (NSException *exception) {
NSLog(#"Store commit - %#",exception);
NSDictionary *dictionary = [NSDictionary dictionaryWithObjectsAndKeys:#"name",#"store commit",#"exception", exception.description, nil];
[Flurry logEvent:#"MyException" withParameters:dictionary timed:YES];
}
#finally {
NSLog(#"Store saved");
}
}
How I'm creating new contexts for each thread :
-(NSManagedObjectContext *)safeManagedObjectContext {
#try {
if(self.managedObjectContexts == nil){
NSMutableDictionary *_dict = [[NSMutableDictionary alloc]init];
self.managedObjectContexts = _dict;
[_dict release];
_dict = nil;
}
NSManagedObjectContext *moc = self.managedObjectContext;
NSThread *thread = [NSThread currentThread];
if ([thread isMainThread]) {
return moc;
}
// a key to cache the context for the given thread
NSString *threadKey = [NSString stringWithFormat:#"%p", thread];
if ( [self.managedObjectContexts valueForKey:threadKey] == nil) {
// create a context for this thread
NSManagedObjectContext *threadContext = [[[NSManagedObjectContext alloc] init] retain];
[threadContext setMergePolicy:NSMergeByPropertyStoreTrumpMergePolicy];
[threadContext setPersistentStoreCoordinator:[moc persistentStoreCoordinator]];
[threadContext setUndoManager:nil];
// cache the context for this thread
[self.managedObjectContexts setObject:threadContext forKey:threadKey];
NSLog(#"added a context to dictionary, length is %d",[self.managedObjectContexts count]);
}
return [self.managedObjectContexts objectForKey:threadKey];
}
#catch (NSException *exception) {
//
}
#finally {
//
}
}
What I have so far :
One Persistent Store coordinator.
Each New thread has its own Managed Object Context.
Strange part is that the same code worked fine on OS6 but not on OS7. I am still using the xcode4.6.3 to compile the code. Most of the code works on this principle, I run a thread, fetch data, commit it and then post a notification. Could the freeze/deadlock be because the notification gets posted and my UI elements fetch the data before the save(&merge) are reflected? Anything else that I'm missing ?

IAP doesn't work on Real iPad

When I run the following code in simulator, it can get the IAP info successfully, but when it run on a real IPAD, "count" always = zero ... any ideas what's wrong?? Thanks.
// Store Kit returns a response from an SKProductsRequest.
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
// Populate the removeAdsButton button with the received product info.
SKProduct *validProduct = nil;
int count = [response.products count];
if (count>0) {
validProduct = [response.products objectAtIndex:0];
}
if (!validProduct) {
[removeAdsButton setTitle:#"No Products Available" forState:UIControlStateNormal];
removeAdsButton.enabled = NO;
return;
}
NSString *buttonText = [[NSString alloc] initWithFormat:#"%# - Buy $%#", validProduct.localizedTitle, validProduct.price];
[removeAdsButton setTitle:buttonText forState:UIControlStateNormal];
removeAdsButton.enabled = YES;
[buttonText release];
}
I found that if the device is JB, it won't work..

How can I wait for result from geocodeAddressString iPhone

I know its something to do with locks or dispatch groups, but I just cant seem to code it...
I need to know if the address was a valid address before leaving the method. Currently the thread just overruns and returns TRUE. I've tried locks, dispatchers the works but can't seem to get it correct. Any help appreciated:
- (BOOL) checkAddressIsReal
{
__block BOOL result = TRUE;
// Lets Build the address
NSString *location = [NSString stringWithFormat:#" %# %#, %#, %#, %#", streetNumberText.text, streetNameText.text, townNameText.text, cityNameText.text, countryNameText.text];
// Put a pin on it if it is valid
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder geocodeAddressString:location
completionHandler:^(NSArray* placemarks, NSError* error) {
result = [placemarks count] != 0;
}];
return result;
}
The documentation says that CLGeocoder calls the completionHandler on the main thread. Since you are probably also calling your method from the main thread it cannot wait for the geocoder's answer without giving it the opportunity to deliver the result.
That would be done by polling the runloop, using some API as -[NSRunLoop runMode:beforeDate:].
The disadvantage is that depending on the mode this will also deliver events and fire timers while waiting for the result.
Just use block as parameter:
- (void) checkAddressIsRealWithComplectionHandler:(void (^)(BOOL result))complectionHandler
{
__block BOOL result = TRUE;
// Lets Build the address
NSString *location = [NSString stringWithFormat:#" %# %#, %#, %#, %#", streetNumberText.text, streetNameText.text, townNameText.text, cityNameText.text, countryNameText.text];
// Put a pin on it if it is valid
CLGeocoder *geocoder = [[CLGeocoder alloc] init];
[geocoder geocodeAddressString:location
completionHandler:^(NSArray* placemarks, NSError* error) {
result = [placemarks count] != 0;
complectionHandler(result);
}];
}

Resources