Im trying to change count of Push notifications in AppDelegate.
I made a property and synthesise it in AppDelegate.h for store NSDictionary with notifications data.
#property (strong,nonatomic) NSMutableDictionary *pushArray;
When I receive notifications I do it:
- (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo
{
_pusharray = [[userInfo valueForKey:#"aps"] valueForKey:#"badges"];
}
Im getting notification in another file (for example mainViewController) well but I can't remove item in _pusharray.
Im makin it like that:
- (IBAction)touchMaenuButtons:(id)sender {
NSUInteger index = [self.menuButtons indexOfObject:sender];
NSMutableDictionary *pushArray = [(AppDelegate *)[[UIApplication sharedApplication] delegate] pushArray];
NSString *key = [NSString stringWithFormat:#"%lu",(unsigned long)index];
UIButton *button = [_badges objectAtIndex:index];
[button setTitle:#"" forState:UIControlStateNormal];
button.hidden=YES;
[pushArray removeObjectForKey:key];
}
In the end string i receive error:
* Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSDictionaryI removeObjectForKey:]: unrecognized selector sent to instance 0x145a78a0'
Please somebody give me answer how to correct it.
It's not a mutable dictionary
you should use the initialiser below to make the dictionary mutable.
_pusharray = [NSMutableDictionary dictionaryWithDictionary:[[userInfo valueForKey:#"aps"] valueForKey:#"badges"]];
Also as a style thing don't call a dictionary an array
Also personally don't like to see this
[(AppDelegate *)[[UIApplication sharedApplication] delegate] pushArray];
If you need this kind of functionality create your own singleton don't overload the app delegate with this king of thing. The app delegate is for the system to talk to your app you should try and avoid putting your app logic in here if possible.
Related
I can't seem to find it and I'm not sure how to Google it. In my app I use background fetches to do checks and if there is new data, I send a notification to the user using UILocalNotification. Now how do I prevent the app from sending a new notification to the user if there is already one there? I'm now ending up with 5 of the same notifications when I don't look at my phone for some hours.
Thanks!
You can use UIApplication's property scheduledLocalNotifications
Here's the sample code:
NSMutableArray *notifications = [[NSMutableArray alloc] init]; [notifications addObject:notification]; myApp.scheduledLocalNotifications = notifications;
//Equivalent: [myApp setScheduledLocalNotifications:notifications];
UIApplication *myApp = [UIApplication sharedApplication];
NSArray *eventArray = [myApp scheduledLocalNotifications];
for (int i=0; i<[eventArray count]; i++)
{
UILocalNotification* oneEvent = [eventArray objectAtIndex:i];
NSDictionary *userInfoCurrent = oneEvent.userInfo;
NSString *uid=[NSString stringWithFormat:#"%#",[userInfoCurrent valueForKey:#"uid"]];
if ([uid isEqualToString:uidtodelete])
{
//Cancelling local notification
[myApp cancelLocalNotification:oneEvent];
break;
}
}
NSArray *arrayOfLocalNotifications = [[UIApplication sharedApplication] scheduledLocalNotifications] ;
for (UILocalNotification *localNotification in arrayOfLocalNotifications) {
if ([localNotification.alertBody isEqualToString:savedTitle]) {
NSLog(#"the notification this is canceld is %#", localNotification.alertBody);
[[UIApplication sharedApplication] cancelLocalNotification:localNotification] ; // delete the notification from the system
}
}
Hope this would help figure out the solution.
The best way to avoid having to cancel something it in the first place is also to simply NOT sending it.
Find a way to know if the user has been notified before, and if he has, don't notify again :)
This is cleaner, more efficient, will last forever, and won't backfire, ever.
Also, this answers your question of "not sending a notification again" instead of sending it and cancelling afterwards, which isn't a good idea if you think about it.
I am trying to launch my iPhone app from watch simulator using the below code :
WKInterfaceController subclass
[WKInterfaceController openParentApplication:[NSDictionary dictionaryWithObject:#"red" forKey:#"color"] reply:^(NSDictionary *replyInfo, NSError *error) {
NSLog(#"replyInfo %#",replyInfo);
NSLog(#"Error: %#",error);
}];
AppDelegate.m
- (void)application:(UIApplication *)application handleWatchKitExtensionRequest:(NSDictionary *)userInfo reply:(void(^)(NSDictionary *replyInfo))reply
{
NSLog(#"appdelegate handleWatchKitExtensionRequest");
NSLog(#"NSDictionary: %#",userInfo);
NSLog(#"replyInfo: %#",replyInfo);
}
The error I am getting is :
Error: Error Domain=com.apple.watchkit.errors Code=2 "The
UIApplicationDelegate in the iPhone App never called reply() in
-[UIApplicationDelegate application:handleWatchKitExtensionRequest:reply:]"
UserInfo=0x7f8603227730 {NSLocalizedDescription=The
UIApplicationDelegate in the iPhone App never called reply() in
-[UIApplicationDelegate application:handleWatchKitExtensionRequest:reply:]}
You need to call the reply block, even if you return nil. The following will resolve your error:
- (void)application:(UIApplication *)application handleWatchKitExtensionRequest:(NSDictionary *)userInfo reply:(void(^)(NSDictionary *replyInfo))reply
{
NSLog(#"appdelegate handleWatchKitExtensionRequest");
NSLog(#"NSDictionary: %#",userInfo);
NSLog(#"replyInfo: %#",replyInfo);
reply(nil);
}
See the Apple documentation for further information. You can also return an NSDictionary reply(myNSDictionary); with whatever information it would be useful to return to your Watchkit extension, although the dictionary can only contain information that can be serializable to a property list file, so for instance you can pass strings but you can't just pass a dictionary containing references to instances of your custom classes without packaging them up as NSData first.
Aside from just not calling the reply block, this can happen for at least a couple reasons:
Your iPhone app crashed while it was processing the request and therefore was never able to call the reply block. Check that you are not accidentally putting nil into an NSMutableDictionary, as that will cause a crash.
You are trying to put something that can't be serialized into a plist file into the replyInfo dictionary (hat tip to #duncan-babbage). If you need to pass an NSAttributedString or your custom object, make sure it conforms to NSCoding and do this:
On the phone side build your reply dictionary:
NSMutableDictionary *reply = [NSMutableDictionary new];
MyCustomObject *myObject = <something you need to send>;
reply[#"myKey"] = [NSKeyedArchiver archivedDataWithRootObject: myObject];
NSAttributedString *myString = <some attributed string>;
reply[#"otherKey"] = [NSKeyedArchiver archivedDataWithRootObject: myString];
And unpack it back on the watch side:
NSData *objectData = replyInfo[#"myKey"];
MyCustomObject *myObject = [NSKeyedUnarchiver unarchiveObjectWithData: objectData];
NSData *stringData = replyInfo[#"otherKey"];
NSAttributedString *myString = [NSKeyedUnarchiver unarchiveObjectWithData: stringData];
I would like to add that it is important to start a background task in handleWatchKitExtensionRequest as specified in the documentation. This ensures that the main app on the iPhone is not suspended before it can send its reply. (Not initiating a background task does not cause a problem in the simulator or when the iPhone app is active. However, it causes a problem when the iPhone app is inactive.)
Code in the app delegate of the main app on iPhone:
- (void)application:(UIApplication *)application handleWatchKitExtensionRequest:(NSDictionary *)userInfo reply:(void ( ^)( NSDictionary * ))reply
{
__block UIBackgroundTaskIdentifier watchKitHandler;
watchKitHandler = [[UIApplication sharedApplication] beginBackgroundTaskWithName:#"backgroundTask"
expirationHandler:^{
watchKitHandler = UIBackgroundTaskInvalid;
}];
if ( [[userInfo objectForKey:#"request"] isEqualToString:#"getData"] )
{
// get data
// ...
reply( data );
}
dispatch_after( dispatch_time( DISPATCH_TIME_NOW, (int64_t)NSEC_PER_SEC * 1 ), dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_DEFAULT, 0 ), ^{
[[UIApplication sharedApplication] endBackgroundTask:watchKitHandler];
} );
}
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"];
I've encountered a weird bug and would like to check if I'm using my Key value observing of changes to NSUserDefaults correctly.
I have used this code in two places in my app without issues, then I added 3rd controller that observes values for "goldCount" and "energyCount". Now when I set the initial value, the app crashes with exc_bad_access. I'm adding this controller to the view 2 seconds after it's parent view appears using performSelectorAfterDelay.
Just before displaying the game screen, I set these properties:
//crash on this line
[[NSUserDefaults standardUserDefaults] setInteger:200 forKey: goldCount];
[[NSUserDefaults standardUserDefaults] setInteger:150 forKey: energyCount];
Within 3 different view controllers, I have this code in viewDidLoad:
NSUserDefaults* defaults = [NSUserDefaults standardUserDefaults];
[defaults addObserver:self
forKeyPath:#"goldCount"
options:NSKeyValueObservingOptionNew
context:NULL];
[defaults addObserver:self
forKeyPath:#"energyCount"
options:NSKeyValueObservingOptionNew
context:NULL];
self.goldLabel.text = [NSString stringWithFormat:#"%i",[[GameDataManager sharedInstance] currentGoldCount]];
self.energyLabel.text = [NSString stringWithFormat:#"%i",[[GameDataManager sharedInstance] currentEnergyCount]];
Here's how the class updates it's labels:
// KVO handler
-(void)observeValueForKeyPath:(NSString *)aKeyPath ofObject:(id)anObject
change:(NSDictionary *)aChange context:(void *)aContext
{
//aKeyPath gives us the name of a user default that has changed
if([aKeyPath isEqualToString:#"goldCount"])
{
//we are interested in the new value
self.goldLabel.text = [NSString stringWithFormat:#"%i",[[aChange objectForKey:#"new"] intValue]];
}else if([aKeyPath isEqualToString:#"energyCount"])
{
self.energyLabel.text = [NSString stringWithFormat:#"%i",[[aChange objectForKey:#"new"] intValue]];
}
}
After adding a call to [[NSUserDefaults standardUserDefaults] synchronize]; I get this exception the second time around:
Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '(
): An -observeValueForKeyPath:ofObject:change:context: message was received but not handled.
Key path: goldCount
Observed object:
Change: {
kind = 1;
new = 205;
}
Context: 0x0'
NSUserDefaults is not documented to be KVO compliant so it's not possible to observe defaults by their key. This might be the reason for the crash but without a stack trace it's not possible to tell.
There is a notification you can register for that announces changes to the defaults system: NSUserDefaultsDidChangeNotification.
Declared in MyViewController.h:
AppDelegate *appDelegate;
#property (nonatomic, strong) AppDelegate *appDelegate;
In viewDidLoad of MyViewController.m:
self.appDelegate = (AppDelegate *)[[UIApplication sharedApplication] delegate];
localAppDelegate= (AppDelegate *)[[UIApplication sharedApplication] delegate];
For some reason appDelegate remains null, but localAppDelegate is properly assigned.
Why doesn't self.appDelegate get properly assigned?
well Erik based on your comment localAppDelegate should be always accessible but be sure to import AppDelegate header to the class you want to use appDelegate inside it .. and if you need to store a variable's like user settings , common variables ..etc. I prefer to use NSUserDefaults its simple and recommended to use in cases like this .. here is a simple example to save and get string from NSUserDefaults
To set :
NSString *str = #"Malek";
[[NSUserDefaults standardUserDefaults] setValue:str forKey:#"myName"];
[[NSUserDefaults standardUserDefaults] synchronize];
2.to get :
NSString *myName = [[NSUserDefaults standardUserDefaults] valueForKey:#"myName"];