7 Xamarin iOS Apps
If you use the Xamarin platform to develop iOS apps, you can use the SDK that Oracle Mobile Hub provides for Xamarin iOS apps. This SDK simplifies authentication with Mobile Hub and provides native wrapper classes for Mobile Hub platform APIs.
Get the SDK
To get the client SDK for Xamarin iOS, go to the Oracle Digital Assistant and Oracle Mobile Cloud Downloads page on OTN.
Create a Backend
Add the SDK
-
If you haven’t already done so, extract the contents from the SDK zip.
-
In Visual Studio, create a Visual C# iOS app.
-
Add the SDK's DLL file to your app by right-clicking the project's References node and selecting Edit References, clicking the .NET Assembly tab, and then browsing to the
IOS.dll
file in the extracted SDK zip. -
Add the configuration file to the app by right-clicking the project's root node and selecting Add > Add Files and then navigating to the SDK's
OMC.plist
file. -
Select the node for
OMC.plist
so that it's properties are displayed in the Properties pane. Then make sure that the Build Action property is set toBundleResource
. -
Add the
SynchStore.momd
folder to the app by right-clicking the project's root node and selecting Add > Add Existing Folder and then navigating to the SDK'sSynchStore
folder. -
For all of the files in the
SynchStore.momd
folder, make sure that the Build Action property is set toBundleResource
.
The next step will be to fill in the OMC.plist
file.
Configure SDK Properties
To use the client SDK in an iOS app, you need to add the OMC.plist
configuration file to the app and fill it in with environment details for your backend in Oracle Mobile Hub, as well as other configuration information. In turn, the SDK classes use this information to help manage authorization, logging, event tracking, data synchronization, and other features.
You package the configuration file in the root of your app’s main bundle.
The file is essentially divided into the following parts:
-
The
mobileBackend
key and its contents.You include this part if you are using a backend with the app. The SDK classes use the environment and authentication details you specify there to access the backend and construct HTTP headers for REST calls made to APIs.
-
Keys that apply to the configuration as a whole, such as
logLevel
andoAuthTokenEndpoint
. These keys generally, but don’t have to, appear at the top of the file.
Here’s the same file in text form:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"
<plist version="1.0">
<dict>
<key>logLevel</key>
<string>debug</string>
<key>logHTTP</key>
<true/>
<key>oAuthTokenEndPoint</key>
<string>https://MY_TOKEN_ENDPOINT</string>
<key>mobileBackend</key>
<dict>
<key>name</key>
<string>EasyShoppingMBE</string>
<key>baseURL</key>
<string>https://MY_CLOUD_DOMAIN.oracle.com</string>
<key>authentication</key>
<dict>
<key>type</key>
<string>oauth</string>
<key>oauth</key>
<dict>
<key>clientID</key>
<string>11dac238ffaa4b029e78e982114642ab</string>
<key>clientSecret</key>
<string>5624cbdd-a7c5-4c10-a758-6019a5ab8da8</string>
<key>enableOffline</key>
<true/>
</dict>
</dict>
</dict>
</dict>
</plist>
And here is a description of some of the more important entries in the OMC.plist
file.
-
oAuthTokenEndPoint
— The URL of the OAuth server from where your application gets its authentication token. This key needs to be provided for all apps that rely on OAuth to authenticate. You get this from the backend’s Settings page. The endpoint should be only the base URL (in the formhttps://host.domain:port
). -
logLevel
— Determines how much SDK logging is displayed in the app’s console. The default value iserror
. Other possible values (in increasing level of detail) arewarning
,info
, anddebug
. It is also possible to set the value tonone
. -
logHTTP
— When set totrue
, the SDK logs the headers and bodies of all HTTP requests and responses. -
mobileBackend
— A dictionary entry containing authentication details for your backend and other optional details, such as synchronization properties.You get the authentication details, such as the OAuth and HTTP credentials, from the backend’s Settings page.
-
mobileBackend/baseUrl
— The base URL for all APIs that you call through the backend. You get this from the backend’s Settings page. -
mobileBackend/authentication
— Contains a dictionary with the following elements:-
The
type
sub-key, with possible (string
) values ofoauth
,basic
,facebook
, andtokenExchange
. -
One or more sub-keys for authentication types, containing a dictionary with the authentication credentials.
Within sub-keys for
basic
andoauth
, you can also add theenableOffline
key. By default, this property is set totrue
.
-
Authentication Properties
The contents and sub-elements of the mobileBackend/authentication
key depend on what kind of authentication the app will be using.
OAuth
-
Set the value of the
type
key tooauth
. -
Create an
oauth
sub-key and fill in theclientID
andclientSecret
credentials provided by the backend. -
At the top level of the file, supply the
oAuthTokenEndPoint
value that is supplied but without theoauth2/v1/token
that is appended on the backend’s Settings page. -
Optionally, if you want to disable offline authentication, add the
enableOffline
sub-key and set it tofalse
.
The resulting authorization
property might look something like this:
<key>authentication</key>
<dict>
<key>type</key>
<string>oauth</string>
<key>oauth</key>
<dict>
<key>clientID</key>
<string>11dac238ffaa4b029e78e982114642ab</string>
<key>clientSecret</key>
<string>5624cbdd-a7c5-4c10-a758-6019a5ab8da8</string>
</dict>
<key>basic</key>
</dict>
HTTP Basic
-
Set the value of the
type
key tobasic
. -
Create a
basic
sub-key and fill in the HTTP Basic credentials (mobileBackendID
andanonymousKey
) provided by the backend. -
Optionally, if you want to disable offline authentication, add the
enableOffline
sub-key and set it tofalse
.
The resulting authentication
entry might look something like this:
<key>authentication</key>
<dict>
<key>type</key>
<string>basic</string>
<key>basic</key>
<dict>
<key>mobileBackendID</key>
<string>a8c6a34f-61bb-4bee-948c-d43dd2c077d7</string>
<key>anonymousKey</key>
<string>dXNlcmlkOnBhc3N3b3Jk</string>
</dict>
</dict>
Token Exchange
If you are authenticating using a third-party token, do the following:
-
Set the value of the
type
key totokenExchange
. -
Create a
tokenExchange
sub-key and fill in the OAuth Consumer credentials provided by the backend.
The resulting authentication
section might look something like this:
<key>authentication</key>
<dict>
<key>type</key>
<string>tokenExchange</string>
<key>tokenExchange</key>
<dict>
<key>oauth</key>
<dict>
<key>clientID</key>
<string>b39ba08d30d54e24970332fcdffec3a7</string>
<key>clientSecret</key>
<string>23953fe8-76ed-4c89-a5cb-6042db10cfaf</string>
</dict>
<key>basic</key>
<dict>
<key>mobileBackendID</key>
<string>8d3744b8-cab2-479c-998b-ebba2c31560f</string>
<key>anonymousKey</key>
<string>ZFJJTUVfREVDRVBUSUNPTl9NT0JJTEVfQU5PTll</string>
</dict>
</dict>
</dict>
Call Mobile APIs
Once you have added the SDK to your app and configured your OMC.plist
file, here are the general steps for calling APIs from your iOS app:
-
Add an SDK call to your app to load the configuration info.
-
Add an SDK call to your app to handle authentication.
-
Add any other SDK calls that you want to use.
Load the Backend's Configuration
For any calls to Mobile Hub APIs using the iOS SDK to successfully complete, you need to have the mobile backend’s configuration loaded from the app’s OMC.plist
file. You do this using the OMCMobileBackend
class:
OMCMobileBackend oMCMobileBackend = OMCMobileBackendManager.SharedManager.MobileBackendForName("MBE_FullCoverage");
Authenticate and Log In
Here is some sample code that you can use for authentication through Mobile Hub in your iOS apps.
OAuth
You can use the following method to handle a user logging in with a user name and password.
OMCAuthorization authorization = oMCMobileBackend.Authorization;
authorization.AuthenticationType = OMCAuthenticationType.OAuth;
authorization.Authenticate(username.Text, password.Text);
This method terminates the connection to Mobile Hub and clears the user name and password from the iOS keychain:
authorization.Logout(HandleOMCAuthorizationLogoutCompletionBlock);
void HandleOMCAuthorizationLogoutCompletionBlock(NSError nsError)
{
if(nsError == null){
Console.WriteLine("Logout success!");
}
}
HTTP Basic
You can use the following method to handle a user logging in with a user name and password.
OMCAuthorization authorization = oMCMobileBackend.Authorization;
authorization.AuthenticationType = OMCAuthenticationType.HTTPBasic;
authorization.Authenticate(username.Text, password.Text);
This method terminates the connection to Mobile Hub and clears the user name and password from the iOS keychain:
authorization.Logout(HandleOMCAuthorizationLogoutCompletionBlock);
void HandleOMCAuthorizationLogoutCompletionBlock(NSError nsError)
{
if(nsError == null){
Console.WriteLine("Logout success!");
}
}
SSO with a Third-Party Token
First, your app needs to get a token from the third-party token issuer. The way you can obtain the token varies by issuer.
Note:
The default expiration time for storing a third-party token in Mobile Hub is 6 hours. You can adjust this time by changing theSecurity_TokenExchangeTimeoutSecs
policy.
OMCAuthorization oMCAuthorization = oMCMobileBackend.Authorization;
oMCAuthorization.AuthenticationType = OMCAuthenticationType.SSOTokenExchange;
NSError nSError = oMCAuthorization.AuthenticateSSOTokenExchange(Token);
oMCAuthorization.AuthenticateSSOTokenExchange(Token, HandleOMCAuthorizationAuthCompletionBlock);
oMCAuthorization.AuthenticateSSOTokenExchange(Token, true, HandleOMCAuthorizationAuthCompletionBlock);
oMCAuthorization.AuthenticateSSOTokenExchange(Token, true);
bool iSLoaded = oMCAuthorization.LoadSSOTokenExchange;
oMCAuthorization.ClearSSOTokenExchange();
For apps that allow login through Facebook, use:
oMCAuthorization.AuthenticationType = OMCAuthenticationType.Facebook;
oMCAuthorization.AuthenticateSocial(HandleOMCAuthorizationAuthCompletionBlock);
If you haven’t already set up the app and its mobile backend to use Facebook as the identity provider, see Facebook Login in Mobile Hub.
Call Platform APIs
Once the mobile backend’s configuration info is loaded into the app and you have made a call to get the mobile backend, you can make calls to SDK classes to access platform features.
Here are some code snippets that illustrate how to access these APIs with the SDK.
User Management
Getting a User
OMCAuthorization oMCAuthorization = oMCMobileBackend.Authorization;
oMCAuthorization.GetCurrentUser(HandleOMCUserRegistrationCompletionBlockWithUser);
void HandleOMCUserRegistrationCompletionBlockWithUser(NSError nSError, OMCUser oMCUser)
{
if(nSError == null){
output.Text = user.FirstName + " User details have been fetched successfully";
}
}
Updating a User
user.SetValueForKey(new NSNumber(26),new NSString("age"));
user.SetValueForKey(new NSString("address"), new NSString("india"));
oMCAuthorization.UpdateCurrentUser(user,HandleOMCUserRegistrationCompletionBlock);
void HandleOMCUserRegistrationCompletionBlock(NSError nSError)
{
if (nSError == null)
{
//user = oMCUser;
if (user != null)
{
if (username.Text == null)
{
username.Text = "Welcome " + user.FirstName;
}
else output.Text = user.FirstName + " User details have been fetched successfully";
}
}
else
{
output.Text = nSError.ToString();
}
}
Location
Initialization
OMCLocation oMCLocation = oMCMobileBackend.Location;
Queries for Places, Devices, and Assets
private static OMCLocation oMCLocation;
private static OMCLocationPlace oMCLocationPlace;
private static OMCLocationDevice oMCLocationDevice;
private static OMCLocationAsset oMCLocationAsset;
oMCLocation = oMCMobileBackend.Location;
OMCLocationPlaceQuery oMCLocationPlaceQuery = oMCLocation.BuildPlaceQuery;
oMCLocationPlaceQuery.Name = "West";
oMCLocationPlaceQuery.ExecuteWithCompletionHandler(completionHandler);
OMCLocationAssetQuery oMCLocationAssetQuery = oMCLocation.BuildAssetQuery;
oMCLocationAssetQuery.Name = "joe";
oMCLocationAssetQuery.ExecuteWithCompletionHandler(completionHandler);
OMCLocationDeviceQuery oMCLocationDeviceQuery = oMCLocation.BuildDeviceQuery;
oMCLocationDeviceQuery.Name = "Beacon";
oMCLocationDeviceQuery.ExecuteWithCompletionHandler(completionHandler);
Fetching
Action<OMCLocationObjectQueryResult, NSError> completionHandler = new Action<OMCLocationObjectQueryResult, NSError>((OMCLocationObjectQueryResult arg1, NSError arg2) =>
{
if (arg2 == null)
{
OMCLocationObject[] LocationObjects = arg1.Items;
OMCLocationPlace oMCLocationPlace;
OMCLocationDevice oMCLocationDevice;
OMCLocationAsset oMCLocationAsset;
foreach (OMCLocationObject locationObject in LocationObjects)
{
Console.WriteLine("Location Object " + locationObject.GetType() + "--> " + i + " is: " + locationObject.ToString());
if(locationObject.GetType().Equals(typeof(OMCLocationPlace))){
oMCLocationPlace = (OMCLocationPlace)locationObject;
oMCLocation.PlaceWithID(oMCLocationPlace.Id_, placeCompletionHandler);
}
else if (locationObject.GetType().Equals(typeof(OMCLocationDevice)))
{
oMCLocationDevice = (OMCLocationDevice)locationObject;
oMCLocation.DeviceWithID(oMCLocationDevice.Id_, deviceCompletionHandler);
}
else if (locationObject.GetType().Equals(typeof(OMCLocationAsset)))
{
oMCLocationAsset = (OMCLocationAsset)locationObject;
oMCLocation.AssetWithID(oMCLocationAsset.Id_, assetCompletionHandler);
}
}
}
});
private static void assetCompletionHandler(OMCLocationAsset arg0, NSError arg1)
{
if (arg1 == null)
{
Console.WriteLine("Location Asset " + arg0.ToString());
}
}
private static void deviceCompletionHandler(OMCLocationDevice arg0, NSError arg1)
{
if (arg1 == null)
{
Console.WriteLine("Location Device " + arg0.ToString() );
}
}
private static void placeCompletionHandler(OMCLocationPlace arg0, NSError arg1)
{
if(arg1 == null){
Console.WriteLine("Location Place " + arg0.ToString());
}
}
Refreshing
Action<OMCLocationObjectQueryResult, NSError> completionHandler = new Action<OMCLocationObjectQueryResult, NSError>((OMCLocationObjectQueryResult arg1, NSError arg2) =>
{
if (arg2 == null)
{
OMCLocationObject[] LocationObjects = arg1.Items;
foreach (OMCLocationObject locationObject in LocationObjects)
{
Console.WriteLine("Location Object " + locationObject.GetType() + "--> " + i + " is: " + locationObject.ToString());
if(locationObject.GetType().Equals(typeof(OMCLocationPlace))){
oMCLocationPlace = (OMCLocationPlace)locationObject;
oMCLocationPlace.RefreshWithCompletionHandler(placeCompletionHandler);
}
else if (locationObject.GetType().Equals(typeof(OMCLocationDevice)))
{
oMCLocationDevice = (OMCLocationDevice)locationObject;
oMCLocationDevice.RefreshWithCompletionHandler(deviceCompletionHandler);
}
else if (locationObject.GetType().Equals(typeof(OMCLocationAsset)))
{
oMCLocationAsset = (OMCLocationAsset)locationObject;
oMCLocationAsset.RefreshWithCompletionHandler(assetCompletionHandler);
}
}
}
});
private static void placeCompletionHandler(NSError arg0)
{
if (arg0 == null)
{
Console.WriteLine("Location Place " + oMCLocationPlace.ToString());
}
}
private static void deviceCompletionHandler(NSError arg0)
{
if (arg0 == null)
{
Console.WriteLine("Location Device " + oMCLocationDevice.ToString());
}
}
private static void assetCompletionHandler(NSError arg0)
{
if (arg0 == null)
{
Console.WriteLine("Location Asset " + oMCLocationAsset.ToString());
}
}
Storage
Initialization
OMCStorage oMCStorage = oMCMobileBackend.Storage;
Getting a Collection
OMCStorageCollection oMCStorageCollection = oMCStorage.GetCollection("SharedCollection");
Getting an Object
oMCStorageObject = collection.Get("Object Id");
System.Console.WriteLine("Storage Object1: " + oMCStorageObject.ToString());
Getting All Objects from a Collection
NSMutableArray nSMutableArray = collection.Get(0, 100, true);
OMCStorageObject oMCStorageObject;
if (nSMutableArray != null && nSMutableArray.Count > 0)
{
for (uint i = 0; i < nSMutableArray.Count; i++){
oMCStorageObject = nSMutableArray.GetItem<OMCStorageObject>(i);
System.Console.WriteLine("Storage Object1: " + oMCStorageObject.ToString());
}
}
Uploading a Text File
NSData text = "This is a sample Text file";
OMCStorageObject txtFile = new OMCStorageObject("Mytext.txt", text, "text/plain");
collection.Put(txtFile);
Uploading an Image File
UIImage image = new UIImage("MyImage.png");
NSData data = image.AsPNG();
OMCStorageObject imageFile = new OMCStorageObject("MyImage", data, "image/png");
collection.Put(imageFile);
Notifications
Initialization
OMCNotifications oMCNotifications = oMCMobileBackend.Notifications;
Registering for Notifications
oMCNotifications.RegisterForNotifications(appDelegate.DeviceToken, HandleOMC_Notifications_SuccessBlock, HandleOMC_Notifications_ErrorBlock);
void HandleOMC_Notifications_SuccessBlock(NSHttpUrlResponse nSHttpUrlResponse)
{
if (nSHttpUrlResponse != null)
{
Console.WriteLine("Response from notification Server: " + nSHttpUrlResponse.StatusCode);
}
}
void HandleOMC_Notifications_ErrorBlock(NSError nSError)
{
if (nSError != null)
{
Console.WriteLine("Error in fetching mobiel file: " + nSError.LocalizedDescription);
}
}
AppDelegate code
public NSData DeviceToken = string.Empty;
public override void RegisteredForRemoteNotifications(UIApplication application, NSData deviceToken)
{
DeviceToken = deviceToken; // Do something to storage deviceToken.
Console.WriteLine("Device Token: " + DeviceToken.ToString());
}
public override void FailedToRegisterForRemoteNotifications(UIApplication application, NSError error)
{
Console.WriteLine("FailedToRegisterForRemoteNotifications.. :(");
}
public override void DidReceiveRemoteNotification(UIApplication application, NSDictionary userInfo, Action<UIBackgroundFetchResult> completionHandler)
{
ProcessNotification(userInfo, false);
}
void ProcessNotification(NSDictionary options, bool fromFinishedLaunching)
{
// Check to see if the dictionary has the aps key. This is the notification payload you would have sent
if (null != options && options.ContainsKey(new NSString("aps")))
{
//Get the aps dictionary
NSDictionary aps = options.ObjectForKey(new NSString("aps")) as NSDictionary;
string alertTitle = string.Empty;
string alert = string.Empty;
string sound = string.Empty;
int badge = -1;
//Extract the alert text
// NOTE: If you're using the simple alert by just specifying
// " aps:{alert:"alert msg here"} ", this will work fine.
// But if you're using a complex alert with Localization keys, etc.,
// your "alert" object from the aps dictionary will be another NSDictionary.
// Basically the JSON gets dumped right into a NSDictionary,
// so keep that in mind.
if (aps.ContainsKey(new NSString("alert")))
alert = (aps[new NSString("alert")] as NSString).ToString();
if (aps.ContainsKey(new NSString("alert")))
alert = (aps[new NSString("alert")] as NSString).ToString();
if (options.ContainsKey(new NSString("alertTitle")))
alertTitle = (options[new NSString("alertTitle")] as NSString).ToString();
//Extract the sound string
if (aps.ContainsKey(new NSString("sound")))
sound = (aps[new NSString("sound")] as NSString).ToString();
//Extract the badge
if (aps.ContainsKey(new NSString("badge")))
{
string badgeStr = (aps[new NSString("badge")] as NSObject).ToString();
int.TryParse(badgeStr, out badge);
}
if (!fromFinishedLaunching)
{
//Manually show an alert
if (!string.IsNullOrEmpty(alert))
{
UIAlertView avAlert = new UIAlertView("Notification", alert, null, "OK", null);
avAlert.Show();
}
}
}
}
public override void ReceivedRemoteNotification(UIApplication application, NSDictionary userInfo)
{
ProcessNotification(userInfo, false);
}
public override bool FinishedLaunching(UIApplication application, NSDictionary launchOptions)
{
Window = new UIWindow(UIScreen.MainScreen.Bounds);
ViewController viewController = new ViewController("LoginScreen", null);
Window.RootViewController = viewController;
Window.MakeKeyAndVisible();
if (UIDevice.CurrentDevice.CheckSystemVersion(8, 0))
{
var notificationSettings = UIUserNotificationSettings.GetSettingsForTypes(
UIUserNotificationType.Alert | UIUserNotificationType.Badge | UIUserNotificationType.Sound, null
);
UIApplication.SharedApplication.RegisterUserNotificationSettings(notificationSettings);
UIApplication.SharedApplication.RegisterForRemoteNotifications();
}
else
{
//==== register for remote notifications and get the device token
// set what kind of notification types we want
UIRemoteNotificationType notificationTypes = UIRemoteNotificationType.Alert | UIRemoteNotificationType.Badge;
// register for remote notifications
UIApplication.SharedApplication.RegisterForRemoteNotificationTypes(notificationTypes);
}
return true;
}
Analytics
Initialization
OMCAnalytics oMCAnalytics = oMCMobileBackend.Analytics;
Logging an Event
oMCAnalytics.LogEvent("this is test event "+ i +" from xamarin");
Setting Context Location
oMCAnalytics.SetContextLocationCountry("india", "Telangana", "Hyderabad", "500081");
Flushing an Event
oMCAnalytics.Flush();
App Policies
Loading the App Config and Getting Policies
oMCMobileBackend.AppConfigWithCompletionHandler(HandleOMCAppConfigCompletionBlock);
lock(obj){
Monitor.Wait(obj);
}
OMCAppConfig oMCAppConfig = oMCMobileBackend.AppConfig;
//Getting String
String str = oMCAppConfig.StringForProperty("Test_String", "No value configured");
Console.WriteLine("oMCAppConfig: String: " + str);
//Getting Number
NSNumber number = oMCAppConfig.NumberForProperty("Test_number", -1);
Console.WriteLine("oMCAppConfig: Number: " + number);
//Getting Boolean
Boolean boolean = oMCAppConfig.BooleanForProperty("Test_Boolean", false);
Console.WriteLine("oMCAppConfig: Boolean: " + boolean.ToString());
void HandleOMCAppConfigCompletionBlock(OMCAppConfig oMCAppConfig, NSError arg1)
{
if(arg1 == null){
Console.WriteLine("oMCAppConfig: " + oMCAppConfig.ToString());
}
}
Call Custom APIs
The SDK provides the CustomCodeClient
class to simplify the calling of custom APIs in Mobile Hub. You can call a REST method (GET, PUT, POST, or DELETE) on an endpoint where the request payload is JSON or empty and the response payload is JSON or empty.
Using this class, you invoke a REST method (GET, PUT, POST, or DELETE) on an endpoint where the request payload is JSON or empty and the response payload is JSON or empty.
In addition you can provide a completion handler to be called when the method invocation is complete (meaning that the handler runs asynchronously).
Use of CustomCodeClient
might look something like this:
oMCMobileBackend.CustomCodeClient.InvokeCustomRequest("mcs_examples_sync_salesplus/reminders", "get", null, HandleOMCCustomRequestCompletionHandler);
void HandleOMCCustomRequestCompletionHandler(NSError arg0, NSHttpUrlResponse arg1, NSObject nSObject)
{
if (nSObject != null)
{
System.Console.WriteLine("response object: " + nSObject.ToString());
}
}