What's CTSubscriber (and how to use it) on iOS 7? - ios

On iOS 7, CTSubscriber was added to the CoreTelephony framework. There is no documentation available, only its header file:
/*
* CTSubscriberTokenRefreshed
*
* Description:
* The name of the NSNotification sent when the carrier token is available.
*/
CORETELEPHONY_EXTERN NSString * const CTSubscriberTokenRefreshed __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_7_0);
CORETELEPHONY_CLASS_AVAILABLE(7_0)
#interface CTSubscriber : NSObject
/*
* carrierToken
*
* Description:
* A data blob containing authorization information about the subscriber.
*
* May return nil if no token is available.
*/
#property (nonatomic, readonly, retain) NSData* carrierToken __OSX_AVAILABLE_STARTING(__MAC_NA,__IPHONE_7_0);
#end
Also, on What's new on iOS 7, this is mentioned:
The Core Telephony framework (CoreTelephony.framework) lets you get information about the type of radio technology in use by the device. Apps developed in conjunction with a carrier can also authenticate against a particular subscriber for that carrier.
I think that CTSubscriber is related to the bold part of the text. However, I haven't found anything related on how this happens.
I have tried to use the following code (added to application:didFinishLaunchingWithOptions:) to experiment with this API, but the notification is never fired and carrierToken returns nil:
CTSubscriber *subscriber = [CTSubscriberInfo subscriber];
NSLog(#"%#", subscriber.carrierToken);
[[NSNotificationCenter defaultCenter] addObserverForName:CTSubscriberTokenRefreshed object:nil queue:[NSOperationQueue mainQueue] usingBlock:^(NSNotification *note) {
NSLog(#"==========");
NSLog(#"%#", note);
NSLog(#"%#", subscriber.carrierToken);
}];
So, I have the following questions:
What exactly ("authorization information") does carrierToken return and how to make it not nil?
How does Apple know if your app is "developed in conjunction with a carrier"?
Is this how Evernote is giving 1 year of premium account to Telefonica users (http://blog.evernote.com/blog/2013/08/13/evernote-and-telefonica-announce-global-partnership/)? (Probably not, since the information they need can be obtained on CTCarrier)

I asked the same question in the developer forums and got this reply :
You should escalate this via the carrier you're working with, who can in turn escalate it to their contact at Apple.
Link to the thread: https://devforums.apple.com/message/934226#934226

The reason you can’t find any documentation is because much of Core Telephony consists of private APIs. Consequently, there isn’t any way to access the SIM card from an app published on the App Store. A jailbroken device is, of course, another story, but in that case you’re pretty much on your own.
Edit:
The Core Telephony framework (CoreTelephony.framework) lets you get
information about the type of radio technology in use by the device.
Apps developed in conjunction with a carrier can also authenticate
against a particular subscriber for that carrier.

Related

how do i get my iPhone device country code without using GPS or location service?

I need help...
I am new to iPhone app development and working on a registration page.
Scenario:->
Location services is off.
Two text fields, one for phone country code and other for phone number.
When registration page pops up, country code should be automatically filled with the country code of the device.
Problem:-> I am having difficulty in fetching the country code from device information.
Also, what info may I use for the same... IMEI, MDN, Locale, etc...
Any response is appreciated..!
You can get this information from iOS' CoreTelephony framework:
#import <CoreTelephony/CTTelephonyNetworkInfo.h>
...
#property (nonatomic, strong) CTTelephonyNetworkInfo* networkInfo;
...
self.networkInfo = [[CTTelephonyNetworkInfo alloc] init];
NSString* country = [self.networkInfo subscriberCellularProvider].isoCountryCode;
I quickly copy-pasted this from one of my apps. I trust you can convert this to Swift if you need to.
subscriberCellularProvider gives access to the CTCarrier object, which has more fields than just the country code: MCC, MNC, carrier name, and a flag if VoIP is allowed.
iOS does not give access to IMEI.
The In App Purchase APIs can tell you on which iTunes Store the user is; an ISO country code again. You probably need to have IAP activated for your app, so when you don't sell anything, this may not be allowed/an option.
The current locale can indeed also give you a country code:
let locale = NSLocale.currentLocale()
let country = currentLocale.objectForKey(NSLocaleCountryCode) as? String

Get NSString out of ENNoteContent with Evernote Cloud SDK 2.0

I am new to Evernote SDK development and am using the evernote cloud SDK 2.0 as recommended by Evernote.
However, I am having trouble to get the NSString content out of the ENNoteContent object. I have tried the followings from searching online but none seems to work with the cloud sdk as I guess they are all for the old version of Evernote SDK...
1 Using "convertENMLToHTML" method.
According to this and this, I could call convertENMLToHTML directly on an ENNoteContent object much like this convertENMLToHTML:note.content. However, in the cloud SDK, this resulted in an exception inside ENMLUtility that terminates the app because convertENMLToHTML is expecting an NSString as opposed to ENNoteContent and the first thing this function does is trying to call [enmlContent dataUsingEncoding:NSUTF8StringEncoding]] which caused the exception if enmlContent is a pointer to ENNoteContent but not a pointer to NSString.
2 Attempting to get _emml object out of the ENNoteContent object
This post has a quote of calling [note.content enml] but this again doesn't work with cloud sdk as object enml isn't defined in the interface.
Does anyone know how one can get an NSString out of ENNoteContent? I would expect this to be a very straightforward process but am surprised that I wasn't able to find anything that works for the Cloud SDK.
3 Using generateWebArchiveData method
Per Sash's answer below, I have also attempted to use the generateWebArchiveData method in the example from the cloud sdk. The code I have looks like this:
[[ENSession sharedSession] downloadNote:result.noteRef progress:^(CGFloat progress) {
} completion:^(ENNote *note, NSError *downloadNoteError) {
if (note) {
NSLog(#"%#", note.title);
[note generateWebArchiveData:^(NSData *data) {
NSString* strContent = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding];
NSLog(#"test content %#", strContent);
}];
} else {
NSLog(#"Error downloading note contents %#", downloadNoteError);
}
}];
However, strContent outputs "null" for a note that I have verified with legitimate content.
As a temporary hack, we added #property (nonatomic, copy) NSString * emml;
in ENNoteContent.h and removed the same line in ENNoteContent.m to get around this for now.
You are close. Technique #1 above is what you want, but as you discovered the enml property is private in the "default" SDK. Import the "advanced" header and you'll have access to note.content.enml. That is a string, and you can send it to convertENMLtoHTML if you prefer an HTML representation.
Do note that there is no "plaintext" string content for an existing note. You'll always see it as markup, and if you want to get rid of the markup, doing so is beyond the scope of the SDK-- how to do that depends very much on what the content you're dealing with looks like.
You should check out their samples included with SDK, seems like
-[ENNote generateWebArchiveData:] will get you HTML NSData in the completion block
https://github.com/evernote/evernote-cloud-sdk-ios/blob/master/Getting_Started.md#downloading-and-displaying-an-existing-note might also help

Objective-C HealthKit identify if source is from Apple iPhone or Apple Watch

I have an app where I am trying to integrate the HealthKit and pull steps related data aggregated by day using the HKStatisticsCollectionQuery. Requirement is to pull steps data specific to only iPhone and Apple Watch devices separately (no de-duplication) which have contributed to the health app.
The HKSource class only exposes the following properties:
name - Cannot be used as the user can change this to anything from just 'XXXX iPhone'
bundleIdentifier - Provides us the UUID for the device (unique per device, so different for every iPhone/Watch), and it looks like com.apple.health.UUID, here's what the Apple documentation says : "For apps, this property holds the app’s bundle identifier. For supported Bluetooth LE devices, this property holds a UUID for the device."
I am able to pull all sources (using a HKSourceQuery) which have the bundleIdentifier prefix of 'com.apple.health', but am unable to deduce which is an Apple iPhone versus which is an Apple iWatch.
Has anybody faced a similar situation before, and is there any other way to identify which source is an iPhone or Apple Watch?
Any help would be great!.Thanks!
Not the best solution but, I have figured out a way to distinguish between the watch and the phone using the following process:
I noticed that all step data coming from the iPhone/Watch have the following bundleIdentifier format:
com.apple.health.DeviceUUID
Note that manually entered data into the Health app has a bundle identifier of com.apple.Health (with a capital 'H').
So, first thing, get the device name for the phone using:
NSString *deviceName = [[UIDevice currentDevice] name];
Next, fetch all the sources for which there is a prefix match of 'com.apple.health' in the bundleIdentifier. This should give you the iPhone and the Apple watch as the valid sources and ignore the manual entries and all other apps.
Next, check if the name of the device is the same in the source, then its your iPhone, the other source should be your Apple Watch.
Here's a sample source query for fetching the sources :
- (void)fetchSources
{
NSString *deviceName = [[UIDevice currentDevice] name];
NSMutableArray *dataSources = [[NSMutableArray alloc] init];
HKQuantityType *stepsCount = [HKQuantityType quantityTypeForIdentifier:HKQuantityTypeIdentifierStepCount];
HKSourceQuery *sourceQuery = [[HKSourceQuery alloc] initWithSampleType:stepsCount
samplePredicate:nil
completionHandler:^(HKSourceQuery *query, NSSet *sources, NSError *error)
{
for (HKSource *source in sources)
{
if ([source.bundleIdentifier hasPrefix:sourceIdentifier])
{
if ([source.name isEqualToString:deviceName])
// Iphone
else
// Apple Watch
[dataSources addObject:source];
}
}
}];
[self.healthStore executeQuery:sourceQuery];
}
You can now create a predicate with each source for your data pull using the NSPredicate class:
NSPredicate *sourcesPredicate = [HKQuery predicateForObjectsFromSource:source];
Note that my first thought was to match the UUID, but when I generate a UUID using the NSUUID class, it does not match with the one present in the bundle identifier in the pulled sources.
Also, you can change the name of the phone to whatever you want, it will automatically update in the Health app as well.
As I said, not the best solution but works for me, and it's the only way I could find to do this. Please let me know if you were able to find a better solution. Thanks.

Updating TXTRecordDictionary doesn't always notify monitoring services

I'm using bonjour to find other devices. Each device uses TXTRecordData to share its name:
NSDictionary* dictionary = #{ #"name": #"Goose" };
[service setTXTRecordData:[NSNetService dataFromTXTRecordDictionary:dictionary]];
Each device listens for changes:
- (void) netService:(NSNetService *)sender didUpdateTXTRecordData:(NSData *)data
{
...
}
About 80% of the time it works - didUpdateTXTRecordData is called when a name is changed.
Sometimes the other devices are never notified.
I've checked and setTXTRecordData returns YES when the data is lost.
How can I make sure updates to the TXTRecordData makes it to other devices?
Someone posted a gist demonstrating what is possibly the above bug.
For you Apple people out there, the gist says the relevant rdar is rdar://11018654

IOS Jailbreak How do intercept SMS / Text Messages

I'm currently trying to write an application that intercepts text messages and reacts depending on the content of that message.
I tried to hook into _receivedMessage:(struct __CKSMSRecord *)message replace:(BOOL)replace method in the CKSMSService class but this seems not do get called at all.
Could someone please tell me what function/class i have to hook in? I need to intercept the text message before it gets displayed and stored into the database. I'm on IOS 5.0.1.
Any help is truly appreciated.
This code snippet should intercept SMS messages- You can extend it for other kinds of notifications. Will work on iOS 5.0.1 as well. Does not work with iMessages though. Link with CoreTelephony framework (there are bunch of private headers there which you'd can class-dump)
#include <dlfcn.h>
#define CORETELPATH "/System/Library/PrivateFrameworks/CoreTelephony.framework/CoreTelephony"
id(*CTTelephonyCenterGetDefault)();
void (*CTTelephonyCenterAddObserver) (id,id,CFNotificationCallback,NSString*,void*,int);
static void telephonyEventCallback(CFNotificationCenterRef center, void *observer, CFStringRef name, const void *object, CFDictionaryRef userInfo)
{
NSString *notifyname=(NSString *)name;
if ([notifyname isEqualToString:#"kCTMessageReceivedNotification"])//received SMS
{
NSLog(#" SMS Notification Received :kCTMessageReceivedNotification");
// Do blocking here.
}
}
-(void) registerCallback {
void *handle = dlopen(CORETELPATH, RTLD_LAZY);
CTTelephonyCenterGetDefault = dlsym(handle, "CTTelephonyCenterGetDefault");
CTTelephonyCenterAddObserver = dlsym(handle,"CTTelephonyCenterAddObserver");
dlclose(handle);
id ct = CTTelephonyCenterGetDefault();
CTTelephonyCenterAddObserver(
ct,
NULL,
telephonyEventCallback,
NULL,
NULL,
CFNotificationSuspensionBehaviorDeliverImmediately);
}
Although the poster already accepted rajagp's answer, I'm pretty sure it doesn't do what the question actually asked, on iOS 5. For iOS 5, I'm no longer seeing the message content anymore, although I do get notified that there is a new message.
So, what I did is take rajagp's notification handler for kCTMessageReceivedNotification, and inside it, use the code posted here to actually get the content of the text message, from the SMS database.
This still works on iOS 7, but I found that you need a slight delay after receiving the kCTMessageReceivedNotification notification. Else you will miss the SMS just received. I use a delay of 0.1 sec, with a [self performSelector .. afterDelay:0.1];

Resources