Get CBPeripheralManager's Subscribed Centrals - ios

When implementing the CBPeripheralManagerDelegate method -peripheralManager:willRestoreState, the object in the dictionary passed to the method for key CBPeripheralManagerRestoredStateServicesKey Apple's documentation states that
All the information about a service is restored, including any included services, characteristics, characteristic descriptors, and subscribed centrals.
From the array of CBMutableServicesthat are returned I can loop through the services and in turn loop through each service's characteristics. However I cannot figure out how to access the subscribed centrals, can anyone help?
Below is the general code I've used which assigns values to local variables etc:
- (void)peripheralManager:(CBPeripheralManager *)peripheral willRestoreState:(NSDictionary *)dict
{
advertisementData = dict[CBPeripheralManagerRestoredStateAdvertisementDataKey];
NSArray *services = dict[CBPeripheralManagerRestoredStateServicesKey];
for (CBMutableService *service in services) {
mainService = service;
for (CBMutableCharacteristic *charactristic in mainService.characteristics) {
if ([charactristic.UUID.UUIDString isEqualToString:AUTH_UUID]) {
authCharacteristic = charactristic;
} else if ([charactristic.UUID.UUIDString isEqualToString:CENTRAL_NAME_UUID]) {
receiveDeviceNameCharacteristic = charactristic;
}
}
// How would I reinstantiate subscribed centrals?
// subscribedCentrals = ?
[manager addService:mainService];
}
}

You can retrieve the subscribed centrals from the CBMutableCharacteristic objects -
so, something like -
NSMutableSet *centrals=[NSMutableSet new];
for (CBMutableCharacteristic *charactristic in mainService.characteristics) {
if ([charactristic.UUID.UUIDString isEqualToString:AUTH_UUID]) {
authCharacteristic = charactristic;
} else if ([charactristic.UUID.UUIDString isEqualToString:CENTRAL_NAME_UUID]) {
receiveDeviceNameCharacteristic = charactristic;
}
for (CBCentral *central in characteristic.subscribedCentrals) {
[centrals addObject:central];
}
}

Related

How can I use the result returned in a delegate method to fire a callback in React-Native (iOS)

