如何使用适用于 Azure 移动应用的 iOS 客户端库How to Use iOS Client Library for Azure Mobile Apps

Note

Visual Studio App Center 正在投资于对移动应用开发至关重要的新集成服务。Visual Studio App Center is investing in new and integrated services central to mobile app development. 开发人员可以使用生成测试分发服务来设置持续集成和交付管道。Developers can use Build, Test and Distribute services to set up Continuous Integration and Delivery pipeline. 部署应用后,开发人员可以使用分析诊断服务监视其应用的状态和使用情况,并使用推送服务与用户互动。Once the app is deployed, developers can monitor the status and usage of their app using the Analytics and Diagnostics services, and engage with users using the Push service. 开发人员还可以利用 Auth 对用户进行身份验证,利用数据服务在云中持久保存和同步应用数据。Developers can also leverage Auth to authenticate their users and Data service to persist and sync app data in the cloud. 立即查看 App CenterCheck out App Center today.

概述Overview

本指南介绍如何使用最新的 Azure 移动应用 iOS SDK 执行常见任务。This guide teaches you to perform common scenarios using the latest Azure Mobile Apps iOS SDK. 对于 Azure 移动应用的新手,请先完成 Azure 移动应用快速入门,创建后端、创建表并下载预先生成的 iOS Xcode 项目。If you are new to Azure Mobile Apps, first complete Azure Mobile Apps Quick Start to create a backend, create a table, and download a pre-built iOS Xcode project. 本指南侧重于客户端 iOS SDK。In this guide, we focus on the client-side iOS SDK. 若要了解有关用于后端的服务器端 SDK 的详细信息,请参阅 Server SDK 操作方法。To learn more about the server-side SDK for the backend, see the Server SDK HOWTOs.

参考文档Reference documentation

iOS 客户端 SDK 的参考文档位于此处:Azure 移动应用 iOS 客户端参考The reference documentation for the iOS client SDK is located here: Azure Mobile Apps iOS Client Reference.

支持的平台Supported Platforms

iOS SDK 支持适用于 iOS 8.0 版及更高版本的 Objective-C 项目、Swift 2.2 项目和 Swift 2.3 项目。The iOS SDK supports Objective-C projects, Swift 2.2 projects, and Swift 2.3 projects for iOS versions 8.0 or later.

“服务器流”身份验证在呈现的 UI 中使用 WebView。The "server-flow" authentication uses a WebView for the presented UI. 如果设备不能显示 Web 视图 UI,则需使用本产品范围外的另一种身份验证方法。If the device is not able to present a WebView UI, then another method of authentication is required that is outside the scope of the product.
因此这个 SDK 不适用于监视类型或同样受限制的设备。This SDK is thus not suitable for Watch-type or similarly restricted devices.

安装与先决条件Setup and Prerequisites

本指南假设已创建了包含表的后端。This guide assumes that you have created a backend with a table. 本指南假设该表的架构与这些教程中的表相同。This guide assumes that the table has the same schema as the tables in those tutorials. 本指南还假设在代码中引用了 MicrosoftAzureMobile.framework 并导入了 MicrosoftAzureMobile/MicrosoftAzureMobile.hThis guide also assumes that in your code, you reference MicrosoftAzureMobile.framework and import MicrosoftAzureMobile/MicrosoftAzureMobile.h.

如何:创建客户端How to: Create Client

若要在项目中访问 Azure 移动应用后端,请创建 MSClientTo access an Azure Mobile Apps backend in your project, create an MSClient. AppUrl 替换为应用 URL。Replace AppUrl with the app URL. 可以将 gatewayURLStringapplicationKey 留空。You may leave gatewayURLString and applicationKey empty. 如果设置了用于身份验证的网关,请使用网关 URL 填充 gatewayURLStringIf you set up a gateway for authentication, populate gatewayURLString with the gateway URL.

Objective-CObjective-C:

MSClient *client = [MSClient clientWithApplicationURLString:@"AppUrl"];

SwiftSwift:

