Receiving Apple Push Notifications when app is in background - xcode

I have implemented push notifications in my iOS8 app. I am trying to play an audio file once the notification is received.
The code is playing the audio when the app is in the foreground, but when the app is in the background, nothing happens.
I have tried regenerating the certificates and provisioning profiles. And I have made sure that the app is running in the background, i.e. the user has not swiped up to remove it. In Background modes, I have enabled Remote Notifications, Background Fetch and Audio & Airplay.
I have added code snippets from my AppDelegate.m file:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
// More code here ---------------------------------------------------
if (launchOptions) {
NSDictionary *userInfo = [launchOptions valueForKey:UIApplicationLaunchOptionsRemoteNotificationKey];
NSDictionary *apsInfo = [userInfo objectForKey:#"aps"];
if (apsInfo) { //apsInfo is not nil
[self performSelector:#selector(playCarAlarmAudio)
withObject:nil
afterDelay:1];
}
}
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0) {
[[UIApplication sharedApplication] registerForRemoteNotifications];
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeBadge
|UIUserNotificationTypeSound
|UIUserNotificationTypeAlert) categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:settings];
}
// More code here ---------------------------------------------------
}
The delegate methods to handle push notifications:
-(void) application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
NSLog(#"Failed to register for push");
}
-(void) application:(UIApplication *)application didRegisterUserNotificationSettings:(UIUserNotificationSettings *)notificationSettings {
}
-(void) application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
[self respondToEventNotification:userInfo];
}
-(void) application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
// [self respondToEventNotification:userInfo];
[self playAlarmAudio];
}
-(void) respondToEventNotification : (NSDictionary *) userInfo {
if ([[UIApplication sharedApplication] applicationState] == UIApplicationStateBackground) {
UILocalNotification *localNotification = [[UILocalNotification alloc] init];
[localNotification setSoundName:#"alarm.mp3"];
[localNotification setFireDate:[NSDate date]];
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
}
else if ([[UIApplication sharedApplication] applicationState] == UIApplicationStateActive) {
[self playAlarmAudio];
}
}
And to play the Alarm:
-(void) playAlarmAudio {
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"alarm" ofType:#"mp3"];
NSURL *fileUrl = [NSURL fileURLWithPath:filePath];
self.audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:fileUrl error:nil];
self.audioPlayer.numberOfLoops = 1;
[self.audioPlayer play];
}

