Okay so I have an app where I want to implement push notifications. The problem is that I want to use the users username to help identify which device they are sending the notifications too. But when it is called in the delegate sometimes the users are not logged in yet so the app crashes. The code is in this method
- (void)applicationDidBecomeActive:(UIApplication *)application
{
PFInstallation *currentInstallation = [PFInstallation currentInstallation];
currentInstallation[#"installationUser"] = [[PFUser currentUser]username];
// here we add a column to the installation table and store the current user’s ID
// this way we can target specific users later
// while we’re at it, this is a good place to reset our app’s badge count
// you have to do this locally as well as on the parse server by updating
// the PFInstallation object
if (currentInstallation.badge != 0) {
currentInstallation.badge = 0;
[currentInstallation saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (error) {
// Handle error here with an alert…
}
else {
// only update locally if the remote update succeeded so they always match
[[UIApplication sharedApplication] setApplicationIconBadgeNumber:0];
NSLog(#"updated badge");
}
}];
}
}
So I need this NOT to be called if the user has not logged in yet but then I want it to be called every time. I am getting confused about how to do this. Let me know if you have any ideas, thanks!
You wouldn't normally call this in applicationDidBecomeActive I see you've read up on the docs a little bit but it's real easy:
if (![PFUser currentUser]) { // No user logged in : this is all you need because in applicationDidRegistreForRemoteNotificationsWithDeviceToken should have set the user or installation ID already
//Enter data
} else {
// User logged in
}
NOTE : This is best to be used in your main view controllers viewDidAppear method. Review their documentation for future guidance : https://parse.com/tutorials/login-and-signup-views
Related
I am using Dropbox and Google Drive integration in my iOS app. I can fetch files from both drives and view listing in tableview. However, when I delete the app on my iPhone without logout from these drives, it still shows logged in when I install new app. How to logout user when I delete the app or remove session?
For Dropbox i am using ObjectiveDropboxOfficial apiV2 and for Google Drive i am using GoogleAPIClientForREST, GTMSessionFetcher etc libraries.
My code:
[DBClientsManager setupWithAppKey:#"my-key"];
[DBClientsManager authorizeFromController:[UIApplication sharedApplication]
controller:self openURL:^(NSURL *url) {
[[UIApplication sharedApplication] openURL:url];
}];
//AppDelegate
if ([DBClientsManager handleRedirectURL:url])
{
if (DBClientsManager.authorizedClient || DBClientsManager.authorizedTeamClient) {
// NSLog(#"App linked successfully!");
// At this point you can start making API calls
NSNotification *notification = [NSNotification notificationWithName:#"DropboxLoggedIn" object:nil];
[[NSNotificationCenter defaultCenter] postNotification:notification];
}
return YES;
}
If these services are designed this way I assume they save credentials in keychain which persists data and your application is already logged in when reinstalled or keychain is anyhow transfered.
If this is not your desired effect I can only assume you will need to log out from these services manually. This means you will need to track these logins and logouts and then when the app starts simply log out from all services which have not been tracked as logged in by you.
It is an ugly thing to do but it is a solution:
When a service is logged in save a value in user defaults
- (void)serviceDidLogin:(ServiceType)type {
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:[self keyForServiceType: type]];
}
Then when it is logged out you need to clear it
- (void)serviceDidLogout:(ServiceType)type {
[[NSUserDefaults standardUserDefaults] removeObjectForKey:[self keyForServiceType: type]];
}
Then when app starts you need to log out from all of the services that you have no recording of being logged into:
- (void)logOutFromAllUnusedService {
for(int i=0; i<ServiceTypeCount; i++) {
ServiceType type = i;
if([[NSUserDefaults standardUserDefaults] boolForKey:[self keyForServiceType: type]] == NO) {
[self logoutFromService:type];
}
}
}
No matter how you do this but my situation assumes ServiceType is an enum like so:
typedef enum {
// Never assign values to enums
ServiceTypeDropbox,
ServiceTypeGoogleDrive,
ServiceTypeCount, // Always last of the valid
// Move deprecated services here
ServiceTypeDeprecated1 // TODO: remove at some point
} ServiceType;
I'm currently working on project that allows user to receive push notifications whenever there is something new on the user account. I'm using Parse as my push notifications service. I'm having no problem until recently our server starting to receive empty token device on every push notification registration, this problem is not always happening. So when I tried the app on my device it just run as it should but when my app tested on our client device , our server receive an empty token device for that client user. How can this happen? How can I fix this? And how is the best practice to get and set the device token?
Here is my code in appdelegate:
- (void) application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken{
const unsigned *tokenBytes = [deviceToken bytes];
NSString *token = [NSString stringWithFormat:#"%08x%08x%08x%08x%08x%08x%08x%08x",
ntohl(tokenBytes[0]), ntohl(tokenBytes[1]), ntohl(tokenBytes[2]),
ntohl(tokenBytes[3]), ntohl(tokenBytes[4]), ntohl(tokenBytes[5]),
ntohl(tokenBytes[6]), ntohl(tokenBytes[7])];
//function for saving device token to server
[[ASEngine defaultEngine] setCurrentDeviceToken:token];
if([[ASEngine defaultEngine] currentCredential] != nil) {
[[ASEngine defaultEngine] webStoreDeviceToken:token];
}
//save current instalation to parse
PFInstallation *currentInstallation = [PFInstallation currentInstallation];
[currentInstallation setDeviceTokenFromData:deviceToken];
[currentInstallation saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
NSLog(#"Error e current installation: %#", error);
}];
//save device token locally
[[NSUserDefaults standardUserDefaults] setObject:deviceToken forKey:#"deviceToken"];
}
An empty token wont ever be generated,
iOS provides didFailToRegisterForRemoteNotificationsWithError method which is being probably called in your case, please make sure to check that for any errors in token creation.
Sometimes it happens when your device is not connected to internet.
Make sure your device is connected to internet.
I have following scenario.
User A sends message to User B in foreground -- this is working
Send push notification to User B, when app is in background, from 'Messages' console -- this is working
I want to send notification alert to User B when app is in background. I read that this is done automatically by quickblox, but is not happening for me.
I have followed instructions on this link
I am using 'Starter' account in development mode. Do we need account with ' server side history' for this functionality?
Edit 1:
Clarification: I want to send 'automatic push notifications for offline user' and not notification from app. I am also sending 'save_to_history' flag as mentioned on the link.
for Sending push notification use below code
-(void)applicationDidEnterBackground:(UIApplication *)application
{
[self sendMessageNotification:#"Hello Push notification" :1234 ];
}
-(void)sendMessageNotification:(NSInteger)recipientID message:(NSString*)message
{
isSentPushNotification = YES;
//[self sendPushNotificationToUser:message ids:#"1" audioFileName:#"default"];
NSMutableDictionary *payload = [NSMutableDictionary dictionary];
NSMutableDictionary *aps = [NSMutableDictionary dictionary];
[aps setObject:#"default" forKey:QBMPushMessageSoundKey];
[aps setObject:message forKey:QBMPushMessageAlertKey];
[aps setObject:#"1" forKey:QBMPushMessageAlertLocArgsKey];
[payload setObject:aps forKey:QBMPushMessageApsKey];
QBMPushMessage *pushMessage = [[QBMPushMessage alloc] initWithPayload:payload];
// Send push to users with ids 292,300,1395
[QBRequest sendPush:pushMessage toUsers:[NSString stringWithFormat:#"%lu",(long)recipientID]successBlock:^(QBResponse *response, QBMEvent *event) {
NSLog(#"Successfully dilivered push notification");
} errorBlock:^(QBError *error) {
NSLog(#"Fail to diliver push notification %#",error);
}];
}
But you should first Subscribe User to receive Push Notifications
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken
{
// Register subscription with device token
[QBRequest registerSubscriptionForDeviceToken:deviceToken successBlock:^(QBResponse *response, NSArray *subscriptions) {
// Registration succeded
} errorBlock:^(QBError *error) {
// Handle error
}];
}
for more detail please have a look of this
Found the problem.
We have to logout from the the chat when the app enters background.
I think, this might be required to let server know that we are actually offline and it should send us push notification instead. (just a guess!!)
I think logout thing was mentioned in documentation as well but I didn't know that it was so important.
I recently released my iOS game to the App Store and was about to send my first Push Notification when I noticed that the # Recipients (= # Installations) is about 1/3 the # Users (on Parse dashboard).
I released using Parse 1.2.21 (and have subsequently upgraded to 1.4.1, but not released yet).
I am skeptical that 2/3 of my users have opted out of notifications.
note: I did not implement didFailToRegisterForRemoteNotificationsWithError (now added for next release).
The only theory I have is as follows:
When I released to App Store, I was unaware of the "Released to production" switch (released w/ NO).
A week later, I noticed this button and switched it to YES.
Yesterday, I tested push notifications and verified that it was sent to a good sampling of the installs.
THEORY: Before I enabled "Released to production" the Development APN was being used and thus failed.
Better ideas on why #Installations is 1/3 of #Users? :-)
Here's my code:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
...
// Parse: Enable auto-user.
[PFUser enableAutomaticUser];
[[PFUser currentUser] incrementKey:#"runCount"];
// Save the user to force a round trip w/ Parse (and obtain objectId if not already).
[[PFUser currentUser] saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (succeeded) {
NSLog(#"AppDelegate::application: Save PFUser succeeded: %#, objectId: %#", [PFUser currentUser], [PFUser currentUser].objectId);
} else {
// Log details of the save failure.
if ([error code] == kPFErrorObjectNotFound) {
// This user does not exist.
NSLog(#"AppDelegate::application: RARE CASE: The current PFUser does not exist on Parse! This is probably due to us deleting the user to force a reset or a mistake. Logging this user out... lazily creating new PFUser...");
[PFUser logOut];
} else {
// Other errors.
NSLog(#"AppDelegate::application: RARE CASE: Saving the PFUser currentUser FAILED! currentUser: %#, objectId: %#.... saving eventually in attempt to re-try...", [PFUser currentUser], [PFUser currentUser].objectId);
// Save eventually to ensure it gets saved.
[[PFUser currentUser] saveEventually];
}
NSString *codeString = [NSString stringWithFormat:#"Save PFUser (app init), code:%d", [error code]];
[PFAnalytics trackEvent:#"error" dimensions:#{ #"code": codeString }];
}
}];
...
// Parse: Register for push notifications
if ([application respondsToSelector:#selector(isRegisteredForRemoteNotifications)])
{
// iOS 8 Notifications
UIUserNotificationSettings *settings =
[UIUserNotificationSettings settingsForTypes:UIUserNotificationTypeAlert |
UIUserNotificationTypeBadge |
UIUserNotificationTypeSound
categories:nil];
[[UIApplication sharedApplication] registerUserNotificationSettings:settings];
[[UIApplication sharedApplication] registerForRemoteNotifications];
} else {
// iOS < 8 Notifications
[application registerForRemoteNotificationTypes: UIRemoteNotificationTypeBadge |
UIRemoteNotificationTypeAlert |
UIRemoteNotificationTypeSound ];
}
}
Successful:
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)newDeviceToken {
// Store the deviceToken in the current installation and save it to Parse.
PFInstallation *currentInstallation = [PFInstallation currentInstallation];
[currentInstallation setDeviceTokenFromData:newDeviceToken];
[currentInstallation saveInBackground];
}
Fail:
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error
{
// An error occured during register push notifs.
NSLog(#"ERROR:didFailToRegisterForRemoteNotificationsWithError: error: %d, %#", error.code, error.description );
NSString *codeString = [NSString stringWithFormat:#"Register Push Notifications, code:%d", [error code]];
[PFAnalytics trackEvent:#"error" dimensions:#{ #"code": codeString }];
}`
I went through the same situation and can speak to it directly. Yes, your 'Released to Production' problem is the most likely culprit.
It also takes time for users to opt in if they aren't using the app every day/week. When we added Push Notifications, our ratio of Installations:Users was around 70%. The problem was that it was only asking for users to add push after their next usage of the app, and some users might go days or weeks before they open the app again and get asked to register (and create their Installation object). Now that we have had the system up and running with pushes for 3 months, the ratio is over 95%; almost all users have opted in for push.
Just be patient and eventually your coverage will go up as users :)
All of my theories were wrong. :-)
I am now 99% certain that the 2/3 of users were indeed denying approval for push notifications because I was asking immediately during app init (i.e. before the new user had any loyalty or trust established with my app).
And, considering that you really only get ONE CHANCE to ask, this may be an important decision for your app.
After reading a lot of advice on best practices, the consensus appears to be:
Ask the user to approve push notifications at a time that makes sense to the user or after something good has happened. Ideally, you make a good case for why they want to approve. Remember that you only get ONE CHANCE (technically, they can change this setting, but is highly unlikely).
Consider using a two-phase modal: The first modal is a custom modal created by you and asks if they would approve push notifications (plus any motivation and trust you can establish). If they tap YES to your modal, THEN pop the Apple push notification request (ios8:registerUserNotificationSettings,
Several folks have shown data that the two-step modal increases conversion rates significantly.
This question already has answers here:
Programmatically Request Access to Contacts
(13 answers)
Closed 9 years ago.
I am importing all the native address book contacts into my app. Currently, the alertview to ask for permission to 'allow access to the Contacts' is done at the launch of the app.
How do I change it so permission is asked elsewhere in the app? At the click of a button, just like Instagram?
See this picture:
You can read the documentation HERE.
Here is some code to get you started:
//First of all import the AddRessBookUI
#import <AddressBookUI/AddressBookUI.h>
// Request to authorise the app to use addressbook
ABAddressBookRef addressBookRef = ABAddressBookCreateWithOptions(nil, nil);
if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusNotDetermined) {
ABAddressBookRequestAccessWithCompletion(addressBookRef, ^(bool granted, CFErrorRef error) {
if (granted) {
// If the app is authorized to access the first time then add the contact
[self _addContactToAddressBook];
} else {
// Show an alert here if user denies access telling that the contact cannot be added because you didn't allow it to access the contacts
}
});
}
else if (ABAddressBookGetAuthorizationStatus() == kABAuthorizationStatusAuthorized) {
// If the user user has earlier provided the access, then add the contact
[self _addContactToAddressBook];
}
else {
// If the user user has NOT earlier provided the access, create an alert to tell the user to go to Settings app and allow access
}
Here are another couple of tutorials that you might like to see that you can find HERE and HERE.
Hope this helps!
UPDATE: You have to use didFinishLaunchingWithOptions to detect the first time your app launches:
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
if ([[NSUserDefaults standardUserDefaults] boolForKey:#"HasLaunchedOnce"])
{
// The app has already launched once
}
else
{
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"HasLaunchedOnce"];
[[NSUserDefaults standardUserDefaults] synchronize];
// This is the first time the app is launched
}
}
App asking for permission when you try retrive contacts.
Just call retriving after button touch, not after launching.