So I'm in a bit of a pickle here...
I'm creating an application that requires me to use an awkward SDK for a specific hardware api - and to get the status of anything, rather than return a value it fires off a delegate method where the value is accessible there as an argument for the delegate method. This is making callbacks to React-Native a challenge.
Here is my basic method being called from React:
RCT_EXPORT_METHOD(openSirenAlarm: (RCTResponseSenderBlock) callback)
{
cameraControl.cameraSiren.delegate = self;
[cameraControl.cameraSiren playCameraSiren];
callback(#[#(error), #(response)]);
}
RCT_EXPORT_METHOD(closeSirenAlarm: (RCTResponseSenderBlock) callback)
{
cameraControl.cameraSiren.delegate = self;
[cameraControl.cameraSiren stopCameraSiren];
callback(#[#(error), #(response)]);
}
And here are the delegate methods provided in the SDK:
#pragma mark CameraSirenDelegate
- (void) cameraSiren:(CameraSiren *)cameraSiren stopResult:(ENUM_APP_RESULT)result
{
NSLog(#"stop result: %u", result);
if (result == RESULT_SUCCESS)
{
NSLog(#"stop success");
error = 0;
response = 1;
}
else
{
error = 1;
response = 0;
}
}
- (void) cameraSiren:(CameraSiren *)cameraSiren playResult:(ENUM_APP_RESULT)result
{
NSLog(#"start result: %u", result);
if (result == RESULT_SUCCESS)
{
NSLog(#"start success");
error = 0;
response = 1;
}
else
{
error = 1;
response = 0;
}
}
I'm having a couple issues:
As you can see, I'm setting the error result in the delegate methods, but the callback fires right away (including before they're even set the first time) - so at best they're one action behind in terms of information.
I've played with RCTEventEmitters but I find them to be generally sloppy and unreliable - generally firing at least twice and causing me a bit of a headache (if this is a common thing that anyone can help me with I'd gladly use emission).
Is there an obvious way to do this that I'm missing, or a smarter design pattern to handle this?

CloudKit CKError extension not available in Objective-C?

I read somewhere here that CKError is not available in Objective-C, and I concur. For instance, this extension is available in Swift.
#available(OSX 10.10, iOS 8.0, watchOS 3.0, *)
extension CKError {
/// Retrieve partial error results associated by item ID.
public var partialErrorsByItemID: [AnyHashable : Error]? { get }
/// The original CKRecord object that you used as the basis for
/// making your changes.
public var ancestorRecord: CKRecord? { get }
/// The CKRecord object that was found on the server. Use this
/// record as the basis for merging your changes.
public var serverRecord: CKRecord? { get }
/// The CKRecord object that you tried to save. This record is based
/// on the record in the CKRecordChangedErrorAncestorRecordKey key
/// but contains the additional changes you made.
public var clientRecord: CKRecord? { get }
/// The number of seconds after which you may retry a request. This
/// key may be included in an error of type
/// `CKErrorServiceUnavailable` or `CKErrorRequestRateLimited`.
public var retryAfterSeconds: Double? { get }
}
The problem is that I need these objects in my Objective-C project.
I've somehow (I believe) managed to get the partialErrorsByItemID in Objective-C by making a category for NSError and a little comprehension of the documentation of CKError.h, like so:
CKErrorCode ckErrorCode = (CKErrorCode) _code;
if (ckErrorCode == CKErrorPartialFailure) {
// When a CKErrorPartialFailure happens this key will be set in the error's userInfo dictionary.
// The value of this key will be a dictionary, and the values will be errors for individual items with the keys being the item IDs that failed.
NSDictionary *dicError = _userInfo;
if ([dicError objectForKey:CKPartialErrorsByItemIDKey] != nil) {
NSDictionary *dic = (NSDictionary *)[dicError objectForKey:CKPartialErrorsByItemIDKey];
for (NSString* key in dic) {
NSError *newError = dic[key];
if (code == newError.code) {
match = YES;
}
}
} else {
return NO;
}
}
But again, my problem is how to get the objects serverRecord and the clientRecord. Any idea?
Here's an Objective-C category that replicates most of the CKError structure of Swift. I didn't add errorCode, localizedDescription or errorUserInfo since NSError already provides those as code, localizedDescription, and userInfo.
CloudKitExtensions.h
#import <CloudKit/CloudKit.h>
NS_ASSUME_NONNULL_BEGIN
extern const double UnknownRetrySeconds;
#interface NSError (CKError)
- (NSDictionary<id, NSError *> * _Nullable)partialErrorsByItemID;
- (CKRecord * _Nullable)ancestorRecord;
- (CKRecord * _Nullable)clientRecord;
- (CKRecord * _Nullable)serverRecord;
- (double)retryAfterSeconds; // returns UnknownRetrySeconds if not available
#end
NS_ASSUME_NONNULL_END
CloudKitExtensions.m
#import "CloudKitExtensions.h"
const double UnknownRetrySeconds = -1;
#implementation NSError (CKError)
- (NSDictionary<id, NSError *> * _Nullable)partialErrorsByItemID {
if ([self.domain isEqualToString:CKErrorDomain] && self.code == CKErrorPartialFailure) {
return self.userInfo[CKPartialErrorsByItemIDKey];
} else {
return nil;
}
}
- (CKRecord * _Nullable)ancestorRecord {
if ([self.domain isEqualToString:CKErrorDomain] && self.code == CKErrorServerRecordChanged) {
return self.userInfo[CKRecordChangedErrorAncestorRecordKey];
} else {
return nil;
}
}
- (CKRecord * _Nullable)clientRecord {
if ([self.domain isEqualToString:CKErrorDomain] && self.code == CKErrorServerRecordChanged) {
return self.userInfo[CKRecordChangedErrorClientRecordKey];
} else {
return nil;
}
}
- (CKRecord * _Nullable)serverRecord {
if ([self.domain isEqualToString:CKErrorDomain] && self.code == CKErrorServerRecordChanged) {
return self.userInfo[CKRecordChangedErrorServerRecordKey];
} else {
return nil;
}
}
- (double)retryAfterSeconds {
if ([self.domain isEqualToString:CKErrorDomain]) {
NSNumber *delayVal = self.userInfo[CKErrorRetryAfterKey];
return delayVal ? [delayVal doubleValue] : UnknownRetrySeconds;
} else {
return UnknownRetrySeconds;
}
}
#end

Cannot access more than one value from function using PromiseKit Swift

TemplateClass.m
+ (AnyPromise *) promisefunctionReturnThreeValus:(NSString *)sampleName {
return [self anotherPromiseFunction:sampleName].then(^(NSMutableDictionary *sampleDict) {
DataArray *data = [DataArray dataArrayFromDict:sampleDict];
PropertyArray *property = [PropertyArray PropertyArrayFromDict:sampleDict];
if ([sampleDict objectForKey:NAME])
{
NameModel *name = [[NameModel alloc]initWithDictionary:[responseDict objectForKey:NAME]];
return (PMKManifold(data,property,name));
}
else
{
return (PMKManifold(data,property,nil));
}
});
}
well i can able to access this from objc using the below code
[TemplateClass promisefunctionReturnThreeValus:#"hello"].then(^(DataArray *data,PropertyArray *property,NameModel *name) {
//Here i can able to access the three values data,property and name
}
But when i try to access this from swift
TemplateClass.promisefunctionReturnThreeValus(sampleName: "hello").then{ data,property,name in
// it show me error " Contextual closure type '(Any?) -> AnyPromise' expects 1 argument, but 3 were used in closure body "
}
i can able to access only data but not the other two
i also tried debug it and print through log it show only the data of DataArray Object
lldb output
<DataArray : 0x1c0631340 count:1 value:{
"id" = 3631;
}
>

Backendless - How To Get Objects From 'Data'

How do I get all the objects from Backendless's database into a UITableView in my iOS app?
Looking at their Documentation, it doesn't clearly state how to get all objects. (I'm new to the platform)
Any help would be appreciated!
Here's how I do it in Swift (for my table of Blurb objects):
func retrieveBlurbs() {
let query = BackendlessDataQuery()
// Use backendless.persistenceService to obtain a ref to a data store for the class
let dataStore = self.backendless.persistenceService.of(Blurb.ofClass()) as IDataStore
dataStore.find(query, response: { (retrievedCollection) -> Void in
print("Successfully retrieved: \(retrievedCollection)")
self.blurbs = retrievedCollection.data as! [Blurb]
self.tableView.reloadData()
}) { (fault) -> Void in
print("Server reported an error: \(fault)")
}
}
I am also new to Backendless and really enjoying it! It's a lot like Parse, but better in a bunch of ways.
Start with this:
https://backendless.com/feature-16-data-retrieval-api-how-to-load-objects-from-an-mbaas-storage/
Then move on to this: https://backendless.com/feature-17-data-paging-or-how-to-efficiently-load-large-data-sets-in-a-mobile-app/
Both articles include concrete examples in Swift.
Try this:
- (void)viewDidLoad {
[super viewDidLoad];
[self getDataFromBackendless];
}
-(void)getDataFromBackendless {
#try {
BackendlessCollection *documents = [backendless.persistenceService of:[YOUR_TABLE_NAME class]];
currentPage =[documents getCurrentPage];
}
#catch (Fault *fault) {
NSLog(#"Server reported an error: %#", fault);
}
}
Then perform UITableView methods:
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [currentPage count];
}

memory leak in xamarin in app purchase

So I have been trying to setup in app purchase within my application and encountered a problem which I don't know how to solve. Im working with xamarin, and I followed their In app purchase guide on how to buy consumables products.
Everything was going great until I tried to get the transaction receipt returned by apple. Everytime I access this property from the SKPaymentTransaction object (anywhere in my project), I get this error which according to some people is a memory leak. This just happens whenever I access this property (SKPaymentTransaction.TransactionReceipt).
The error:
*** error: can't allocate region
*** set a breakpoint in malloc_error_break to debug
My code looks a lot like the one found on the guide I previously told you about. First of all I created a in app purchase manager which is the one in charge of making requests to apple in order to obtain product information (this works great), and update my UI whenever a transaction is succeeded or failed:
public class InAppPurchaseManager : SKProductsRequestDelegate
{
IMobileServiceTable receiptTable = AppDelegate.MobileService.GetTable("Receipt");
public InAppPurchaseManager ()
{
SKPaymentQueue.DefaultQueue.AddTransactionObserver (new TransactionObserver(this));
}
public void RequestProductData (List<NSString> productIds)
{
var array = new NSString[productIds.Count];
for (var i = 0; i < productIds.Count; i++) {
array[i] = productIds[i];
}
NSSet productIdentifiers = NSSet.MakeNSObjectSet<NSString>(array);
var productsRequest = new SKProductsRequest(productIdentifiers);
productsRequest.Delegate = this; // for SKProductsRequestDelegate.ReceivedResponse
productsRequest.Start();
}
public override void ReceivedResponse (SKProductsRequest request, SKProductsResponse response)
{
SKProduct[] products = response.Products;
NSDictionary userInfo = null;
if (products.Length > 0) {
NSObject[] productIdsArray = new NSObject[response.Products.Length];
NSObject[] productsArray = new NSObject[response.Products.Length];
for (int i = 0; i < response.Products.Length; i++) {
productIdsArray[i] = new NSString(response.Products[i].ProductIdentifier);
productsArray[i] = response.Products[i];
}
userInfo = NSDictionary.FromObjectsAndKeys (productsArray, productIdsArray);
}
NSNotificationCenter.DefaultCenter.PostNotificationName ("InAppPurchaseManagerProductsFetchedNotification", this, userInfo);
}
public override void RequestFailed (SKRequest request, NSError error)
{
Console.WriteLine (" ** InAppPurchaseManager RequestFailed() " + error.LocalizedDescription);
}
public void PuchaseProduct (SKProduct product)
{
SKPayment payment = SKPayment.PaymentWithProduct (product);
SKPaymentQueue.DefaultQueue.AddPayment (payment);
}
public void CompleteTransaction (SKPaymentTransaction transaction)
{
var productId = transaction.Payment.ProductIdentifier;
// Register the purchase, so it is remembered for next time
FinishTransaction(transaction, true);
}
public void FinishTransaction(SKPaymentTransaction transaction, bool wasSuccessful)
{
// remove the transaction from the payment queue.
SKPaymentQueue.DefaultQueue.FinishTransaction(transaction);
using (var pool = new NSAutoreleasePool()) {
NSDictionary userInfo = NSDictionary.FromObjectsAndKeys(new NSObject[] {transaction},new NSObject[] {new NSString("transaction")});
if (wasSuccessful) { NSNotificationCenter.DefaultCenter.PostNotificationName (new NSString("InAppPurchaseManagerTransactionSuccedeedNotification"), this, userInfo);
} else {
// send out a notification for the failed transaction
NSNotificationCenter.DefaultCenter.PostNotificationName (new NSString("InAppPurchaseManagerTransacionFailedNotification"), this, userInfo);
}
}
}
public void FailedTransaction (SKPaymentTransaction transaction)
{
if (transaction.Error.Code == 2) // user cancelled
Console.WriteLine("User CANCELLED FailedTransaction Code=" + transaction.Error.Code + " " + transaction.Error.LocalizedDescription);
else // error!
Console.WriteLine("FailedTransaction Code=" + transaction.Error.Code + " " + transaction.Error.LocalizedDescription);
FinishTransaction(transaction,false);
}
}}
On the method FinishTransaction I would like to insert the returned receipt in my server (before insert, of course verify it against apple servers) if the transaction was successful. So at this point I need to access SKPaymentTransaction.TransactionReceipt, encode this receipt to base64, and send it to my server. This just does not work and I dont know why.
My transaction observer:
public class TransactionObserver : SKPaymentTransactionObserver
{
private InAppPurchaseManager iap;
public TransactionObserver (InAppPurchaseManager manager) : base()
{
iap = manager;
}
public override void UpdatedTransactions (SKPaymentQueue queue, SKPaymentTransaction[] transactions)
{
foreach (SKPaymentTransaction transaction in transactions)
{
switch (transaction.TransactionState)
{
case SKPaymentTransactionState.Purchased:
iap.CompleteTransaction (transaction);
break;
case SKPaymentTransactionState.Failed:
iap.FailedTransaction(transaction);
break;
default:
break;
}
}
}
}
So, another info I can give you is that my project compiles, and when it tries to deploy the app to my device, it crashes.
Also, I am on the xamarin beta channel since I am already using async and await.
So please if you see anything wrong let me know.
UPDATE
Everytime i clean the project it works, and stops giving that error!!! I dont know why this is behaving like this!
This is a known bug.
There is also a workaround in the bug report: add "-f" to the additional mtouch arguments in the project's iOS Build options page.

Resources