According to the following Apple documentation, the notification sound to be played is specified inside the notification payload dictionary (https://developer.apple.com/library/ios/documentation/NetworkingInternet/Conceptual/RemoteNotificationsPG/Chapters/ApplePushService.html):
The Notification Payload
Each remote notification includes a payload. The payload contains information about how the system should alert the user as well as any custom data you provide. In iOS 8 and later, the maximum size allowed for a notification payload is 2 kilobytes; Apple Push Notification service refuses any notification that exceeds this limit. (Prior to iOS 8 and in OS X, the maximum payload size is 256 bytes.)
For each notification, compose a JSON dictionary object (as defined by RFC 4627). This dictionary must contain another dictionary identified by the key aps. The aps dictionary can contain one or more properties that specify the following user notification types:
An alert message to display to the user
A number to badge the app icon with
A sound to play

Related

How can PHP code use an APNS device token for Apple Push Notification?

I'm looking at the PHP code here:
https://gist.github.com/valfer/18e1052bd4b160fed86e6cbb426bb9fc
It looks good. I'd love to use it. But I'm confused about this:
* #param $token the token of the device
So I need the device token? For PHP code that is going to live on a server? How do I get the device token?
when iOS application is started, it registers itself for Apple Push Notifications by using the following code in your AppDelegate's didFinishLaunchingWithOptions.
if([[[UIDevice currentDevice] systemVersion] floatValue]>=8.0) {
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:(UIUserNotificationTypeBadge|UIUserNotificationTypeSound|UIUserNotificationTypeAlert) categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:settings];
[[UIApplication sharedApplication] registerForRemoteNotifications];
} else {
//register to receive notifications
UIRemoteNotificationType myTypes = UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeAlert | UIRemoteNotificationTypeSound;
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:myTypes];
}
and then following delegate methods may be called based on success or failure
-(void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken {
NSString* strdeviceToken = [[NSString alloc]init];
strdeviceToken=[self stringWithDeviceToken:deviceToken];
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
[prefs setObject:strdeviceToken forKey:PREF_DEVICE_TOKEN];
[prefs synchronize];
NSLog(#"My token is===========> : %#",strdeviceToken);
}
- (void)application:(UIApplication*)application didFailToRegisterForRemoteNotificationsWithError:(NSError*)error {
// NSLog(#"Failed to get token, error: %#", error);
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
[prefs setObject:#"" forKey:PREF_DEVICE_TOKEN];
[prefs synchronize];
}
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
if (application.applicationState == UIApplicationStateActive) {
// [self showToastMessage:#"Active"];
}
else if (application.applicationState == UIApplicationStateBackground) {
// [self showToastMessage:#"Background"];
}
else if (application.applicationState == UIApplicationStateInactive) {
// [self showToastMessage:#"Inactive"];
}
// [self handleIncomingNotification:userInfo delay:0.0];
}
All the above logic should be handled in AppDelegate class of your project. Then you can make some API call in your PHP code to be called from iOS and send this device token on your server and save it for future use.

iOS App not registering for push notifications

I am trying to register for push notifications in my iOS app. But it is calling neither the didRegisterForRemoteNotificationsWithDeviceToken nor didFailToRegisterForRemoteNotificationsWithError callback methods. I have revoked and regenerated the provisioning profile for the app.
I am using iOS8 and I have enabled the following background modes in my Info.plist
App registers for location updates
App downloads content in response to push notifications
App downloads content from the network
The code is:
-(void) application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
NSLog(#"Failed to register for push");
}
-(void) application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
NSLog(#"did succeed in register for push");
// Get the device token string
const char* data = [deviceToken bytes];
NSMutableString* token = [NSMutableString string];
for (int i = 0; i < [deviceToken length]; i++) {
[token appendFormat:#"%02.2hhX", data[i]];
}
[[NSUserDefaults standardUserDefaults] setObject:token forKey:#"DeviceToken"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
-(void) application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
[self respondToEventNotification:userInfo];
}
-(void) application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
[self respondToEventNotification:userInfo];
}
Have you register your app with didFinishLaunchingWithOptions method for iOS8 like,
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
if ([[[UIDevice currentDevice] systemVersion] floatValue] >= 8.0) {
[[UIApplication sharedApplication] registerForRemoteNotifications];
UIUserNotificationSettings *settings = [UIUserNotificationSettings settingsForTypes:(UIRemoteNotificationTypeBadge
|UIRemoteNotificationTypeSound
|UIRemoteNotificationTypeAlert) categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:settings];
}
else
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:
(UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert)];
}
You have to consider registration process of Push Notification in iOS8.
May this help you.

Expected identifier or "(" error

EDITED: Thanks to your help I am closing in on the solution to my problem. I have fixed the missing "#end error", and am left with just one "Expected identifier or "("" error.
This is the code, the error is noted:
#define SYSTEM_VERSION_LESS_THAN(v) ([[[UIDevice currentDevice] systemVersion] \
compare:v options:NSNumericSearch] == NSOrderedAscending)
#import "WViewController.h"
#import <SkillzSDK-iOS/Skillz.h>
#interface WViewController ()
#end
#implementation WViewController
- (void)viewDidLoad
{
[super viewDidLoad];
//create backdrop image
NSMutableString *imgFile = [[NSMutableString alloc] init];
if (![globalBackgroundImage isEqualToString:#""]) [imgFile setString:globalBackgroundImage]; //falls back to first image setting for all devices;
if ((IS_WIDESCREEN_PHONE)&&(![widescreenBackgroundImage isEqualToString:#""])) [imgFile setString:widescreenBackgroundImage]; //you can specify a different image for
if ((IS_IPAD)&&(![iPadBackgroundImage isEqualToString:#""])) [imgFile setString:iPadBackgroundImage]; //widescreen phones & for iPad
if (![imgFile isEqualToString:#""]) {
UIImage *img = [[UIImage imageNamed:imgFile] retain];
CGSize imgSz = [img size];
CGSize screenSz = [[UIScreen mainScreen] bounds].size;
float imgWH = imgSz.width/imgSz.height;
float screenWH = screenSz.width/screenSz.height;
CGRect backdropFrame;
if (imgWH>=screenWH) backdropFrame = CGRectMake((screenSz.width/2)-((screenSz.height*imgWH)/2), 0, screenSz.height*imgWH, screenSz.height); //image wider than screen
else backdropFrame = CGRectMake(0, ((screenSz.height/2)-((screenSz.width/imgWH)/2)), screenSz.width, screenSz.width/imgWH);
UIImageView *backdropImageView = [[UIImageView alloc] initWithFrame:backdropFrame];
[backdropImageView setImage:img];
[backdropImageView setAlpha:backgroundImageOpacity];
[self.view addSubview:backdropImageView];
}
[self.view setBackgroundColor:globalBackgroundColor];
//init GameCenter
[[GameCenterManager sharedManager] setupManager];
[[GameCenterManager sharedManager] setDelegate:self];
//initialize the view for when the player is in the game
gameView = [[WgameView alloc] initWithFrame:[[UIScreen mainScreen] bounds] fromViewController:self];
[gameView setHidden:YES];
[self.view addSubview:gameView];
//initialize the view for then the player is on the home screen
homeView = [[WhomeView alloc] initWithFrame:[[UIScreen mainScreen] bounds] fromViewController:self];
[homeView setHidden:YES];
[self.view addSubview:homeView];
}
- (void) viewDidAppear:(BOOL)animated {
//go to home screen right away
[self goHome];
//show a RevMob fullscreen ad if we're supposed to
if (revMobActive) {
if (showRevMobFullscreenOnLaunch) {
[[RevMobAds session] showFullscreen];
}
}
//show a Chartboost ad if we're supposed to
if (chartboostActive) {
if (showChartboostOnLaunch) {
[[Chartboost sharedChartboost] showInterstitial:CBLocationHomeScreen];
}
}
}
#pragma mark game flow
-(void) multiplayerButtonPressed:(id)sender
{
NSLog(#"Multiplayer button pressed, launching Skillz!");
// Launching Skillz in landscape mode
[[Skillz skillzInstance] launchSkillzForOrientation:SkillzLandscape
launchHasCompleted:^{
// This code is called after the Skillz UI launches.
NSLog(#"Skillz just launched.");
} tournamentWillBegin:^(NSDictionary *gameRules) {
// This code is called when a player starts a game in the Skillz portal.
NSLog(#"Tournament with rules: %#", gameRules);
NSLog(#"Now starting a game…");
// INCLUDE CODE HERE TO START YOUR GAME
// …..
// …..
// …..
// END OF CODE TO START GAME
} skillzWillExit:^{
// This code is called when exiting the Skillz portal
//back to the normal game.
NSLog(#"Skillz exited.");
}];
}
- (void) startGame:(UIButton*)sender {
//hide RevMob banner ad if we're supposed to
if (revMobActive) {
if (showRevMobBannerOnHomeScreen) {
[[RevMobAds session] hideBanner];
}
}
//starts game in the mode corresponding to which button was tapped
[[WGameModeEngine sharedInstance] setCurrentGameMode:sender.titleLabel.text];
[gameView startGame];
[homeView setHidden:YES];
[gameView setHidden:NO];
//init timer if timed game
if ([[WGameModeEngine sharedInstance] isTimedGame]) {
timedGameTimer = [[NSTimer scheduledTimerWithTimeInterval:1.0 target:self selector:#selector(decrementTime) userInfo:nil repeats:YES] retain];
}
//notify game engine to play sound if configured
[[WGameModeEngine sharedInstance] soundEventDidHappen:#"BeginGame"];
}
- (void) goHomeButtonPressed {
[self stopGame];
[self goHome];
}
- (void) stopGame {
//stop timer if this was a timed game
if (timedGameTimer) {
[timedGameTimer invalidate];
[timedGameTimer release];
timedGameTimer=nil;
}
}
- (void) goHome {
[gameView setHidden:YES];
[homeView setHidden:NO];
//show a RevMob banner ad if we're supposed to
if (revMobActive) {
if (showRevMobBannerOnHomeScreen) {
[[RevMobAds session] showBanner];
}
}
}
- (void) decrementTime {
[[WGameModeEngine sharedInstance] timeDecreased]; //report to our game model that time has decreased
if ([[WGameModeEngine sharedInstance] timeLeft]<=0) { //if 0 seconds left,
[self timedGameEnded]; //game has ended
}
if (([[WGameModeEngine sharedInstance] timeLeft]<6)&&([[WGameModeEngine sharedInstance] timeLeft]>0)) {
//notify game engine to play sound if configured
[[WGameModeEngine sharedInstance] soundEventDidHappen:#"FiveSecondCountdown"];
}
[gameView updateLabels]; //update gameView's score and time labels
}
- (void) timedGameEnded {
//game over!
[self stopGame];
//notify game engine to play sound if configured
[[WGameModeEngine sharedInstance] soundEventDidHappen:#"GameOver"];
//show an alert with score and list of words found (if you want, you can add a whole separate screen for this instead of simple alert!)
UIAlertView *alert = [[UIAlertView alloc] initWithTitle: #"Game Over" message:[NSString stringWithFormat:#"You scored %d points!\n\nWords found:\n%#",[[WGameModeEngine sharedInstance] getScore],[[[WGameModeEngine sharedInstance] getWordsFound] componentsJoinedByString:#" "]] delegate: nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert setDelegate:self];
[alert show];
[alert release];
//report score to GameCenter
int sc = [[WGameModeEngine sharedInstance] getScore];
if (sc>0) [self reportScore:sc forCategory:[[[[WGameModeEngine sharedInstance] getCurrentGameMode] componentsSeparatedByString:#" "] componentsJoinedByString:#"_"]];
[#"com.bundle.appname" stringByAppendingString:[[[[[WGameModeEngine sharedInstance] getCurrentGameMode] lowercaseString] componentsSeparatedByString:#" "] componentsJoinedByString:#""]];
}
- (void) alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
//share latest score on Facebook if we're supposed to
if (FacebookShareEnabled) {[self facebookShare];}
//go to home screen
[self goHome];
//show a RevMob fullscreen ad if we're supposed to
if (revMobActive) {
if (showRevMobFullscreenWhenGameOver) {
[[RevMobAds session] showFullscreen];
}
}
//show a Chartboost ad if we're supposed to
if (chartboostActive) {
if (showChartboostWhenGameOver) {
[[Chartboost sharedChartboost] showInterstitial:CBLocationHomeScreen];
}
}
}
#pragma mark GameCenter
- (void)gameCenterManager:(GameCenterManager *)manager authenticateUser:(UIViewController *)gameCenterLoginController {
if (revMobActive) {
if (showRevMobBannerOnHomeScreen) {
[[RevMobAds session] hideBanner];
}
}
[self presentViewController:gameCenterLoginController animated:YES completion:^(void)
{if (revMobActive) {
if (showRevMobBannerOnHomeScreen) {
[[RevMobAds session] showBanner];
}}}];
}
if (isGameOver) {<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<ERROR IS HERE>>>>>>>>>>>>>>>
if ([[Skillz skillzInstance] tournamentIsInProgress]) {
// The game ended and it was in a Skillz tournament,
// so report the score and go back to Skillz.
[[Skillz skillzInstance] completeTurnWithGameData:gameData
playerScore:playerScore
playerCurrentTotalScore:playerCurrentTotalScore
opponentCurrentTotalScore:opponentCurrentTotalScore
roundOutcome:turnOutcome
matchOutcome:matchOutcome
withCompletion:^{
// Code in this block is called when exiting to Skillz
// and reporting the score.
NSLog(#"Reporting score to Skillz…");
}];
} else {
// Otherwise single player game, so take the normal action
}
}
- (void) reportScore: (int64_t) score forCategory: (NSString*) category
ORIGINAL QUESTION:
I am really new to coding and hoping that someone can help me with what I am sure is a very simple problem. I have looked at a lot of other answers regarding this error, but they don't seem to apply to my situation.
The following code is part of a game app I am working on using Xcode, trying to integrate it with a third party system called SKILLZ. I did not write any of the code and am trying to understand it as I proceed with the integration.
I have noted within the code where I am getting the error:
#import "WAppDelegate.h"
#import "WViewController.h"
#import <SkillzSDK-iOS/Skillz.h>
#implementation WAppDelegate
- (void)dealloc
{
[_window release];
[_viewController release];
[super dealloc];
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
//initialize language engine
[WLanguageEngine sharedInstance];
//initialize game mode engine
[WGameModeEngine sharedInstance];
//initialize RevMob
if (revMobActive) {
[RevMobAds startSessionWithAppID:RevMobAppID];
}
if (revMobActive&&revMobTestingMode) {
[RevMobAds session].testingMode = RevMobAdsTestingModeWithAds;
// or
//[RevMobAds session].testingMode = RevMobAdsTestingModeWithoutAds;
}
self.window = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]] autorelease];
// Override point for customization after application launch.
self.viewController = [[[WViewController alloc] initWithNibName:#"WViewController" bundle:nil] autorelease];
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
return YES;
}
{<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<**<<EXPECTED IDENTIFIER OR "(">> ERROR OCCURS HERE**
// INITIALIZE SKILLZ HERE
// 940 is the game ID that was given to us by the Skillz Developer Portal.
// SkillzSandbox specifies that we will use the sandbox server since we
// are still developing the game.
// SkillzProduction specifies that we will use the production server since
// the game is ready for release to the AppStore
[[Skillz skillzInstance] skillzInitForGameId:#"940"
environment:SkillzSandbox];
I am getting a second occurrence of this error in a different part of the app code, but am hoping that if I can sort this one out that it will help me to understand the other.
Hoping that this is not off-topic or too specific, and that someone might be able to help.
Cheers
Jen
{<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<> ERROR OCCURS HERE
// INITIALIZE SKILLZ HERE
// 940 is the game ID that was given to us by the Skillz Developer Portal.
// SkillzSandbox specifies that we will use the sandbox server since we
// are still developing the game.
// SkillzProduction specifies that we will use the production server since
// the game is ready for release to the AppStore
[[Skillz skillzInstance] skillzInitForGameId:#"940"
environment:SkillzSandbox];
return YES;
}
This block is outside of - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
Perhaps you forgot a declaration or added an extraneous } somewhere

Local Notification is displayed without didFinishLaunchingWithOptions:

I have quite an understanding issue with displaying local notifications.
As far as I was reading in other threads, one has first create and schedule the local notification with the application.
For displaying that notification, one has to use the delegates didFinishLaunchingWithOptions: (if app is in background operation) and didReceiveLocalNotification: (if app is in foreground).
Now even though I did NOT change the didFinishLaunchingWithOptions: method, the notification is already getting viewed when my app is in the background.
It wouldn't be that big of a problem if the didFinishLaunchingWithOptions: would at least be used when I specify it at all. But it doesn't.
So my problem is, that even though I haven't used the didFinishLaunchingWithOptions: method, the notification is getting displayed. When the user clicks on the notification, the app gets to foreground and the didReceiveLocalNotification: method is triggered and the notification is displayed again.
What I originally wanted to do is to cancelAllLocalNotifications on execution of didFinishLaunchingWithOptions:, but since it is not getting executed, I'm kinda stuck here.
Ok, there might be a workaround with applicationWillEnterForeground: but honestly, I'd like to understand, why the notification is getting displayed even without having specified that in didFinishLaunchingWithOptions:.
All of your help is really appreaciated!! Thanks!!
//
// myNotificationsClass.m
//
#import "myNotificationsClass.h"
#implementation myNotificationsClass
//Sets up a Local Notification with Message, TimeFromNow, BadgeNumber and UserInfo
//no Class Instance for calling this method needed!!
+ (void)setupLocalNotificationsWithMessage: (NSString *) message andTimeFromNow: (NSTimeInterval) seconds andAlertAction: (NSString *) alertAction andBadgeNumber: (NSInteger) badgeNumber andUserInfo: (NSDictionary *) infoDict {
//[[UIApplication sharedApplication] cancelAllLocalNotifications];
UILocalNotification *localNotification = [[UILocalNotification alloc] init];
// create date/time information
localNotification.fireDate = [NSDate dateWithTimeIntervalSinceNow:seconds];
localNotification.timeZone = [NSTimeZone defaultTimeZone];
//setup Appearence and Message
localNotification.alertBody = message; //#"Time to get up!";
localNotification.alertAction = alertAction;
localNotification.soundName = UILocalNotificationDefaultSoundName;
localNotification.applicationIconBadgeNumber = badgeNumber;
localNotification.userInfo = infoDict;
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
}
#end
//overwrites the viewWillAppear: Method from the primary Class to display a Test Notification
#implementation UIViewController (localNotification)
- (void)viewWillAppear:(BOOL)animated {
[myNotificationsClass setupLocalNotificationsWithMessage:#"First Test after 2 Seconds" andTimeFromNow:2 andAlertAction:#"GoTo iSnah" andBadgeNumber:7 andUserInfo:nil];
}
#end
//receive Local Notifications even if the App is in Foreground
//overwrites the primery method
#implementation UIResponder (localNotificationForeground)
- (void)application:(UIApplication *)application didReceiveLocalNotification:(UILocalNotification *)notification {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:[[[NSBundle mainBundle] infoDictionary] objectForKey:#"CFBundleName"]
message:notification.alertBody
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alertView show];
//reset Badge
application.applicationIconBadgeNumber = 0;
}
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
//Because I don't want the Notification to be displayed twice
[[UIApplication sharedApplication] cancelAllLocalNotifications];
UILocalNotification *notification = [launchOptions objectForKey:UIApplicationLaunchOptionsLocalNotificationKey];
if (notification) {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:[[[NSBundle mainBundle] infoDictionary] objectForKey:#"CFBundleName"]
message:notification.alertBody
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alertView show];
//reset Badge
application.applicationIconBadgeNumber = 0;
}
return YES;
}
#end
You are very close. If the app is running in the background, the user responding to the notification will not cause the app to "launch". "Launch" means the app is not running at all (foreground or background). So only put code in there that you want to execute when the app launches or starts as a result of the user responding to your local notification.
So what you need for your didReceiveLocalNotification method is something to check the application state to see if it was in the foreground or in the background when the user responded to the local notification.
You might do something like the following to differentiate your logic between foreground and background:
- (void)application:(UIApplication *)application didReceiveLocalNotification: (UILocalNotification *)notification
if ( application.applicationState == UIApplicationStateActive ){
NSLog(#"Was already in the foreground");
}
else{
NSLog(#"Was in the background");
}
}
Minor point, however. Documentation states that the didReceiveLocalNotification method is invoked after application:didFinishLaunchingWithOptions: (if that method is implemented). So if you have both methods implemented you'll need to make sure the combination of the 2 work correctly in a "launch" situation.

Problems creating a new Document-based Core Data project

I am trying to create a Core Data-based Mac OS X application. I've used Core Data on iOS, but things are a little different with AppKit. I am also using Xcode 4.1 on 10.7, and I know some Core Data things have changed on Lion. The only up-to-date reference from Apple is a recent WWDC session, which promises/suggests updated references on Apple.com, but most of the Core Data stuff is very out of date.
To start, I used Xcode to generate a Document-based project using Core Data. I hollowed out some of the Main Menu bits (deleted some menu items) and got rid of the NSWindow object inside Main Menu.xib, creating my own MyDocument.xib with a NSWindow (and sub-views) instead.
Questions:
I assume that a Persistent Store Controller always needs a persistent store, so if a new document is created, I'm just adding an in-memory store. When opening up from disk, I create it using the SQLite store. Is that correct?
I (ostensibly) do a store migration in writeToURL:ofType:error: if the url is different. But if I set a breakpoint in writeToURL:ofType:error:, and then hit Save, my breakpoint is not hit. Why not?
If I choose "Revert to Saved", the application crashes -- hard. I have to force-quit it (and quit Xcode). Why?
(Very) old video tutorials suggest a way to easily bind data to a UIView like a table. Following along with Xcode is impossible since so much has changed. Any newer references out there for that?
Code:
MyDocument.h:
#import <Cocoa/Cocoa.h>
#interface MyDocument : NSPersistentDocument
{
#private
NSManagedObjectModel* mom;
NSPersistentStoreCoordinator* psc;
NSManagedObjectContext* moc;
}
#end
MyDocument.m:
#import "MyDocument.h"
#interface MyDocument ()
- (void)setUpManagedObjectModel;
- (void)setUpPersistentStoreCoordinator;
- (void)setUpManagedObjectContext;
- (BOOL)addPersistentStore:(NSString*)type url:(NSURL*)url error:(NSError**)outError;
#end
#implementation MyDocument
- (void)dealloc
{
[moc release];
[psc release];
[mom release];
[super dealloc];
}
- (id)init
{
self = [super init];
if (self)
{
[self setUpManagedObjectModel];
[self setUpPersistentStoreCoordinator];
[self setUpManagedObjectContext];
}
return self;
}
- (id)initWithType:(NSString *)typeName error:(NSError **)outError
{
self = [super initWithType:typeName error:outError];
if (self)
{
[self addPersistentStore:NSInMemoryStoreType url:nil error:outError];
}
return self;
}
- (BOOL)readFromURL:(NSURL *)url ofType:(NSString *)typeName error:(NSError **)outError
{
return [self addPersistentStore:NSSQLiteStoreType url:url error:outError];
}
- (BOOL)writeToURL:(NSURL *)url ofType:(NSString *)typeName error:(NSError **)outError
{
NSPersistentStore* currentStore = [[psc persistentStores] lastObject];
if (![[currentStore URL] isEqual:url])
{
NSPersistentStore* newStore = [psc migratePersistentStore:currentStore
toURL:url
options:nil
withType:typeName
error:outError];
if (!newStore)
{
return NO;
}
}
return [moc save:outError];
}
- (NSString *)windowNibName
{
// Override returning the nib file name of the document
// If you need to use a subclass of NSWindowController or if your document supports multiple NSWindowControllers, you should remove this method and override -makeWindowControllers instead.
return #"MyDocument";
}
- (void)windowControllerDidLoadNib:(NSWindowController *)aController
{
[super windowControllerDidLoadNib:aController];
// Add any code here that needs to be executed once the windowController has loaded the document's window.
}
+ (BOOL)autosavesInPlace
{
return YES;
}
- (BOOL)validateMenuItem:(NSMenuItem *)item
{
NSLog(#"menu item: %#", [item title]);
return YES;
}
#pragma mark Private Methods
- (void)setUpManagedObjectModel
{
NSURL* modelUrl = [[NSBundle mainBundle] URLForResource:#"MyDatabase" withExtension:#"momd"];
mom = [[NSManagedObjectModel alloc] initWithContentsOfURL:modelUrl];
}
- (void)setUpPersistentStoreCoordinator
{
psc = [[NSPersistentStoreCoordinator alloc] initWithManagedObjectModel:mom];
}
- (void)setUpManagedObjectContext
{
moc = [[NSManagedObjectContext alloc] init];
[moc setPersistentStoreCoordinator:psc];
}
- (BOOL)addPersistentStore:(NSString*)type url:(NSURL*)url error:(NSError**)outError
{
NSDictionary* options = [NSDictionary dictionaryWithObjectsAndKeys:
[NSNumber numberWithBool:YES], NSMigratePersistentStoresAutomaticallyOption,
[NSNumber numberWithBool:YES], NSInferMappingModelAutomaticallyOption, nil];
NSPersistentStore* ps = [psc addPersistentStoreWithType:type
configuration:nil
URL:url
options:options
error:outError];
return (ps != nil);
}
#end
If you're using NSPersistentDocument, by default all of this is handled for you, you don't need to do any Core Data setup. NSPersistentDocument will automatically handle creating, saving and migrating the persistent store.
If you want to control how the persistent store is configured, just override
-configurePersistentStoreCoordinatorForURL:ofType:modelConfiguration:storeOptions:error:
Then you can just ask the document for its managedObjectContext when you need it and code away.
I highly recommend you read Apple's NSPersistentDocument tutorial for more detail.

Resources