let client = MSClient(applicationURLString: "AppUrl")

如何:创建表引用How to: Create Table Reference

若要访问或更新数据,请创建到后端表的引用。To access or update data, create a reference to the backend table. TodoItem 替换为表名称Replace TodoItem with the name of your table

Objective-CObjective-C:

MSTable *table = [client tableWithName:@"TodoItem"];

SwiftSwift:

let table = client.tableWithName("TodoItem")

如何:查询数据How to: Query Data

若要创建数据库查询,请查询 MSTable 对象。To create a database query, query the MSTable object. 以下查询将获取 TodoItem 中的所有项,并记录每个项的文本。The following query gets all the items in TodoItem and logs the text of each item.

Objective-CObjective-C:

[table readWithCompletion:^(MSQueryResult *result, NSError *error) {
        if(error) { // error is nil if no error occurred
                NSLog(@"ERROR %@", error);
        } else {
                for(NSDictionary *item in result.items) { // items is NSArray of records that match query
                        NSLog(@"Todo Item: %@", [item objectForKey:@"text"]);
                }
        }
}];

SwiftSwift:

table.readWithCompletion { (result, error) in
    if let err = error {
        print("ERROR ", err)
    } else if let items = result?.items {
        for item in items {
            print("Todo Item: ", item["text"])
        }
    }
}

如何:筛选返回的数据How to: Filter Returned Data

可以使用许多可用选项来筛选结果。To filter results, there are many available options.

若要使用谓词进行筛选,请使用 NSPredicatereadWithPredicateTo filter using a predicate, use an NSPredicate and readWithPredicate. 以下筛选器返回的数据只用于查找未完成的待办事项。The following filters returned data to find only incomplete Todo items.

Objective-CObjective-C:

// Create a predicate that finds items where complete is false
NSPredicate * predicate = [NSPredicate predicateWithFormat:@"complete == NO"];
// Query the TodoItem table
[table readWithPredicate:predicate completion:^(MSQueryResult *result, NSError *error) {
        if(error) {
                NSLog(@"ERROR %@", error);
        } else {
                for(NSDictionary *item in result.items) {
                        NSLog(@"Todo Item: %@", [item objectForKey:@"text"]);
                }
        }
}];

SwiftSwift:

// Create a predicate that finds items where complete is false
let predicate =  NSPredicate(format: "complete == NO")
// Query the TodoItem table
table.readWithPredicate(predicate) { (result, error) in
    if let err = error {
        print("ERROR ", err)
    } else if let items = result?.items {
        for item in items {
            print("Todo Item: ", item["text"])
        }
    }
}

如何:使用 MSQueryHow to: Use MSQuery

若要执行复杂查询(包括排序和分页),请使用谓词直接创建 MSQuery 对象:To perform a complex query (including sorting and paging), create an MSQuery object, directly or by using a predicate:

Objective-CObjective-C:

MSQuery *query = [table query];
MSQuery *query = [table queryWithPredicate: [NSPredicate predicateWithFormat:@"complete == NO"]];

SwiftSwift:

let query = table.query()
let query = table.queryWithPredicate(NSPredicate(format: "complete == NO"))

MSQuery 允许用户控制几种查询行为。MSQuery lets you control several query behaviors.

  • 指定结果顺序Specify order of results
  • 限制要返回的字段Limit which fields to return
  • 限制要返回的记录数Limit how many records to return
  • 指定响应中的总计数Specify total count in response
  • 指定请求中的自定义查询字符串参数Specify custom query string parameters in request
  • 应用其他函数Apply additional functions

通过对对象调用 readWithCompletion 来执行 MSQuery 查询。Execute an MSQuery query by calling readWithCompletion on the object.

如何:使用 MSQuery 对数据排序How to: Sort Data with MSQuery

让我们先看一个示例,来了解如何对结果排序。To sort results, let's look at an example. 如果要对字段“text”进行升序排序,并对“complete”进行降序排序,请调用 MSQuery ,如下所示:To sort by field 'text' ascending, then by 'complete' descending, invoke MSQuery like so:

