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

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.

Related

This app cannot be approved with CallKit functionality active in China. Please make the appropriate changes and resubmit this app for review

Recently, the Chinese Ministry of Industry and Information Technology (MIIT) requested that CallKit functionality be deactivated in all apps available on the China App Store. During our review, we found that your app currently includes CallKit functionality and has China listed as an available territory in iTunes Connect.
Now, Question is what next, Which kind of changes require in app
If there isn't any way, How can i remove china from Apple store.
Please share your suggestion if anyone faced this kind of problem.
Regards,
My approach to this issue was inspired by this response on the Apple Developer forums. The general developer consensus right now seems to be that App Review is not giving specific recommendations nor are they currently explaining or requiring a specific technical solution. I think that as long as you can explain to App Review how you’re disabling CallKit for users in China, that would be acceptable.
I updated my app as I discuss below and it passed App Store review first try and we re-released in China on July 24, 2018.
When I submitted my updated app to the App Store, I included a short message in the reviewer info section saying
"In this version and onwards, we do not use CallKit features for users in China. We detect the user's region using NSLocale."
My app was approved 12hr later without any questions or comments from the App Review team.
Detecting users in China
In my app, I use NSLocale to determine if the user is in China. If so, I do not initialize or use CallKit features in my app.
- (void)viewDidLoad {
[super viewDidLoad];
NSLocale *userLocale = [NSLocale currentLocale];
if ([userLocale.countryCode containsString: #"CN"] || [userLocale.countryCode containsString: #"CHN"]) {
NSLog(#"currentLocale is China so we cannot use CallKit.");
self.cannotUseCallKit = YES;
} else {
self.cannotUseCallKit = NO;
// setup CallKit observer
self.callObserver = [[CXCallObserver alloc] init];
[self.callObserver setDelegate:self queue:nil];
}
}
To test this, you can change the region in Settings > General > Language and Region > Region. When I set Region to 'China' but left language set as English, [NSLocale currentLocale] returned "en_CN".
I was using CXCallObserver to observe the state of a call initiated from my app. My general workaround when I could not use CallKit to monitor the call was:
save the NSDate when the call begins
observer for UIApplicationDidBecomeActiveNotification with a UIBackgroundTask with expiration handler (my app already has background modes enabled)
when the app returns from the background, check the elapsed time and if it is was than 5s and less than 90 minutes, assume the call ended and save it (I needed to track call duration).
If the backgroundTaskExpirationHandler is called, assume the call ended and save the end time.
I decided to wait til at least 5s had elapsed because I noticed that -applicationDidBecomeActive was often called once or twice as the call began, usually within the first 1-3 seconds.
Go to “Pricing and Availability” in iTunes Connect.
Availability” (Click blue button Edit).
Deselect China in the list “Deselect” button.
Click “Done”.

How to delete all the Events of My App Calendar when app is deleted from device in iOS

I am having a calendar with name MyCalendar.
I have stored some events to that calendar..
I know how to delete all the events from the calendar..as below
NSDate *startDate = [NSDate date];
NSDate* endDate = [NSDate dateWithTimeIntervalSinceNow:[[NSDate distantFuture] timeIntervalSinceReferenceDate]];
NSArray *calendarArray = [NSArray arrayWithObject:self.defaultCalendar];
NSPredicate *predicate = [self.eventStore predicateForEventsWithStartDate:startDate
endDate:endDate
calendars:calendarArray];
NSArray *events = [self.eventStore eventsMatchingPredicate:predicate];
for (EKEvent *event in events) {
NSError* err = nil;
[self.eventStore removeEvent:event span:EKSpanFutureEvents commit:YES error:&err];
}
But when user deleted my app from device. How can I delete all the events related to Calendar MyCalendar.
As unnecessary some events of the deleted app still exists at device calendar.
It is worst to keep events related to My App if user don't want to use My App..
Any Ideas & Suggestions are appreciated..Is it possible?
Thanks in Advance..
It is pretty straightforward that we don't have any control to do some action when user is going to delete application from iPhone. May be you can provide Delete All Events option inside application to notify user with proper massage. i think user at least single time used your application before deleting. so i think if he/she has proper idea of how this app is working then defiantly they can delete app events from application and then deleting app.
Actually we don't have any option that why i would suggest you this one. what so amount of user taking advantage of this option will helpful for them.
Actually weather apple handle such functionality or not it up to them. because some time events is not just related to app like Adding birthday event from facebook is not just related to facebook. user need that reminder even though facebook app is deleted. so i think apple need to figure out certain solution from handling such situation.
Hope this help you.
I also don't think this is possible. And I would also think that there may had been countless radars requesting it during the years.
I think the problem is the way you store the events. If they are just related to your app, then you should store them in app, and they are only accessible in the app, and deleted when the app is deleted.
If you store them to device Calendar, then I see your app just as a manager of calendar events. Not your app events, but the USER events. He can use your app to manage them, or another app. If they are in the user Calendar, they are probably saved on cloud too, and he can manage them from the mac too.
If they would allow the functionality that you want, what would stop you from being destructive and delete all the events in the user Calendar, as a revenge because it deletes your app?
An iOS application doesn't receive any callbacks when it is about to be uninstalled, so it's not possible to do what you want.
See this question Detect iOS application about to delete?

NSUserDefaults Values Are Lost Periodically

I use [NSUserDefaults standardDefaults] to store a boolean to see if it is the first time that application is being launched ... If so, the app should show a registration window.
It was working fine till last week, but now, sometimes when I switch to other apps and come back after a little while, I see that registration page loads while it shouldn't.
I used NSLog to see what is stored in [NSUserDefaults standardDefaults] and I see that the values I stored has been set to nil (null) while I haven't done that anywhere in my code.
Does anyone know why the values do reset ?
P.S: Actually the values are not permanently lost , because if I don't do anything in registration page and quit the app instead, It will launch normally the next time I enter the app !!!
A long time ago I encountered this issue, turns out a third party library that I was using uses the same key when storing values to NSUserDefaults. Try searching your project for this key, maybe something else is resetting it.
Here are the ways I know about to lose values in NSUserDefaults, in order of likelihood:
The key was never saved in the first place
Your app deletes it later on
The app is deleted and reinstalled
Another app overwrites or removes the key
The phone or simulator is reset
During the night, the phone is replaced by an identical-looking, different phone
It sounds like, from the discussion here, that you've ruled out 1,2,4, and probably 3 & 5. The only next debug step I can think of is to store the test phone in a locked drawer at all times.
But I'd leave my money on an intermittent problem causing #1. For that, we'd need posted code to investigate.
EDIT -
A high % of NSUserDefaults problems posted here are about storing BOOLs and other scalar types. It looks like the OP knows about wrapping in NSNumbers, but BOOLS in particular are fraught because it's easy to confuse false-y values like NO no and nil, and NSNull instance.
Let's throw that on the list for this question at #2.5. There again, would need code to confirm.
If this is happening while testing, it's normal. The fact that the program is even making this decision (should I show the registration page?) suggests that the app has been forcibly quit and is starting from scratch. When testing, this can result in clearing out the app sandbox as the app is reloaded from Xcode. In the real life of a real user, however, that won't happen (unless the user deletes the app from the device).
Make sure you are calling [[NSUserDefaults standardUserDefaults] synchronize] just after setting preferences and you are not overwriting you preferences.

Restricting iOS app use by date

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!

Setting an Expiry date for iPhone app [duplicate]

This question already has answers here:
NSDate: Get precise time independent of device clock? [duplicate]
(4 answers)
Closed 9 years ago.
Currently i am developing one gaming iOS application which i will give to some of my customer for demo.
But while distribution of my app i want to set an expiry mechanism in my app, so that it will get expire after "N" number of days.
It means that (e.g. after 30 days) my app won't work.i can able to this by using system date & time, it means whenever my program starts i check today date and expire date.
The problem is if the user changes system time, my comparison won't be correct.
Also if user delete & reinstall the app,it wont work.
Any help or suggestion?
Look at Ad Hoc Distribution
Ad-hoc development builds last for 3 months, however if you want to add a time limit then you could:
Add a key-value in NSUserDefaults which stores the start date of the app (time taken from a webservice such as http://www.earthtools.org/webservices.htm#timezone) then every time the app enters the foreground check the time against it and if it is over the threshold then to cancel loading the app.
NSDate *now = //load current datetime from restkit
NSDate *startDate = [[NSUserDefaults standardUserDefaults] objectForKey:#"startDate"];
NSDate *futureDate = [NSDate dateWithTimeInterval:60 * 60 * 24 * 30 sinceDate:startDate];
if ([futureDate compare:now] == NSOrderedAscending) {
//stop the application loading by loading a NIB and displaying a message.
}
I would recommend using RestKit to request data from the web-service.
When the app expires, store something in the Keychain to indicate this fact. Then just check that at startup, rather than the date.
Alternatively, you can have it check a web service you control.

Resources