Is there any ways to detect the roaming status on iOS 6? - ios

My application using below methods to detect roamming in iOS 4 and 5.
NSString *carrierPListSymLinkPath = #"/var/mobile/Library/Preferences/com.apple.carrier.plist";
NSString *operatorPListSymLinkPath = #"/var/mobile/Library/Preferences/com.apple.operator.plist";
NSFileManager *fm = [NSFileManager defaultManager];
NSError *error = nil;
NSString *carrierPListPath = [fm destinationOfSymbolicLinkAtPath:carrierPListSymLinkPath error:&error];
NSString *operatorPListPath = [fm destinationOfSymbolicLinkAtPath:operatorPListSymLinkPath error:&error];
return (![operatorPListPath isEqualToString:carrierPListPath]);
But this code always return false on iOS6 (even i am not roaming, it always return false), i think it maybe the plist file location changed by Apple, does any one face the same issue, can anyone help me on this?
Thanks.

This is an answer I got from apple :
"Indeed. Unfortunately these files were never documented as public API and, as such, were never intended to be used by third party apps. In a lot of cases such restrictions are enforced, either technically by the iOS sandbox or administratively by App Review. However, neither of these processes is perfect, and there are occasions where apps end up doing things they shouldn't be doing. Unfortunately this puts these apps in a difficult position when the enforcement improves, as has happened in this case.
As to direct workarounds there really isn't one. The only public telephony APIs on iOS are provided by the Core Telephony framework. Its CTTelephonyNetworkInfo class gives you information about the user's 'home' cellular server, not about the network that they are roaming on."
They suggest you use geo-ip check.

Facing the same problem here since iOS6 beta 1, since that version /var/mobile/Library/Preferences/com.apple.operator.plist sym-link is no more reversable.
No other device-only way are public now, in the meanwhile the unique solution is to check for the ip via geo-ip, as Trausti Thor said, or checking for the ip class if you are developing an app for a single carrier.

Related

No "kCFBundleVersionKey" sometimes: all possible scenarios?