Objective-CObjective-C:

[query orderByAscending:@"text"];
[query orderByDescending:@"complete"];
[query readWithCompletion:^(MSQueryResult *result, NSError *error) {
        if(error) {
                NSLog(@"ERROR %@", error);
        } else {
                for(NSDictionary *item in result.items) {
                        NSLog(@"Todo Item: %@", [item objectForKey:@"text"]);
                }
        }
}];

SwiftSwift:

query.orderByAscending("text")
query.orderByDescending("complete")
query.readWithCompletion { (result, error) in
    if let err = error {
        print("ERROR ", err)
    } else if let items = result?.items {
        for item in items {
            print("Todo Item: ", item["text"])
        }
    }
}

如何:使用 MSQuery 限制字段和展开查询字符串参数How to: Limit Fields and Expand Query String Parameters with MSQuery

若要限制在查询中返回的字段,请在 selectFields 属性中指定字段的名称。To limit fields to be returned in a query, specify the names of the fields in the selectFields property. 此示例只返回 text 和 completed 字段:This example returns only the text and completed fields:

Objective-CObjective-C:

query.selectFields = @[@"text", @"complete"];

SwiftSwift:

query.selectFields = ["text", "complete"]

若要在服务器请求中包含其他查询字符串参数(例如,某个自定义服务器端脚本要使用这些参数),请按如下所示填充 query.parametersTo include additional query string parameters in the server request (for example, because a custom server-side script uses them), populate query.parameters like so:

Objective-CObjective-C:

query.parameters = @{
    @"myKey1" : @"value1",
    @"myKey2" : @"value2",
};

SwiftSwift:

query.parameters = ["myKey1": "value1", "myKey2": "value2"]

如何:配置页面大小How to: Configure Page Size

在 Azure 移动应用中,页面大小控制每次从后端表提取的记录数。With Azure Mobile Apps, the page size controls the number of records that are pulled at a time from the backend tables. pull 数据的调用稍后会基于此页面大小对数据进行批量处理,直到没有更多要提取的记录。A call to pull data would then batch up data, based on this page size, until there are no more records to pull.

可使用 MSPullSettings 配置页面大小,如下所示。It's possible to configure a page size using MSPullSettings as shown below. 默认页面大小为 50,以下示例中则改为 3。The default page size is 50, and the example below changes it to 3.

可以配置不同的页面大小,以提高性能。You could configure a different page size for performance reasons. 如果有大量小型数据记录,增大页面大小可减少服务器往返次数。If you have a large number of small data records, a high page size reduces the number of server round-trips.

此设置仅控制客户端侧的页面大小。This setting controls only the page size on the client side. 如果客户端所需的页面大小大于移动应用后端支持的页面大小,则页面大小的上限为后端配置所支持的最大大小。If the client asks for a larger page size than the Mobile Apps backend supports, the page size is capped at the maximum the backend is configured to support.

此设置也是数据记录的数目 ,而不是字节大小 。This setting is also the number of data records, not the byte size.

如果要增加客户端页面大小,还应增加服务器上的页面大小。If you increase the client page size, you should also increase the page size on the server. 请参阅“如何:调整表分页大小”以获取执行此操作的步骤。See "How to: Adjust the table paging size" for the steps to do this.

Objective-CObjective-C:

  MSPullSettings *pullSettings = [[MSPullSettings alloc] initWithPageSize:3];
  [table  pullWithQuery:query queryId:@nil settings:pullSettings
                        completion:^(NSError * _Nullable error) {
                               if(error) {
                    NSLog(@"ERROR %@", error);
                }
                           }];

SwiftSwift:

let pullSettings = MSPullSettings(pageSize: 3)
table.pullWithQuery(query, queryId:nil, settings: pullSettings) { (error) in
    if let err = error {
        print("ERROR ", err)
    }
}

如何:插入数据How to: Insert Data

