I have an app with multiple local notifications. When I try to clear all the delivered notifications, I call this removeAllDeliveredNotifications method. It's working fine till ios 11.1. In ios 11.2 and above, it doesn't work as expected. The notification still remains in the notification center. Could someone please help me out on this.
Thanks in advance.
It is still working for us. I just checked it on iOS 11.2.2.
I am using removeDeliveredNotificationsWithIdentifiers: inside getDeliveredNotificationsWithCompletionHandler:, calling getDeliveredNotificationsWithCompletionHandler on Main Thread.
- (void)removePendingNotificationsForObjectID:(SJFObjectID *)objectID {
__weak __typeof(self) weakSelf = self;
[self.userNotificationCenter getDeliveredNotificationsWithCompletionHandler:^(NSArray<UNNotification *> *notifications) {
__strong __typeof(weakSelf) self = weakSelf;
NSMutableArray <NSString *> *identifiersToRemove = [#[] mutableCopy];
for (UNNotification *notification in notifications) {
SJFObjectID *objectIDFromNotification = [self.notificationToObjectIDMarshaller marshalNotification:notification];
if ([objectID isEqual:objectIDFromNotification]) {
[identifiersToRemove addObject:notification.request.identifier];
}
}
[self.userNotificationCenter removeDeliveredNotificationsWithIdentifiers:identifiersToRemove];
}];
}
Though I experience strange behavior if I am debugging the completionHandler. If a pause too long (whatever that means) the completion handler will not finish (even on continue process execution) resulting in an unresponsive app. Maybe the completionhandler gets terminated.
This appears to be a bug in iOS that was fixed in iOS 11.3.
Did you try using this method:
removeDeliveredNotifications(withIdentifiers:)
You will need to pass an array of all the notification identifiers that you need to delete.
Related
I am trying to get device motion updates on an iPhone or iPad in the background using CMMotionManager. I have reviewed all previous posts on this topic and thought that I had code that would work. My app also uses background audio, and this works properly in the foreground and background. In Info.plist, I have background audio and background location updates enabled.
For testing purposes, I have declared "var motionManager = CMMotionManager()" in AppDelegate, and included the following code in didFinishLaunchingWithOptions:
motionManager.deviceMotionUpdateInterval = 0.10
let queue = NSOperationQueue()
motionManager.startDeviceMotionUpdatesToQueue(queue, withHandler: {
data, error in
let accelerationVector = sqrt(pow(data!.userAcceleration.x, 2) + pow(data!.userAcceleration.y, 2) + pow(data!.userAcceleration.z, 2))
print("\(accelerationVector)")
})
When I run the app on my device, the code executes in the foreground as expected, but when I press the home button, I get about 10 more readings before it stops. When I tap on the app icon, the readings start again. I have also placed breakpoints on the code in the handler, and get similar results.
What am I missing?
Do NOT trust the NSLog or other similar log output
I used to meet this problem. I want collect the motion data in background and I made a demo about this. I found I can get all the data and log when my app in active, but the xCode log console output nothing when the application in background.
I used to think the problem is the CoreMotion data can only be collected in foreground, but I am wrong. All the callbacks still working when application enter background, Just the NSLog stop telling me the data.
If you don't believe, just collect all the data into a NSMutableArray or other collections, then check the data collected when app in background.
e.g.
#property (nonatomic, strong) NSMutableArray *arrAltimeters;
...
[self.altimeter startRelativeAltitudeUpdatesToQueue:self.operationQueue withHandler:^(CMAltitudeData * _Nullable altitudeData, NSError * _Nullable error) {
[self.arrAltimeters addObject:altitudeData];
NSLog(#"Altimate data count = %ld", self.arrAltimeters.count);
}];
I have declared the motion manager in ViewDidlLoad
CMMotionManager *motionManager;
/*--Initialising Motion Manager--*/
motionManager = [[CMMotionManager alloc] init];
motionManager.deviceMotionUpdateInterval = 1.0;
As whenever the app goes in background the motionManager stop providing callbacks in this case , You need to restart the motionmanager when the app goes in background or in foreground.To do this we need to follow the process below:
1) we first need to Register for the app transition notification :
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(appDidEnterBackground) name:UIApplicationDidEnterBackgroundNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(appDidBecomeActive) name:UIApplicationDidBecomeActiveNotification object:nil];
2)Notification callback functions, in which you will restart the motion manager
-(void)appDidEnterBackground{
[self restartMotionUpdates];
}
-(void)appDidBecomeActive{
[self restartMotionUpdates];
}
3)This function which will restart the motion manager
-(void)restartMotionUpdates{
[motionManager stopDeviceMotionUpdates];
[motionManager startDeviceMotionUpdatesToQueue:[NSOperationQueue mainQueue] withHandler:^(CMDeviceMotion *motion, NSError *error) {
NSLog(#"x=%f y=%f z=%f",fabs(motion.userAcceleration.x),fabs(motion.userAcceleration.y),fabs(motion.userAcceleration.z));
}];
}
After doing more research, I figured out the problem. I was using mpmusicplayercontroller to play background audio. This will play the audio in the background, but this does not keep my app running in the background. When I switched to using avplayer to play audio, the app runs in the background including the device motion updates.
I'm working on the WatchKit Extension of my app, and have some issues with complications.
I have a complication that displays a given total amount, which depends of what the user is doing on the iOS app. When the WatchKit Extension is running, the iOS app updates the watch app context using the -[WCSession updateApplicationContext:] method. It works fine, and then in the ExtensionDelegate of my Watch app, I manually update the complication with the new data.
But this is OK only when the extension is running (if it's not, it won't get the application context until the next launch).
So I edited my code to send the complication data directly to the Watch when user changed something in the iOS app, using the -[WCSession transferCurrentComplicationUserInfo:] method (it's written in the documentation that the ExtensionDelegate should be woken up to receive the user info in background).
I've implemented the -session:didReceiveUserInfo: method of the ExtensionDelegate to update the complication when it received data from the iOS app, but it doesn't work when the extension is not running... (and I don't know if it ever receives the user info as I can't log it)
How should I do to keep my complications up to date even when the extension is not running??
Thanks
PS: I'm using the Watch Simulator, and to "close" the extension I just Reboot the Watch (from the Hardware menu)
Edit: I managed to log out statements when the app is not running (by opening the Watch Simulator system log), and I get these lines when the iOS send a new complication user data to the watch extension:
Oct 18 18:08:11 pc16 WatchApp Extension[26615]: Extension received
request to wake up for complication support.
Oct 18 18:08:11 pc16 assertiond[26585]: assertion failed: 15A284 13S343: assertiond + 15398 [B48FCADB-A071-3A46-878B-538DC0AFF60B]: 0x1
So the watch receives well the user info dictionary, but seems to fail waking up the extension...
Edit 2: here is the part of code in the ExtensionDelegate that should receive the complication user info (but which is not called when the app is not running):
- (void) session: (WCSession *)session didReceiveUserInfo: (NSDictionary *)userInfo
{
NSLog(#"session:didReceiveUserInfo: %#", userInfo);
NSString *userInfoType = userInfo[KHWASessionTransferUserInfoType];
NSDictionary *userInfoContents = userInfo[KHWASessionTransferUserInfoContents];
// Complication Data
if ([userInfoType isEqualToString:KHWASessionTransferUserInfoTypeComplicationData]) {
// Store the complication data into user defaults
[[NSUserDefaults standardUserDefaults] setValue:userInfoContents[KHWAComplicationTotalBalance] forKey:KHWAComplicationTotalBalance];
[[NSUserDefaults standardUserDefaults] synchronize];
// And refresh the complications
CLKComplicationServer *complicationServer = [CLKComplicationServer sharedInstance];
for (CLKComplication *complication in complicationServer.activeComplications) {
[complicationServer reloadTimelineForComplication:complication];
}
}
}
Edit 3: the WCSession is set in the extension delegate applicationDidFinishLaunching method:
- (void) applicationDidFinishLaunching
{
// Setup the WatchConnectivity session
WCSession *session = [WCSession defaultSession];
session.delegate = self;
[session activateSession];
[...]
}
Wow, I finally resolved the issue!
It seems that, even if I didn't see it in the log files (see my last comment), the init method of WCExtensionDelegate is well called when waking up the app.
So I just had to move the WCSession setting bloc into the init method :
- (id) init
{
if (self = [super init]) {
// Setup the WatchConnectivity session
WCSession *session = [WCSession defaultSession];
session.delegate = self;
[session activateSession];
}
return self;
}
And for the while it works fine...
_container = [CKContainer containerWithIdentifier:#"iCloud.com.komocode.TestApp"];
_privateDatabase = [_container privateCloudDatabase];
[_privateDatabase fetchAllSubscriptionsWithCompletionHandler:^(NSArray *subscriptions, NSError *error) {
// Never called
NSLog(#"Test");
}];
This piece of code only works on my iPad, but on my iPhone 6, the completion handler never gets called. Both my iPad and iPhone 6 are logged into the same iCloud account. Any ideas?
Rebooting the phone fixed it... I guess CloudKit is not ready for production?
I'm developing a game with Sprite Kit but I am having a multitasking issue. When I press the home button, during the game execution, my SKScene.paused becomes true, and I make the proper changes to the app in my applicationDidEnterBackground method at AppDelegate.m, such as saving stuff with NSUserDefaults. Anyway, if I opened my app again, it should resume from where it left off, but what happens is that my app terminates and starts again. This only happens in my iPhone (in the iOS Simulator it works fine). Since I am new at creating games with Sprite Kit and creating apps at all, I was hoping for some clue of what could the problem be...
PS: I think the problem is something about the app not being "suspended" correctly, because if I press the home button and immediately reopen the app, it works fine.
Here is my code:
- (void)applicationDidEnterBackground:(UIApplication *)application
{
if(self.scene == nil) return;
[self.scene saveUserDefaults];
[self.scene pausar];
self.scene.paused = true;
}
In MyScene.m, inside the initWithSize method:
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
/* Setup your scene here */
AppDelegate *appDelegate = [[UIApplication sharedApplication] delegate];
appDelegate.scene = self;
And in the AppDelegate.h file: #property (weak, nonatomic) MyScene * scene;
Now, the saveUserDefaults method, which is inside the MyScene.m file:
-(void) saveUserDefaults
{
[userDefaults setBool:true forKey:#"active"];
[userDefaults setInteger:highScore forKey:#"highScore"];
[userDefaults setBool:soundOn forKey:#"soundOn"];
[userDefaults setBool:musicOn forKey:#"musicOn"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
Well, without seeing more of your code, it is impossible to know what is causing your issue.
First, read this question. Similarly, you should read up on the UIAppDelegate class since it is something you must know very well when programming on a phone where frequent interruptions like phone calls, etc can happen while someone is using your app.
More than likely, you have a problem with how you are responding to one of the protocol methods in the delegate.
When I enter the app it is working fine but when I re-enter the app it will not shown the particular page or starting page of the app but it is again displaying the Launcher image and crashes and after crash again it is working fine.
[NSThread sleepForTimeInterval:1.0];
UILocalNotification *locationNotification = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
if (locationNotification) {
// Set icon badge number to zero
NSLog(#"Recieved Notification %#",locationNotification);
}
return YES;
please give the solution I am new bee to iOS.
Anupma
From seeing the problem which is described here I suspect some methods or notifications are calling in applicationDidEnterBackground. Please check it and if it is you can find the solution to solve it.
Hope this helps.
-(void)applicationDidEnterBackground:(UIApplication *)application
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
[self Backgroundmethods];
dispatch_async(dispatch_get_main_queue(), ^{
//your code to update UI if any goes here
});
});
}
I worte the all my background methods in seperate method and calling that method with in applicationdidEnterbackground in seperate thread.