Always getting AppSync initial values when sending new values from iOS app - ios

I am creating a Pebble companion app for an iOS application. I have setup my AppSync with some initial values:
Tuplet initial_values[] = {
TupletCString(SYNC_KEY_LANGUAGE, language),
TupletCString(SYNC_KEY_TOTAL_AMOUNT, totalAmount),
TupletCString(SYNC_KEY_TODAY_AMOUNT, todayAmount),
TupletCString(SYNC_KEY_TRIP_NAME, tripName)
};
app_sync_init(&sync, sync_buffer, sizeof(sync_buffer), initial_values,
ARRAY_LENGTH(initial_values), sync_tuple_changed_callback,
sync_error_callback, NULL);
The problem is that when I push new data from my iPhone, the initial values are getting set to my text layers, instead of the data sent:
static void sync_tuple_changed_callback(const uint32_t key, const Tuple* new_tuple, const Tuple* old_tuple, void* context) {
switch (key) {
case SYNC_KEY_TRIP_NAME: {
text_layer_set_text(tripNameLayer, new_tuple->value->cstring);
layer_mark_dirty((Layer *)tripNameLayer);
}
break;
case SYNC_KEY_TOTAL_AMOUNT: {
text_layer_set_text(totalAmountLayer, new_tuple->value->cstring);
layer_mark_dirty((Layer *)totalAmountLayer);
}
break;
case SYNC_KEY_TODAY_AMOUNT: {
text_layer_set_text(todayAmountLayer, new_tuple->value->cstring);
layer_mark_dirty((Layer *)todayAmountLayer);
//storeData(STORAGE_KEY_TODAY_AMOUNT, (char *)new_tuple->value->cstring);
}
break;
case SYNC_KEY_LANGUAGE: {
// DO NOTHING
}
break;
default: {
debugMessage("default case");
}
break;
}
}
This is the code I am using from the iPhone side:
NSDictionary *tripInfo = #{
#(SyncKeyTripName) : #"Denver",
#(SyncKeyLanguage) : #"en",
#(SyncKeyTotalAmount) : #"154.43",
#(SyncKeyTodayAmount) : #"23.50"
};
[self.watch appMessagesPushUpdate:tripInfo
onSent:^(PBWatch *watch, NSDictionary *update, NSError *error) {
if (error) {
NSLog(#"error sending update! %#", error);
} else {
NSLog(#"update: %#", update);
}
}];
I have setup my watch buttons to clear out any values in those layers, this is how I know when the app is getting updates from the phone.
Why is AppSync continually using old data, instead of new data?

Turns out this behavior is exhibited when calling the app_sync_init function more than once. For some reason I had it being called twice when the application is being setup. Once I removed the extraneous call, my callbacks contained the new data, not the initial.

Related

IOS 10.3.1 cordova based App crashes when trying to write to IndexedDB

I'm developing a cordova-based multi-platform web-app using sapui5 framework v1.44 and indexedDB for storing data.The app was working fine untill last ios update, 10.3.1, now it crashes when trying to write to indexedDB. I'm using put method for updating data and i did a clean install of the app. The code frame where i try to write to indexedDB is this:
writeToIDB: function (objStoreName, result, success, error) {
//Asynchronous function
var defer = Q.defer();
var res = [];
if (!!result && Array.isArray(result)) {
res = result;
} else if (!!result && result.hasOwnProperty("results") && Array.isArray(result.results)) {
res = result.results;
} else if (!!result && typeof result === 'object') {
res.push(result);
}
if (res.length >= 0) {
if (window.myDB) {
if (!window.myDB.objectStoreNames.contains(objStoreName)) {
console.log("ObjectStore for " + objStoreName + " doesn't exist");
if (error) {
error("ko")
} else {
defer.reject("ko");
}
} else {
var oTransaction = window.myDB.transaction([objStoreName], "readwrite");
var oDataStore = oTransaction.objectStore(objStoreName);
oTransaction.oncomplete = function (event) {
console.log("Transaction completed: database modification for " + objStoreName + " finished.");
if (success) {
success();
} else {
defer.resolve("ok");
}
};
oTransaction.onerror = function (event) {
console.log("Transaction for " + objStoreName + " not opened due to error. Check for duplicate items or missing properties!");
console.log(event.target.error);
if (error) {
error("ko")
} else {
defer.reject("ko");
}
};
var oRecord = {};
for (var i = 0; i < res.length; i++) {
oRecord = res[i];
oDataStore.put(oRecord);
}
}
} else {
this.createIDB().then(
function (resCreate) {
console.log("DB Created successfully");
if (!window.myDB.objectStoreNames.contains(objStoreName)) {
console.log("ObjectStore for " + objStoreName + " doesn't exist");
if (error) {
error("ko")
} else {
defer.reject("ko");
}
} else {
var oTransaction = window.myDB.transaction([objStoreName], "readwrite");
var oDataStore = oTransaction.objectStore(objStoreName);
oTransaction.oncomplete = function (event) {
console.log("Transaction completed: database modification for " + objStoreName + " finished.");
if (success) {
success();
} else {
defer.resolve("ok");
}
};
oTransaction.onerror = function (event) {
console.log("Transaction for " + objStoreName + " not opened due to error. Check for duplicate items or missing properties!");
console.log(event.target.error);
if (error) {
error("ko")
} else {
defer.reject("ko");
}
};
var oRecord = {};
for (var i = 0; i < res.length; i++) {
oRecord = res[i];
oDataStore.put(oRecord);
}
}
}.bind(this),
function (err) {
console.log("DB Creation failed");
if (error) {
error("ko")
} else {
defer.reject("ko");
}
}.bind(this)
);
}
} else {
if (error) {
error("ko")
} else {
defer.reject("ko");
}
}
if (typeof success === 'undefined' && typeof error === 'undefined') {
return defer.promise;
}
},
P.S.I have omitted parts of the code.
This was working fine with the previous version of ios, i think i had installed the 10.2.1, now it simply crashes after calling the put method. I tried upgrading now ios to the beta of 10.3.2 but the result is the same. Anyone else noticed this or have any idea of how to resolve this problem?
Thanks
K
UPDATE
I've found the issue: the complex dataTypes. Since IndexedDB supports saving and retrieving complex dataTypes, i had some properties which were arrays or objects that i used to save in some of my ObjectStores. This is definitely a big problem for me because the only workaround i can think for this is to stingify the complex fields but since i work with a lot of data this would create a big performance issue. I hope the ios developer team will find a solution for this soon enough
Are you sure every key in the res[] array is a valid key? There is a closed bug here:
https://bugs.webkit.org/show_bug.cgi?id=170000
It looks if you pass in an invalid key it will cause webkit to crash.
This fix for this will likely be contained in the next public release of iOS.
To determine what a valid key is see this section of the W3.org spec:
3.1.3 Keys
In order to efficiently retrieve records stored in an indexed database, each record is organized according to its key. A value is said to be a valid key if it is one of the following ECMAScript [ECMA-262] types: Number primitive value, String primitive value, Date object, or Array object. An Array is only a valid key if every item in the array is defined and is a valid key (i.e. sparse arrays can not be valid keys) and if the Array doesn't directly or indirectly contain itself. Any non-numeric properties on an Array are ignored, and thus do not affect whether the Array is a valid key. If the value is of type Number, it is only a valid key if it is not NaN. If the value is of type Date it is only a valid key if its [[PrimitiveValue]] internal property, as defined by [ECMA-262], is not NaN. Conforming user agents must support all valid keys as keys.
This was taken from here:
https://www.w3.org/TR/IndexedDB/#key-construct
Not sure if it's the same issue, but I had a crash on iOS 10.3 that I didnt get in any other browser. Using Dexie wrapper for indexedDB, I did a get all records from table search:
db.table.toArray(function (results) {
// process...
})
and got flames from Xcode to what looked like a threading issue in WebKit so I just added setTimeout( ... ,1) and that hacked around the problem for me.

restore in-app purchases not working properly

I'm using Flash CS6 to build apps for iOS and Android.
I have built 6 working apps and in-app purchases and restoring was working properly. Now, I'm building my 7th app and I tested my in-app purchases. Purchasing works very well but when I remove the app and re-install and come to buy a product. It tells that I have already purchases the product and it will restore it for free, but it keeps loading and nothing happens.
This happened for all old apps as well.
*Note: I publish the SWF using Flash then I build the IPA using cmd
I use this as3 code:
function log(s:String):void
{
trace(s);
log_txt.text = s;
}
function initStorekit():void
{
log("initializing StoreKit..");
if (! StoreKit.isSupported())
{
log("Store Kit iOS purchases is not supported on this platform.");
return;
}
else
{
supported = true;
}
StoreKit.create();
log("StoreKit Initialized.");
// make sure that purchases will actually work on this device before continuing!
// (for example, parental controls may be preventing them.)
if (! StoreKit.storeKit.isStoreKitAvailable())
{
log("Store is disabled on this device.");
return;
}
// add listeners here
StoreKit.storeKit.addEventListener(StoreKitEvent.PRODUCT_DETAILS_LOADED,onProductsLoaded);
StoreKit.storeKit.addEventListener(StoreKitEvent.PURCHASE_SUCCEEDED,onPurchaseSuccess);
StoreKit.storeKit.addEventListener(StoreKitEvent.PURCHASE_CANCELLED,onPurchaseUserCancelled);
StoreKit.storeKit.addEventListener(StoreKitEvent.TRANSACTIONS_RESTORED, onTransactionsRestored);
// adding error events. always listen for these to avoid your program failing.;
StoreKit.storeKit.addEventListener(StoreKitErrorEvent.PRODUCT_DETAILS_FAILED,onProductDetailsFailed);
StoreKit.storeKit.addEventListener(StoreKitErrorEvent.PURCHASE_FAILED,onPurchaseFailed);
StoreKit.storeKit.addEventListener(StoreKitErrorEvent.TRANSACTION_RESTORE_FAILED, onTransactionRestoreFailed);
// initialize a sharedobject that's holding our inventory.;
initSharedObject();
var productIdList:Vector.<String>=new Vector.<String>();
productIdList.push(LEVELPACK1_ID);
productIdList.push(LEVELPACK2_ID);
productIdList.push(LEVELPACK3_ID);
productIdList.push(REMOVEADS_ID);
productIdList.push(ALLINPACKAGE_ID);
// when this is done, we'll get a PRODUCT_DETAILS_LOADED or PRODUCT_DETAILS_FAILED event and go on from there...;
log("Loading product details...");
StoreKit.storeKit.loadProductDetails(productIdList);
}
function onProductsLoaded(e:StoreKitEvent):void
{
log("products loaded.");
for each (var product:StoreKitProduct in e.validProducts)
{
trace("ID: "+product.productId);
trace("Title: "+product.title);
trace("Description: "+product.description);
trace("String Price: "+product.localizedPrice);
trace("Price: "+product.price);
}
log("Loaded "+e.validProducts.length+" Products.");
// if any of the product ids we tried to pass in were not found on the server,
// we won't be able to by them so something is wrong.
if (e.invalidProductIds.length > 0)
{
log("[ERR]: these products not valid:"+e.invalidProductIds.join(","));
return;
}
}
function onProductDetailsFailed(e:StoreKitErrorEvent):void
{
log("ERR loading products:"+e.text);
}
function initSharedObject():void
{
this.sharedObject = SharedObject.getLocal("myPurchases");
// check if the application has been loaded before. if not, create a store of our purchases in the sharedobject.
if (sharedObject.data["inventory"] == null)
{
sharedObject.data["inventory"]=new Object();
}
updateInventoryMessage();
}
/** Update Inventory Message */
function updateInventoryMessage():void
{
var inventory:Object = sharedObject.data["inventory"];
// if the value is set to something, you have it
if (inventory[LEVELPACK1_ID] != null)
{
unlockLP1_fnc();
}
if (inventory[LEVELPACK2_ID] != null)
{
unlockLP2_fnc();
}
if (inventory[LEVELPACK3_ID] != null)
{
unlockLP3_fnc();
}
if (inventory[REMOVEADS_ID] != null)
{
hideAds_fnc();
}
if (inventory[ALLINPACKAGE_ID] != null)
{
unlockLP1_fnc();unlockLP2_fnc();unlockLP3_fnc();hideAds_fnc();
}
log("Has hasUnlocked? ");
}
function purchaseUnlock(s:String):void
{
enableLoading();
// for this to work, you must have added the value of LEVELPACK_PRODUCT_ID in the iTunes Connect website
log("start purchase of non-consumable '"+s+"'...");
// we won't let you purchase it if its already in your inventory!
var inventory:Object = sharedObject.data["inventory"];
if (inventory[s] != null)
{
log("You already have unlocked this!");
return;
}
StoreKit.storeKit.purchaseProduct(s);
}
/** Example of how to restore transactions */
function restoreTransactions():void
{
enableLoading();
log("requesting transaction restore...");
StoreKit.storeKit.restoreTransactions();
}
function onPurchaseSuccess(e:StoreKitEvent):void
{
log("Successful purchase of '"+e.productId+"'");
disableLoading();
// update our sharedobject with the state of this inventory item.
// this is just an example to make the process clear. you will
// want to make your own inventory manager class to handle these
// types of things.
var inventory:Object = sharedObject.data["inventory"];
switch (e.productId)
{
case ALLINPACKAGE_ID :
inventory[ALLINPACKAGE_ID] = "purchased";
break;
case LEVELPACK1_ID :
inventory[LEVELPACK1_ID] = "purchased";
break;
case LEVELPACK2_ID :
inventory[LEVELPACK2_ID] = "purchased";
gotoAndStop("lvls");
break;
case LEVELPACK3_ID :
inventory[LEVELPACK3_ID] = "purchased";
gotoAndStop("lvls");
break;
case REMOVEADS_ID :
inventory[REMOVEADS_ID] = "purchased";
gotoAndStop("mm");
break;
default :
log("xxxxx");
// we don't do anything for unknown items.
}
// save state!
sharedObject.flush();
// update the message on screen;
updateInventoryMessage();
}
function onPurchaseFailed(e:StoreKitErrorEvent):void
{
disableLoading();
log("FAILED purchase="+e.productId+",t="+e.transactionId+",o="+e.originalTransactionId);
}
function onPurchaseUserCancelled(e:StoreKitEvent):void
{
disableLoading();
log("CANCELLED purchase="+e.productId+","+e.transactionId);
}
function onTransactionsRestored(e:StoreKitEvent):void
{
disableLoading();
log("All previous transactions restored!");
var inventory:Object = sharedObject.data["inventory"];
switch (e.productId)
{
case ALLINPACKAGE_ID :
inventory[ALLINPACKAGE_ID] = "purchased";
break;
case LEVELPACK1_ID :
inventory[LEVELPACK1_ID] = "purchased";
break;
case LEVELPACK2_ID :
inventory[LEVELPACK2_ID] = "purchased";
gotoAndStop("lvls");
break;
case LEVELPACK3_ID :
inventory[LEVELPACK3_ID] = "purchased";
gotoAndStop("lvls");
break;
case REMOVEADS_ID :
inventory[REMOVEADS_ID] = "purchased";
gotoAndStop("mm");
break;
default :
log("xxxxx");
// we don't do anything for unknown items.
}
// save state!
sharedObject.flush();
updateInventoryMessage();
}
function onTransactionRestoreFailed(e:StoreKitErrorEvent):void
{
disableLoading();
log("an error occurred in restore purchases:"+e.text);
}

Error Handling When Saving a CKRecord

I am looking for an example of proper error handling when saving a CKRecord. According to the Apple docs I should "Use the information in the error object to determine whether a problem has a workaround."
I understand that the error object has a userInfo dictionary, but how do I figure out what the keys are for the dictionary and how to handle the errors?
The following example illustrates how I'm currently saving a CKRecord:
CKRecord *record = [[CKRecord alloc] initWithRecordType:#"MyRecordType"];
[record setValue:[NSNumber numberWithInt:99] forKey:#"myInt"];
[db saveRecord:record completionHandler:^(CKRecord *savedPlace, NSError *error) {
// handle errors here
if (savedPlace) {
NSLog(#"save successful");
}else{
NSLog(#"save unsuccessful");
}
if (error) {
NSLog(#"Error saving %#", error.localizedDescription);
}
}];
How can I improve this code to provide work arounds for potential saving issues?
In my library EVCloudKitDao I have a separate method that will return a error type based on the error code. Depending on that type you can decide what to do. Here is that method:
public enum HandleCloudKitErrorAs {
case Success,
Retry(afterSeconds:Double),
RecoverableError,
Fail
}
public static func handleCloudKitErrorAs(error:NSError?, retryAttempt:Double = 1) -> HandleCloudKitErrorAs {
if error == nil {
return .Success
}
let errorCode:CKErrorCode = CKErrorCode(rawValue: error!.code)!
switch errorCode {
case .NetworkUnavailable, .NetworkFailure, .ServiceUnavailable, .RequestRateLimited, .ZoneBusy, .ResultsTruncated:
// Use an exponential retry delay which maxes out at half an hour.
var seconds = Double(pow(2, Double(retryAttempt)))
if seconds > 1800 {
seconds = 1800
}
// Or if there is a retry delay specified in the error, then use that.
if let userInfo = error?.userInfo {
if let retry = userInfo[CKErrorRetryAfterKey] as? NSNumber {
seconds = Double(retry)
}
}
NSLog("Debug: Should retry in \(seconds) seconds. \(error)")
return .Retry(afterSeconds: seconds)
case .UnknownItem, .InvalidArguments, .IncompatibleVersion, .BadContainer, .MissingEntitlement, .PermissionFailure, .BadDatabase, .AssetFileNotFound, .OperationCancelled, .NotAuthenticated, .AssetFileModified, .BatchRequestFailed, .ZoneNotFound, .UserDeletedZone, .InternalError, .ServerRejectedRequest, .ConstraintViolation:
NSLog("Error: \(error)")
return .Fail;
case .QuotaExceeded, .LimitExceeded:
NSLog("Warning: \(error)")
return .Fail;
case .ChangeTokenExpired, .ServerRecordChanged:
NSLog("Info: \(error)")
return .RecoverableError
default:
NSLog("Error: \(error)") //New error introduced in iOS...?
return .Fail;
}
}
Inside the callback of a CloudKit method you can then use this function like this:
func loadContacts(retryCount:Double = 1) {
// Look who of our contact is also using this app.
EVCloudKitDao.publicDB.allContactsUserInfo({ users in
EVLog("AllContactUserInfo count = \(users.count)");
Async.main{
self.contacts = users
self.tableView.reloadData()
}
}, errorHandler: { error in
switch EVCloudKitDao.handleCloudKitErrorAs(error, retryAttempt: retryCount) {
case .Retry(let timeToWait):
Async.background(after: timeToWait) {
self.loadContacts(retryCount + 1)
}
case .Fail:
Helper.showError("Something went wrong: \(error.localizedDescription)")
default: // For here there is no need to handle the .Success, .Fail and .RecoverableError
break
}
})
}
In my the case above I use a separate error callback handler. You can also call it directly form within a CloudKit method callback. Just first check if there is an error.
Here is an implementation in which I handle the common error of CKErrorNetworkFailure by retrying to save after the recommended retry after time interval which is stored in the userInfo dictionary.
-(void)saveRecord:(CKRecord*)record toDatabase:(CKDatabase*)database{
[database saveRecord:record completionHandler:^(CKRecord *record, NSError *error) {
if (error==nil) {
NSLog(#"The save was successful");
//Do something
}else{
NSLog(#"Error saving with localizedDescription: %#", error.localizedDescription);
NSLog(#"CKErrorCode = %lu", [error code]);
if ([error code]==CKErrorNetworkFailure) {
double retryAfterValue = [[error.userInfo valueForKey:CKErrorRetryAfterKey] doubleValue];
NSLog(#"Error code network unavailable retrying after %f", retryAfterValue);
NSTimer *timer = [NSTimer timerWithTimeInterval:retryAfterValue target:self selector:#selector(testOutCloudKit) userInfo:nil repeats:NO];
[timer fire];
}
}
}];
}
In Swift 3, I handle my CloudKit errors this way:
privateDB.perform(myQuery, inZoneWith: nil) {records, error in
if error != nil {
print (error?.localizedDescription)
if error?._code == CKError.notAuthenticated.rawValue {
// Solve problems here...
}
Note that the CKError.notAuthenticated is selected from the list presented by code completion after writing CKError.
Hope this helps!

Prevent a closure from running until another has completed

Here is code for two closures in two different IBAction button presses. The desired outcome is for the button press to turn on/off an LED, then to access a light sensor and read the light value after the change in LED status.
What happens is a race condition where the function getVariable runs and returns before the callFunction has implemented the change. The result is that the value displayed in getLightLabel.text is that of the prior condition, not the current condition.
My question is how to rewrite the code below so that myPhoton!.getVariable does not execute until after the myPhoton!.callFunction has returned (completed its task).
I have tried placing getVariable inside callFunction, both before and after the } closing if (error == nil), but the result was identical to the code shown here.
#IBAction func lightOn(sender: AnyObject) {
let funcArgs = [1]
myPhoton!.callFunction("lightLed0", withArguments: funcArgs) { (resultCode : NSNumber!, error : NSError!) -> Void in
if (error == nil) {
self.lightStateLabel.text = "LED is on"
}
}
myPhoton!.getVariable("Light", completion: { (result:AnyObject!, error:NSError!) -> Void in
if let e = error {
self.getLightLabel.text = "Failed reading light"
}
else {
if let res = result as? Float {
self.getLightLabel.text = "Light level is \(res) lumens"
}
}
})
}
#IBAction func lightOff(sender: AnyObject) {
let funcArgs = [0]
myPhoton!.callFunction("lightLed0", withArguments: funcArgs) { (resultCode : NSNumber!, error : NSError!) -> Void in
if (error == nil) {
self.lightStateLabel.text = "LED is off"
}
}
myPhoton!.getVariable("Light", completion: { (result:AnyObject!, error:NSError!) -> Void in
if let e = error {
self.getLightLabel.text = "Failed reading light"
}
else {
if let res = result as? Float {
self.getLightLabel.text = "Light level is \(res) lumens"
}
}
})
}
Here is the callFunction comments and code from the .h file. This SDK is written in Objective C. I am using it in Swift with a bridging header file.
/**
* Call a function on the device
*
* #param functionName Function name
* #param args Array of arguments to pass to the function on the device. Arguments will be converted to string maximum length 63 chars.
* #param completion Completion block will be called when function was invoked on device. First argument of block is the integer return value of the function, second is NSError object in case of an error invoking the function
*/
-(void)callFunction:(NSString *)functionName withArguments:(NSArray *)args completion:(void (^)(NSNumber *, NSError *))completion;
/*
-(void)addEventHandler:(NSString *)eventName handler:(void(^)(void))handler;
-(void)removeEventHandler:(NSString *)eventName;
*/
Here is the .m file code
-(void)callFunction:(NSString *)functionName withArguments:(NSArray *)args completion:(void (^)(NSNumber *, NSError *))completion
{
// TODO: check function name exists in list
NSURL *url = [self.baseURL URLByAppendingPathComponent:[NSString stringWithFormat:#"v1/devices/%#/%#", self.id, functionName]];
NSMutableDictionary *params = [NSMutableDictionary new]; //[self defaultParams];
// TODO: check response of calling a non existant function
if (args) {
NSMutableArray *argsStr = [[NSMutableArray alloc] initWithCapacity:args.count];
for (id arg in args)
{
[argsStr addObject:[arg description]];
}
NSString *argsValue = [argsStr componentsJoinedByString:#","];
if (argsValue.length > MAX_SPARK_FUNCTION_ARG_LENGTH)
{
// TODO: arrange user error/codes in a list
NSError *err = [self makeErrorWithDescription:[NSString stringWithFormat:#"Maximum argument length cannot exceed %d",MAX_SPARK_FUNCTION_ARG_LENGTH] code:1000];
if (completion)
completion(nil,err);
return;
}
params[#"args"] = argsValue;
}
[self setAuthHeaderWithAccessToken];
[self.manager POST:[url description] parameters:params success:^(AFHTTPRequestOperation *operation, id responseObject) {
if (completion)
{
NSDictionary *responseDict = responseObject;
if ([responseDict[#"connected"] boolValue]==NO)
{
NSError *err = [self makeErrorWithDescription:#"Device is not connected" code:1001];
completion(nil,err);
}
else
{
// check
NSNumber *result = responseDict[#"return_value"];
completion(result,nil);
}
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error)
{
if (completion)
completion(nil,error);
}];
}
One solution is to put the second closure inside the first, where the first returns and provides and Error value. If no error,then execuet the second closure. That is one way to tightly couple the two closures without resorting to semaphores or other messaging schemes.
In this application, the problem I was encountering cannot be solved on the IOS/Swift side of the stack. The cloud API and embedded uP are not tightly coupled, so the cloud returns to the IOS with a completion before the full function code has run on the Particle uP.
The solution to this overall problem actually lies in either modifying the cloud API or adding some additional code to the uP firmware to tightly couple the process to the IOS app with additional communication.

IOS Photon Cloud SDK getRoomList function doesn't work

I am developing cocos2d-x game which have online game mode.
Online game designed and implemented by Photon Cloud SDK(http://www.exitgames.com).
I implemented only ios version but it doesn't work.
The codes that I have implemented are blow.
void NetworkLogic::opJoinRandomRoom()
{
ExitGames::Common::JVector<ExitGames::LoadBalancing::Room> roomList;
roomList = mLoadBalancingClient.getRoomList();
int count = roomList.getSize();
CCLog("Room Count = %d", count);
if(count == 0)
{
this->opCreateRoom();
}else{
mLoadBalancingClient.opJoinRandomRoom();
}
}
void NetworkLogic::update(float dt)
{
this->run();
}
void NetworkLogic::run(void)
{
if(mLastInput == INPUT_EXIT && mStateAccessor.getState() != STATE_DISCONNECTING && mStateAccessor.getState() != STATE_DISCONNECTED)
{
disconnect();
mStateAccessor.setState(STATE_DISCONNECTING);
}
else
{
State state = mStateAccessor.getState();
switch(state)
{
case STATE_INITIALIZED:
connect();
mStateAccessor.setState(STATE_CONNECTING);
break;
case STATE_CONNECTING:
break; // wait for callback
case STATE_CONNECTED:
{
ExitGames::Common::JVector<ExitGames::LoadBalancing::Room> roomList;
roomList = mLoadBalancingClient.getRoomList();
int count = roomList.getSize();
ExitGames::Common::JString tmp;
tmp = count;
EGLOG(ExitGames::Common::DebugLevel::INFO, tmp);
CCLog("Room count in Room = %d", count);
switch(mLastInput)
{
case INPUT_CREATE_GAME: // create Game
opCreateRoom();
break;
case INPUT_JOIN_RANDOM_GAME: // join Game
opJoinRandomRoom();
mStateAccessor.setState(STATE_JOINING);
break;
default: // no or illegal input -> stay waiting for legal input
break;
}
break;
}
case STATE_JOINING:
break; // wait for callback
case STATE_JOINED:
switch(mLastInput)
{
case INPUT_LEAVE_GAME: // leave Game
mLoadBalancingClient.opLeaveRoom();
mStateAccessor.setState(STATE_LEAVING);
break;
default: // no or illegal input -> stay waiting for legal input
break;
}
break;
case STATE_LEAVING:
break; // wait for callback
case STATE_LEFT:
mStateAccessor.setState(STATE_CONNECTED);
break;
case STATE_DISCONNECTING:
break; // wait for callback
default:
break;
}
}
mLastInput = INPUT_NON;
mLoadBalancingClient.service();
}
First I run one app then getRoomList function returns 0 values.
Also after first room created and run second app but it also returns getRoomList function 0.
Please help me.
I have just taken the code that you have provided in your question and copied it into the according place inside the demo of an otherwise unchanged version 3.2.2.0 build of the Photon C++ Client SDK (and removed the two CCLog() lines to make it compile without cocos2d-x) and it worked just fine for me:
The demo prints 0 for the size of the room list until I let one client create a room. Afterwards the other client prints 1.

Resources