若要插入新的表行,请创建 NSDictionary 并调用 table insertTo insert a new table row, create a NSDictionary and invoke table insert. 如果启用动态架构,Azure App Service 移动后端将根据 NSDictionary 自动生成新列。If Dynamic Schema is enabled, the Azure App Service mobile backend automatically generates new columns based on the NSDictionary.

如果未提供 id ,后端会自动生成新的唯一 ID。If id is not provided, the backend automatically generates a new unique ID. 提供自己的 id,以使用电子邮件地址、用户名或自己的自定义值作为 ID。Provide your own id to use email addresses, usernames, or your own custom values as ID. 提供自己的 ID 可以让联接和业务导向型数据库逻辑变得更容易。Providing your own ID may ease joins and business-oriented database logic.

result 包含插入的新项。The result contains the new item that was inserted. 根据服务器逻辑,与传递给服务器的项相比,它可能包含其他或已修改的数据。Depending on your server logic, it may have additional or modified data compared to what was passed to the server.

Objective-CObjective-C:

NSDictionary *newItem = @{@"id": @"custom-id", @"text": @"my new item", @"complete" : @NO};
[table insert:newItem completion:^(NSDictionary *result, NSError *error) {
    if(error) {
        NSLog(@"ERROR %@", error);
    } else {
        NSLog(@"Todo Item: %@", [result objectForKey:@"text"]);
    }
}];

SwiftSwift:

let newItem = ["id": "custom-id", "text": "my new item", "complete": false]
table.insert(newItem) { (result, error) in
    if let err = error {
        print("ERROR ", err)
    } else if let item = result {
        print("Todo Item: ", item["text"])
    }
}

如何:修改数据How to: Modify Data

若要更新现有的行,请修改项并调用 updateTo update an existing row, modify an item and call update:

Objective-CObjective-C:

NSMutableDictionary *newItem = [oldItem mutableCopy]; // oldItem is NSDictionary
[newItem setValue:@"Updated text" forKey:@"text"];
[table update:newItem completion:^(NSDictionary *result, NSError *error) {
    if(error) {
        NSLog(@"ERROR %@", error);
    } else {
        NSLog(@"Todo Item: %@", [result objectForKey:@"text"]);
    }
}];

SwiftSwift:

if let newItem = oldItem.mutableCopy() as? NSMutableDictionary {
    newItem["text"] = "Updated text"
    table2.update(newItem as [NSObject: AnyObject], completion: { (result, error) -> Void in
        if let err = error {
            print("ERROR ", err)
        } else if let item = result {
            print("Todo Item: ", item["text"])
        }
    })
}

或者,提供行 ID 和更新的字段:Alternatively, supply the row ID and the updated field:

Objective-CObjective-C:

[table update:@{@"id":@"custom-id", @"text":"my EDITED item"} completion:^(NSDictionary *result, NSError *error) {
    if(error) {
        NSLog(@"ERROR %@", error);
    } else {
        NSLog(@"Todo Item: %@", [result objectForKey:@"text"]);
    }
}];

SwiftSwift:

table.update(["id": "custom-id", "text": "my EDITED item"]) { (result, error) in
    if let err = error {
        print("ERROR ", err)
    } else if let item = result {
        print("Todo Item: ", item["text"])
    }
}

进行更新时,至少必须设置 id 属性。At minimum, the id attribute must be set when making updates.

如何:删除数据How to: Delete Data

若要删除某个项,请对该项调用 deleteTo delete an item, invoke delete with the item:

Objective-CObjective-C:

[table delete:item completion:^(id itemId, NSError *error) {
    if(error) {
        NSLog(@"ERROR %@", error);
    } else {
        NSLog(@"Todo Item ID: %@", itemId);
    }
}];

SwiftSwift:

table.delete(newItem as [NSObject: AnyObject]) { (itemId, error) in
    if let err = error {
        print("ERROR ", err)
    } else {
        print("Todo Item ID: ", itemId)
    }
}

或者,提供行 ID 来进行删除:Alternatively, delete by providing a row ID:

Objective-CObjective-C:

