I've done as the guide says
This is the message manager
[GNSMessageManager setDebugLoggingEnabled:YES];
messageManager = [[GNSMessageManager alloc] initWithAPIKey:API_KEY paramsBlock:^(GNSMessageManagerParams *params) {
params.bluetoothPowerErrorHandler = ^(BOOL hasError) {
// Update the UI for Bluetooth power
};
params.bluetoothPermissionErrorHandler = ^(BOOL hasError) {
// Update the UI for Bluetooth permission
};
}];
These are my methods to publish and subscribe with the Nearby API.
- (IBAction)onPublish:(id)sender {
NSLog(#"publish");
NSString* str = #"hello world";
NSData* data = [str dataUsingEncoding:NSUTF8StringEncoding];
GNSMessage* message = [GNSMessage messageWithContent:data];
id<GNSPublication> publication = [messageManager publicationWithMessage:message paramsBlock:^(GNSPublicationParams *publicationParams) {
publicationParams.strategy = [GNSStrategy strategyWithParamsBlock:^(GNSStrategyParams * strategyParams) {
strategyParams.allowInBackground = YES;
strategyParams.discoveryMediums = kGNSDiscoveryMediumsBLE;
strategyParams.discoveryMode = kGNSDiscoveryModeDefault;
}];;
}];
}
- (IBAction)onSubscribe:(id)sender {
NSLog(#"subscribe");
id<GNSSubscription> subscription = [messageManager subscriptionWithMessageFoundHandler:^(GNSMessage *msg) {
// Add the name to a list for display
NSLog(#"message found %#", [msg description]);
} messageLostHandler:^(GNSMessage *msg) {
// Add the name to a list for display
NSLog(#"message lost %#", [msg description]);
} paramsBlock:^(GNSSubscriptionParams *subscriptionParams) {
subscriptionParams.strategy = [GNSStrategy strategyWithParamsBlock:^(GNSStrategyParams * strategyParams) {
strategyParams.allowInBackground = YES;
strategyParams.discoveryMediums = kGNSDiscoveryMediumsBLE;
strategyParams.discoveryMode = kGNSDiscoveryModeDefault;
}];;
}];
}
Both Bletooth central and peripheral background capabilities are enabled, and the permission string for the peripheral is set.
Finally I subscribe on an iOS device and publish from another one but nothing happens.
Be sure to retain the publication and subscription objects. They stop publishing/subscribing when they're deallocated. The usual way is to store them as properties/ivars in one of your classes.
The developer docs are misleading on this point, and I apologize. We'll improve the docs in the next release.
As we know that secure Enclave is a coprocessor fabricated in the Apple A7 and its available in A7 and later on but its use publicly in iOS 9 kSecAttrTokenIDSecureEnclave but how do we check either some device support secure enclave or not ?
Thanks
I didnt find any so I made my own check:
+ (BOOL) isDeviceOkForSecureEnclave
{
double OSVersionNumber = floor(NSFoundationVersionNumber);
UIUserInterfaceIdiom deviceType = [[UIDevice currentDevice] userInterfaceIdiom];
BOOL isOSForSecureEnclave = OSVersionNumber > NSFoundationVersionNumber_iOS_8_4 ? YES:NO;
//iOS 9 and up are ready for SE
BOOL isDeviceModelForSecureEnclave = NO;
switch (deviceType) {
case UIUserInterfaceIdiomPhone:
//iPhone
isDeviceModelForSecureEnclave = [self isPhoneForSE];
break;
case UIUserInterfaceIdiomPad:
//iPad
isDeviceModelForSecureEnclave = [self isPadForSE];
break;
default:
isDeviceModelForSecureEnclave = false;
break;
}
return (isOSForSecureEnclave && isDeviceModelForSecureEnclave) ? YES:NO;
}
/**
The arrays are models that we know not having SE in hardware, so if the current device is on the list it means it dosent have SE
*/
+ (BOOL) isPhoneForSE
{
NSString *thisPlatform = [self platform];
NSArray * oldModels = [NSArray arrayWithObjects:
#"x86_64",
#"iPhone1,1",
#"iPhone1,2",
#"iPhone2,1",
#"iPhone3,1",
#"iPhone3,3",
#"iPhone4,1",
#"iPhone5,1",
#"iPhone5,2",
#"iPhone5,3",
#"iPhone5,4", nil];
BOOL isInList = [oldModels containsObject: thisPlatform];
return !isInList;
}
+ (BOOL) isPadForSE
{
//iPad Mini 2 is the earliest with SE // "iPad4,4"
NSString *thisPlatform = [self platform];
NSArray * oldModels = [NSArray arrayWithObjects:
#"x86_64",
#"#iPad",
#"#iPad1,0",
#"#iPad1,1",
#"iPad2,1",
#"iPad2,2",
#"iPad2,3",
#"iPad2,4",
#"iPad2,5",
#"iPad2,6",
#"iPad2,7",
#"iPad3,1",
#"iPad3,2",
#"iPad3,3",
#"iPad3,4",
#"iPad3,5",
#"iPad3,6",nil];
BOOL isInList = [oldModels containsObject: thisPlatform];
return !isInList;
}
+ (NSString *)platform
{
size_t size;
sysctlbyname("hw.machine", NULL, &size, NULL, 0);
char *machine = malloc(size);
sysctlbyname("hw.machine", machine, &size, NULL, 0);
NSString *platform = [NSString stringWithUTF8String:machine];
free(machine);
return platform;
}
#end
TO Check Touch ID
- (BOOL)canAuthenticateByTouchId {
if ([LAContext class]) {
return [[[LAContext alloc] init] canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:nil];
}
return YES;
}
You can also find for detecting Secure Enclave here you find
Above solution has no problem but it seems like hack, so I am adding another solution in Swift 4.
To check Secure Enclave availability
enum Device {
//To check that device has secure enclave or not
public static var hasSecureEnclave: Bool {
return !isSimulator && hasBiometrics
}
//To Check that this is this simulator
public static var isSimulator: Bool {
return TARGET_OS_SIMULATOR == 1
}
//Check that this device has Biometrics features available
private static var hasBiometrics: Bool {
//Local Authentication Context
let localAuthContext = LAContext()
var error: NSError?
/// Policies can have certain requirements which, when not satisfied, would always cause
/// the policy evaluation to fail - e.g. a passcode set, a fingerprint
/// enrolled with Touch ID or a face set up with Face ID. This method allows easy checking
/// for such conditions.
var isValidPolicy = localAuthContext.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error)
guard isValidPolicy == true else {
if #available(iOS 11, *) {
if error!.code != LAError.biometryNotAvailable.rawValue {
isValidPolicy = true
} else{
isValidPolicy = false
}
}
else {
if error!.code != LAError.touchIDNotAvailable.rawValue {
isValidPolicy = true
}else{
isValidPolicy = false
}
}
return isValidPolicy
}
return isValidPolicy
}
}
To check that touch id available or not
let hasTouchID = LAContext().canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error)
if(hasTouchID || (error?.code != LAError.touchIDNotAvailable.rawValue)) {
print("Touch Id Available in device")
}
If you want solution in Objective C, then refer this link.
Solution in Objective C.
I'm trying to use BatteryCenter and CommonUtilities private frameworks under iOS 9.1 with the help of nst's iOS Runtime Headers. It's for research purposes and won't make it to the AppStore.
Here are their respective codes:
- (void)batteryCenter {
NSBundle *bundle = [NSBundle bundleWithPath:#"/System/Library/PrivateFrameworks/BatteryCenter.framework"];
BOOL success = [bundle load];
if(success) {
Class BCBatteryDevice = NSClassFromString(#"BCBatteryDevice");
id si = [[BCBatteryDevice alloc] init];
NSLog(#"Charging: %#", [si valueForKey:#"charging"]);
}
}
- (void)commonUtilities {
NSBundle *bundle = [NSBundle bundleWithPath:#"/System/Library/PrivateFrameworks/CommonUtilities.framework"];
BOOL success = [bundle load];
if(success) {
Class CommonUtilities = NSClassFromString(#"CUTWiFiManager");
id si = [CommonUtilities valueForKey:#"sharedInstance"];
NSLog(#"Is Wi-Fi Enabled: %#", [si valueForKey:#"isWiFiEnabled"]);
NSLog(#"Wi-Fi Scaled RSSI: %#", [si valueForKey:#"wiFiScaledRSSI"]);
NSLog(#"Wi-Fi Scaled RSSI: %#", [si valueForKey:#"lastWiFiPowerInfo"]);
}
}
Although I get the classes back, all of their respected values are NULL which is weird since some must be true, e.g. I'm connected to Wi-Fi so isWiFiEnabled should be YES.
What exactly is missing that my code doesn't return whats expected? Does it need entitlement(s)? If so what exactly?
In Swift, I managed to get this working without the BatteryCenter headers. I'm still looking for a way to access the list of attached batteries without using BCBatteryDeviceController, but this is what I have working so far:
Swift 3:
guard case let batteryCenterHandle = dlopen("/System/Library/PrivateFrameworks/BatteryCenter.framework/BatteryCenter", RTLD_LAZY), batteryCenterHandle != nil else {
fatalError("BatteryCenter not found")
}
guard let batteryDeviceControllerClass = NSClassFromString("BCBatteryDeviceController") as? NSObjectProtocol else {
fatalError("BCBatteryDeviceController not found")
}
let instance = batteryDeviceControllerClass.perform(Selector(("sharedInstance"))).takeUnretainedValue()
if let devices = instance.value(forKey: "connectedDevices") as? [AnyObject] {
// You will have more than one battery in connectedDevices if your device is using a Smart Case
for battery in devices {
print(battery)
}
}
Swift 2.2:
guard case let batteryCenterHandle = dlopen("/System/Library/PrivateFrameworks/BatteryCenter.framework/BatteryCenter", RTLD_LAZY) where batteryCenterHandle != nil else {
fatalError("BatteryCenter not found")
}
guard let c = NSClassFromString("BCBatteryDeviceController") as? NSObjectProtocol else {
fatalError("BCBatteryDeviceController not found")
}
let instance = c.performSelector("sharedInstance").takeUnretainedValue()
if let devices = instance.valueForKey("connectedDevices") as? [AnyObject] {
// You will have more than one battery in connectedDevices if your device is using a Smart Case
for battery in devices {
print(battery)
}
}
This logs:
<BCBatteryDevice: 0x15764a3d0; vendor = Apple; productIdentifier = 0; parts = (null); matchIdentifier = (null); baseIdentifier = InternalBattery-0; name = iPhone; percentCharge = 63; lowBattery = NO; connected = YES; charging = YES; internal = YES; powerSource = YES; poweredSoureState = AC Power; transportType = 1 >
You need to first access the BCBatteryDeviceController, after success block is executed, through which you can get list of all the connected devices.
Here is the code for the same.
Class CommonUtilities = NSClassFromString(#"BCBatteryDeviceController");
id si = [CommonUtilities valueForKey:#"sharedInstance"];
BCBatteryDeviceController* objBCBatteryDeviceController = si;
NSLog(#"Connected devices: %#", objBCBatteryDeviceController.connectedDevices);
I am having a wrapper class from WebViewJavascriptBridge which get the data from JavaScript to Objective C where I need to send this data to its extended or other class for appropriate class, also from down to up I need the same thing like any thing change from viewController change orientation then my OrientationHandler Class notified, then EventManager then, CommunicationManager and that notify to that bridge class.
This is bridge class code to initiate
[self.bridge registerHandler:#"callHandler" handler:^(id data, WVJBResponseCallback responseCallback){
[[CommunicationManager sharedInstance] initWithActionRequest:data];
responseCallback(#200);
}];
Here my CommunicationManager choose what class need to load..
-(BOOL)initWithActionRequest:(id)request {
if ([request isKindOfClass:[NSDictionary class]]) {
NSDictionary* requestType = (NSDictionary*)request;
NSAssert((requestType != nil), #"request dictionary is nill");
NSArray* params = [requestType[#"eventType"] componentsSeparatedByString:#"."];
NSAssert(([params count] == 3), #"params array is nill");
if ([params[0] isEqualToString:#"DeviceManager"]) {
DeviceManager* deviceManager = [[DeviceManager alloc] initWithDeviceManagerRequestType:requestType];
deviceManager.serviceName = params[1];
deviceManager.serviceAction = params[2];
[deviceManager performActionBasedOnRequest];
return YES;
} else if ([params[0] isEqualToString:#"EventManager"]) {
EventManager* eventManager = [[EventManager alloc] initWithEventManagerRequestType:requestType];
eventManager.serviceName = params[1];
eventManager.serviceAction = params[2];
[eventManager performEventBasedOnRequest];
return YES;
}
}
return NO;
}
then my DeviceManager Class execute which service need to start
-(BOOL)performActionBasedOnRequest {
if ([_serviceName isEqualToString:#"SipHandler"]) {
SipHandler* sipHandler = [[SipHandler alloc] init];
[sipHandler startPJSIPWithOptions:nil];
return YES;
} else {
}
return NO;
}
then in the end my service class SipHandler load and run the method
-(BOOL)startPJSIPWithOptions:(NSDictionary *)options {
startPjsip(CLIENT_NAME, SERVER_HOST);
return YES;
}
I have confusion how to flow the data in efficient way?
I'm developing an iOS application with Simple Notification Service (SNS) from Amazon Web Services. At this point the app registers the device to a Topic and can receive push notifications, which are published to the Topic. It is possible to subscribe a device to many Topics.
Now I'm trying to unsubscribe a device from a specific Topic, but the SNSUnsubscribeRequest needs a SubscriptionARN. I've tried to use the EndpointARN from the device, but it seems I've to use an extra SubscriptionARN for the combination of EndpointARN and TopicARN. How do I get this ARN?
In this post: How do you get the arn of a subscription? they ask for the whole list of subscribers and compare each EndpointARN with the EndpointARN of the device. This cant be the right way i think.
Subscribe to Topic
// Check if endpoint exist
if (endpointARN == nil) {
dispatch_async(dispatch_get_main_queue(), ^{
[[self universalAlertsWithTitle:#"endpointARN not found!" andMessage:#"Please create an endpoint for this device before subscribe to topic"] show];
});
return NO;
}
// Create topic if not exist
NSString *topicARN = [self findTopicARNFor:topic];
if (!topicARN) {
[self createTopic:topic];
topicARN = [self findTopicARNFor:topic];
}
// Subscribe to topic if exist
if (topicARN) {
SNSSubscribeRequest *subscribeRequest = [[SNSSubscribeRequest alloc] initWithTopicArn:topicARN andProtocol:#"application" andEndpoint:endpointARN];
SNSSubscribeResponse *subscribeResponse = [snsClient subscribe:subscribeRequest];
if (subscribeResponse.error != nil) {
NSLog(#"Error: %#", subscribeResponse.error);
dispatch_async(dispatch_get_main_queue(), ^{
[[self universalAlertsWithTitle:#"Subscription Error" andMessage:subscribeResponse.error.userInfo.description] show];
});
return NO;
}
}
return YES;
The method findTopicARNForTopic already iterates over the list of Topics and compare the suffix with the topic name. I really don't know if this is the best practice.
Unsubscribe from Topic
NSString *topicARN = [self findTopicARNFor:topic];
if (topicARN) {
SNSUnsubscribeRequest *unsubscribeRequest = [[SNSUnsubscribeRequest alloc] initWithSubscriptionArn:topicARN];
SNSUnsubscribeResponse *unsubscribeResponse = [snsClient unsubscribe:unsubscribeRequest];
if (unsubscribeResponse.error) {
NSLog(#"Error: %#", unsubscribeResponse.error);
}
}
For now I ask for the whole subscriber list and compare the EndpointARN with the EndpointARN of the Device. With the following method i get the subscription arn:
- (NSString *)findSubscriptionARNForTopicARN:(NSString *)topicARN
{
// Iterate over each subscription arn list for a topic arn
NSString *nextToken = nil;
do {
SNSListSubscriptionsByTopicRequest *listSubscriptionRequest = [[SNSListSubscriptionsByTopicRequest alloc] initWithTopicArn:topicARN];
SNSListSubscriptionsByTopicResponse *response = [snsClient listSubscriptionsByTopic:listSubscriptionRequest];
if (response.error) {
NSLog(#"Error: %#", response.error);
return nil;
}
// Compare endpoint arn of subscription arn with endpoint arn of this device
for (SNSSubscription *subscription in response.subscriptions) {
if ([subscription.endpoint isEqualToString:endpointARN]) {
return subscription.subscriptionArn;
}
}
nextToken = response.nextToken;
} while (nextToken != nil);
return nil;
}
and with this method i remove the device from a topic:
- (void)unsubscribeDeviceFromTopic:(NSString *)topic
{
NSString *subscriptionARN = [self findSubscriptionARNForTopic:topic];
if (subscriptionARN) {
SNSUnsubscribeRequest *unsubscribeRequest = [[SNSUnsubscribeRequest alloc] initWithSubscriptionArn:subscriptionARN];
SNSUnsubscribeResponse *unsubscribeResponse = [snsClient unsubscribe:unsubscribeRequest];
if (unsubscribeResponse.error) {
NSLog(#"Error: %#", unsubscribeResponse.error);
}
}
}
You could also store the SubscriptionArn in the SubscribeResponse and use this value in the UnSubscribeRequest.