i just released my app and realized that NSUbiquitousKeyValueStoreDidChangeExternallyNotification is not fired.
I need it that it is fired the first time a user open the app to register him (and check if it was already a user before).
The problem is that, if the notification isn't fired, then my app will be stuck.
I'm using key-value and it's properly setted in Xcode
Now, with my iphone, with developer profile, everything works great.
With a distribution profile, it does not.
I already have a production profile with icloud enabled (and app id with icloud enabled too).
In my app, this two parameters,even in the app store version, are fine:
_currentiCloudToken = [[NSFileManager defaultManager] ubiquityIdentityToken];
_myUbiquityContainer = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier: nil];
Here's my code:
- (id)initWithCloudStore:(NSUbiquitousKeyValueStore *) store {
if ((self = [super init])) {
_iCloudStore = store;
_currentiCloudToken = [[NSFileManager defaultManager] ubiquityIdentityToken];
_myUbiquityContainer = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier: nil];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(storeDidChange:)
name:NSUbiquitousKeyValueStoreDidChangeExternallyNotification
object:_iCloudStore];
}
return self;
}
-(BOOL) iCloudIsEnabled {
if(_currentiCloudToken && _myUbiquityContainer != nil) return TRUE;
else return FALSE;
}
-(void) sync {
if([_iCloudStore synchronize]) NSLog(#"iCloud Sync OK");
}
-(void) storeDidChange:(NSNotification*)notification {
NSString *iCloudUUID = [_iCloudStore objectForKey:userUUIDKey];
if(iCloudUUID != nil) {
NSLog(#"iCloud UUID present with value %#",iCloudUUID);
//[[iCloudHelper sharedInstance] removeObjectForKey:userUUIDKey];
// sostituisco uuid locale con quello di icloud
[[NSUserDefaults standardUserDefaults] setObject:iCloudUUID forKey:userUUIDKey];
[[NSUserDefaults standardUserDefaults] synchronize];
} else {
// salvo uuid in iCloud
NSLog(#"UUID not present on iCloud");
[_iCloudStore setObject:[self createNewUUID] forKey:userUUIDKey];
}
int reason = [[[notification userInfo] objectForKey:NSUbiquitousKeyValueStoreChangeReasonKey] intValue];
if(reason == NSUbiquitousKeyValueStoreInitialSyncChange) {
NSLog(#"This was the initial sync");
}
[[NSNotificationCenter defaultCenter] postNotificationName:#"iCloudDone" object:nil];
}
What I'm missing?
Related
I just uploaded my Firebase pods and got this warning message:
'isDirectChannelEstablished' is deprecated: FCM direct channel is
deprecated, please use APNs channel for downstream message delivery
Warning appears here:
Messaging.messaging().shouldEstablishDirectChannel = true
How can I remove this warning?
It sounds like you should just remove that line. According to the API documentation:
When set to YES, Firebase Messaging will automatically establish a
socket-based, direct channel to the FCM server. Enable this only if
you are sending upstream messages or receiving non-APNS, data-only
messages in foregrounded apps. Default is NO.
So, unless your app is doing either of the things described here, it's not necessary. According to the release notes:
Deprecated FCM direct channel messaging via
shouldEstablishDirectChannel. Instead, use APNs for downstream message
delivery. Add content_available key to your payload if you want to
continue use legacy APIs, but we strongly recommend HTTP v1 API as it
provides full APNs support. The deprecated API will be removed in
Firebase 7 (#4710).
If you use cordova-plugin-firebasex for data message notifications in ios, and not getting data message properly?
enter link description here
Change AppDelegate+FirebasePlugin.m file like below
#import "AppDelegate+FirebasePlugin.h"
#import "FirebasePlugin.h"
#import "Firebase.h"
#import <objc/runtime.h>
#import UserNotifications;
#import FirebaseFirestore;
// Implement UNUserNotificationCenterDelegate to receive display notification via APNS for devices running iOS 10 and above.
// Implement FIRMessagingDelegate to receive data message via FCM for devices running iOS 10 and above.
#interface AppDelegate () <UNUserNotificationCenterDelegate, FIRMessagingDelegate>
#end
#define kApplicationInBackgroundKey #"applicationInBackground"
#implementation AppDelegate (FirebasePlugin)
static AppDelegate* instance;
+ (AppDelegate*) instance {
return instance;
}
static NSDictionary* mutableUserInfo;
static FIRAuthStateDidChangeListenerHandle authStateChangeListener;
static bool authStateChangeListenerInitialized = false;
static bool shouldEstablishDirectChannel = false;
+ (void)load {
Method original = class_getInstanceMethod(self, #selector(application:didFinishLaunchingWithOptions:));
Method swizzled = class_getInstanceMethod(self, #selector(application:swizzledDidFinishLaunchingWithOptions:));
method_exchangeImplementations(original, swizzled);
}
- (void)setApplicationInBackground:(NSNumber *)applicationInBackground {
objc_setAssociatedObject(self, kApplicationInBackgroundKey, applicationInBackground, OBJC_ASSOCIATION_RETAIN_NONATOMIC);
}
- (NSNumber *)applicationInBackground {
return objc_getAssociatedObject(self, kApplicationInBackgroundKey);
}
- (BOOL)application:(UIApplication *)application swizzledDidFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
[self application:application swizzledDidFinishLaunchingWithOptions:launchOptions];
#try{
instance = self;
bool isFirebaseInitializedWithPlist = false;
if(![FIRApp defaultApp]) {
// get GoogleService-Info.plist file path
NSString *filePath = [[NSBundle mainBundle] pathForResource:#"GoogleService-Info" ofType:#"plist"];
// if file is successfully found, use it
if(filePath){
[FirebasePlugin.firebasePlugin _logMessage:#"GoogleService-Info.plist found, setup: [FIRApp configureWithOptions]"];
// create firebase configure options passing .plist as content
FIROptions *options = [[FIROptions alloc] initWithContentsOfFile:filePath];
// configure FIRApp with options
[FIRApp configureWithOptions:options];
isFirebaseInitializedWithPlist = true;
}else{
// no .plist found, try default App
[FirebasePlugin.firebasePlugin _logError:#"GoogleService-Info.plist NOT FOUND, setup: [FIRApp defaultApp]"];
[FIRApp configure];
}
}else{
// Firebase SDK has already been initialised:
// Assume that another call (probably from another plugin) did so with the plist
isFirebaseInitializedWithPlist = true;
}
shouldEstablishDirectChannel = [[[NSBundle mainBundle] objectForInfoDictionaryKey:#"shouldEstablishDirectChannel"] boolValue];
// Set FCM messaging delegate
[FIRMessaging messaging].delegate = self;
[FIRMessaging messaging].shouldEstablishDirectChannel = true;
// Setup Firestore
[FirebasePlugin setFirestore:[FIRFirestore firestore]];
// Setup Google SignIn
[GIDSignIn sharedInstance].clientID = [FIRApp defaultApp].options.clientID;
[GIDSignIn sharedInstance].delegate = self;
authStateChangeListener = [[FIRAuth auth] addAuthStateDidChangeListener:^(FIRAuth * _Nonnull auth, FIRUser * _Nullable user) {
#try {
if(!authStateChangeListenerInitialized){
authStateChangeListenerInitialized = true;
}else{
[FirebasePlugin.firebasePlugin executeGlobalJavascript:[NSString stringWithFormat:#"FirebasePlugin._onAuthStateChange(%#)", (user != nil ? #"true": #"false")]];
}
}#catch (NSException *exception) {
[FirebasePlugin.firebasePlugin handlePluginExceptionWithoutContext:exception];
}
}];
// Set NSNotificationCenter observer
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(tokenRefreshNotification:)
name:kFIRInstanceIDTokenRefreshNotification object:nil];
self.applicationInBackground = #(YES);
}#catch (NSException *exception) {
[FirebasePlugin.firebasePlugin handlePluginExceptionWithoutContext:exception];
}
return YES;
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
self.applicationInBackground = #(NO);
[FIRMessaging messaging].shouldEstablishDirectChannel = true;
[FirebasePlugin.firebasePlugin _logMessage:[NSString stringWithFormat:#"Enter foreground: FCM direct channel = %#", shouldEstablishDirectChannel ? #"true" : #"false"]];
}
- (void)applicationDidEnterBackground:(UIApplication *)application {
self.applicationInBackground = #(YES);
[FIRMessaging messaging].shouldEstablishDirectChannel = true;
[FirebasePlugin.firebasePlugin _logMessage:#"Enter background: FCM direct channel = false"];
}
# pragma mark - Google SignIn
- (void)signIn:(GIDSignIn *)signIn
didSignInForUser:(GIDGoogleUser *)user
withError:(NSError *)error {
#try{
CDVPluginResult* pluginResult;
if (error == nil) {
GIDAuthentication *authentication = user.authentication;
FIRAuthCredential *credential =
[FIRGoogleAuthProvider credentialWithIDToken:authentication.idToken
accessToken:authentication.accessToken];
NSNumber* key = [[FirebasePlugin firebasePlugin] saveAuthCredential:credential];
NSMutableDictionary* result = [[NSMutableDictionary alloc] init];
[result setValue:#"true" forKey:#"instantVerification"];
[result setValue:key forKey:#"id"];
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_OK messageAsDictionary:result];
} else {
pluginResult = [CDVPluginResult resultWithStatus:CDVCommandStatus_ERROR messageAsString:error.description];
}
if ([FirebasePlugin firebasePlugin].googleSignInCallbackId != nil) {
[[FirebasePlugin firebasePlugin].commandDelegate sendPluginResult:pluginResult callbackId:[FirebasePlugin firebasePlugin].googleSignInCallbackId];
}
}#catch (NSException *exception) {
[FirebasePlugin.firebasePlugin handlePluginExceptionWithoutContext:exception];
}
}
- (void)signIn:(GIDSignIn *)signIn
didDisconnectWithUser:(GIDGoogleUser *)user
withError:(NSError *)error {
NSString* msg = #"Google SignIn delegate: didDisconnectWithUser";
if(error != nil){
[FirebasePlugin.firebasePlugin _logError:[NSString stringWithFormat:#"%#: %#", msg, error]];
}else{
[FirebasePlugin.firebasePlugin _logMessage:msg];
}
}
# pragma mark - FIRMessagingDelegate
- (void)messaging:(FIRMessaging *)messaging didReceiveRegistrationToken:(NSString *)fcmToken {
[FirebasePlugin.firebasePlugin _logMessage:[NSString stringWithFormat:#"didReceiveRegistrationToken: %#", fcmToken]];
#try{
[FirebasePlugin.firebasePlugin sendToken:fcmToken];
}#catch (NSException *exception) {
[FirebasePlugin.firebasePlugin handlePluginExceptionWithoutContext:exception];
}
}
- (void)tokenRefreshNotification:(NSNotification *)notification {
// Note that this callback will be fired everytime a new token is generated, including the first
// time. So if you need to retrieve the token as soon as it is available this is where that
// should be done.
#try{
[[FIRInstanceID instanceID] instanceIDWithHandler:^(FIRInstanceIDResult * _Nullable result,
NSError * _Nullable error) {
#try{
if (error == nil) {
NSString *refreshedToken = result.token;
[FirebasePlugin.firebasePlugin _logMessage:[NSString stringWithFormat:#"tokenRefreshNotification: %#", refreshedToken]];
[FirebasePlugin.firebasePlugin sendToken:refreshedToken];
}else{
[FirebasePlugin.firebasePlugin _logError:[NSString stringWithFormat:#"tokenRefreshNotification: %#", error.description]];
}
}#catch (NSException *exception) {
[FirebasePlugin.firebasePlugin handlePluginExceptionWithoutContext:exception];
}
}];
}#catch (NSException *exception) {
[FirebasePlugin.firebasePlugin handlePluginExceptionWithoutContext:exception];
}
}
- (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken {
[FIRMessaging messaging].APNSToken = deviceToken;
[FirebasePlugin.firebasePlugin _logMessage:[NSString stringWithFormat:#"didRegisterForRemoteNotificationsWithDeviceToken: %#", deviceToken]];
[FirebasePlugin.firebasePlugin sendApnsToken:[FirebasePlugin.firebasePlugin hexadecimalStringFromData:deviceToken]];
// Set UNUserNotificationCenter delegate
[UNUserNotificationCenter currentNotificationCenter].delegate = self;
}
//Tells the app that a remote notification arrived that indicates there is data to be fetched.
// Called when a message arrives in the foreground and remote notifications permission has been granted
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler {
#try{
[[FIRMessaging messaging] appDidReceiveMessage:userInfo];
mutableUserInfo = [userInfo mutableCopy];
NSDictionary* aps = [mutableUserInfo objectForKey:#"aps"];
bool isContentAvailable = false;
if([aps objectForKey:#"alert"] != nil){
isContentAvailable = [[aps objectForKey:#"content-available"] isEqualToNumber:[NSNumber numberWithInt:1]];
[mutableUserInfo setValue:#"notification" forKey:#"messageType"];
NSString* tap;
if([self.applicationInBackground isEqual:[NSNumber numberWithBool:YES]] && !isContentAvailable){
tap = #"background";
}
[mutableUserInfo setValue:tap forKey:#"tap"];
}else{
[mutableUserInfo setValue:#"data" forKey:#"messageType"];
}
[FirebasePlugin.firebasePlugin _logMessage:[NSString stringWithFormat:#"didReceiveRemoteNotification: %#", mutableUserInfo]];
completionHandler(UIBackgroundFetchResultNewData);
if([self.applicationInBackground isEqual:[NSNumber numberWithBool:YES]] && isContentAvailable){
[FirebasePlugin.firebasePlugin _logError:#"didReceiveRemoteNotification: omitting foreground notification as content-available:1 so system notification will be shown"];
}else{
[self processMessageForForegroundNotification:mutableUserInfo];
}
if([self.applicationInBackground isEqual:[NSNumber numberWithBool:YES]] || !isContentAvailable){
[FirebasePlugin.firebasePlugin sendNotification:mutableUserInfo];
}
}#catch (NSException *exception) {
[FirebasePlugin.firebasePlugin handlePluginExceptionWithoutContext:exception];
}
}
// Receive data messages on iOS 10+ directly from FCM (bypassing APNs) when the app is in the foreground.
// Called when a data message is arrives in the foreground and remote notifications permission has been NOT been granted
- (void)messaging:(FIRMessaging *)messaging didReceiveMessage:(FIRMessagingRemoteMessage *)remoteMessage {
#try{
[FirebasePlugin.firebasePlugin _logMessage:[NSString stringWithFormat:#"didReceiveMessage: %#", remoteMessage.appData]];
NSDictionary* appData = [remoteMessage.appData mutableCopy];
[appData setValue:#"data" forKey:#"messageType"];
[self processMessageForForegroundNotification:appData];
// This will allow us to handle FCM data-only push messages even if the permission for push
// notifications is yet missing. This will only work when the app is in the foreground.
[FirebasePlugin.firebasePlugin sendNotification:appData];
}#catch (NSException *exception) {
[FirebasePlugin.firebasePlugin handlePluginExceptionWithoutContext:exception];
}
}
// Scans a message for keys which indicate a notification should be shown.
// If found, extracts relevant keys and uses then to display a local notification
-(void)processMessageForForegroundNotification:(NSDictionary*)messageData {
bool showForegroundNotification = [messageData objectForKey:#"notification_foreground"];
if(!showForegroundNotification){
return;
}
NSString* title = nil;
NSString* body = nil;
NSString* sound = nil;
NSNumber* badge = nil;
// Extract APNS notification keys
NSDictionary* aps = [messageData objectForKey:#"aps"];
if([aps objectForKey:#"alert"] != nil){
NSDictionary* alert = [aps objectForKey:#"alert"];
if([alert objectForKey:#"title"] != nil){
title = [alert objectForKey:#"title"];
}
if([alert objectForKey:#"body"] != nil){
body = [alert objectForKey:#"body"];
}
if([aps objectForKey:#"sound"] != nil){
sound = [aps objectForKey:#"sound"];
}
if([aps objectForKey:#"badge"] != nil){
badge = [aps objectForKey:#"badge"];
}
}
// Extract data notification keys
if([messageData objectForKey:#"notification_title"] != nil){
title = [messageData objectForKey:#"notification_title"];
}
if([messageData objectForKey:#"notification_body"] != nil){
body = [messageData objectForKey:#"notification_body"];
}
if([messageData objectForKey:#"notification_ios_sound"] != nil){
sound = [messageData objectForKey:#"notification_ios_sound"];
}
if([messageData objectForKey:#"notification_ios_badge"] != nil){
badge = [messageData objectForKey:#"notification_ios_badge"];
}
if(title == nil || body == nil){
return;
}
[[UNUserNotificationCenter currentNotificationCenter] getNotificationSettingsWithCompletionHandler:^(UNNotificationSettings * _Nonnull settings) {
#try{
if (settings.alertSetting == UNNotificationSettingEnabled) {
UNMutableNotificationContent *objNotificationContent = [[UNMutableNotificationContent alloc] init];
objNotificationContent.title = [NSString localizedUserNotificationStringForKey:title arguments:nil];
objNotificationContent.body = [NSString localizedUserNotificationStringForKey:body arguments:nil];
NSDictionary* alert = [[NSDictionary alloc] initWithObjectsAndKeys:
title, #"title",
body, #"body"
, nil];
NSMutableDictionary* aps = [[NSMutableDictionary alloc] initWithObjectsAndKeys:
alert, #"alert",
nil];
if(![sound isKindOfClass:[NSString class]] || [sound isEqualToString:#"default"]){
objNotificationContent.sound = [UNNotificationSound defaultSound];
[aps setValue:sound forKey:#"sound"];
}else if(sound != nil){
objNotificationContent.sound = [UNNotificationSound soundNamed:sound];
[aps setValue:sound forKey:#"sound"];
}
if(badge != nil){
[aps setValue:badge forKey:#"badge"];
}
NSString* messageType = #"data";
if([mutableUserInfo objectForKey:#"messageType"] != nil){
messageType = [mutableUserInfo objectForKey:#"messageType"];
}
NSDictionary* userInfo = [[NSDictionary alloc] initWithObjectsAndKeys:
#"true", #"notification_foreground",
messageType, #"messageType",
aps, #"aps"
, nil];
objNotificationContent.userInfo = userInfo;
UNTimeIntervalNotificationTrigger *trigger = [UNTimeIntervalNotificationTrigger triggerWithTimeInterval:0.1f repeats:NO];
UNNotificationRequest *request = [UNNotificationRequest requestWithIdentifier:#"local_notification" content:objNotificationContent trigger:trigger];
[[UNUserNotificationCenter currentNotificationCenter] addNotificationRequest:request withCompletionHandler:^(NSError * _Nullable error) {
if (!error) {
[FirebasePlugin.firebasePlugin _logMessage:#"Local Notification succeeded"];
} else {
[FirebasePlugin.firebasePlugin _logError:[NSString stringWithFormat:#"Local Notification failed: %#", error.description]];
}
}];
}else{
[FirebasePlugin.firebasePlugin _logError:#"processMessageForForegroundNotification: cannot show notification as permission denied"];
}
}#catch (NSException *exception) {
[FirebasePlugin.firebasePlugin handlePluginExceptionWithoutContext:exception];
}
}];
}
- (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error {
[FirebasePlugin.firebasePlugin _logError:[NSString stringWithFormat:#"didFailToRegisterForRemoteNotificationsWithError: %#", error.description]];
}
// Asks the delegate how to handle a notification that arrived while the app was running in the foreground
// Called when an APS notification arrives when app is in foreground
- (void)userNotificationCenter:(UNUserNotificationCenter *)center
willPresentNotification:(UNNotification *)notification
withCompletionHandler:(void (^)(UNNotificationPresentationOptions options))completionHandler {
#try{
if (![notification.request.trigger isKindOfClass:UNPushNotificationTrigger.class] && ![notification.request.trigger isKindOfClass:UNTimeIntervalNotificationTrigger.class]){
[FirebasePlugin.firebasePlugin _logError:#"willPresentNotification: aborting as not a supported UNNotificationTrigger"];
return;
}
[[FIRMessaging messaging] appDidReceiveMessage:notification.request.content.userInfo];
mutableUserInfo = [notification.request.content.userInfo mutableCopy];
NSString* messageType = [mutableUserInfo objectForKey:#"messageType"];
if(![messageType isEqualToString:#"data"]){
[mutableUserInfo setValue:#"notification" forKey:#"messageType"];
}
// Print full message.
[FirebasePlugin.firebasePlugin _logMessage:[NSString stringWithFormat:#"willPresentNotification: %#", mutableUserInfo]];
NSDictionary* aps = [mutableUserInfo objectForKey:#"aps"];
bool isContentAvailable = [[aps objectForKey:#"content-available"] isEqualToNumber:[NSNumber numberWithInt:1]];
if(isContentAvailable){
[FirebasePlugin.firebasePlugin _logError:#"willPresentNotification: aborting as content-available:1 so system notification will be shown"];
return;
}
bool showForegroundNotification = [mutableUserInfo objectForKey:#"notification_foreground"];
bool hasAlert = [aps objectForKey:#"alert"] != nil;
bool hasBadge = [aps objectForKey:#"badge"] != nil;
bool hasSound = [aps objectForKey:#"sound"] != nil;
if(showForegroundNotification){
[FirebasePlugin.firebasePlugin _logMessage:[NSString stringWithFormat:#"willPresentNotification: foreground notification alert=%#, badge=%#, sound=%#", hasAlert ? #"YES" : #"NO", hasBadge ? #"YES" : #"NO", hasSound ? #"YES" : #"NO"]];
if(hasAlert && hasBadge && hasSound){
completionHandler(UNNotificationPresentationOptionAlert + UNNotificationPresentationOptionBadge + UNNotificationPresentationOptionSound);
}else if(hasAlert && hasBadge){
completionHandler(UNNotificationPresentationOptionAlert + UNNotificationPresentationOptionBadge);
}else if(hasAlert && hasSound){
completionHandler(UNNotificationPresentationOptionAlert + UNNotificationPresentationOptionSound);
}else if(hasBadge && hasSound){
completionHandler(UNNotificationPresentationOptionBadge + UNNotificationPresentationOptionSound);
}else if(hasAlert){
completionHandler(UNNotificationPresentationOptionAlert);
}else if(hasBadge){
completionHandler(UNNotificationPresentationOptionBadge);
}else if(hasSound){
completionHandler(UNNotificationPresentationOptionSound);
}
}else{
[FirebasePlugin.firebasePlugin _logMessage:#"willPresentNotification: foreground notification not set"];
}
if(![messageType isEqualToString:#"data"]){
[FirebasePlugin.firebasePlugin sendNotification:mutableUserInfo];
}
}#catch (NSException *exception) {
[FirebasePlugin.firebasePlugin handlePluginExceptionWithoutContext:exception];
}
}
// Asks the delegate to process the user's response to a delivered notification.
// Called when user taps on system notification
- (void) userNotificationCenter:(UNUserNotificationCenter *)center
didReceiveNotificationResponse:(UNNotificationResponse *)response
withCompletionHandler:(void (^)(void))completionHandler
{
#try{
if (![response.notification.request.trigger isKindOfClass:UNPushNotificationTrigger.class] && ![response.notification.request.trigger isKindOfClass:UNTimeIntervalNotificationTrigger.class]){
[FirebasePlugin.firebasePlugin _logMessage:#"didReceiveNotificationResponse: aborting as not a supported UNNotificationTrigger"];
return;
}
[[FIRMessaging messaging] appDidReceiveMessage:response.notification.request.content.userInfo];
mutableUserInfo = [response.notification.request.content.userInfo mutableCopy];
NSString* tap;
if([self.applicationInBackground isEqual:[NSNumber numberWithBool:YES]]){
tap = #"background";
}else{
tap = #"foreground";
}
[mutableUserInfo setValue:tap forKey:#"tap"];
if([mutableUserInfo objectForKey:#"messageType"] == nil){
[mutableUserInfo setValue:#"notification" forKey:#"messageType"];
}
// Dynamic Actions
if (response.actionIdentifier && ![response.actionIdentifier isEqual:UNNotificationDefaultActionIdentifier]) {
[mutableUserInfo setValue:response.actionIdentifier forKey:#"action"];
}
// Print full message.
[FirebasePlugin.firebasePlugin _logInfo:[NSString stringWithFormat:#"didReceiveNotificationResponse: %#", mutableUserInfo]];
[FirebasePlugin.firebasePlugin sendNotification:mutableUserInfo];
completionHandler();
}#catch (NSException *exception) {
[FirebasePlugin.firebasePlugin handlePluginExceptionWithoutContext:exception];
}
}
// Receive data message on iOS 10 devices.
- (void)applicationReceivedRemoteMessage:(FIRMessagingRemoteMessage *)remoteMessage {
// Print full message
[FirebasePlugin.firebasePlugin _logInfo:[NSString stringWithFormat:#"applicationReceivedRemoteMessage: %#", [remoteMessage appData]]];
}
#end**
Dropbox documentation explains by default the response for authentication gets fired into Appdelegate.m
How do I make the same fire my own class's delegate?
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url
sourceApplication:(NSString *)source annotation:(id)annotation {
if ([[DBSession sharedSession] handleOpenURL:url]) {
if ([[DBSession sharedSession] isLinked]) {
NSLog(#"App linked successfully!");
// At this point you can start making API calls
}
return YES;
}
// Add whatever other url handling code your app requires here
return NO;
}
In info plist url type --> in url schemes just add db-YourAppKey this method will get called.
This method is getting called automatically. I hope you already created app from dropbox developer site and get the appKey and appSecret. Use this code in app delegate NSString* appKey = #"";
NSString* appSecret = #"";
NSString *root = kDBRootDropbox;
NSString* errorMsg = nil;
if ([appKey rangeOfCharacterFromSet:[[NSCharacterSet alphanumericCharacterSet] invertedSet]].location != NSNotFound) {
errorMsg = #"Make sure you set the app key correctly in DBRouletteAppDelegate.m";
} else if ([appSecret rangeOfCharacterFromSet:[[NSCharacterSet alphanumericCharacterSet] invertedSet]].location != NSNotFound) {
errorMsg = #"Make sure you set the app secret correctly in DBRouletteAppDelegate.m";
} else if ([root length] == 0) {
errorMsg = #"Set your root to use either App Folder of full Dropbox";
} else {
NSString *plistPath = [[NSBundle mainBundle] pathForResource:#"Info" ofType:#"plist"];
NSData *plistData = [NSData dataWithContentsOfFile:plistPath];
NSDictionary *loadedPlist =
[NSPropertyListSerialization
propertyListFromData:plistData mutabilityOption:0 format:NULL errorDescription:NULL];
NSString *scheme = [[[[loadedPlist objectForKey:#"CFBundleURLTypes"] objectAtIndex:0] objectForKey:#"CFBundleURLSchemes"] objectAtIndex:0];
if ([scheme isEqual:#"db-APP_KEY"]) {
errorMsg = #"Set your URL scheme correctly in DBRoulette-Info.plist";
}
}
DBSession* session =
[[DBSession alloc] initWithAppKey:appKey appSecret:appSecret root:root];
session.delegate = self; // DBSessionDelegate methods allow you to handle re-authenticating
[DBSession setSharedSession:session];
[DBRequest setNetworkRequestDelegate:self];
// [[DBSession sharedSession]unlinkAll];
if ([[DBSession sharedSession] isLinked])
{
isAccountForDropBox = YES;
}
else{
isAccountForDropBox = NO;
}
//After using this that open url will get call automatically.
These method only respond in AppDelegate.m,you can't use outside it.
To use in your ViewController or any class, you should use post notification
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url
sourceApplication:(NSString *)source annotation:(id)annotation {
if ([[DBSession sharedSession] handleOpenURL:url]) {
if ([[DBSession sharedSession] isLinked]) {
NSLog(#"App linked successfully!");
// Post Notify here
[[NSNotificationCenter defaultCenter] postNotificationName:#"applicationDidLinkWithDropbox" object:self];
}
return YES;
}
// Add whatever other url handling code your app requires here
return NO;
}
Then receive this notification in your class, in a ViewController for example:
- (void)viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(dropBoxDidLink:)
name:#"applicationDidLinkWithDropbox"
object:nil];
}
- (void) dropBoxDidLink:(NSNotification *)notification {
if ([[notification name] isEqualToString:#"applicationDidLinkWithDropbox"]) {
//Handle your task here
}
}
I have a label and I need it to show the score, coins ect on every ViewController, That means when The score changes it changes every where throughout the whole app...
I've tried to set a label to show score on the whole app but I cant figure out how!
Please help
This is what I have so Far In the View Controller:
-(void)viewDidLoad
{
{
[super viewDidLoad];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES); //1
NSString *documentsDirectory = [paths objectAtIndex:0]; //2
path = [documentsDirectory stringByAppendingPathComponent:#"SettingsList.plist"]; //3
NSFileManager *fileManager = [NSFileManager defaultManager];
if (![fileManager fileExistsAtPath: path]) //4
{
NSString *bundle = [[NSBundle mainBundle] pathForResource:#"SettingsList"ofType:#"plist"]; //5 //5
[fileManager copyItemAtPath:bundle toPath: path error:&error]; //6
}
savedStock = [[NSMutableDictionary alloc] initWithContentsOfFile: path];
nPoint = [[savedStock objectForKey:#"point"] intValue];
[giftAmount setText:[NSString stringWithFormat:#"%d",nPoint]];
[self updateCurrencyBalance];
[self zoneLoading];
}
//adcolony
- (void) viewDidAppear:(BOOL)animated
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(updateCurrencyBalance) name:kCurrencyBalanceChange object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(zoneReady) name:kZoneReady object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(zoneOff) name:kZoneOff object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(zoneLoading) name:kZoneLoading object:nil];
}
// Get currency balance from persistent storage and display it
- (void)updateCurrencyBalance {
NSNumber* wrappedBalance = [[NSUserDefaults standardUserDefaults] objectForKey:kCurrencyBalance];
NSUInteger balance = wrappedBalance && [wrappedBalance isKindOfClass:[NSNumber class]] ? [wrappedBalance unsignedIntValue] : 0;
[giftAmount setText:[NSString stringWithFormat:#"%u", balance]];
[savedStock setObject:[NSNumber numberWithFloat:nPoint = balance] forKey:#"point"];
[savedStock writeToFile: path atomically:YES];
}
I have an action in the other PlayViewController which (minusus) -200 coins, but its not updating in the ViewController?
One way is to use NSNotificationCenter.
Add this code to all the places that change the value of your score:
- (void)updateScore:(NSNumber *)newValue
// update the score
self.score = newValue;
// create an dictionary object containing the score to be sent with the notification
NSMutableDictionary* userInfo = [NSMutableDictionary dictionary];
[userInfo setObject:self.score forKey:#"score"];
// Add this to send a notification to all the listeners in the whole app
[[NSNotificationCenter defaultCenter] postNotificationName:#"NotificationScoreChanged" object:nil userInfo:userInfo];
}
In the viewDidLoad methods of your view controllers, add this code:
- (void)viewDidLoad:(BOOL)animated
{
[super viewDidLoad:animated];
// Add this code to start receiving notifications (become a listener)
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(scoreChanged:) name:#"NotificationScoreChanged" object:nil];
}
Then somewhere in your view controllers, add this method to update your UI:
- (void)scoreChanged:(NSNotification *)notification
{
// retrieve the score results
if ([notification.name isEqualToString:#"NotificationScoreChanged"])
{
NSDictionary *userInfo = notification.userInfo;
NSNumber *score = [userInfo objectForKey:#"score"];
// and update your labels
self.scoreLabel.text = [score description];
}
And in your view controllers, add dealloc:
- (void)dealloc
{
//Unregister yourself (stop listening)
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
Note: you should adapt the code depending on how you store and retrieve your score. Ie, if you use NSUserDefaults (see #erid's answer), CoreDate, etc.
Use NSUserDefaults for storing data (Data gets deleted only when program is removed from iOS Device or you can remove it manually).
Storing Value on NSUserDefaults
//text is your label.text, and each time you change it, save it to user details
NSString *text;
//Store them to NSUserDefaults with a specific key
[[NSUserDefaults standardUserDefaults] setObject:text forKey:#"label"];
Getting value back
NSString *textValue = [[NSUserDefaults standardUserDefaults] objectForKey:#"label" ];
label.text = textValue;
You can try adding NSNotification to your program to notificate you when the NSString you set to change the label changes, and there you can set this value to NSUserDefaults.
As David said this is not the best way to do this, but you have to read more about Singletons if you need to save your DATA until the app is closed.
Hope it helps
I want to use a UISwitch to enable/disable push notifications. Like in Tweetbot.
Does anyone know how to trigger that?
You can also do it in the following way.
create a IBOutlet for UISwitch
#property (strong, nonatomic) IBOutlet *pushNotificationSwitch;
and in Action method, store the value in NSUserDefaults.
- (IBAction)pushNotificationSwitchChanged:(id)sender
{
NSNumber *switch_value = [NSNumber numberWithBool:[self.pushNotificationSwitch isOn]];
[[NSUserDefaults standardUserDefaults] setObject:switch_value forKey:RECIEVE_APNS];
[[NSUserDefaults standardUserDefaults] synchronize];
}
and check it in viewdidload.
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view from its nib.
NSNumber *sett = [[NSUserDefaults standardUserDefaults] valueForKey:RECIEVE_APNS];
if( [sett boolValue] )
{
[self.pushNotificationSwitch setOn:YES];
}
else{
[self.pushNotificationSwitch setOn:NO];
}
}
and In AppDelegate.m, add the following code
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
NSNumber *sett = [[NSUserDefaults standardUserDefaults] objectForKey:RECIEVE_APNS];
if( [sett boolValue] )
{
int currentBadgeCount = [[NSUserDefaults standardUserDefaults] integerForKey:#"BadgeCount"];
//Set the baadge count on the app icon in the home screen
int badgeValue = [[[userInfo valueForKey:#"aps"] valueForKey:#"badge"] intValue];
[UIApplication sharedApplication].applicationIconBadgeNumber = badgeValue + currentBadgeCount;
[[NSUserDefaults standardUserDefaults] setInteger:badgeValue + currentBadgeCount forKey:#"BadgeCount"];
NSString *alertString = [[userInfo objectForKey:#"aps"] objectForKey:#"alert"];
NSString *playSoundOnAlert = [NSString stringWithFormat:#"%#", [[userInfo objectForKey:#"aps"] objectForKey:#"sound"]];
NSURL *url = [NSURL fileURLWithPath:[NSString stringWithFormat:#"%#/%#",[[NSBundle mainBundle] resourcePath],playSoundOnAlert]];
NSError *error;
if (alertString.length > 0)
{
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"App Name" message:alertString delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
audioPlayer = [[AVAudioPlayer alloc] initWithContentsOfURL:url error:&error];
audioPlayer.numberOfLoops = 1;
[audioPlayer play];
[alert show];
}
}
}
enter code here
You can not do that directly from the application. If you want to do this, you need to make the UISwitch send the information to your backend, store this information in your database and stop sending push notifications to this user.
An app registers for Push Notifications (APN) when it first launches. You cannot have it initialize APNs with a switch once it has already launched. You can however code your app that a switch can choose to do "something" with the user interface once a APN is received.
For example, you can have this code:
-(void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
NSDictionary *apsInfo = [userInfo objectForKey:#"aps"];
NSString *alert = [apsInfo objectForKey:#"alert"];
// do what you need with the data...
[[NSNotificationCenter defaultCenter] postNotificationName:#"ReceivedNotificationAlert" object:self];
}
You can use your UISwitch to either do something, or not, with the NSNotification "ReceivedNotificationAlert". For example:
if(switchAPNprocess.on){
// process APN
}
else {
// ignore APN
}
I am trying to use iCloud to store my app's userSetting, here is my Save & Load Code: , it usually doing fine but sometimes crash with message like: attempt to open or a revert document that already has an open or revert operation in flight or send to dealloc instance so i add fileState logs befere openWithCompletionHandler it always show state = UIDocumentStateClosed no matter will crash or not, i save data when applecationDidEnterBackground and load when applicationDidBecomeActive.
save:
-(void)storeToiCloud{
NSURL *baseURL = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];
if (baseURL) {
NSURL *documentsURL = [baseURL URLByAppendingPathComponent:#"Documents"];
NSURL *documentURL = [documentsURL URLByAppendingPathComponent:[NSString stringWithFormat:#"userSetting"]];
if (!loadDocument) {
self.loadDocument = [[MyUserDefaultsDocument alloc] initWithFileURL:documentURL];
}
loadDocument.myUserDefault = [MyUserDefaults standardUserDefaults];
[loadDocument saveToURL:documentURL forSaveOperation:UIDocumentSaveForCreating completionHandler:^(BOOL success) {
}];
}
}
load:
-(BOOL)shouldSynciCloud{
if (![Utility iCloudEnable]) {
return NO;
}
NSURL *baseURL = [[NSFileManager defaultManager] URLForUbiquityContainerIdentifier:nil];
if (baseURL) {
self.query = [[[NSMetadataQuery alloc] init] autorelease];
[self.query setSearchScopes:[NSArray arrayWithObject:NSMetadataQueryUbiquitousDocumentsScope]];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"%K == 'userSetting'", NSMetadataItemFSNameKey];
[self.query setPredicate:predicate];
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self selector:#selector(queryDidFinish:) name:NSMetadataQueryDidFinishGatheringNotification object:self.query];
[self.query startQuery];
[Utility showSpinner];
return YES;
}
return NO;
}
- (void)queryDidFinish:(NSNotification *)notification {
NSMetadataQuery *query = [notification object];
// Stop Updates
[query disableUpdates];
// Stop Query
[query stopQuery];
[query.results enumerateObjectsUsingBlock:^(id obj, NSUInteger idx, BOOL *stop) {
NSURL *documentURL = [(NSMetadataItem *)obj valueForAttribute:NSMetadataItemURLKey];
if([[documentURL lastPathComponent] hasPrefix:#"userSetting"]){
self.document = [[MyUserDefaultsDocument alloc] initWithFileURL:documentURL];
NSString* message;
if (document.documentState == UIDocumentStateNormal){
message = #"UIDocumentStateNormal";
}else if (document.documentState == UIDocumentStateClosed) {
message = #"UIDocumentStateClosed";
}else if(document.documentState == UIDocumentStateEditingDisabled){
message = #"UIDocumentStateEditingDisabled";
}else if(document.documentState == UIDocumentStateInConflict){
message = #"UIDocumentStateInConflict";
}else if(document.documentState == UIDocumentStateSavingError){
message = #"UIDocumentStateSavingError";
}
NSLog(#"state = %#",message);
[document openWithCompletionHandler:^(BOOL success) {
if (success) {
MyUserDefaults *prefs = [MyUserDefaults standardUserDefaults];
NSData *book =[document.myUserDefault.realDict objectForKey:#"realbook"];
NSData *readSetting = [document.myUserDefault.realDict objectForKey:#"epubRS"];
if (book&&[[NSUserDefaults standardUserDefaults] boolForKey:#"iCloudBook"]) {
[prefs setObject:book forKey:#"realbook"];
[Utility reloadRealBooks];
}
if (readSetting&&[[NSUserDefaults standardUserDefaults] boolForKey:#"iCloudSetting"]) {
[prefs setObject:readSetting forKey:#"epubRS"];
[Utility setEpubReadSettingFromData:readSetting];
}
[prefs save];
[[NSNotificationCenter defaultCenter]postNotificationName:#"iCloudSynced" object:nil];
[Utility removeSpinner];
}
else{
[[NSNotificationCenter defaultCenter]postNotificationName:#"iCloudSyncfailed" object:nil];
[Utility removeSpinner];
}
}];
}
}];
if ([query.results count]==0) {
[[NSNotificationCenter defaultCenter]postNotificationName:#"iCloudSyncfailed" object:nil];
[Utility removeSpinner];
}
[[NSNotificationCenter defaultCenter] removeObserver:self name:NSMetadataQueryDidFinishGatheringNotification object:nil];
}
As noted in this question, the error occurs if your app attempts to call [document openWithCompletionHandler:] method twice in close succession.
Because the openWithCompletionHandler: opens the document asynchronously, the document may still be opening when the method is called again.
If this happens, your app ends up trying to open the document twice (as the document state will remain UIDocumentStateClosed until completion) and this causes the exception to be thrown.