[table deleteWithId:@"37BBF396-11F0-4B39-85C8-B319C729AF6D" completion:^(id itemId, NSError *error) {
    if(error) {
        NSLog(@"ERROR %@", error);
    } else {
        NSLog(@"Todo Item ID: %@", itemId);
    }
}];

SwiftSwift:

table.deleteWithId("37BBF396-11F0-4B39-85C8-B319C729AF6D") { (itemId, error) in
    if let err = error {
        print("ERROR ", err)
    } else {
        print("Todo Item ID: ", itemId)
    }
}

进行删除时,至少必须设置 id 属性。At minimum, the id attribute must be set when making deletes.

如何:调用自定义 APIHow to: Call Custom API

使用自定义 API 可以公开任何后端功能。With a custom API, you can expose any backend functionality. 无需映射到表操作。It doesn't have to map to a table operation. 不仅能进一步控制消息,甚至还可以读取或设置标头,并更改响应正文格式。Not only do you gain more control over messaging, you can even read/set headers and change the response body format.

若要调用自定义 API,请调用 MSClient.invokeAPITo call a custom API, call MSClient.invokeAPI. 请求和响应内容被视为 JSON。The request and response content are treated as JSON. 若要使用其他媒体类型,请使用 invokeAPI 的其他重载To use other media types, use the other overload of invokeAPI. 若要发出 GET 请求而不是 POST 请求,请将参数 HTTPMethod 设置为 "GET",将参数 body 设置为 nil(因为 GET 请求没有消息正文)。如果自定义 API 支持其他 HTTP 谓词,请相应地更改 HTTPMethodTo make a GET request instead of a POST request, set parameter HTTPMethod to "GET" and parameter body to nil (since GET requests do not have message bodies.) If your custom API supports other HTTP verbs, change HTTPMethod appropriately.

Objective-CObjective-C:

[self.client invokeAPI:@"sendEmail"
                  body:@{ @"contents": @"Hello world!" }
            HTTPMethod:@"POST"
            parameters:@{ @"to": @"bill@contoso.com", @"subject" : @"Hi!" }
               headers:nil
            completion: ^(NSData *result, NSHTTPURLResponse *response, NSError *error) {
                if(error) {
                    NSLog(@"ERROR %@", error);
                } else {
                    // Do something with result
                }
            }];

SwiftSwift:

client.invokeAPI("sendEmail",
            body: [ "contents": "Hello World" ],
            HTTPMethod: "POST",
            parameters: [ "to": "bill@contoso.com", "subject" : "Hi!" ],
            headers: nil)
            {
                (result, response, error) -> Void in
                if let err = error {
                    print("ERROR ", err)
                } else if let res = result {
                          // Do something with result
                }
        }

如何:注册推送模板以发送跨平台通知How to: Register push templates to send cross-platform notifications

若要注册模板,请在客户端应用中连同 client.push registerDeviceToken 方法一起传递模板即可。To register templates, pass templates with your client.push registerDeviceToken method in your client app.

Objective-CObjective-C:

[client.push registerDeviceToken:deviceToken template:iOSTemplate completion:^(NSError *error) {
    if(error) {
        NSLog(@"ERROR %@", error);
    }
}];

SwiftSwift:

client.push?.registerDeviceToken(NSData(), template: iOSTemplate, completion: { (error) in
    if let err = error {
        print("ERROR ", err)
    }
})

模板类型是 NSDictionary,可以包含采用以下格式的多个模板:Your templates are of type NSDictionary and can contain multiple templates in the following format:

Objective-CObjective-C:

NSDictionary *iOSTemplate = @{ @"templateName": @{ @"body": @{ @"aps": @{ @"alert": @"$(message)" } } } };

SwiftSwift:

let iOSTemplate = ["templateName": ["body": ["aps": ["alert": "$(message)"]]]]

出于安全考虑,从请求中删除所有标记。All tags are stripped from the request for security. 若要将标记添加到安装或安装中的模板,请参阅使用适用于 Azure 移动应用的 .NET 后端服务器 SDKTo add tags to installations or templates within installations, see Work with the .NET backend server SDK for Azure Mobile Apps. 若要使用这些注册的模板发送通知,请参阅通知中心 APITo send notifications using these registered templates, work with Notification Hubs APIs.

