I may not quite get your use case but here’s what I did in a single iOS app that has 3 roles that appears to be working correctly so far (I’m still build) and may be able to help. I’m not much of a coder so I’m really digging in to leverage the capabilities already available to me in DF, so this may be an oversimplification, but nonetheless…
My use case is:
- DEVICE: I need to register all devices on launch and be able to track/manage them starting from first launch. This way I am not relying on a user to register before getting any of the installed device information.
- DEMO: I need to provide users a fully functional, networked demo app. My app is a data-driven business app that can have multiple users in the same company (and multiple companies) all sharing data so I cannot simply just seed demo data.
- LIVE - of course…
One key thing to grasp is that you have a single DF User but that User can access any app / role / script / service on your DF instance.
Here’s the DF setup I’ve got working so far for my use case:
-
I created 2 Databases, One LIVE (MySQL) and 1 DEMO (Mongo). Let’s call them LIVEDB and DEMODB. I put the DEMODB on Mongo so I can learn more about it and see how it behaves with my app without affecting any live users. So far I’ve been able to interact between both dB types in my app without modifying my code to distinguish between them. One caveat/difference is that the MySQL primary keys start at 1 and increment on create, Mongo hashes a 16-char primary key on create. To avoid any future problems with synching records and pulling common data between the two dB’s is I have my app create a unique 36 char hash with each new object and store it as ‘objectId’ (users, customers, accounts, devices, inventory, transactions, etc.- everything gets one before stored.)
-
I created 3 Roles - LIVEROLE, DEMOROLE, DEVICEROLE
-
I created 3 Apps - LIVEAPP, DEMOAPP, DEVICEAPP
-
I assigned the proper role to each app as default - LIVEAPP/LIVEROLE - DEMOAPP/DEMOROLE - DEVICEAPP/DEVICEROLL.
-
I created ‘Device’ tables in my LIVEDB and DEMODB. One thing to note, on the Mongo DB I just needed to create a table name. When my app sends data if the fields don’t exist in the table Mongo creates them. The MySQL database I have to manually create every field first or they won’t get logged.
-
I went to the USER service and checked OpenReg and assigned the DEMOROLE. This allows not only my devices to register/update on launch without a user session, but also assigns all users to the DEMOROLE upon self-registration.
-
in my DEVICEROLE, I assigned GET, POST and PATCH access to both dB’s /_table/Device and /_tableDevice*
-
in my DEMOROLE, I assigned to my DEMODB GET, POST, PATCH, PUT, DELETE to /_table and /_table*. This give DEMO users access only to my DEMODB I did the same for my LIVE users in my LIVEDB, so now only the LIVEROLE users can access the LIVEDB. Down the road I’ll lock it down some more with scripts, lookup keys and what have you as I learn more about this awesome platform…
-
I haven’t done it yet but I’m going to do the same thing I did with my db’s with the File service - create 2 storage containers for LIVE and DEMO then assign the appropriate roles.
-
I’ve read some posts and blogs that you need to enable CORS with *. I originally did then deleted the CORS record and everything still communicates fine between my app and user dB’s…
That’s it for the DF setup. Now I’ll explain how it works out in my app so far…
- The trick is to create service definitions to distinguish the APIs between apps (for me device, live and demo). Once you do this, the logic can be built into your app to call the correct Role through the API, keep your JSON structures as is and switch easily between APIs (ROLES) without a bunch of coding. It’s probably easier for me to show you my code than explain.
I used the DF iOS starter app as a basis and pattern for my app to follow. In the API management class (RESTEngine.h / .m in the starter app), I set up my global variables like this:
//BASE URL
#define kBaseUrl @"http://<yourUrl>/api/v2"
//dB Services
#define kDemoService @"/demomdb"
#define kLiveService @"/ipaysql"
#define kDbServiceName @"/_table"
//Header API Key Parameters
#define kDemoApiKey @"<the DEMOAPP Api Key>"
#define kLiveApiKey @"<the LIVEAPP Api Key>"
#define kDeviceApiKey @"the DEVICEAPP Api Key"
//Header Key and Content Params
#define kHeaderApiKeyParam @"X-DreamFactory-Api-Key"
#define kContentType @"application/json"
//user Session services
#define kSessionTokenKey @"SessionToken"
#define kUserEmail @"UserEmail"
#define kPassword @"UserPassword"
In my implementation file (RESTEngine.m) I created routing/path methods using these global variables. Here’s an example:
+ (NSString *)userWithResourceName: (NSString *)resourceName {
return [NSString stringWithFormat:@"%@/user/%@", kBaseUrl, resourceName];
}
+ (NSString *)customUserWithResourceName: (NSString *)resourceName {
return [NSString stringWithFormat:@"%@/system/user/%@",kBaseUrl, resourceName];
}
Now here is where I set up conditional-based routing/paths that I will call in my app. For me, it will be a simple LIVE/DEMO switch. If the user is in LIVE mode and switched to DEMO, I have a global BOOL value that sets the kApi parameter to start using the kDemoApiKey DEMOAPP API in it’s serviceWithTableName calls. This will ‘assume’ the DEMOROLE and start using the DEMO database. If the User switches back to LIVE then the kLiveApiKey is set in the serviceWithTableName to the LIVEAPP and the user assumes the LIVEROLE and interacts with the LIVEDB (and scripts, and services and whatever else the LIVEROLE is assigned to do). Look closely at the returns in this call referencing my global definitions above, it should explain it all…
This is my code to set the header params to set on my BOOL determining what ‘mode’ the user requests. The myControl method is my own private class to store vars.
- (NSDictionary *)headerParams {
if ([[[myControl controller]getTransactionValue:@"deviceSetup"]boolValue] == TRUE) {
if (_headerParams == nil) {
_headerParams = @{@"X-DreamFactory-Api-Key": kDeviceApiKey};
LOGI(@"USING DEVICE API");
}
}
if ([[[myControl controller]getSessionValue:@"liveMode"]boolValue] == TRUE) {
if (_headerParams == nil) {
_headerParams = @{@"X-DreamFactory-Api-Key": kLiveApiKey};
LOGI(@"USING LIVE API");
}
} else {
if (_headerParams == nil) {
_headerParams = @{@"X-DreamFactory-Api-Key": kDemoApiKey};
LOGI(@"USING DEMO API");
}
}
return _headerParams;
}
And here is how I set up my path routing services based on the live/demo BOOL value.
+ (NSString *)serviceWithTableName: (NSString *)tableName {
if ([[[myControl controller]getSessionValue:@"liveMode"]boolValue] == TRUE) {
return [NSString stringWithFormat:@"%@%@%@%@", kBaseUrl, kLiveService, kDbServiceName, tableName];
} else {
return [NSString stringWithFormat:@"%@%@%@%@", kBaseUrl, kDemoService, kDbServiceName, tableName];
}
}
To wrap it up, on launch I set the kDeviceApiKey and the and check local storage for the LIVE/DEMO value and set a flag for it ‘isLive = TRUE’. Once the app gathers all the required device information I check to see if the local storage/keychain has a unique UUID (my 36-char hash deviceId). If deviceId = null, then it is flagged a ‘firstRun = TRUE’. I create a new hash and run a POST (all new installs and registrations start in DEMO mode). Remember way back when we started that the DEVICEAPP has the DEVICEROLE default - and the DEVICEROLE is configured to ONLY GET, POST, PATCH to DEMODB /_tables/Device - can’t do anything else anywhere else (I hope).
If deviceId exists in localstore/keychain on launch then I check the local/store/keychain value for a BOOL = ‘isLive’ which stored the last user setting for LIVE/DEMO. If TRUE, I set the kApiLive in the header and route paths (see code above). If FALSE then I set the kApiDemo header and router paths. Then I do a GET for the device to check a necessary flag on database record = isActive. If = FALSE then I deactivate the app and clear the ApiPaths. If TRUE then I run a PATCH to update the record in the (live/demo) database with the current device/location details. Once complete I set myGlobal var for device mode to FALSE.
THEN is when the Login screen becomes available. If firstRun, header/route has already been set to kApiDemo. If firstRun = FALSE, then header/path is LIVE or DEMO based on whatever the user had last session stored in the localStore/Keychain. Finally, the Login screen can also have a switch available for the user to set the kApiLive or kApiDemo API and routes prior to logging on…
Hope this helps…