I'm writing an app that optionally allows you to send an SMS to a list of users. The app currently detects whether the device allows the sending of text messages using MFMessageComposeViewController.canSendText() and only displays the "Send SMS" button when the result is true.
Today, one of my users complained that when he hit the button on his Wifi-only iPad, nothing happened. As it turns out, canSendText() also returns true if the iOS device has the iMessage capability turned on, even if SMS is not supported. Turning the iMessage capability off in the iOS setting properly disables the "Send SMS" button.
The problem with sending the message via iMessage is that some recipients will have iMessage turned on and thus will receive the text message. But for the recipients without iMessage (it may be turned off or they may not be using an Apple device in the first place), those messages will not arrive. (When the MFMessageComposeViewController eventually comes up, those addresses are marked in red. But my average user is not going to understand that this means the message will not delivered.)
Does anybody know of a reliable method that tests for SMS capability specifically, not just for text messages (SMS, iMessage and MMS) in general?
As far as I know devices with iOS 5+ will return TRUE for MFMessageComposeViewController.canSendText() as they are capable of iMessage
So I assume you only need to send text if SIM card is installed on device.
But there is no way to detect if SIM card is installed on device. Still you can use workaround like :
#import<CoreTelephony/CTCallCenter.h>
#import<CoreTelephony/CTCall.h>
#import<CoreTelephony/CTCarrier.h>
#import<CoreTelephony/CTTelephonyNetworkInfo.h>
CTTelephonyNetworkInfo* info = [[CTTelephonyNetworkInfo alloc] init];
CTCarrier* carrier = info.subscriberCellularProvider;
NSString *mobileCountryCode = carrier.mobileCountryCode;
NSString *carrierName = carrier.carrierName;
NSString *isoCountryCode = carrier.isoCountryCode;
NSString *mobileNetworkCode = carrier.mobileNetworkCode;
NSLog(#"\n %# \n %# \n %# \n %#", mobileCountryCode, carrierName, isoCountryCode, mobileNetworkCode);
Here, carrierName will return name when SIM card is present OR last use SIM card info. But others will be null if there is no SIM card installed.
Related
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”.
The question is not for BLE device, its just normal bluetooth device.
currently my code works like this,
I call the function :
[[EAAccessoryManager sharedAccessoryManager] showBluetoothAccessoryPickerWithNameFilter:nil completion:^(NSError *error)
{
}];
}
and it opens the popup with list of available Bluetooth device, then i click on my desired device and get an object and go ahead.
Is there any way that i can skip this picker step and directly get an object of my device?
No, it's not possible to connect without pairing first. The first time, you must have the user pair with the device either from the Settings app or from the picker. After the first pairing, however, you can skip the picker and get an EAAccesory * for your accessory, if the accessory is already connected to the iOS device. Here is how you can query the list of connected accessories:
NSArray<EAAccessory *> *connectedAccessories = [EAAccessoryManager sharedAccessoryManager].connectedAccessories;
for (EAAccessory *accessory in connectedAccessories) {
// Implement needed filter to recognize your device.
// You can use for instance accessory.protocolStrings
// The MAC address is available with [accessory valueForKey:#"macAddress"]
}
With the EAAccessory framework you can't initiate a connection to a device programmatically. For subsequent connections, you can have your device reconnect to the last connected device (if you control the firmware). This will trigger the EAAccessoryDidConnectNotification if your app is in the foreground, otherwise it will queue the notification and update the list of connected accessories.
I would like to know if some of theses functions are possible with the IOS sdk.
In background process:
to catch
-the available memory space on the ipad
-number of cpu
-ram size of the ipad
-type of screen
-langage of the user
-HostName
-Ip adress
-bluetooth adress
-name of the ipad
-IOS version
-Installed apps on the Ipad
-memory used by our app
To catch all these data in background and to send it to a web service. Do you think all this, is possible and allowed by the ios sdk ?
Thank you
Most of it is, check the UIDevice class and the folowing questions:
iPhone/iPad/OSX: How to get my IP address programmatically?
How to get iOS device MAC address programmatically
You can send this info to your webservice without notifying the user, but it's not a nice thing to do.
The available memory space on the ipad = Maybe not tried it but don't see why not
Number of CPU = YES
Ram size of the ipad = YES
Type of screen = Not sure what you mean
Language of the user = YES (CAN NOT send in background to webservice without users permission)
HostName = Not sure what you mean (Whilst I don't know what you mean I can see this not be allowed to be sent without the users permission)
IP address = YES (CAN NOT send in background to webservice without users permission)
bluetooth address = Not sure what you mean
Name of the ipad = YES (CAN NOT send in background to webservice without users permission)
iOS version = YES
Installed apps on the Ipad = NO
Memory used by our app = Maybe never tried but don't see why not
Basically any personal information about the user you can't send without the users permission.
what you can get, if app is in foreground are below.
1) available memory space on the ipad
2) type of screen andlangage of the user
3) name of the ipad and iOS version
left-over you can't fetch even if app is in foreground.
apple doesn't allow to run app in background to prevent battery consumption, even if, allow then for around 20-30 seconds, when app goes in background from foreground.
[UIDevice currentDevice].model
[UIDevice currentDevice].systemVersion
[UIDevice currentDevice].name
[UIDevice currentDevice].systemName
[UIDevice currentDevice].localizedModel
[UIDevice currentDevice].userInterfaceIdiom
[UIDevice currentDevice].identifierForVendor
[[UIDevice currentDevice] platformType]
[[UIDevice currentDevice] platformString]
I have very strange behaviour with my IOS application. Basically this application contains multiple buttons and after click I want to send SMS text message with some command to GPRS tracker. Here is my code for sending SMS
void SendMessage (string command)
{
if (MFMessageComposeViewController.CanSendText){
this.messageController = new MessageComposeViewController();
this.messageController.Recipients = new string[] { PhoneNumber };
this.messageController.Body = command;
//this.messageController.MessageComposeDelegate = new MessageComposerDelegate();
this.messageController.Finished += HandleFinished;
this.PresentViewController(this.messageController, false, null);
} else{
ShowUiAlert("Alert","It is not possible to send SMS!");
}
}
void HandleFinished (object sender, MFMessageComposeResultEventArgs e)
{
DismissViewController (true, null);
}
I had very strange problem with testing in DEBUG mode on iPhone 5 device and currently I have the same issue on the application in App store.
When user click on send message the iMessage screen is displayed with prefiled text and phone number. Problem is that after the modal window with SMS is shown everything freeze for couple of seconds and it is not possible to click on Send button or change text. After some time this is possible, but when the user click on Send SMS in iMessage this message is not send.
Another strange problem is that I cannot see the text messages send from iMessage triggered from my app and normal iMessage app. So I assume that I hace some issue with provisioning profile or distribution profile. When I used adhoc distribution it was working for some time. And also when I used App store distribution profiles for App store it was working for some time, but now it is not working again.
Do you have any clue what can happened this or did you have similar issues?
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