Detecting changed user defaults - iphone

I have an iPhone app and have implemented local notifications. The app has a page in Settings that allows a user to schedule notifications a specified number of days in advance of an event. To make sure that changes to the settings take effect every time a user activates the app, I have the following in my app delegate:
- (void)applicationDidBecomeActive:(UIApplication *)application {
[[NSUserDefaults standardUserDefaults] synchronize];
[self rescheduleAllNotificationsWithUserNotification:NO];
}
The problem is that the call to rescheduleAllNotificationsWithUserNotification: takes a few seconds and the app feels a little sluggish on start.
I only need to call rescheduleAllNotificationsWithUserNotification: if any of the settings have been changed. Is there a way to detect if the user has changed any of the settings between app activations so I can avoid always rescheduling the notifications?

I think you might be looking for the NSUserDefaultsDidChangeNotification notification.
You can register to listen to this notification and be informed whenever the user's preferences change.

You should use a way to put this method call in another thread is possible. performSelectorInBackground is an easy way to do it:
[self performSelectorInBackground:#selector(rescheduleAllNotificationsWithUserNotification:) withObject:NO];
That should help you get rid of the laggy performance. You could even use ^blocks, as you seem to be on iOS 4.

What I have done in the past, if there are not too many preference values to monitor, is to have alternate versions, sort of "the last time I ran" versions. One set of values is accessible through the Settings application but the other is only set from within the application.
NSString *name = (NSString *) CFPreferencesCopyAppValue( (CFStringRef) #"name_preference", kCFPreferencesCurrentApplication );
NSString *nameLastTime = (NSString *) CFPreferencesCopyAppValue( (CFStringRef) #"name_last_execution", kCFPreferencesCurrentApplication );
// Need obvious checks for empty or missing entries, etc., etc.
if( ![nameLastTime isEqual: name] ) {
// Store the new name as the name at last execution...
CFPreferencesSetAppValue( (CFStringRef) #"name_last_execution", (CFStringRef) name, kCFPreferencesCurrentApplication );
CFPreferencesAppSynchronize( kCFPreferencesCurrentApplication );
[self rescheduleAllNotificationsWithUserNotification:NO];
}
It is not real elegant with fancy object-oriented call-back methods, etc., but it gets the job done cheaply and reliably.

Related

How to make scollView (for T&C) only show up once at begin of app, then never show up again?

My questions is...how to make....this...
I am trying to make a scollview to show the term&condition at beginning of my app when the user is 1st time using the app.
if the user accepted the T&C (by clicking accept button), this T&C scollview will never show up again at beginning of the app, as he already accepted. So he will be free to use the app in future.
How do I implement this? any suggestions?
Use NSUserDefaults with a key like "TCShown". If the key does not exist in the NSUserDefaults at the beginning of the launch, you show the T&C and create "TCShown" value, set it to YES ([NSNumber numberWithBool:YES];) and store it to the NSUserDefaults.
Edit:
Assuming that you want to present the T&C in your first viewController,
#define kTCViewedFlag = #"tcViewed"
-(void) viewDidAppear {
NSUserDefaults *myDefaults = [NSUserDefaults standardUserDefaults];
if(![myDefaults objectForKey:kTCViewedFlag]) {
//show the TC
}
}
-(IBAction) userAcceptedTC {
[[NSUserDefaults standardUserDefaults] setObject:[NSNumber numberWithBool:YES] forKey:kTCViewedFlag];
[[NSUserDefaults standardUserDefaults] synchronize];
//dismiss the scrollView
}
-(IBAction) userDidDeclineTC {
//handle refusal of TC
}
In addition to Kaan's answer, you can add the TCShown field to the server and update the values accordingly. This will take care of the case when the user who has already accepted the T&C's logs in from a different device.
Maybe you'll find this useful: RLAgreement View Controller
This project allows developers to include
and Agreement, Terms of Service, Non Disclosure Agreement, etc. to an
iPhone App. The controller stores a variable in the user's settings
when the user has a valid agreement and it checks every time the user
opens the App.

Alternative to InAppSettingsKit

Is there an alternative to InAppSettingsKit that is simpler? I find myself needing only 20% of what it offers.
How about ESCOZ's QuickDialog library? Seems like a reasonable alternative.
Well, one alternative is to just build your own settings panel with a regular UIViewController and some buttons and switches, etc. and then save the settings using NSUserDefaults, e.g.
- (IBAction)mySettingSwitchAction:(UISwitch *)theSwitch
{
//save the switch setting
[[NSUserDefaults standardUserDefaults] setBool:theSwitch.on forKey:#"myPreferenceName"];
}
then you can load it again anywhere in your app using
BOOL theValueISet = [[NSUserDefaults standardUserDefaults] boolForKey:#"myPreferenceName"];
Values you set in NSUserDefaults are persistent so if the app is closed and opened again they retain their values. You can call synchronize on NSUserDefaults to force it to save/load the values but this happens automatically on app open/close anyway.

iPhone, call another phone number in response to the first not answering?

I am attempting to create an application that will initiate a call to a priority 1 contact on a call-center-like list.
Then, if that contact does not answer (let's forget the whole problem of answering machines here), I'd like to call the priority 2 contact, and so on, until one of them answers or I exhaust my list.
Is this possible?
I've tried the following:
Hook into the CTCallCenter.CallEventHandler event, and checking the call state for CTCallStateConnected and CTCallStateDisconnected, and I get it to respond to the fact that the call disconnected, without ever connecting, and then attempt to initiate another call like I did the first, but this second attempt just sits dead in the water.
Override the DidEnterBackground method, and periodically check the CTCall.CallState property, basically again trying to respond to a disconnect that was never connected, but this does not appear to work either
I also tried adding a short delay (1 second, 2.5 seconds and 10 seconds) after detecting the disconnected state before attempting the next dial, to allow for the phone application to "settle down" after aborting the call, this did not change anything.
I'm of the opinion that this is better solved at the destination of the phone call. I would either have the phone company configure a "follow me" service, use Twilio or some other 3rd party service (as already suggested), or configure my own PBX using something like Asterisk (Asterisk includes the ability to configure "follow me" type behavior). It provides you much more flexibility and control, even if you did find a way to do this natively in iOS.
Having said that, I did get this to work in iOS assuming the following:
Your app initiates the call.
The phone app is opened, dials the number, and disconnects.
The user explicitly returns to your app. If you managed to get the events while your app was backgrounded, I want to know more :-).
On return of control to your app, the phone events are sent and a new call is initiated.
I have the following snippet of code in my UIApplicationDelegate didFinishLaunchingWithOptions method:
// In appdelegate header, ct is declared as #property (strong, nonatomic) CTCallCenter *ct;
self.ct = [[CTCallCenter alloc] init];
self.ct.callEventHandler = ^(CTCall *call) {
if (call.callState == CTCallStateConnected) {
// do some state management to track the call
} else if (call.callState == CTCallStateDisconnected) {
// check that this is the expected call and setup the
// new phone number
NSURL *telURL = [NSURL URLWithString:myNewNumberURL];
[application openURL:telURL];
}
};
This will make the new call. I'm using the iOS 5 SDK; tested on an iPhone 4s.
EDIT:
Using Return to app behavior after phone call different in native code than UIWebView as a starting point, I've managed to get this to work. Note that I have punted on memory management for clarity. Assuming you use the web view technique for getting back to your app after the call is complete, try something like this in the call completed block:
else if (call.callState == CTCallStateDisconnected) {
// check that this is the expected call and setup the
// new phone number
NSURL *telURL = [NSURL URLWithString:myNewNumberURL];
dispatch_async(dispatch_get_main_queue(), ^{
UIWebView *callWebview = [[UIWebView alloc] init] ;
[self.window.rootViewController.view addSubview:callWebview];
[callWebview loadRequest:[NSURLRequest requestWithURL:telURL]];
// and now callWebView sits around until the app is killed....so don't follow this to the letter.
});
}
However, this may not quite give you what you want either. The user will get an alert on each call request, providing an opportunity to cancel the call.
You could use http://labs.twilio.com/twimlets/findme. You could have the app call a Twilio number and it could use findme to call all the numbers in order.
I didn't take a deeper look at it, but the Deutsche Telekom SDK might contain what you're looking after:
http://www.developergarden.com/fileadmin/microsites/ApiProject/Dokumente/Dokumentation/ObjectiveC-SDK-2.0/en/interface_voice_call_service.html
I really am not sure though (don't have time to really look at it at the moment) - I just remembered I'd read somewhere that they have an iOS SDK that is supposed to also handle call management, so I'm posting the link here for you to find out (and hopefully tell us if it works).
#pragma mark -
#pragma mark Call Handler Notification
-(void)notificationCallHandler {
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(callReceived:) name:CTCallStateIncoming object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(callEnded:) name:CTCallStateDisconnected object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(callConnected:) name:CTCallStateConnected object:nil];
}
-(void)callEnded:(NSNotification*)notification {
NSLog(#"callEnded");
}
-(void)callReceived:(NSNotification*)notification {
NSLog(#"callReceived");
}
-(void)callConnected:(NSNotification*)notification {
NSLog(#"callConnected");
}
May this will help you
if you wanna setup a new call, while app is in background, i dont see any proper way for this, a lil hack could be, getting location update (because u can get location updates while app is in background), and location service automatically wakes up your application when new location data arrives, and small amount of time is given to application in which u can execute some code, in that time you may start a new call.
u can read further here:
search this ''Starting the Significant-Change Location Service'' in this link Location Aware programming guide
, and read the paragraph that is written after the code block.

UILocalNotification fires after reinstalling the app

My app has an alarm function using UILocalNotification, and it works great. However, if the user uninstalls the app, then later REINSTALLS it, he would receive all the "in between" notifications at once.
I have tried to call:
[[UIApplication sharedApplication] cancelAllLocalNotifications];
if it's the first time the app is launched, but it doesn't help, because the notification is received even before application:didFinishLaunchingWithOptions: is called.
This was worse in 4.0 when the alarm was repeated even if the user has deleted the app, but at least that bug was fixed by Apple in later release. However now I'm stuck with this. Anyone has an idea?
According to Apple, this is not a bug (I filed a bug report). The system retains the UILocalNotifications for uninstalled apps for 24 hours just in case the user deleted the app by accident, and restores the said UILocalNotifications if the app is re-installed within that time frame.
The solution would be to remove all UILocalNotifications on first startup, like so:
- (BOOL) application: (UIApplication*) application
didFinishLaunchingWithOptions: (NSDictionary*) launchOptions
{
if (self.isFirstRun)
{
[[UIApplication sharedApplication] cancelAllLocalNotifications];
self.firstRun = NO;
}
/* Other code here */
...
}
of course, implement your own firstRun setter and getter to fetch/save into persistent storage, like NSUserDefaults.
This is actually a bug in iPhone. If you removed the application and install it later also, it will have same app id, so when the application is reinstalled all the past local notifications were fired even if you didn't open the app.

Problem with applicationShouldTerminate on iPhone

I'm having a problem with applicationShouldTerminate.
What ever I do it seams that has no effect. Any help would be
appreciated.
I'm well versed in programing but this just gives me headache. Im going
over some basic tutorials for xcode , as I'm new to mac in general, and am currently looking at a simple flashlight app.
It exists but I would like to add a alert box here with option not to
quit.
(void)applicationWillTerminate:(UIApplication *)application
{
[application setIdleTimerDisabled:NO];
}
this has no effect, alert is closed even before its created.
(void)applicationWillTerminate:(UIApplication *)application
{
[application setIdleTimerDisabled:NO];
UIAlertView *alertTest = [[UIAlertView alloc]
initWithTitle:#"This is a Test"
message:#"This is the message contained
with a UIAlertView"
delegate:self
cancelButtonTitle:#"Button #1"
otherButtonTitles:nil];
[alertTest addButtonWithTitle:#"Button #2"];
[alertTest show];
[alertTest autorelease];
NSLog(#"Termination");
}
I did some reading online and found that it should be possible to do
this with
(NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication*)sender
But no mater where I put that declaration I get error: syntax error
before NSApplicationTerminateReply.
There is no syntax error except that xcode seems not to recognize
NSApplicationTerminateReply as valid input.
Any sample code would be greatly appreciated.
I know this is a non-answer, but hopefully I can be helpful:
Displaying a "Really quit?"-type alert like this, even if you can pull it off technically (and I'm not sure you can), is a bad idea and is likely to either cause rejection from the App Store or, at best, an inconsistent user experience because no other apps do this.
The convention with iPhone apps is to save state if necessary, then yield control (for termination) as quickly as possible when the user hits the home button or switches apps.
To ensure a consistent experience, Apple probably has an aggressive timer in place to restrict what you can do in applicationWillTerminate. And even if they don't have a technical measure in place, they probably have an App Store approval policy to ensure that applications quit immediately when they're asked to.
applicationShouldTerminate and NSApplication do not exist on the iPhone. You have to use UIApplication.
The alert view is never shown because the 'show' method does not block, and therefore, the end of 'applicationWillTerminate' is reached immediately after you create the alert view and try to show it. I believe this is by design. You can't really begin asynchronous operations in 'applicationWillTerminate'.
With regards to the applicationShouldTerminate error, in case anyone's curious, NSApplicationTerminateReply and NSApplication seem to be deprecated...even though the OP's method is exactly how it appears in the docs!
Defining your method as the below should build with no errors:
-(BOOL)applicationShouldTerminate :(UIApplication *)application
I think I found the answer to what I wanted to do but will need to check it when I get back home.
Some directions were found here
http://blog.minus-zero.org/
The iPhone 2.0 software was recently released, and with it came the
ability for users to download native apps (i.e., not web sites)
directly to their phones from within the iPhone UI or via iTunes.
Developers (anyone who pays Apple 59GBP for the privilege) can then
write their own apps and have them available for purchase in the App
Store.
One limitation of the Apple-sanctioned SDK is that only one
application is allowed to be running at a time. This presents a
problem for apps such as IM clients, music players and other programs
whose functionality relies on being able to run in the background.
Another example (courtesy of James) would be an app that takes
advantage of the iPhone 3G's GPS chip to create a log of all the
places you visit.
However, there is a neat trick that I discovered: your app will only
get terminated if you switch away from it, and hitting the iPhone's
power button while your app is in the foreground doesn't count as
switching away. The upshot of this is you can create apps which
continue to run while the iPhone is in your pocket - perfect for the
GPS example.
Achieving this is as simple as implementing two methods in your
UIApplication delegate - applicationWillResignActive: and
applicationDidBecomeActive:. Here's a simple example to demonstrate
the effect.
In your UIApplication delegate header file, add a new ivar: BOOL
activeApp. Then, in your implementation, add the following three
methods:
- (void)applicationWillResignActive:(UIApplication *)application {
NSLog(#"resigning active status...");
activeApp = NO;
[self performSelector:#selector(sayHello) withObject:nil afterDelay:1.0];
}
- (void)applicationDidBecomeActive:(UIApplication *)application {
NSLog(#"becoming the active app...");
activeApp = YES;
}
- (void)sayHello {
NSLog(#"Hello!");
if (!activeApp)
[self performSelector:#selector(sayHello) withObject:nil afterDelay:1.0];
}