I have a code like this in my app:
NSString* version = [[NSBundle mainBundle] objectForInfoDictionaryKey:(NSString *)kCFBundleVersionKey];
In most cases it works, and returns the Bundle version, but sometimes (let's say in 2% of cases) it returns nil.
The code is called within the function [AppDelegate application:didFinishLaunchingWithOptions:], in Main thread, an app is in foreground.
I could imagine this being an Apple's bug with some file reading error, but the percentage is quite high as for a rare Apple bug.
Also I know a one could mess with versions/bundles/Info.plist - but the percentage is too small for such case.
So, the first question: what can be the reason of [[NSBundle mainBundle] objectForInfoDictionaryKey:(NSString *)kCFBundleVersionKey] returning nil in this case?
The second question: do you know if these hypotheses make sense / are easy to check:
The user launches an app the first time after update, and [NSBundle mainBundle] becomes fully configured after application:didFinishLaunchingWithOptions ?
The app is in the process of an auto-update (from AppStore), the user opens it, and the system is currently writing a new data to Info.plist.
Some background thread in my app is also reading the [NSBundle mainBundle], the system uses some weird lock, so the read from a Main thread fails.
UPD: I've seen this question, but it's not related.
By default all files of the application, on a device that uses content protection, are encrypted. If you try to read them before they are decrypted(by the OS) you will get nil. The files are decrypted and they are available shortly after the user unlocks her phone, this might change and be more strict if you set a different value for the data protection entitlement. So the data might not be available when the application launches because they are not yet decrypted. This might be one reason that you get nil some times, the solution would be to wait to be notified in the app delegate that they data are ready before you read them.
The answers to all parts of the second questions are NO. There is no documented configuration that the app needs to do after an update that might affect the main Bundle. The user can not open an application if it is in the process of updating. The access to the main Bundle is thread-safe.

Use system channel instead of ringer channel when using EPSSampler in iOS

I'm using EPSSampler to play some notes in my tiny iOS app. It works nicely, except that the sound is controlled through the ringer settings instead of the system volume settings. The problem is that, if the ringer is turned off, my app makes no sound.
The closest I could find was this SO question but I can't quite translate that solution into mine, as I can't match it to EPSSampler's code. Any insight?
EDIT To clarify, the only code that deals specifically with audio is contained in the EPSSampler class, my app merely instances it, loads an AUSampler preset, and calls the note-playing methods in EPSSampler, so a solution would need to change something in EPSSampler rather than in my app -- that's why I'm not posting any source code.
Turns out this had nothing to do with EPSSampler, and everything with configuring the audio session. The following code did the trick.
NSError *error = nil;
[[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayback error:&error];
That'll teach me to RTFM.
For swift(Use when initialising the music):
_ = try AVAudioSession.sharedInstance().setCategory(AVAudioSessionCategoryPlayback)

Will performFetchWithCompletionHandler be called if the app has been terminated

It's surprisingly difficult to find a definitive answer to this; couldn't find it mentioned in the Apple documentation and couldn't find a definite yes/no after searching past questions.
The question is simple - if the app requests a background fetch to be performed after N time, then the user terminates the app. Will the OS still launch the app into the background to perform the background fetch?
Okay, once again background modes cause confusion. No offense to the other people trying to help, but this is more complicated than it seems.
First of all:
This is out of date, as Sausage guessed in the comments. I know this for a fact, because the section about VoIP apps is still explaining the "old way" to do this, with a handler that gets called periodically. I investigated this a bit for this answer, so I suggest you go and read that. The important lesson for this case here is that iOS makes a distinction between an app being terminated by the user or by the system, plus it also plays a role whether the phone was rebooted or not.
So to sum this (and your question) up you basically want to know whether this part of the above, outdated documentation is still correct word for word:
In most cases, the system does not relaunch apps after they are force quit by the user. One exception is location apps, which in iOS 8 and later are relaunched after being force quit by the user. In other cases, though, the user must launch the app explicitly or reboot the device before the app can be launched automatically into the background by the system. When password protection is enabled on the device, the system does not launch an app in the background before the user first unlocks the device.
Apple: Understanding When Your App Gets Launched into the Background
I thoroughly investigated the rest of the docs, but did not find any definite answer, so it unfortunately boils down to what dan already suggested: Test it. My gut feeling is that the documentation is still correct in that regard, though (as said what's not is the VoIP stuff). I say that because the UI in the Settings app calls the feature "Background App Refresh", so users are probably supposed to understand that an app having this permission won't refresh when they "push" them out of background (i.e. home button -> swipe it out). For regular users, apps are either quit (not in the task manager at all), in the foreground (using them) or in background (they're in the task manager and another app is in foreground and/or the phone is locked).
To really test this you'd have to write an app and actually carry it around a bit (I assume at least two days) in each condition. First while it is in background (the OS should periodically let it fetch, as you probably know this can also be triggered in Xcode) and then while it is force-quit. The problem is to verify that it fetched stuff. I'd go with a logfile that can be shared via iTunes. I have typed up some code for this:
-(void)application:(UIApplication *)application performFetchWithCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler
{
NSLog(#"We're awake! Booyah!");
NSURLSessionConfiguration *config = [NSURLSessionConfiguration defaultSessionConfiguration];
NSURLSession *session = [NSURLSession sessionWithConfiguration:config
delegate:nil
delegateQueue:[NSOperationQueue mainQueue]];
NSMutableURLRequest *request = [NSMutableURLRequest new];
request.HTTPMethod = #"GET";
request.URL = [NSURL URLWithString:#"https://www.google.com"];
NSURLSessionDataTask *task = [session dataTaskWithRequest:request
completionHandler:^(NSData * _Nullable data,
NSURLResponse * _Nullable response,
NSError * _Nullable error) {
NSDate *now = [NSDate date];
NSString *toLog = [NSString stringWithFormat:#"%# - fetched\n",
[now description]];
[self updateTestDocumentWithString:toLog];
NSLog(#"Yay, done!");
completionHandler(UIBackgroundFetchResultNewData);
}];
[task resume];
}
- (void)updateTestDocumentWithString:(NSString *)toAppend {
NSString *docDir = [NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) firstObject];
NSString *filePath = [[docDir stringByAppendingPathComponent:#"logfile.txt"] copy];
if (![[NSFileManager defaultManager] fileExistsAtPath:filePath]) {
if (![[NSFileManager defaultManager] createFileAtPath:filePath contents:nil attributes:nil]) {
NSLog(#"We're effed...");
return;
}
}
NSFileHandle *file = [NSFileHandle fileHandleForUpdatingAtPath:filePath];
if (!file) {
NSLog(#"We're effed again...");
return;
}
[file seekToEndOfFile];
// ensure this is never nil
[file writeData:[toAppend dataUsingEncoding:NSUTF8StringEncoding]];
[file closeFile];
}
This would go into the app delegate, and don't forget to add the Application supports iTunes file sharing boolean setting in your app's plist. I will leave this running on my development device for a bit and check the logfile, eventually reporting back here. Feel free to test it yourself, too.
EDIT:
https://devforums.apple.com/message/873265#873265 (login required)
Also keep in mind that if you kill your app from the app switcher
(i.e. swiping up to kill the app) then the OS will never relaunch the
app regardless of push notification or background fetch. In this case
the user has to manually relaunch the app once and then from that
point forward the background activities will be invoked. -pmarcos
That post was by an Apple employee so I think i can trust that this information is correct.
OLD answer:
According to this answer wrote by a top user: iOS background fetch: your app won't be woken up again.
Make sure you're not killing the app (i.e. by double tapping on the
home button and swiping up on your app for force the app to
terminate). If the app is killed, it will prevent background fetch
from working correctly.
It really doesn't make sense for it to be woken up...it kinda invalidates the user killing the app.
Having that said there are different ways a terminated/force quit app can be launched again:
Tapping on a notification.
Tapping on the app icon.
Using openUrl to open your app from another app.
If you use PushKit...then your app would be launched. Imagine if had a VOIP app e.g. Skype, WhatsApp and a friend was calling you but you had have force-quit the app, you wouldn't receive calls. For more see here.
Location updates either through use of region monitoring or the significant-change location service. See this answer and make sure to read this entire page from Apple docs.
Rebooting the device would also undo anything blocked through force-quit
Reading the Apple documentation here I found this text snippet which should explain your question:
The techniques offered by iOS fall into three categories:
Apps that start a short task in the foreground can ask for time to finish that task when the app moves to the background.
**Apps that initiate downloads in the foreground can hand off management of those downloads to the system, thereby allowing the app to be suspended or terminated while the download continues.**
Apps that need to run in the background to support specific types of tasks can declare their support for one or more background execution modes.
The second option is exactly about downloading the data, which can be delegated to the system even if the can be terminated.

Get serial number of device on iOS 8

For an in-house application, we were using the following code UIDevice+serialNumber to get the device serial number.
However, it seems that with iOS 8, the registry key "IOPlatformSerialNumber" is empty.
Can the serial number be obtained any other way?
Answer: No.
There are currently floating solutions around abusing the .mobileconfig, but they add other problems due their nature.
UUID was removed for a reason. See the news around privacy and the summon of Apple by the US Senat from 2011 to gain an understanding why this had to be changed.
http://arstechnica.com/apple/2011/04/apple-google-asked-to-join-judiciary-hearing-on-mobile-device-privacy/
Abusing the underlying march port to get the device serial number as replacement of the UUID is not the solution. Same now for abusing the mobile config and guess how long that will work.
You have plenty of decent ways to handle any situation where you used device identification before. Especial in an enterprise environment where you have cryptography and MDM is absolutely no point to track a device like this.
Retrieving the device serial number directly is no longer possible in iOS.
However, if it's simply for an internal enterprise app, something the company I work for is going to implement is this:
During device configuration, copy the serial number and set the device name as the serial number.
Then use Apple's UIDevice API to retrieve the device name.
As Helger stated above, UDID is no longer available in iOS 6+ due to security / privacy reasons. Instead, use identifierForVendor or advertisingIdentifier
Please check UDID Replacement APIs
For iOS 8+
+ (NSString *)deviceUUID
{
if([[NSUserDefaults standardUserDefaults] objectForKey:[[NSBundle mainBundle] bundleIdentifier]])
return [[NSUserDefaults standardUserDefaults] objectForKey:[[NSBundle mainBundle] bundleIdentifier]];
#autoreleasepool {
CFUUIDRef uuidReference = CFUUIDCreate(nil);
CFStringRef stringReference = CFUUIDCreateString(nil, uuidReference);
NSString *uuidString = (__bridge NSString *)(stringReference);
[[NSUserDefaults standardUserDefaults] setObject:uuidString forKey:[[NSBundle mainBundle] bundleIdentifier]];
[[NSUserDefaults standardUserDefaults] synchronize];
CFRelease(uuidReference);
CFRelease(stringReference);
return uuidString;
}
}
You can use identifierForVendor after that store them to keychain and use later. Because value of keychain will not changed when re-install app.
I used to use Open UDID for this purpose. Don't know if it still works.
The serial number for Apple devices can be found under Settings -> General -> About -> Serial number.

Programmatically get own phone number in iOS

Is there any way to get own phone number by standard APIs from iPhone SDK?
At the risk of getting negative marks, I want to suggest that the highest ranking solution (currently the first response) violates the latest SDK Agreement as of Nov 5, 2009. Our application was just rejected for using it. Here's the response from Apple:
"For security reasons, iPhone OS restricts an application (including its preferences and data) to a unique location in the file system. This restriction is part of the security feature known as the application's "sandbox." The sandbox is a set of fine-grained controls limiting an application's access to files, preferences, network resources, hardware, and so on."
The device's phone number is not available within your application's container. You will need to revise your application to read only within your directory container and resubmit your binary to iTunes Connect in order for your application to be reconsidered for the App Store.
This was a real disappointment since we wanted to spare the user having to enter their own phone number.
No, there's no legal and reliable way to do this.
If you find a way, it will be disabled in the future, as it has happened with every method before.
Update: capability appears to have been removed by Apple on or around iOS 4
Just to expand on an earlier answer, something like this does it for me:
NSString *num = [[NSUserDefaults standardUserDefaults] stringForKey:#"SBFormattedPhoneNumber"];
Note: This retrieves the "Phone number" that was entered during the iPhone's iTunes activation and can be null or an incorrect value. It's NOT read from the SIM card.
At least that does in 2.1. There are a couple of other interesting keys in NSUserDefaults that may also not last. (This is in my app which uses a UIWebView)
WebKitJavaScriptCanOpenWindowsAutomatically
NSInterfaceStyle
TVOutStatus
WebKitDeveloperExtrasEnabledPreferenceKey
and so on.
Not sure what, if anything, the others do.
Using Private API you can get user phone number on the following way:
extern NSString* CTSettingCopyMyPhoneNumber();
+(NSString *) phoneNumber {
NSString *phone = CTSettingCopyMyPhoneNumber();
return phone;
}
Also include CoreTelephony.framework to your project.
You cannot use iOS APIs alone to capture the phone number (even in a private app with private APIs), as all known methods of doing this have been patched and blocked as of iOS 11. Even if a new exploit is found, Apple has made clear that they will reject any apps from the app store for using private APIs to do this. See #Dylan's answer for details.
However, there is a legal way to capture the phone number without any user data entry. This is similar to what Snapchat does, but easier, as it does not require the user to type in their own phone number.
The idea is to have the app programmatically send a SMS message to a server with the app’s unique installation code. The app can then query the same server to see if it has recently received a SMS message from a device with this unique app installation code. If it has, it can read the phone number that sent it. Here’s a demo video showing the process. As you can see, it works like a charm!
This is not super easy to set up, but it be configured in a few hours at no charge on a free AWS tier with the sample code provided in the tutorial here.
As you probably all ready know if you use the following line of code, your app will be rejected by Apple
NSString *num = [[NSUserDefaults standardUserDefaults] stringForKey:#"SBFormattedPhoneNumber"];
here is a reference
http://ayeapi.blogspot.com/2009/12/sbformatphonenumber-is-lie.html
you can use the following information instead
NSString *phoneName = [[UIDevice currentDevice] name];
NSString *phoneUniqueIdentifier = [[UIDevice currentDevice] uniqueIdentifier];
and so on
#property(nonatomic,readonly,retain) NSString *name; // e.g. "My iPhone"
#property(nonatomic,readonly,retain) NSString *model; // e.g. #"iPhone", #"iPod Touch"
#property(nonatomic,readonly,retain) NSString *localizedModel; // localized version of model
#property(nonatomic,readonly,retain) NSString *systemName; // e.g. #"iPhone OS"
#property(nonatomic,readonly,retain) NSString *systemVersion; // e.g. #"2.0"
#property(nonatomic,readonly) UIDeviceOrientation orientation; // return current device orientation
#property(nonatomic,readonly,retain) NSString *uniqueIdentifier; // a string unique to each device based on various hardware info.
Hope this helps!
To get you phone number you can read a plist file. It will not work on non-jailbroken iDevices:
NSString *commcenter = #"/private/var/wireless/Library/Preferences/com.apple.commcenter.plist";
NSDictionary *dict = [NSDictionary dictionaryWithContentsOfFile:commcenter];
NSString *PhoneNumber = [dict valueForKey:#"PhoneNumber"];
NSLog([NSString stringWithFormat:#"Phone number: %#",PhoneNumber]);
I don't know if Apple allow this but it works on iPhones.
No official API to do it. Using private API you can use following method:
-(NSString*) getMyNumber {
NSLog(#"Open CoreTelephony");
void *lib = dlopen("/Symbols/System/Library/Framework/CoreTelephony.framework/CoreTelephony",RTLD_LAZY);
NSLog(#"Get CTSettingCopyMyPhoneNumber from CoreTelephony");
NSString* (*pCTSettingCopyMyPhoneNumber)() = dlsym(lib, "CTSettingCopyMyPhoneNumber");
NSLog(#"Get CTSettingCopyMyPhoneNumber from CoreTelephony");
if (pCTSettingCopyMyPhoneNumber == nil) {
NSLog(#"pCTSettingCopyMyPhoneNumber is nil");
return nil;
}
NSString* ownPhoneNumber = pCTSettingCopyMyPhoneNumber();
dlclose(lib);
return ownPhoneNumber;
}
It works on iOS 6 without JB and special signing.
As mentioned creker on iOS 7 with JB you need to use entitlements to make it working.
How to do it with entitlements you can find here:
iOS 7: How to get own number via private API?
AppStore will reject it, as it's reaching outside of application container.
Apps should be self-contained in their bundles, and may not read or write data outside the designated container area
Section 2.5.2 :
https://developer.apple.com/app-store/review/guidelines/#software-requirements

Resources