如何:处理错误How to: Handle Errors

调用 Azure 应用服务移动后端时,完成块包含 NSError 参数。When you call an Azure App Service mobile backend, the completion block contains an NSError parameter. 如果出错,此参数为非 nil 值。When an error occurs, this parameter is non-nil. 应该在代码中检查此参数,并根据需要处理错误,如前面的代码片段中所示。In your code, you should check this parameter and handle the error as needed, as demonstrated in the preceding code snippets.

文件 <WindowsAzureMobileServices/MSError.h> 定义常量 MSErrorResponseKeyMSErrorRequestKeyMSErrorServerItemKeyThe file <WindowsAzureMobileServices/MSError.h> defines the constants MSErrorResponseKey, MSErrorRequestKey, and MSErrorServerItemKey. 若要获取与错误相关的更多数据,请执行以下操作:To get more data related to the error:

Objective-CObjective-C:

NSDictionary *serverItem = [error.userInfo objectForKey:MSErrorServerItemKey];

SwiftSwift:

let serverItem = error.userInfo[MSErrorServerItemKey]

此外,该文件还定义每个错误代码的常量:In addition, the file defines constants for each error code:

Objective-CObjective-C:

if (error.code == MSErrorPreconditionFailed) {

SwiftSwift:

if (error.code == MSErrorPreconditionFailed) {

如何:使用 Active Directory 身份验证库对用户进行身份验证How to: Authenticate users with the Active Directory Authentication Library

可以借助 Active Directory 身份验证库 (ADAL) 使用 Azure Active Directory 将用户登录到应用程序。You can use the Active Directory Authentication Library (ADAL) to sign users into your application using Azure Active Directory. 使用标识提供者 SDK 的客户端流身份验证优于使用 loginWithProvider:completion: 方法。Client flow authentication using an identity provider SDK is preferable to using the loginWithProvider:completion: method. 客户端流身份验证提供更自然的 UX 体验,并允许进行额外的自定义。Client flow authentication provides a more native UX feel and allows for additional customization.

  1. 根据 How to configure App Service for Active Directory login (如何为 Active Directory 登录配置应用服务)教程的说明,为 AAD 登录配置移动应用。Configure your mobile app backend for AAD sign-in by following the How to configure App Service for Active Directory login tutorial. 请务必完成注册本机客户端应用程序的可选步骤。Make sure to complete the optional step of registering a native client application. 对于 iOS,建议重定向 URI 采用 <app-scheme>://<bundle-id> 格式。For iOS, we recommend that the redirect URI is of the form <app-scheme>://<bundle-id>. 有关详细信息,请参阅 ADAL iOS 快速入门For more information, see the ADAL iOS quickstart.

  2. 使用 Cocoapods 安装 ADAL。Install ADAL using Cocoapods. 编辑 Podfile 以包含以下定义,将 YOUR-PROJECT 替换为 Xcode 项目的名称:Edit your Podfile to include the following definition, replacing YOUR-PROJECT with the name of your Xcode project:

    source 'https://github.com/CocoaPods/Specs.git'
    link_with ['YOUR-PROJECT']
    xcodeproj 'YOUR-PROJECT'
    

    Pod:and the Pod:

     pod 'ADALiOS'
    
  3. 使用终端,从包含项目的目录运行 pod install,然后打开生成的 Xcode 工作区(而不是项目)。Using the Terminal, run pod install from the directory containing your project, and then open the generated Xcode workspace (not the project).

  4. 根据使用的语言,将以下代码添加到应用程序。Add the following code to your application, according to the language you are using. 在每个应用程序中,进行以下替换:In each, make these replacements:

    • INSERT-AUTHORITY-HERE 替换为在其中预配应用程序的租户的名称。Replace INSERT-AUTHORITY-HERE with the name of the tenant in which you provisioned your application. 格式应为 https://login.chinacloudapi.cn/contoso.onmicrosoft.comThe format should be https://login.chinacloudapi.cn/contoso.onmicrosoft.com. 可以在 Azure 门户中从 Azure Active Directory 的域选项卡复制此值。This value can be copied from the Domain tab in your Azure Active Directory in the Azure portal.
    • INSERT-RESOURCE-ID-HERE 替换移动应用后端的客户端 ID。Replace INSERT-RESOURCE-ID-HERE with the client ID for your mobile app backend. 可以在门户中“Azure Active Directory 设置” 下面的“高级” 选项卡获取此客户端 ID。You can obtain the client ID from the Advanced tab under Azure Active Directory Settings in the portal.
    • INSERT-CLIENT-ID-HERE 替换为从本机客户端应用程序复制的客户端 ID。Replace INSERT-CLIENT-ID-HERE with the client ID you copied from the native client application.
    • INSERT-REDIRECT-URI-HERE 替换为站点的 /.auth/login/done 终结点(使用 HTTPS 方案)。Replace INSERT-REDIRECT-URI-HERE with your site's /.auth/login/done endpoint, using the HTTPS scheme. 此值应类似于 https://contoso.chinacloudsites.cn/.auth/login/doneThis value should be similar to https://contoso.chinacloudsites.cn/.auth/login/done.

Objective-CObjective-C:

#import <ADALiOS/ADAuthenticationContext.h>
#import <ADALiOS/ADAuthenticationSettings.h>
// ...
- (void) authenticate:(UIViewController*) parent
            completion:(void (^) (MSUser*, NSError*))completionBlock;
{
    NSString *authority = @"INSERT-AUTHORITY-HERE";
    NSString *resourceId = @"INSERT-RESOURCE-ID-HERE";
    NSString *clientId = @"INSERT-CLIENT-ID-HERE";
    NSURL *redirectUri = [[NSURL alloc]initWithString:@"INSERT-REDIRECT-URI-HERE"];
    ADAuthenticationError *error;
    ADAuthenticationContext *authContext = [ADAuthenticationContext authenticationContextWithAuthority:authority error:&error];
    authContext.parentController = parent;
    [ADAuthenticationSettings sharedInstance].enableFullScreen = YES;
    [authContext acquireTokenWithResource:resourceId
                                    clientId:clientId
                                redirectUri:redirectUri
                            completionBlock:^(ADAuthenticationResult *result) {
                                if (result.status != AD_SUCCEEDED)
                                {
                                    completionBlock(nil, result.error);;
                                }
                                else
                                {
                                    NSDictionary *payload = @{
                                                            @"access_token" : result.tokenCacheStoreItem.accessToken
                                                            };
                                    [client loginWithProvider:@"aad" token:payload completion:completionBlock];
                                }
                            }];
}

SwiftSwift:

// add the following imports to your bridging header:
//        #import <ADALiOS/ADAuthenticationContext.h>
//        #import <ADALiOS/ADAuthenticationSettings.h>

func authenticate(parent: UIViewController, completion: (MSUser?, NSError?) -> Void) {
    let authority = "INSERT-AUTHORITY-HERE"
    let resourceId = "INSERT-RESOURCE-ID-HERE"
    let clientId = "INSERT-CLIENT-ID-HERE"
    let redirectUri = NSURL(string: "INSERT-REDIRECT-URI-HERE")
    var error: AutoreleasingUnsafeMutablePointer<ADAuthenticationError?> = nil
    let authContext = ADAuthenticationContext(authority: authority, error: error)
    authContext.parentController = parent
    ADAuthenticationSettings.sharedInstance().enableFullScreen = true
    authContext.acquireTokenWithResource(resourceId, clientId: clientId, redirectUri: redirectUri) { (result) in
            if result.status != AD_SUCCEEDED {
                completion(nil, result.error)
            }
            else {
                let payload: [String: String] = ["access_token": result.tokenCacheStoreItem.accessToken]
                client.loginWithProvider("aad", token: payload, completion: completion)
            }
        }
}