Restricting iOS app use by date - ios

In a few weeks I'll be releasing my iOS app to a group of beta testers. I'm allowing them to test from a Friday evening until the following Sunday night. I want to disallow any usage after that Sunday. I don't want to use NSDate because testers could change the date on their device and keep playing after the session ended. Any ideas?

The date option is probably good enough as most people won't bother changing the date on their device, it would mess up too many other apps. The only case to worry about there would be someone with a device dedicated to your game.
Another option though: add a network request to your server with some code that says if it's allowed to play or not, then you can just change the flag on your server.

If you don't want to set up a server or worry about network connectivity, you can do two things that make it stronger than using a single NSDate:
Record the NSDate whenever someone opens the app (and when they leave)
The next time they use the app, make sure the current time is higher than the previous open/close time. And make sure it is less than your future date.
This will give a shrinking time window that will be harder and harder to circumvent.

There are dozens of ways to prevent this, some more complicated that others. But the first two that come to my mind are:
Option 1: You can check the date on a server instead of the system time.
Option 2: If you want to avoid the network hassle... If the user opens the app after sunday, write a boolean in userDefaults (e.g. expired). When starting the app check both the date and the expired flag. Even if the user changes the date, the expired flag would be set.

If you do not wish network and server connectivity, use the keychain to store the last open date. The keychain is more secure and harder for people to change than the user defaults, which are a mere plist file in the app's library.

Here is the secure option. You make it know that the BETA version of your app requires internet access. So when application starts, it will send a request to the beta server asking if it's alright to play. By default, your app won't play unless it gets a valid response.
The server create a valid response, the server will send at least two pieces of information. The first piece is a flag indicating that the beta period is valid. And the second is the current date/time. The server will sign the response using it's private key.
The application when it receive the response will verify its a valid by using the server public key. The server public key should be in the application as a resource. After verifying the message is valid and authentic, it compare the date/time that was sent to the device's date/time. If the time differences is less than 10 seconds for example, the application know its a current message. Not a message that has been saved and replay using some sort of HTTP proxy.

First, check out this link for a good way to check your time against an NTP server:
https://github.com/jbenet/ios-ntp
Then, you can get the date by calling [NSDate networkDate];
Now, you should be able to do your restriction in your appDelegate. I have done something similar for restricting running an iPhone app on an iPad:
if ([[NSDate networkDate] compare:startDate] == NSOrderedAscending) {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Beta Not Started" message:#"Beta starts [[STARTDATE]]." delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alertView show];
return NO;
} else if ([[NSDate networkDate] compare:endDate] == NSOrderedDescending) {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Beta Ended" message:#"Beta ended [[ENDDATE]]." delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alertView show];
return NO;
}
Then, in your UIAlertViewDelegate:
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex {
exit(0);
}
Hope this helps!

Related

Realm Platform: How do I force sync before app goes into background?

I need to keep track of devices that are actively running the app. Right now I have a status field that changes to 1 when the app is first launched or when the device becomes active. When the user presses the home button, I set the status field to 0 upon receiving the notification UIApplicationWillResignActiveNotification
if(self.device) {
[self.realm transactionWithBlock:^{
self.device.status = 0;
}];
}
self.device = nil;
[self.realm refresh];
However, I check the data in Realm Cloud, and it doesn't seem to be updating at all. Is there some way to force update the sync of my Realm data?
If you want to achieve this by only using realm platform, I think you should implement a data updating logic, for example, every 5-second client should update a value, it can be the current timestamp and you can recognize online users by the last timestamp value.
in my opinion, a simple socket.io implementation can be a better solution, you can save users connection status by saving them in the socket server. you just need to save timestamp when the client has connected to the server and when he has disconnected.
I believe you can do same with ROS but I'm not sure they provide public API for user connection event or status ...

Strange behaviour with iOS kABPersonModificationDateProperty, seems to update all the time

