I have an ios app which loads a webpage to show data. I use push notifications for receiving news and I want that depending of which push is received, goes to one page or other (sections inside the same page).
In the text received in push notification, I add a word before the text, something like:
page1 - text
page2 - text2
page3 - text3
...
In android I take the first word and add to the webpage url: www.page.com/ + pageAdded
In iOs I think I haveto add this code to didFinishLaunchWithOptions function. But I don't know where I should add the new code for passing the arguments. I add my function so you can tell me where to put it.
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
KeychainItemWrapper *keychainItem = [[KeychainItemWrapper alloc] initWithIdentifier:#"CityInfoPush" accessGroup:nil];
udid = [keychainItem objectForKey:(__bridge id)kSecValueData];
//if udid is empty , that means we need to generate one and save it on KeyChain
if([udid length] == 0){
NSLog(#"No CFUUID found. Creating one...");
//creating CFUUID
CFUUIDRef cfuuid = CFUUIDCreate(kCFAllocatorDefault);
NSString *cfuuidString = (NSString*)CFBridgingRelease(CFUUIDCreateString(kCFAllocatorDefault, cfuuid));
NSLog(#"Device CFUUID created is : %#" , cfuuidString);
//saving CFUUID on KeyChain
[keychainItem setObject: cfuuidString forKey:(__bridge id)kSecValueData];
//retrieving CFUUID from KeyChain and passing it to udid String.
udid = [keychainItem objectForKey:(__bridge id)kSecValueData];
}
//For reseting the keyChain (testing)
//[keychainItem resetKeychainItem];
NSLog(#"Password Saved in KeyChain is: %#" , udid);
self.window = [[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] bounds]];
// Override point for customization after application launch.
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone) {
self.viewController = [[ViewController alloc] initWithNibName:#"ViewController_iPhone" bundle:nil];
} else {
self.viewController = [[ViewController alloc] initWithNibName:#"ViewController_iPad" bundle:nil];
}
self.window.rootViewController = self.viewController;
[self.window makeKeyAndVisible];
// Let the device know we want to receive push notifications
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:
(UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert)];
return YES;
}
I add the message to the payload for sending it to the apple server like this:
// Create the payload body
$body['aps'] = array(
'alert' => $message,
'sound' => 'default'
);
// Encode the payload as JSON
$payload = json_encode($body);
I am trying to get the alert text (message) in ios like this, but always fails and crash the app:
NSString *text=[[lastNotif valueForKeyPath:#"aps"][0] objectForKey:#"alert"];
First, you can check if you app has been launched with a puch notification in your application:
didFinishLaunchingWithOptions: function with :
if ([launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey])
What I do is save this notification in the userDefaults :
[[NSUserDefaults standardUserDefaults] setObject:[launchOptions objectForKey:UIApplicationLaunchOptionsRemoteNotificationKey] forKey:#"notificationReceived"];
[[NSUserDefaults standardUserDefaults] synchronize];
Then in the viewDidLoad where you want to catch your push notification, check if you have one in memory :
NSDictionary *lastNotif = [[NSUserDefaults standardUserDefaults] objectForKey:#"notificationReceived"];
if (lastNotif != nil)
{
//handle what you want to do with your notification
//now don't forget to clean userDefaults
[[NSUserDefaults standardUserDefaults] removeObjectForKey:#"notificationReceived"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
Related
Is the a way of knowing if the user declined the push notifications permissions?
I know that didRegisterForRemoteNotificationsWithDeviceToken will be called in case the user allowed push notifications - but what will be called if he didn't?
A simple method to check whether notification is enabled in app or not.
-(BOOL)checkNotificationEnabled
{
if ([[[UIDevice currentDevice] systemVersion] floatValue] < 8.0) {
UIRemoteNotificationType types = [[UIApplication sharedApplication] enabledRemoteNotificationTypes];
if (types == UIRemoteNotificationTypeNone)
{
return FALSE; //Notification not enabled
}
else
{
return TRUE;//Notification is enabled
}
}
else // for iOS 8 devices checking will be different
{
return [[UIApplication sharedApplication] isRegisteredForRemoteNotifications];
// if return TRUE then Notification is enabled
// if return False then Notification is not enabled
}
}
According to the UIApplicationDelegate Protocol Reference, application(_:didFailToRegisterForRemoteNotificationsWithError:) is called if an error occurs during the registration process.
After you call the registerForRemoteNotifications method of the UIApplication object, the app calls this method when there is an error in the registration process.
For more information about how to implement remote notifications in your app, see Local and Remote Notification Programming Guide.
You can implement in following way. Suppose that if user allows notification then you can store device token into user default. Now, next time you can check with same user default weather user allowed or not.
- (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken
{
NSString *aStrToken = [[deviceToken description] stringByTrimmingCharactersInSet: [NSCharacterSet characterSetWithCharactersInString:#"<>"]];
aStrToken = [aStrToken stringByReplacingOccurrencesOfString:#" " withString:#""];
NSString *aStrStoredToken = [_appDelegate validateString:[[NSUserDefaults standardUserDefaults]objectForKey:#"deviceID"] :#""];
if([aStrStoredToken length]==0) {
[[NSNotificationCenter defaultCenter] postNotificationName:#"getDeviceToken" object:nil];
[self setApplicationBadgeNumber:0];
[[NSUserDefaults standardUserDefaults]setObject:aStrToken forKey:#"deviceID"];
[[NSUserDefaults standardUserDefaults]synchronize];
if(![[NSUserDefaults standardUserDefaults]objectForKey:#"setDeviceID"]) {
[[NSUserDefaults standardUserDefaults]setObject:#"0" forKey:#"setDeviceID"];
[[NSUserDefaults standardUserDefaults]synchronize];
} else {
[[NSUserDefaults standardUserDefaults]setObject:#"1" forKey:#"setDeviceID"];
[[NSUserDefaults standardUserDefaults]synchronize];
}
}
}
In my application i want to display the remote notification in UILabel. So I'm trying pass the notification message form the "Appdelegate" to my storyboard its not working please tell me how to resolve this one.
My "Appdelegate" code:
-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo{
NSString *messag = [[userInfo description] stringByTrimmingCharactersInSet: [NSCharacterSet characterSetWithCharactersInString:#"<>"]];
[[NSUserDefaults standardUserDefaults] setObject: messag forKey:#"message"];
[[NSUserDefaults standardUserDefaults]synchronize];
NSLog (#"message %#", [[NSUserDefaults standardUserDefaults] objectForKey:#"message"]);
UIStoryboard *mainstoryboard = [UIStoryboard storyboardWithName:#"Main" bundle:nil];
personalnottypoliticalViewController *ringingVC = [mainstoryboard instantiateViewControllerWithIdentifier:#"notifymess"];
[self.window setRootViewController: ringingVC];
}
When i print my data its coming like:
message {
aps = {
alert = home;
badge = 3;
sound = "";
};
}
in my viewcontroller i have used like this.
self.message.text = [[NSUserDefaults standardUserDefaults] objectForKey:#"message"];
NSLog (#"message %#", [[NSUserDefaults standardUserDefaults] objectForKey:#"message"]);
In the above data i need only the "Alert" value how to get the alert value and store into the data please tell me is that possible to do and how to achieve this one. I'm stack here for long not please give some ideas to achieve this one.
Thanks.
The description method creates a string representation of an object for logging purposes, but you wouldn't use it in that way to access the contents of the object.
From the Apple documentation -
The userInfo dictionary contains the aps key whose value is another
dictionary. Although you should not need the information in the aps
dictionary, you can retrieve its contents using the following keys:
alert—The value is either a string for the alert message or a
dictionary with two keys: body and show-view. The value of the body
key is a string containing the alert message and the value of the
show-view key is a Boolean. If the value of the show-view key is
false, the alert’s View button is not shown. The default is to show
the View button which, if the user taps it, launches the app.
badge —A
number indicating the quantity of data items to download from the
provider. This number is to be displayed on the app icon. The absence
of a badge property indicates that any number currently badging the
icon should be removed.
sound —The name of a sound file in the app
bundle to play as an alert sound. If “default” is specified, the
default sound should be played.
So, you can use the code from this answer - How to handle push notifications if the application is already running?
NSDictionary *aps=(NSDictionary *)[userInfo objectForKey:#"aps"];
NSString *message;
id alert = [aps objectForKey:#"alert"];
if ([alert isKindOfClass:[NSString class]]) {
message = alert;
} else if ([alert isKindOfClass:[NSDictionary class]]) {
message = [alert objectForKey:#"body"];
}
Hi try using this code
NSDictionary *dict=userInfo;
NSString *messag=[[[dict objectForKey:#"message"]objectForKey:#"aps"]objectForKey:#"alert"];
in my iOS App i have an UIWebView so how i can open a specified URL in the UIWebView, with a push notification?
If somebody open the App with the notification, i want to show a specified Website in
the UIWebView.
Can I bind the URL (in background) with the push notification?
Thank you.
According to Apple...
If the app is running and receives a remote notification, the app
calls this method to process the notification. Your implementation of
this method should use the notification to take an appropriate course
of action. ... If the app is not running when a push notification
arrives, the method launches the app and provides the appropriate
information in the launch options dictionary. The app does not call
this method to handle that push notification. Instead, your
implementation of the application:willFinishLaunchingWithOptions: or
application:didFinishLaunchingWithOptions: method needs to get the
push notification payload data and respond appropriately.
So, there are three possible scenarios:
1) App is in foreground: you will have full control, just implement didReceiveNotification and do whatever you want.
2) App is running, but in background: the action won't be triggered until the user actually open your app using the received notification.
3) The app is not running: in this case you should implement didFinishLaunchingWithOptions in order to get the additional info and perform the task.
So the code should look like this for didFinishLaunchingWithOptions
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
NSDictionary *userInfo = [launchOptions valueForKey:#"UIApplicationLaunchOptionsRemoteNotificationKey"];
NSDictionary *apsInfo = [userInfo objectForKey:#"aps"];
if(apsInfo) {
// Get the URL or any other data
}
}
And this is an approximation for didReceiveNotification
/**
* Remote Notification Received while application was open.
*/
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo {
#if !TARGET_IPHONE_SIMULATOR
UIApplicationState state = [application applicationState];
if (state == UIApplicationStateActive)
{
NSString *message = nil;
id aps = [userInfo objectForKey:#"aps"];
if ([aps isKindOfClass:[NSDictionary class]]) {
message = [aps objectForKey:#"alert"];
}
if (message) {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Notificación"
message:message
delegate:self
cancelButtonTitle:#"Aceptar"
otherButtonTitles:nil, nil];
[alertView show];
}
}
// Aditional data
NSString *url = [userInfo objectForKey:#"url"];
NSLog(#"Received Push URL: %#", url);
if(url!=nil)
{
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:url]];
}
NSLog(#"remote notification: %#",[userInfo description]);
NSDictionary *apsInfo = [userInfo objectForKey:#"aps"];
NSString *alert = [apsInfo objectForKey:#"alert"];
NSLog(#"Received Push Alert: %#", alert);
NSString *sound = [apsInfo objectForKey:#"sound"];
NSLog(#"Received Push Sound: %#", sound);
AudioServicesPlaySystemSound(kSystemSoundID_Vibrate);
NSString *badge = [apsInfo objectForKey:#"badge"];
NSLog(#"Received Push Badge: %#", badge);
application.applicationIconBadgeNumber = [[apsInfo objectForKey:#"badge"] integerValue];
#endif
}
I have this peace of code to get device id :
-(void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
NSString *devicetokenRN = [NSString stringWithFormat:#"%#", deviceToken];
devicetokenRN = [devicetokenRN stringByReplacingOccurrencesOfString:#" " withString:#""];
devicetokenRN = [devicetokenRN stringByReplacingOccurrencesOfString:#"<" withString:#""];
devicetokenRN = [devicetokenRN stringByReplacingOccurrencesOfString:#">" withString:#""];
// send device token for APNS without spaces or <>
DeviceID *DeviceIdentifier = [[DeviceID alloc ] init];
// sending it to the Device.h
[DeviceIdentifier setDeviceID:devicetokenRN];
}
I need to retrieve this DeviceID as the first thing on my ViewController. As it doesn't do this function before it runs the view controller, because if I use NSlog in the ViewController it is NULL as it hasn't run this first. I need to get some variable like this first before it runs the UITableView. Any ideas would be brilliant thanks.
In AppDelegate, you can save the deviceToken value in NSUserDefaults like
put these line in didRegisterForRemoteNotificationsWithDeviceToken method in appdelegate.m like this
-(void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
[[NSUserDefaults standardUserDefaults] setObject:deviceToken forKey:#"DeviceToken"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
also put this line of code in your view Controller.m and In ViewDidLoad method
and Retrieve that value.
[[NSUserDefaults standardUserDefaults] objectForKey:#"DeviceToken"];
You can get and store your deviceId in NSUserDefaults
-(void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
NSUUID *identifierForVendor = [[UIDevice currentDevice] identifierForVendor];
NSString *deviceId = [identifierForVendor UUIDString];
NSUserDefaults *userRegistrarionDetails = [NSUserDefaults standardUserDefaults];
[userRegistrarionDetails setObject:deviceId forKey:#"deviceID"];
[userRegistrarionDetails synchronize];
}
And you can get anywhere in your whole project by
NSUserDefaults *userRegistrarionDetails = [NSUserDefaults standardUserDefaults];
NSString * deviceId = [userRegistrarionDetails objectForKey:#"deviceID"];
NSLog(#"%#", deviceId);
My published application makes use of the CFUUID and SSKeychain in order to identify the device (and to keep that ID unchanged even if the app is uninstalled and reinstalled)
I save those device ID in the server, and I recently noticed that some users have several of those IDs for the same real device. The only explanation I see is that the ID is not being saved or loaded from the Keychain and thus the device generates a new one. The strange thing is that it works fine on some other devices running the same iOS version.
Any ideas on what could be going on?
This is my related code in (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
NSString* identifier = #"appName";
NSString* serviceName = #"com.company.appName";
NSString *retrieveuuid = [SSKeychain passwordForService:serviceName account:identifier];
if (retrieveuuid == nil) {
CFUUIDRef theUUID = CFUUIDCreate(NULL);
CFStringRef string = CFUUIDCreateString(NULL, theUUID);
CFRelease(theUUID);
NSString *uuid = (NSString*) string;
[SSKeychain setPassword:uuid forService:serviceName account:identifier];
}
EDIT: My guess is that retrieveuuid == nil is not working as expected for some reason. Later in the app I register for push notifications and send the push token to the server together with this CFUUID that I read with the same exact line [SSKeychain passwordForService:serviceName account:identifier] but when it is sent to the server it is not nil (So I can see several CFUUIDs with the same push token).
EDIT 2 to attach more actual code.
AppDelegate.m
NSString* identifier = #"appName";
NSString* serviceName = #"com.company.appName";
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
//Creating UUID
NSString *retrieveuuid = [AppDelegate getDeviceId];
if (retrieveuuid == nil) {
CFUUIDRef theUUID = CFUUIDCreate(NULL);
CFStringRef string = CFUUIDCreateString(NULL, theUUID);
CFRelease(theUUID);
NSString *uuid = (NSString*) string;
[SSKeychain setPassword:uuid forService:serviceName account:identifier];
}
[[UIApplication sharedApplication] registerForRemoteNotificationTypes:
(UIRemoteNotificationTypeBadge | UIRemoteNotificationTypeSound | UIRemoteNotificationTypeAlert)];
}
+ (NSString*) getDeviceId {
return [SSKeychain passwordForService:serviceName account:identifier];
}
- (void)application:(UIApplication*)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData*)deviceToken
{
NSString *newToken = [deviceToken description];
newToken = [newToken stringByTrimmingCharactersInSet:[NSCharacterSet characterSetWithCharactersInString:#"<>"]];
newToken = [newToken stringByReplacingOccurrencesOfString:#" " withString:#""];
_deviceToken = [[NSString alloc] initWithString:newToken];
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
NSString *user = [prefs objectForKey:#"appNameUsername"];
if(user && ![user isEqualToString:#""]){
RestClient *rest = [[RestClient alloc] init];
rest.delegate = self;
rest.tag = 2;
[rest updateToken:newToken ForUsername:user];
[rest release];
}
}
RestClient.m
-(void) updateToken:(NSString *)token ForUsername:(NSString *)userName{
NSArray* info = [NSArray arrayWithObject: [NSMutableDictionary dictionaryWithObjectsAndKeys:
userName, #"Username",
token, #"TokenNo",
[[UIDevice currentDevice].model hasPrefix:#"iPad"] ? #"iPad" : #"iPhone", #"Device",
[UIDevice currentDevice].systemVersion, #"OSVersion",
[AppDelegate getDeviceId], #"DeviceID",
#"updateToken", #"CMD",
nil]];
[self doAction:info];
}
The doAction method just sends the data to the server and then callback the delegate, that part works fine. I can see on the server the logs of receiving this command:
"JSONCMD":"[
{ "TokenNo" : "f0d3aa21758350333b7e6315c38_EDIT_257c1838f49c43049f8380ec1ff63",
"AppVersion" : "1.0.4",
"Username" : "user#server.com",
"CMD" : "updateToken",
"OSVersion" : "7.0.4",
"DeviceID" : "9B794E11-6EF7-470C-B319-5A9FCCDAFD2B",
"Device" : "iPhone"
}
]
I see 2 possible candidates causing the strange behaviour, the NSStrings in the controller body and the static getDevice method. However, I don't see how this could work in many devices but fail in others.
I've had the same problem and I've found the solution. The Problem occurs with devices where user has the passcode set to unlock the phone. On iOS7 some code can run in the background (push notifications, background fetch) and if you save something in the keychain with standard accessibility type, you cannot read it when the device is locked (and the app is running in the background). Try to set this:
[SSKeychain setAccessibilityType:kSecAttrAccessibleAlways];
before you read/write the keychain.
Hope it helps.
NSString *retrieveuuid = [SSKeychain passwordForService:serviceName
account:identifier];
The retrieveuuid depends with the service, witch is always the same thing, but it depends also with the account. I think your problem is that one user can have multiple accounts in the same device, then your code are generating a UUID for each account.