I'm seeing really weird behaviour in my iOS app using the ABAddressBook library. Wondering if anyone can give me some insight into whats happening in the background or if I have a logic error I just can't see.
Long story short I'm making a VOIP app that relies heavily on importing the user's contacts. I keep an online backup of these that are also used in conjunction with push notifications.
As they are modified on the phone I need to send an update request to the server to keep them accurate. The problem is many user devices are frequently spamming the server with very large blocks of contacts, at random intervals. One day it will be 50+ contacts, 5 mins later another 10, then it will be a month before a single one is updated. I have asked a few users that are friends and they can't recall modifying half of their address book over night.
My code is very simple, I store an NSDate every time I am required to send an update to the server, e.g. addition, modification or deletion. I know there is a callback that triggers when the address book has been updated, but this requires keeping a reference to the address book in memory at all times. I've done some testing and if the app crashes or the user kills the app, I loose any updates. Given its a background running VOIP app I feel it is possible people will kill it on an occasion to save battery or whatever. So for that reason I loop through all the contacts checking the kABPersonModificationDateProperty property against the last NSDate I have recorded. If the modified is newer I begin my update, like so.
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
[dateFormatter setTimeStyle:NSDateFormatterMediumStyle];
[dateFormatter setDateStyle:NSDateFormatterMediumStyle];
NSDate *lastChecked = [dateFormatter dateFromString:[PListData readStringFromFile:#"lastContactsArchive"]];
CFDateRef modifyDate = ABRecordCopyValue(ref, kABPersonModificationDateProperty);
...
...
else if ([(__bridge NSDate*)modifyDate compare:lastChecked]==NSOrderedDescending) // if modified after last check, create new contact and update
{
...
}
Is there something that I'm missing, is the modification date only updated when the name / phone numbers / email etc are changed ? Are they modified when iCloud syncs for example ? is a counter updated if they call the person on the phone ? etc.
I've tried debugging on 3 phones and all behave as I would expect. I'm really drawing a blank here and the server isn't happy with me so any help would be appreciated.
Contacts may also update in background when iphone syncs contacts with iCloud / CardDav / Gmail / Exchange account.
Note that it also may happen while your app is running, so you should subscribe for address book updates.

Close app when internet is not available

I want to close my app when an Internet connection is not available.
I check that, but how can I create an alert, and then close my app?
You shouldn't force close an app as the standard way to terminate an application is to press the home button (or use the multitasking bar)
Don’t Quit Programmatically
Never quit an iOS application programmatically because people tend to
interpret this as a crash. However, if external circumstances prevent
your application from functioning as intended, you need to tell your
users about the situation and explain what they can do about it.
Depending on how severe the application malfunction is, you have two
choices.
Display an attractive screen that describes the problem and suggests a
correction. A screen provides feedback that reassures users that
there’s nothing wrong with your application. It puts users in control,
letting them decide whether they want to take corrective action and
continue using your application or press the Home button and open a
different application
If only some of your application's features are not working, display
either a screen or an alert when people activate the feature. Display
the alert only when people try to access the feature that isn’t
functioning.
Source
Your app should never close itself. iOS does not have the concept of quitting an app. You can inform the user that there is no internet connectivity and present a waiting screen or something else that shows them that your app is useless until the internet connection is available, but your app should continue running until the OS decides to shut you down.
Instead of closing it, consider explaining the situation to the user by the means of a popup.
First of all, download Reachability from Apple.
Add the classes Reachability.h,.m,delegates to your project. Then in your .m class import Reachability
#import "Reachability.h"
And in viewWillAppear or when you should display the alert:
//Connection check
Reachability *reach = [Reachability reachabilityForInternetConnection];
NetworkStatus netStatus = [reach currentReachabilityStatus];
if (netStatus == NotReachable)
{
UIAlertView * alert = [[UIAlertView alloc] initWithTitle:#"No Internet Connection" message:#"Explain the situation to the user" delegate:nil cancelButtonTitle:nil otherButtonTitles:#"Ok", nil];
[alert show];
[alert release];
}
else {
//other actions.
}
As others said before me.
According to August's ans here
"On the iPhone there is no concept of quitting an app. The only action that should cause an app to quit is touching the Home button on the phone, and that's not something developers have access to.
According to Apple, your app should not terminate on its own. Since the user did not hit the Home button, any return to the Home screen gives the user the impression that your app crashed. This is confusing, non-standard behavior and should be avoided."
But if you still want to quit your app programmatically then
there are two commands to quit the app.
1.exit(0)
2.[[NSThread mainThread] exit]

SKProductsRequest - how to handle timeouts / connection errors?

Cheers,
It appears to me like SKProductsRequest does not handle timeouts or connection errors in any way. It either calls -(void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response on its delegate in case of success, or it doesn't.
I'd like to present my users with some kind of activity indicator while the products are being retrieved, or maybe pop up an alert if the appstore can't be reached. Since (in case of failure) there's no feedback from the SKProductsRequest however, I wonder to which event I should tie the presentation of that feedback - other then waiting for an arbitrary amount of time.
So, the question is: Is there a known amount of time after which it is safe to assume the request has failed? Or is there any way to check upon the status of a pending request that I just failed to see?
I run this in my project for whenever an SKRequest fails (which includes SKProductRequest):
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error
{
alert = [[UIAlertView alloc] initWithTitle:#"In-App Store unavailable" message:#"The In-App Store is currently unavailable, please try again later." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
[alert show];
}
Works a treat. Of course you can place anything inside the brackets to replace my alert, but this works well for the user.
Hope this is of use to you.
Note: This is in SKRequestDelegate not in SKProductsRequestDelegate, which is slightly confusing. The SKRequestDelegate is used for purchasing and for product requests. The delegate set using request.delegate can implement methods for both.
I don't believe you can do anything other than wait an arbitrary amount of time. In some of my apps I wait 30 seconds (while showing a modal activity view) and then bail out with a generic error alert. The problem is, in reality 30 seconds is beyond most users' attention span for such issues, but if you make it short enough to be useful (say 15 seconds), you might actually bail too early.
I don't think there is a better option ... but I'm willing to learn otherwise!

Proper way to exit iPhone application?

I am programming an iPhone app, and I need to force it to exit due to certain user actions. After cleaning up memory the app allocated, what's the appropriate method to call to terminate the application?
On the iPhone there is no concept of quitting an app. The only action that should cause an app to quit is touching the Home button on the phone, and that's not something developers have access to.
According to Apple, your app should not terminate on its own. Since the user did not hit the Home button, any return to the Home screen gives the user the impression that your app crashed. This is confusing, non-standard behavior and should be avoided.
Have you tried exit(0)?
Alternatively, [[NSThread mainThread] exit], although I have not tried that it seems like the more appropriate solution.
exit(0) appears to a user as crashes, so show a confirmation message to user. After confirmation suspend(home button press programmatically) and wait 2 seconds while app is going background with animation then exit behind user's view
-(IBAction)doExit
{
//show confirmation message to user
UIAlertView* alert = [[UIAlertView alloc] initWithTitle:#"Confirmation"
message:#"Do you want to exit?"
delegate:self
cancelButtonTitle:#"Cancel"
otherButtonTitles:#"OK", nil];
[alert show];
}
-(void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex != 0) // 0 == the cancel button
{
//home button press programmatically
UIApplication *app = [UIApplication sharedApplication];
[app performSelector:#selector(suspend)];
//wait 2 seconds while app is going background
[NSThread sleepForTimeInterval:2.0];
//exit app when app is in background
exit(0);
}
}
Check the Q&A here: https://developer.apple.com/library/content/qa/qa1561/_index.html
Q: How do I programmatically quit my iOS application?
There is no API provided for gracefully terminating an iOS application.
In iOS, the user presses the Home button to close applications. Should your application have conditions in which it cannot provide its intended function, the recommended approach is to display an alert for the user that indicates the nature of the problem and possible actions the user could take — turning on WiFi, enabling Location Services, etc. Allow the user to terminate the application at their own discretion.
WARNING: Do not call the exit function. Applications calling exit will appear to the user to have crashed, rather than performing a graceful termination and animating back to the Home screen.
Additionally, data may not be saved, because -applicationWillTerminate: and similar UIApplicationDelegate methods will not be invoked if you call exit.
If during development or testing it is necessary to terminate your application, the abort function, or assert macro is recommended
Its not really a way to quit the program, but a way to force people to quit.
UIAlertView *anAlert = [[UIAlertView alloc] initWithTitle:#"Hit Home Button to Exit" message:#"Tell em why they're quiting" delegate:self cancelButtonTitle:nil otherButtonTitles:nil];
[anAlert show];
Go to your info.plist and check the key "Application does not run in background". This time when the user clicks the home button, the application exits completely.
Add UIApplicationExitsOnSuspend property on application-info.plist to true.
After some tests, I can say the following:
using the private interface : [UIApplication sharedApplication] will cause the app looking like it crashed, BUT it will call - (void)applicationWillTerminate:(UIApplication *)application before doing so;
using exit(0); will also terminate the application, but it will look "normal" (the springboard's icons appears like expected, with the zoom out effect), BUT it won't call the - (void)applicationWillTerminate:(UIApplication *)application delegate method.
My advice:
Manually call the - (void)applicationWillTerminate:(UIApplication *)application on the delegate.
Call exit(0);.
Your ApplicationDelegate gets notified of intentional quitting by the user:
- (void)applicationWillResignActive:(UIApplication *)application {
When I get this notification I just call
exit(0);
Which does all the work. And the best thing is, it is the useres intent to quit, which is why this should not be a problem calling it there.
On my Audio-App it was necessary to quit the app after people were syncing their device while the music was still playing. As soon as the syncing is complete I get a notification. But quitting the app right after that would actually look like a crash.
So instead I set a flag to REALLY quit the app on the next backgrounding action. Which is okay for refreshing the app after a sync.
My App has been rejected recently bc I've used an undocumented method. Literally:
"Unfortunately it cannot be added to the App Store because it is using a private API. Use of non-public APIs, which as outlined in the iPhone Developer Program License Agreement section 3.3.1 is prohibited:
"3.3.1 Applications may only use Documented APIs in the manner prescribed by Apple and must not use or call any private APIs."
The non-public API that is included in your application is terminateWithSuccess"
Apple say:
"Warning: Do not call the exit function. Applications calling exit will appear to the user to have crashed, rather than performing a graceful termination and animating back to the Home screen."
I think that this is a bad assumption. If the user tap a quit button and a message appears that say something like: "The application will now quit.", it doesn't appear to be crashed. Apple should provide a valid way to quit an application (not exit(0)).
You should not directly call the function exit(0) as it will quit the application immediately and will look like your app is crashed. So better to show users a confirmation alert and let them do this themselves.
Swift 4.2
func askForQuit(_ completion:#escaping (_ canQuit: Bool) -> Void) {
let alert = UIAlertController(title: "Confirmation!", message: "Do you want to quit the application", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "Yes", style: UIAlertAction.Style.default, handler: { (action) in
alert.dismiss(animated: true, completion: nil)
completion(true)
}))
alert.addAction(UIAlertAction(title: "No", style: UIAlertAction.Style.cancel, handler: { (action) in
alert.dismiss(animated: true, completion: nil)
completion(false)
}))
self.present(alert, animated: true, completion: nil)
}
/// Will quit the application with animation
func quit() {
UIApplication.shared.perform(#selector(NSXPCConnection.suspend))
/// Sleep for a while to let the app goes in background
sleep(2)
exit(0)
}
Usage:
self.askForQuit { (canQuit) in
if canQuit {
self.quit()
}
}
This has gotten a good answer but decided to expand a bit:
You can't get your application accepted to AppStore without reading Apple's iOS Human Interface Guidelines well. (they retain the right to reject you for doing anything against them) The section "Don't Quit Programmatically" http://developer.apple.com/library/ios/#DOCUMENTATION/UserExperience/Conceptual/MobileHIG/UEBestPractices/UEBestPractices.html
is an exact guideline in how you should treat in this case.
If you ever have a problem with Apple platform you can't easily find a solution for, consult HIG. It's possible Apple simply doesn't want you to do it and they usually (I'm not Apple so I can't guarantee always) do say so in their documentation.
Hm, you may 'have to' quit the application if, say, your application requires an internet connection. You could display an alert and then do something like this:
if ([[UIApplication sharedApplication] respondsToSelector:#selector(terminate)]) {
[[UIApplication sharedApplication] performSelector:#selector(terminate)];
} else {
kill(getpid(), SIGINT);
}
We can not quit app using exit(0), abort() functions, as Apple strongly discourage the use of these functions. Though you can use this functions for development or testing purpose.
If during development or testing it is necessary to terminate your
application, the abort function, or assert macro is recommended
Please find this Apple Q&A thread to get more information.
As use of this function create impression like application is crashing. So i got some suggestion like we can display Alert with termination message to aware user about closing the app, due to unavailability of certain functionality.
But iOS Human Interface Guideline for Starting And Stopping App, suggesting that Never use Quit or Close button to terminate Application. Rather then that they are suggesting to display proper message to explain situation.
An iOS app never displays a Close or Quit option. People stop using an
app when they switch to another app, return to the Home screen, or put
their devices in sleep mode.
Never quit an iOS app programmatically. People tend to interpret this
as a crash. If something prevents your app from functioning as
intended, you need to tell users about the situation and explain what
they can do about it.
In addition to the above, good, answer I just wanted to add, think about cleaning up your memory.
After your application exits, the iPhone OS will automatically clean up anything your application left behind, so freeing all memory manually can just increase the amount of time it takes your application to exit.
- (IBAction)logOutButton:(id)sender
{
//show confirmation message to user
CustomAlert* alert = [[CustomAlert alloc] initWithTitle:#"Confirmation" message:#"Do you want to exit?" delegate:self cancelButtonTitle:#"Cancel" otherButtonTitles:#"OK", nil];
alert.style = AlertStyleWhite;
[alert setFontName:#"Helvetica" fontColor:[UIColor blackColor] fontShadowColor:[UIColor clearColor]];
[alert show];
}
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex
{
if (buttonIndex != 0) // 0 == the cancel button
{
//home button press programmatically
UIApplication *app = [UIApplication sharedApplication];
[app performSelector:#selector(suspend)];
//wait 2 seconds while app is going background
[NSThread sleepForTimeInterval:2.0];
//exit app when app is in background
NSLog(#"exit(0)");
exit(0);
}
}
I used the [[NSMutableArray new] addObject:nil] approach mentioned above to force-quit (crash) the app without making a tell-tale exit(0) function call.
Why? Because my app uses certificate pinning on all network API calls to prevent man-in-the-middle attacks. These include the initialization calls my financial app makes on startup.
If certificate authentication fails, all of my initialization calls error out and leave my app in an indeterminate state. Letting the user go home and then back into the app doesn't help, as unless the app has been purged by the OS it's still uninitialized and untrustworthy.
So, in this one case, we deemed it best to pop an alert informing the user that the app is operating in an insecure environment and then, when they hit "Close", force quit the app using the aforementioned method.
[[UIApplication sharedApplication] terminateWithSuccess];
It worked fine and automatically calls
- (void)applicationWillTerminateUIApplication *)application delegate.
to remove compile time warning add this code
#interface UIApplication(MyExtras)
- (void)terminateWithSuccess;
#end
The user should decide when an app exits.
I don't think it is a good user interaction when an app quits. Therefore there is no nice API for it, only the home button has one.
If there is an error: Implement it better or Notify the user.
If there have to be a restart: Implement it better of Notify the user.
It sounds dumb, but it's bad practice to exit the app without letting the user decide and not notifying him. And since there is a home button for the user interaction, Apple states, there should not be 2 things for the same function (exiting an app).
Exit an app other way than the home button is really non-iOS-esque approach.
I did this helper, though, that use no private stuff:
void crash()
{ [[NSMutableArray new] addObject:NSStringFromClass(nil)]; }
But still not meant for production in my case. It is for testing crash reportings, or to fast restart after a Core Data reset. Just made it safe not to be rejected if function left in the production code.
It may be appropriate to exit an app if it is a long lived app that also executes in the background, for example to get location updates (using the location updates background capability for that).
For example, let's say the user logs out of your location based app, and pushes the app to the background using the home button. In this case your app may keep running, but it could make sense to completely exit it. It would be good for the user (releases memory and other resources that don't need to be used), and good for app stability (i.e. making sure the app is periodically restarted when possible is a safety net against memory leaks and other low memory issues).
This could (though probably shouldn't, see below :-) be achieved with something like:
- (void)applicationDidEnterBackground:(UIApplication *)application
{
if (/* logged out */) {
exit(0);
} else {
// normal handling.
}
}
Since the app would then exit out of the background it will not look wrong to the user, and will not resemble a crash, providing the user interface is restored the next time they run the app. In other words, to the user it would not look any different to a system initiated termination of the app when the app is in the background.
Still, it would be preferable to use a more standard approach to let the system know that the app can be terminated. For example in this case, by making sure the GPS is not in use by stopping requesting location updates, including turning off show current location on a map view if present. That way the system will take care of terminating the app a few minutes (i.e. [[UIApplication sharedApplication] backgroundTimeRemaining]) after the app enters the background. This would get all the same benefits without having to use code to terminate the app.
- (void)applicationDidEnterBackground:(UIApplication *)application
{
if (/* logged out */) {
// stop requesting location updates if not already done so
// tidy up as app will soon be terminated (run a background task using beginBackgroundTaskWithExpirationHandler if needed).
} else {
// normal handling.
}
}
And of course, using exit(0) would never be appropriate for the average production app that runs in the foreground, as per other answers that reference http://developer.apple.com/iphone/library/qa/qa2008/qa1561.html
Swift 4.2 (or older)
Library called Darvin can be used.
import Darwin
exit(0) // Here you go
NB: This is not recomanded in iOS applications.
Doing this will get you crash log.
In iPadOS 13 you can now close all scene sessions like this:
for session in UIApplication.shared.openSessions {
UIApplication.shared.requestSceneSessionDestruction(session, options: nil, errorHandler: nil)
}
This will call applicationWillTerminate(_ application: UIApplication) on your app delegate and terminate the app in the end.
But beware of two things:
This is certainly not meant to be used for closing all scenes. (see https://developer.apple.com/design/human-interface-guidelines/ios/system-capabilities/multiple-windows/)
It compiles and runs fine on iOS 13 on an iPhone, but seems to do nothing.
More info about scenes in iOS/iPadOS 13: https://developer.apple.com/documentation/uikit/app_and_environment/scenes

Resources