Crie aplicativos iOS Objective-C com o Microsoft Graph
Este tutorial ensina como criar um aplicativo React Native que usa a API do Microsoft Graph para recuperar informações de calendário para um usuário.
Dica
Se você preferir apenas baixar o tutorial concluído, poderá baixar ou clonar o GitHub repositório.
Pré-requisitos
Antes de iniciar este tutorial, você deve ter o seguinte instalado em sua máquina de desenvolvimento.
Você também deve ter uma conta pessoal da Microsoft com uma caixa de correio em Outlook.com, ou uma conta de trabalho ou de estudante da Microsoft. Se você não tiver uma conta da Microsoft, há algumas opções para obter uma conta gratuita:
- Você pode se inscrever em uma nova conta pessoal da Microsoft.
- Você pode se inscrever no programa Microsoft 365 desenvolvedor para obter uma assinatura Microsoft 365 gratuita.
Observação
Este tutorial foi escrito usando xcode versão 12.3 e CocoaPods versão 1.10.1. As etapas neste guia podem funcionar com outras versões, mas que não foram testadas.
Comentários
Forneça qualquer comentário sobre este tutorial no repositório GitHub.
Crie um aplicativo iOS Objective-C
Comece criando um novo projeto Objective-C.
Abra Xcode. No menu Arquivo, selecione Novo, e Project.
Escolha o modelo de aplicativo e selecione Próximo.

De definir o Nome do Produto como e o
GraphTutorialidioma como Objective-C.Preencha os campos restantes e selecione Next.
Escolha um local para o projeto e selecione Criar.
Instalar dependências
Antes de continuar, instale algumas dependências adicionais que você usará mais tarde.
- Biblioteca de Autenticação da Microsoft (MSAL) para iOS para autenticação com o Azure AD.
- Microsoft Graph SDK para Objetivo C para fazer chamadas para o Microsoft Graph.
- Microsoft Graph SDK de Modelos para Objetivo C para objetos fortemente digitados que representam a Microsoft Graph recursos como usuários ou eventos.
Encerrar Xcode.
Abra Terminal e altere o diretório para o local do seu projeto GraphTutorial.
Execute o seguinte comando para criar um Podfile.
pod initAbra o Podfile e adicione as seguintes linhas logo após a
use_frameworks!linha.pod 'MSAL', '~> 1.1.13' pod 'MSGraphClientSDK', ' ~> 1.0.0' pod 'MSGraphClientModels', '~> 1.3.0'Salve o Podfile e execute o seguinte comando para instalar as dependências.
pod installDepois que o comando é concluído, abra o graphTutorial.xcworkspace recém-criado em Xcode.
Design do aplicativo
Nesta seção, você criará as exibições do aplicativo: uma página de login, um navegador de barra de guias, uma página de boas-vindas e uma página de calendário. Você também criará uma sobreposição de indicador de atividade.
Criar página de login
Expanda a pasta GraphTutorial em Xcode e selecione o arquivo ViewController.m.
No Inspetor de Arquivos, altere o Nome do arquivo para
SignInViewController.m.
Abra SignInViewController.m e substitua seu conteúdo pelo código a seguir.
#import "SignInViewController.h" @interface SignInViewController () @end @implementation SignInViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. } - (IBAction)signIn { [self performSegueWithIdentifier: @"userSignedIn" sender: nil]; } @endSelecione o arquivo ViewController.h.
No Inspetor de Arquivos, altere o Nome do arquivo para
SignInViewController.h.Abra SignInViewController.h e altere todas as instâncias
ViewControllerde paraSignInViewController.Abra o arquivo Main.storyboard.
Expanda a Cena do Controlador de Exibição e selecione Exibir Controlador.

Selecione o Inspetor de Identidade e altere o menu suspenso Classe para SignInViewController.

Selecione a Biblioteca e, em seguida, arraste um botão para o Controlador de Exibição de Login.

Com o botão selecionado, selecione o Inspetor de Atributos e altere o Título do botão para
Sign In.
Com o botão selecionado, selecione o botão Alinhar na parte inferior do storyboard. Selecione horizontalmente no contêiner e verticalmente em restrições de contêiner, deixe seus valores como 0 e selecione Adicionar 2 restrições.

Selecione o Controlador de Exibição de Login e selecione o Inspetor de Conexões.
Em Ações Recebidas, arraste o círculo não preenchido ao lado de entrar no botão. Selecione Tocar para Dentro no menu pop-up.

Criar barra de guias
Selecione a Biblioteca e arraste um Controlador de Barra de Guias para o storyboard.
Selecione o Controlador de Exibição de Login e selecione o Inspetor de Conexões.
Em Segues Disparados, arraste o círculo não preenchido ao lado do manual para o Controlador da Barra de Guias no storyboard. Selecione Apresentar Modally no menu pop-up.

Selecione o segue que você acabou de adicionar e selecione o Inspetor de Atributos. De definir o campo Identificador como
userSignedIn, e definir Apresentação como Tela Inteira.
Selecione a Cena do Item 1 e selecione o Inspetor de Conexões.
Em Segues Disparados, arraste o círculo não preenchido ao lado do manual para o Controlador de Exibição de Login no storyboard. Selecione Apresentar Modally no menu pop-up.
Selecione o segue que você acabou de adicionar e selecione o Inspetor de Atributos. De definir o campo Identificador como
userSignedOut, e definir Apresentação como Tela Inteira.
Criar página de boas-vindas
Selecione o arquivo Assets.xcassets.
No menu Editor, selecione Adicionar Novo Ativo e, em seguida, Conjunto de Imagens.
Selecione o novo ativo Image e use o Inspetor de Atributos para definir seu Nome como
DefaultUserPhoto.Adicione qualquer imagem que você gosta de servir como uma foto de perfil de usuário padrão.

Crie um novo arquivo da Classe Cocoa Touch na pasta GraphTutorial chamada
WelcomeViewController. Escolha UIViewController na Subclasse do campo.Abra WelcomeViewController.h e adicione o seguinte código dentro da
@interfacedeclaração.@property (nonatomic) IBOutlet UIImageView *userProfilePhoto; @property (nonatomic) IBOutlet UILabel *userDisplayName; @property (nonatomic) IBOutlet UILabel *userEmail;Abra WelcomeViewController.m e substitua seu conteúdo pelo código a seguir.
#import "WelcomeViewController.h" @interface WelcomeViewController () @end @implementation WelcomeViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. // TEMPORARY self.userProfilePhoto.image = [UIImage imageNamed:@"DefaultUserPhoto"]; self.userDisplayName.text = @"Default User"; [self.userDisplayName sizeToFit]; self.userEmail.text = @"default@contoso.com"; [self.userEmail sizeToFit]; } - (IBAction)signOut { [self performSegueWithIdentifier: @"userSignedOut" sender: nil]; } @endAbra Main.storyboard. Selecione a Cena do Item 1 e selecione o Inspetor de Identidade. Altere o valor class para WelcomeViewController.
Usando a Biblioteca, adicione os itens a seguir à Cena do Item 1.
- One Image View
- Dois rótulos
- Um botão
Usando o Inspetor de Conexões, faça as seguintes conexões.
- Vincule a saída userDisplayName ao primeiro rótulo.
- Vincule a saída userEmail ao segundo rótulo.
- Vincule a saída userProfilePhoto ao exibição de imagem.
- Vincule a ação signOut recebida ao botão Touch Up Inside.
Selecione o exibição de imagem e selecione o Inspetor de Tamanhos.
De definir Width e Height como 196.
Use o botão Alinhar para adicionar a restrição Horizontalmente no contêiner com um valor 0.
Use o botão Adicionar Novas Restrições (ao lado do botão Alinhar) para adicionar as seguintes restrições:
- Alinhar Superior a: Cofre Área, valor: 0
- Espaço inferior para: Nome de Exibição do Usuário, valor: Standard
- Altura, valor: 196
- Largura, valor: 196

Selecione o primeiro rótulo e, em seguida, use o botão Alinhar para adicionar a restrição Horizontalmente no contêiner com um valor 0.
Use o botão Adicionar Novas Restrições para adicionar as seguintes restrições:
- Espaço Superior para: Foto do Perfil de Usuário, valor: Padrão
- Espaço inferior para: Email do Usuário, valor: Standard
Selecione o segundo rótulo e selecione o Inspetor de Atributos.
Altere a cor para Cor cinza escuro e altere a Fonte para o Sistema 12.0.
Use o botão Alinhar para adicionar a restrição Horizontalmente no contêiner com um valor 0.
Use o botão Adicionar Novas Restrições para adicionar as seguintes restrições:
- Espaço Superior para: Nome de Exibição do Usuário, valor: Padrão
- Espaço inferior para: Sair, valor: 14
Selecione o botão e selecione o Inspetor de Atributos.
Altere o título para
Sign Out.Use o botão Alinhar para adicionar a restrição Horizontalmente no contêiner com um valor 0.
Use o botão Adicionar Novas Restrições para adicionar as seguintes restrições:
- Espaço Superior para: Email do Usuário, valor: 14
Selecione o item da barra de guias na parte inferior da cena e selecione o Inspetor de Atributos. Altere o título para
Me.No menu Editor, selecione Resolver Problemas de Layout Automático e, em seguida, selecione Adicionar Restrições Ausentes abaixo de Todos os Exibições no Controlador de Modo de Exibição de Boas-Vindas.
A cena de boas-vindas deve ser semelhante a essa depois de terminar.

Criar página de calendário
Crie um novo arquivo da Classe Cocoa Touch na pasta GraphTutorial chamada
CalendarViewController. Escolha UIViewController na Subclasse do campo.Abra CalendarViewController.h e adicione o seguinte código dentro da
@interfacedeclaração.@property (nonatomic) IBOutlet UITextView *calendarJSON;Abra CalendarViewController.m e substitua seu conteúdo pelo código a seguir.
#import "CalendarViewController.h" @interface CalendarViewController () @end @implementation CalendarViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. // TEMPORARY self.calendarJSON.text = @"Calendar"; [self.calendarJSON sizeToFit]; } @endAbra Main.storyboard. Selecione a Cena do Item 2 e selecione o Inspetor de Identidade. Altere o valor class para CalendarViewController.
Usando a Biblioteca, adicione um Texto Exibição à Cena do Item 2.
Selecione o exibição de texto que você acabou de adicionar. No Editor, escolha Incorporar em, em seguida, Exibir Rolagem.
Usando o Inspetor de Conexões, conecte a saída calendarJSON ao exibição de texto.
Selecione o item da barra de guias na parte inferior da cena e selecione o Inspetor de Atributos. Altere o título para
Calendar.No menu Editor, selecione Resolver Problemas de Layout Automático e, em seguida, selecione Adicionar Restrições Ausentes abaixo de Todos os Exibições no Controlador de Modo de Exibição de Boas-Vindas.
A cena do calendário deve ser semelhante a essa depois de terminar.

Criar indicador de atividade
Crie um novo arquivo da Classe Cocoa Touch na pasta GraphTutorial chamada
SpinnerViewController. Escolha UIViewController na Subclasse do campo.Abra SpinnerViewController.h e adicione o seguinte código dentro da
@interfacedeclaração.- (void) startWithContainer:(UIViewController*) container; - (void) stop;Abra SpinnerViewController.m e substitua seu conteúdo pelo código a seguir.
#import "SpinnerViewController.h" @interface SpinnerViewController () @property (nonatomic) UIActivityIndicatorView* spinner; @end @implementation SpinnerViewController - (void)viewDidLoad { [super viewDidLoad]; _spinner = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle: UIActivityIndicatorViewStyleLarge]; self.view.backgroundColor = [UIColor colorWithWhite:0 alpha:0.7]; [self.view addSubview:_spinner]; _spinner.translatesAutoresizingMaskIntoConstraints = false; [_spinner startAnimating]; [_spinner.centerXAnchor constraintEqualToAnchor:self.view.centerXAnchor].active = true; [_spinner.centerYAnchor constraintEqualToAnchor:self.view.centerYAnchor].active = true; } - (void) startWithContainer:(UIViewController *)container { [container addChildViewController:self]; self.view.frame = container.view.frame; [container.view addSubview:self.view]; [self didMoveToParentViewController:container]; } - (void) stop { [self willMoveToParentViewController:nil]; [self.view removeFromSuperview]; [self removeFromParentViewController]; } @end
O aplicativo de teste
Salve suas alterações e iniciar o aplicativo. Você deve ser capaz de mover entre as telas usando os botões Entrar e Sair e a barra de guias.

Registrar o aplicativo no portal
Neste exercício, você criará um novo aplicativo nativo do Azure AD usando o Azure Active Directory de administração.
Abra um navegador, navegue até o centro de administração do Azure Active Directory e faça logon usando uma conta pessoal (também conhecida como conta da Microsoft) ou Conta Corporativa ou de Estudante.
Selecione Azure Active Directory na navegação esquerda e selecione Registros de aplicativos em Gerenciar.

Selecione Novo registro. Na página Registrar um aplicativo, defina os valores da seguinte forma.
- Defina Nome para
iOS Objective-C Graph Tutorial. - Defina Tipos de conta com suporte para Contas em qualquer diretório organizacional e contas pessoais da Microsoft.
- Deixe o URI de Redirecionamento vazio.

- Defina Nome para
Selecione Registrar. Na página Tutorial do iOS Objective-C Graph, copie o valor da ID do Aplicativo (cliente) e salve-a, você precisará dele na próxima etapa.

Selecione Autenticação em Gerenciar. Selecione Adicionar uma plataforma, em seguida, iOS /macOS.
Insira a ID do Pacote do seu aplicativo e selecione Configurar, em seguida, selecione Feito.
Adicionar autenticação do Azure AD
Neste exercício, você estenderá o aplicativo do exercício anterior para dar suporte à autenticação com o Azure AD. Isso é necessário para obter o token de acesso OAuth necessário para chamar o microsoft Graph. Para fazer isso, você integrará a Biblioteca de Autenticação da Microsoft (MSAL) para iOS ao aplicativo.
Crie um novo arquivo de Lista de Propriedades no projeto GraphTutorial chamado AuthSettings.plist.
Adicione os itens a seguir ao arquivo no dicionário raiz.
Chave Tipo Valor AppIdCadeia de caracteres A ID do aplicativo do portal do Azure GraphScopesMatriz Três valores de cadeia de User.Readcaracteres:MailboxSettings.Read, eCalendars.ReadWrite
Importante
Se você estiver usando o controle de origem como git, agora seria um bom momento para excluir o arquivo AuthSettings.plist do controle de origem para evitar o vazamento inadvertida da ID do aplicativo.
Implementar login
Nesta seção, você configurará o projeto para MSAL, criará uma classe gerenciador de autenticação e atualizará o aplicativo para entrar e sair.
Configurar projeto para MSAL
Adicione um novo grupo de chaveiros aos recursos do seu projeto.
- Selecione o projeto GraphTutorial e, em seguida, Assinando & Recursos.
- Selecione + Funcionalidade e clique duas vezes em Compartilhamento de Chave.
- Adicione um grupo de chaveiros com o valor
com.microsoft.adalcache.
Controle clique em Info.plist e selecione Abrir como, em seguida, Código-fonte.
Adicione o seguinte dentro do
<dict>elemento.<key>CFBundleURLTypes</key> <array> <dict> <key>CFBundleURLSchemes</key> <array> <string>msauth.$(PRODUCT_BUNDLE_IDENTIFIER)</string> </array> </dict> </array> <key>LSApplicationQueriesSchemes</key> <array> <string>msauthv2</string> <string>msauthv3</string> </array>Abra AppDelegate.m e adicione a seguinte instrução de importação na parte superior do arquivo.
#import <MSAL/MSAL.h>Adicione a função a seguir à classe
AppDelegate.- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options { return [MSALPublicClientApplication handleMSALResponse:url sourceApplication:options[UIApplicationOpenURLOptionsSourceApplicationKey]]; }
Criar gerenciador de autenticação
Crie uma nova classe cocoa touch no projeto GraphTutorial chamado AuthenticationManager. Escolha NSObject na Subclasse do campo.
Abra AuthenticationManager.h e substitua seu conteúdo pelo código a seguir.
#import <Foundation/Foundation.h> #import <MSAL/MSAL.h> #import <MSGraphClientSDK/MSGraphClientSDK.h> NS_ASSUME_NONNULL_BEGIN typedef void (^GetTokenCompletionBlock)(NSString* _Nullable accessToken, NSError* _Nullable error); @interface AuthenticationManager : NSObject<MSAuthenticationProvider> + (id) instance; - (void) getTokenInteractivelyWithParentView: (UIViewController*) parentView andCompletionBlock: (GetTokenCompletionBlock)completionBlock; - (void) getTokenSilentlyWithCompletionBlock: (GetTokenCompletionBlock)completionBlock; - (void) signOut; - (void) getAccessTokenForProviderOptions:(id<MSAuthenticationProviderOptions>)authProviderOptions andCompletion:(void (^)(NSString *, NSError *))completion; @end NS_ASSUME_NONNULL_ENDAbra AuthenticationManager.m e substitua seu conteúdo pelo código a seguir.
#import "AuthenticationManager.h" @interface AuthenticationManager() @property NSString* appId; @property NSArray<NSString*>* graphScopes; @property MSALPublicClientApplication* publicClient; @end @implementation AuthenticationManager + (id) instance { static AuthenticationManager *singleInstance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^ { singleInstance = [[self alloc] init]; }); return singleInstance; } - (id) init { if (self = [super init]) { // Get app ID and scopes from AuthSettings.plist NSString* authConfigPath = [NSBundle.mainBundle pathForResource:@"AuthSettings" ofType:@"plist"]; NSDictionary* authConfig = [NSDictionary dictionaryWithContentsOfFile:authConfigPath]; self.appId = authConfig[@"AppId"]; self.graphScopes = authConfig[@"GraphScopes"]; // Create the MSAL client self.publicClient = [[MSALPublicClientApplication alloc] initWithClientId:self.appId error:nil]; } return self; } - (void) getAccessTokenForProviderOptions:(id<MSAuthenticationProviderOptions>) authProviderOptions andCompletion:(void (^)(NSString * _Nonnull, NSError * _Nonnull)) completion { [self getTokenSilentlyWithCompletionBlock:completion]; } - (void) getTokenInteractivelyWithParentView:(UIViewController *) parentView andCompletionBlock:(GetTokenCompletionBlock) completionBlock { MSALWebviewParameters* webParameters = [[MSALWebviewParameters alloc] initWithAuthPresentationViewController:parentView]; MSALInteractiveTokenParameters* interactiveParameters = [[MSALInteractiveTokenParameters alloc] initWithScopes:self.graphScopes webviewParameters:webParameters]; // Call acquireToken to open a browser so the user can sign in [self.publicClient acquireTokenWithParameters:interactiveParameters completionBlock:^(MSALResult * _Nullable result, NSError * _Nullable error) { // Check error if (error) { completionBlock(nil, error); return; } // Check result if (!result) { NSMutableDictionary* details = [NSMutableDictionary dictionary]; [details setValue:@"No result was returned" forKey:NSDebugDescriptionErrorKey]; completionBlock(nil, [NSError errorWithDomain:@"AuthenticationManager" code:0 userInfo:details]); return; } NSLog(@"Got token interactively: %@", result.accessToken); completionBlock(result.accessToken, nil); }]; } - (void) getTokenSilentlyWithCompletionBlock:(GetTokenCompletionBlock)completionBlock { // Check if there is an account in the cache NSError* msalError; MSALAccount* account = [self.publicClient allAccounts:&msalError].firstObject; if (msalError || !account) { NSMutableDictionary* details = [NSMutableDictionary dictionary]; [details setValue:@"Could not retrieve account from cache" forKey:NSDebugDescriptionErrorKey]; completionBlock(nil, [NSError errorWithDomain:@"AuthenticationManager" code:0 userInfo:details]); return; } MSALSilentTokenParameters* silentParameters = [[MSALSilentTokenParameters alloc] initWithScopes:self.graphScopes account:account]; // Attempt to get token silently [self.publicClient acquireTokenSilentWithParameters:silentParameters completionBlock:^(MSALResult * _Nullable result, NSError * _Nullable error) { // Check error if (error) { completionBlock(nil, error); return; } // Check result if (!result) { NSMutableDictionary* details = [NSMutableDictionary dictionary]; [details setValue:@"No result was returned" forKey:NSDebugDescriptionErrorKey]; completionBlock(nil, [NSError errorWithDomain:@"AuthenticationManager" code:0 userInfo:details]); return; } NSLog(@"Got token silently: %@", result.accessToken); completionBlock(result.accessToken, nil); }]; } - (void) signOut { NSError* msalError; NSArray* accounts = [self.publicClient allAccounts:&msalError]; if (msalError) { NSLog(@"Error getting accounts from cache: %@", msalError.debugDescription); return; } for (id account in accounts) { [self.publicClient removeAccount:account error:nil]; } } @end
Adicionar entrar e sair
Abra o arquivo SignInViewController.m e substitua seu conteúdo pelo código a seguir.
#import "SignInViewController.h" #import "SpinnerViewController.h" #import "AuthenticationManager.h" @interface SignInViewController () @property SpinnerViewController* spinner; @end @implementation SignInViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. self.spinner = [SpinnerViewController alloc]; [self.spinner startWithContainer:self]; [AuthenticationManager.instance getTokenSilentlyWithCompletionBlock:^(NSString * _Nullable accessToken, NSError * _Nullable error) { dispatch_async(dispatch_get_main_queue(), ^{ [self.spinner stop]; if (error || !accessToken) { // If there is no token or if there's an error, // no user is signed in, so stay here return; } // Since we got a token, user is signed in // Go to welcome page [self performSegueWithIdentifier: @"userSignedIn" sender: nil]; }); }]; } - (IBAction)signIn { [self.spinner startWithContainer:self]; [AuthenticationManager.instance getTokenInteractivelyWithParentView:self andCompletionBlock:^(NSString * _Nullable accessToken, NSError * _Nullable error) { dispatch_async(dispatch_get_main_queue(), ^{ [self.spinner stop]; if (error || !accessToken) { // Show the error and stay on the sign-in page UIAlertController* alert = [UIAlertController alertControllerWithTitle:@"Error signing in" message:error.debugDescription preferredStyle:UIAlertControllerStyleAlert]; UIAlertAction* okButton = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]; [alert addAction:okButton]; [self presentViewController:alert animated:true completion:nil]; return; } // Since we got a token, user is signed in // Go to welcome page [self performSegueWithIdentifier: @"userSignedIn" sender: nil]; }); }]; } @endAbra WelcomeViewController.m e adicione a seguinte
importinstrução à parte superior do arquivo.#import "AuthenticationManager.h"Substitua a função
signOutexistente pela seguinte.- (IBAction)signOut { [AuthenticationManager.instance signOut]; [self performSegueWithIdentifier: @"userSignedOut" sender: nil]; }Salve suas alterações e reinicie o aplicativo no Simulador.
Se você entrar no aplicativo, verá um token de acesso exibido na janela de saída em Xcode.

Obter detalhes do usuário
Nesta seção, você criará uma classe auxiliar para manter todas as chamadas para o Microsoft Graph e atualizar a para usar essa nova classe para obter o usuário WelcomeViewController conectado.
Crie uma nova classe cocoa touch no projeto GraphTutorial chamado GraphManager. Escolha NSObject na Subclasse do campo.
Abra GraphManager.h e substitua seu conteúdo pelo código a seguir.
#import <Foundation/Foundation.h> #import <MSGraphClientSDK/MSGraphClientSDK.h> #import <MSGraphClientModels/MSGraphClientModels.h> #import <MSGraphClientModels/MSCollection.h> #import "AuthenticationManager.h" NS_ASSUME_NONNULL_BEGIN typedef void (^GetMeCompletionBlock)(MSGraphUser* _Nullable user, NSError* _Nullable error); @interface GraphManager : NSObject + (id) instance; - (void) getMeWithCompletionBlock: (GetMeCompletionBlock) completion; @end NS_ASSUME_NONNULL_ENDAbra GraphManager.m e substitua seu conteúdo pelo código a seguir.
#import "GraphManager.h" @interface GraphManager() @property MSHTTPClient* graphClient; @end @implementation GraphManager + (id) instance { static GraphManager *singleInstance = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^ { singleInstance = [[self alloc] init]; }); return singleInstance; } - (id) init { if (self = [super init]) { // Create the Graph client self.graphClient = [MSClientFactory createHTTPClientWithAuthenticationProvider:AuthenticationManager.instance]; } return self; } - (void) getMeWithCompletionBlock: (GetMeCompletionBlock) completion { // GET /me NSString* meUrlString = [NSString stringWithFormat:@"%@/me?%@", MSGraphBaseURL, @"$select=displayName,mail,mailboxSettings,userPrincipalName"]; NSURL* meUrl = [[NSURL alloc] initWithString:meUrlString]; NSMutableURLRequest* meRequest = [[NSMutableURLRequest alloc] initWithURL:meUrl]; MSURLSessionDataTask* meDataTask = [[MSURLSessionDataTask alloc] initWithRequest:meRequest client:self.graphClient completion:^(NSData *data, NSURLResponse *response, NSError *error) { if (error) { completion(nil, error); return; } // Deserialize the response as a user NSError* graphError; MSGraphUser* user = [[MSGraphUser alloc] initWithData:data error:&graphError]; if (graphError) { completion(nil, graphError); } else { completion(user, nil); } }]; // Execute the request [meDataTask execute]; } @endAbra WelcomeViewController.m e adicione as instruções a seguir
#importna parte superior do arquivo.#import "SpinnerViewController.h" #import "GraphManager.h" #import <MSGraphClientModels/MSGraphClientModels.h>Adicione a seguinte propriedade à
WelcomeViewControllerdeclaração da interface.@property SpinnerViewController* spinner;Substitua o existente
viewDidLoadpelo código a seguir.- (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. self.spinner = [SpinnerViewController alloc]; [self.spinner startWithContainer:self]; self.userProfilePhoto.image = [UIImage imageNamed:@"DefaultUserPhoto"]; [GraphManager.instance getMeWithCompletionBlock:^(MSGraphUser * _Nullable user, NSError * _Nullable error) { dispatch_async(dispatch_get_main_queue(), ^{ [self.spinner stop]; if (error) { // Show the error UIAlertController* alert = [UIAlertController alertControllerWithTitle:@"Error getting user profile" message:error.debugDescription preferredStyle:UIAlertControllerStyleAlert]; UIAlertAction* okButton = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]; [alert addAction:okButton]; [self presentViewController:alert animated:true completion:nil]; return; } // Set display name self.userDisplayName.text = user.displayName ? : @"Mysterious Stranger"; [self.userDisplayName sizeToFit]; // AAD users have email in the mail attribute // Personal accounts have email in the userPrincipalName attribute self.userEmail.text = user.mail ? : user.userPrincipalName; [self.userEmail sizeToFit]; // Save user time zone [GraphManager.instance setGraphTimeZone:(user.mailboxSettings.timeZone ? : @"UTC")]; }); }]; }
Se você salvar suas alterações e reiniciar o aplicativo agora, depois de entrar na interface do usuário é atualizado com o nome de exibição do usuário e o endereço de email.
Obter uma exibição de calendário
Neste exercício, você incorporará o microsoft Graph no aplicativo. Para este aplicativo, você usará o Microsoft Graph SDK para Objetivo C para fazer chamadas para o Microsoft Graph.
Obtenha eventos de calendário do Outlook
Nesta seção, você estenderá a classe para adicionar uma função para obter os eventos do usuário para a semana atual e atualizar para GraphManager CalendarViewController usar essa nova função.
Abra GraphManager.h e adicione o seguinte código acima da
@interfacedeclaração.typedef void (^GetCalendarViewCompletionBlock)(NSData* _Nullable data, NSError* _Nullable error);Adicione o código a seguir à
@interfacedeclaração.- (void) getCalendarViewStartingAt: (NSString*) viewStart endingAt: (NSString*) viewEnd withCompletionBlock: (GetCalendarViewCompletionBlock) completion;Abra GraphManager.m e adicione a seguinte função à
GraphManagerclasse.- (void) getCalendarViewStartingAt: (NSString *) viewStart endingAt: (NSString *) viewEnd withCompletionBlock: (GetCalendarViewCompletionBlock) completion { // Set calendar view start and end parameters NSString* viewStartEndString = [NSString stringWithFormat:@"startDateTime=%@&endDateTime=%@", viewStart, viewEnd]; // GET /me/calendarview NSString* eventsUrlString = [NSString stringWithFormat:@"%@/me/calendarview?%@&%@&%@&%@", MSGraphBaseURL, viewStartEndString, // Only return these fields in results @"$select=subject,organizer,start,end", // Sort results by start time @"$orderby=start/dateTime", // Request at most 25 results @"$top=25"]; NSURL* eventsUrl = [[NSURL alloc] initWithString:eventsUrlString]; NSMutableURLRequest* eventsRequest = [[NSMutableURLRequest alloc] initWithURL:eventsUrl]; // Add the Prefer: outlook.timezone header to get start and end times // in user's time zone NSString* preferHeader = [NSString stringWithFormat:@"outlook.timezone=\"%@\"", self.graphTimeZone]; [eventsRequest addValue:preferHeader forHTTPHeaderField:@"Prefer"]; MSURLSessionDataTask* eventsDataTask = [[MSURLSessionDataTask alloc] initWithRequest:eventsRequest client:self.graphClient completion:^(NSData *data, NSURLResponse *response, NSError *error) { if (error) { completion(nil, error); return; } // TEMPORARY completion(data, nil); }]; // Execute the request [eventsDataTask execute]; }Observação
Considere o que o código
getCalendarViewStartingAtestá fazendo.- O URL que será chamado é
/v1.0/me/calendarview.- Os
startDateTimeendDateTimeparâmetros e consulta definem o início e o fim do exibição de calendário. - O parâmetro de consulta limita os campos retornados para cada evento para apenas aqueles que o
selectexibição realmente usará. - O
orderbyparâmetro de consulta classifica os resultados por hora de início. - O
topparâmetro de consulta solicita 25 resultados por página. - o header faz com que o microsoft Graph retornar os horários de início e término de cada evento no
Prefer: outlook.timezonefuso horário do usuário.
- Os
- O URL que será chamado é
Crie uma nova classe cocoa touch no projeto GraphTutorial chamado GraphToIana. Escolha NSObject na Subclasse do campo.
Abra GraphToIana.h e substitua seu conteúdo pelo código a seguir.
#import <Foundation/Foundation.h> NS_ASSUME_NONNULL_BEGIN @interface GraphToIana : NSObject + (NSString*) getIanaIdentifierFromGraphIdentifier: (NSString *) graphIdentifier; @end NS_ASSUME_NONNULL_ENDAbra GraphToIana.m e substitua seu conteúdo pelo código a seguir.
#import "GraphToIana.h" @implementation GraphToIana // Basic lookup for mapping Windows time zone identifiers to // IANA identifiers // Mappings taken from // https://github.com/unicode-org/cldr/blob/master/common/supplemental/windowsZones.xml + (NSString*) getIanaIdentifierFromGraphIdentifier: (NSString *) graphIdentifier { static NSDictionary* timeZoneMap = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ timeZoneMap = @{ @"Dateline Standard Time" : @"Etc/GMT+12", @"UTC-11" : @"Etc/GMT+11", @"Aleutian Standard Time" : @"America/Adak", @"Hawaiian Standard Time" : @"Pacific/Honolulu", @"Marquesas Standard Time" : @"Pacific/Marquesas", @"Alaskan Standard Time" : @"America/Anchorage", @"UTC-09" : @"Etc/GMT+9", @"Pacific Standard Time (Mexico)" : @"America/Tijuana", @"UTC-08" : @"Etc/GMT+8", @"Pacific Standard Time" : @"America/Los_Angeles", @"US Mountain Standard Time" : @"America/Phoenix", @"Mountain Standard Time (Mexico)" : @"America/Chihuahua", @"Mountain Standard Time" : @"America/Denver", @"Central America Standard Time" : @"America/Guatemala", @"Central Standard Time" : @"America/Chicago", @"Easter Island Standard Time" : @"Pacific/Easter", @"Central Standard Time (Mexico)" : @"America/Mexico_City", @"Canada Central Standard Time" : @"America/Regina", @"SA Pacific Standard Time" : @"America/Bogota", @"Eastern Standard Time (Mexico)" : @"America/Cancun", @"Eastern Standard Time" : @"America/New_York", @"Haiti Standard Time" : @"America/Port-au-Prince", @"Cuba Standard Time" : @"America/Havana", @"US Eastern Standard Time" : @"America/Indianapolis", @"Turks And Caicos Standard Time" : @"America/Grand_Turk", @"Paraguay Standard Time" : @"America/Asuncion", @"Atlantic Standard Time" : @"America/Halifax", @"Venezuela Standard Time" : @"America/Caracas", @"Central Brazilian Standard Time" : @"America/Cuiaba", @"SA Western Standard Time" : @"America/La_Paz", @"Pacific SA Standard Time" : @"America/Santiago", @"Newfoundland Standard Time" : @"America/St_Johns", @"Tocantins Standard Time" : @"America/Araguaina", @"E. South America Standard Time" : @"America/Sao_Paulo", @"SA Eastern Standard Time" : @"America/Cayenne", @"Argentina Standard Time" : @"America/Buenos_Aires", @"Greenland Standard Time" : @"America/Godthab", @"Montevideo Standard Time" : @"America/Montevideo", @"Magallanes Standard Time" : @"America/Punta_Arenas", @"Saint Pierre Standard Time" : @"America/Miquelon", @"Bahia Standard Time" : @"America/Bahia", @"UTC-02" : @"Etc/GMT+2", @"Azores Standard Time" : @"Atlantic/Azores", @"Cape Verde Standard Time" : @"Atlantic/Cape_Verde", @"UTC" : @"Etc/GMT", @"GMT Standard Time" : @"Europe/London", @"Greenwich Standard Time" : @"Atlantic/Reykjavik", @"Sao Tome Standard Time" : @"Africa/Sao_Tome", @"Morocco Standard Time" : @"Africa/Casablanca", @"W. Europe Standard Time" : @"Europe/Berlin", @"Central Europe Standard Time" : @"Europe/Budapest", @"Romance Standard Time" : @"Europe/Paris", @"Central European Standard Time" : @"Europe/Warsaw", @"W. Central Africa Standard Time" : @"Africa/Lagos", @"Jordan Standard Time" : @"Asia/Amman", @"GTB Standard Time" : @"Europe/Bucharest", @"Middle East Standard Time" : @"Asia/Beirut", @"Egypt Standard Time" : @"Africa/Cairo", @"E. Europe Standard Time" : @"Europe/Chisinau", @"Syria Standard Time" : @"Asia/Damascus", @"West Bank Standard Time" : @"Asia/Hebron", @"South Africa Standard Time" : @"Africa/Johannesburg", @"FLE Standard Time" : @"Europe/Kiev", @"Israel Standard Time" : @"Asia/Jerusalem", @"Kaliningrad Standard Time" : @"Europe/Kaliningrad", @"Sudan Standard Time" : @"Africa/Khartoum", @"Libya Standard Time" : @"Africa/Tripoli", @"Namibia Standard Time" : @"Africa/Windhoek", @"Arabic Standard Time" : @"Asia/Baghdad", @"Turkey Standard Time" : @"Europe/Istanbul", @"Arab Standard Time" : @"Asia/Riyadh", @"Belarus Standard Time" : @"Europe/Minsk", @"Russian Standard Time" : @"Europe/Moscow", @"E. Africa Standard Time" : @"Africa/Nairobi", @"Iran Standard Time" : @"Asia/Tehran", @"Arabian Standard Time" : @"Asia/Dubai", @"Astrakhan Standard Time" : @"Europe/Astrakhan", @"Azerbaijan Standard Time" : @"Asia/Baku", @"Russia Time Zone 3" : @"Europe/Samara", @"Mauritius Standard Time" : @"Indian/Mauritius", @"Saratov Standard Time" : @"Europe/Saratov", @"Georgian Standard Time" : @"Asia/Tbilisi", @"Volgograd Standard Time" : @"Europe/Volgograd", @"Caucasus Standard Time" : @"Asia/Yerevan", @"Afghanistan Standard Time" : @"Asia/Kabul", @"West Asia Standard Time" : @"Asia/Tashkent", @"Ekaterinburg Standard Time" : @"Asia/Yekaterinburg", @"Pakistan Standard Time" : @"Asia/Karachi", @"Qyzylorda Standard Time" : @"Asia/Qyzylorda", @"India Standard Time" : @"Asia/Calcutta", @"Sri Lanka Standard Time" : @"Asia/Colombo", @"Nepal Standard Time" : @"Asia/Katmandu", @"Central Asia Standard Time" : @"Asia/Almaty", @"Bangladesh Standard Time" : @"Asia/Dhaka", @"Omsk Standard Time" : @"Asia/Omsk", @"Myanmar Standard Time" : @"Asia/Rangoon", @"SE Asia Standard Time" : @"Asia/Bangkok", @"Altai Standard Time" : @"Asia/Barnaul", @"W. Mongolia Standard Time" : @"Asia/Hovd", @"North Asia Standard Time" : @"Asia/Krasnoyarsk", @"N. Central Asia Standard Time" : @"Asia/Novosibirsk", @"Tomsk Standard Time" : @"Asia/Tomsk", @"China Standard Time" : @"Asia/Shanghai", @"North Asia East Standard Time" : @"Asia/Irkutsk", @"Singapore Standard Time" : @"Asia/Singapore", @"W. Australia Standard Time" : @"Australia/Perth", @"Taipei Standard Time" : @"Asia/Taipei", @"Ulaanbaatar Standard Time" : @"Asia/Ulaanbaatar", @"Aus Central W. Standard Time" : @"Australia/Eucla", @"Transbaikal Standard Time" : @"Asia/Chita", @"Tokyo Standard Time" : @"Asia/Tokyo", @"North Korea Standard Time" : @"Asia/Pyongyang", @"Korea Standard Time" : @"Asia/Seoul", @"Yakutsk Standard Time" : @"Asia/Yakutsk", @"Cen. Australia Standard Time" : @"Australia/Adelaide", @"AUS Central Standard Time" : @"Australia/Darwin", @"E. Australia Standard Time" : @"Australia/Brisbane", @"AUS Eastern Standard Time" : @"Australia/Sydney", @"West Pacific Standard Time" : @"Pacific/Port_Moresby", @"Tasmania Standard Time" : @"Australia/Hobart", @"Vladivostok Standard Time" : @"Asia/Vladivostok", @"Lord Howe Standard Time" : @"Australia/Lord_Howe", @"Bougainville Standard Time" : @"Pacific/Bougainville", @"Russia Time Zone 10" : @"Asia/Srednekolymsk", @"Magadan Standard Time" : @"Asia/Magadan", @"Norfolk Standard Time" : @"Pacific/Norfolk", @"Sakhalin Standard Time" : @"Asia/Sakhalin", @"Central Pacific Standard Time" : @"Pacific/Guadalcanal", @"Russia Time Zone 11" : @"Asia/Kamchatka", @"New Zealand Standard Time" : @"Pacific/Auckland", @"UTC+12" : @"Etc/GMT-12", @"Fiji Standard Time" : @"Pacific/Fiji", @"Chatham Islands Standard Time" : @"Pacific/Chatham", @"UTC+13" : @"Etc/GMT-13", @"Tonga Standard Time" : @"Pacific/Tongatapu", @"Samoa Standard Time" : @"Pacific/Apia", @"Line Islands Standard Time" : @"Pacific/Kiritimati" }; }); // If a mapping was not found, assume the value passed // was already an IANA identifier return timeZoneMap[graphIdentifier] ? : graphIdentifier; } @endIsso faz uma análise simples para encontrar um identificador de fuso horário IANA com base no nome do fuso horário retornado pela Microsoft Graph.
Abra CalendarViewController.m e substitua todo o conteúdo pelo código a seguir.
#import "CalendarViewController.h" #import "SpinnerViewController.h" #import "GraphManager.h" #import "GraphToIana.h" #import <MSGraphClientModels/MSGraphClientModels.h> @interface CalendarViewController () @property SpinnerViewController* spinner; @end @implementation CalendarViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. self.spinner = [SpinnerViewController alloc]; [self.spinner startWithContainer:self]; // Calculate the start and end of the current week NSString* timeZoneId = [GraphToIana getIanaIdentifierFromGraphIdentifier: [GraphManager.instance graphTimeZone]]; NSDate* now = [NSDate date]; NSCalendar* calendar = [NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian]; NSTimeZone* timeZone = [NSTimeZone timeZoneWithName:timeZoneId]; [calendar setTimeZone:timeZone]; NSDateComponents* startOfWeekComponents = [calendar components:NSCalendarUnitCalendar | NSCalendarUnitYearForWeekOfYear | NSCalendarUnitWeekOfYear fromDate:now]; NSDate* startOfWeek = [startOfWeekComponents date]; NSDate* endOfWeek = [calendar dateByAddingUnit:NSCalendarUnitDay value:7 toDate:startOfWeek options:0]; // Convert start and end to ISO 8601 strings NSISO8601DateFormatter* isoFormatter = [[NSISO8601DateFormatter alloc] init]; NSString* viewStart = [isoFormatter stringFromDate:startOfWeek]; NSString* viewEnd = [isoFormatter stringFromDate:endOfWeek]; [GraphManager.instance getCalendarViewStartingAt:viewStart endingAt:viewEnd withCompletionBlock:^(NSData * _Nullable data, NSError * _Nullable error) { dispatch_async(dispatch_get_main_queue(), ^{ [self.spinner stop]; if (error) { // Show the error UIAlertController* alert = [UIAlertController alertControllerWithTitle:@"Error getting events" message:error.debugDescription preferredStyle:UIAlertControllerStyleAlert]; UIAlertAction* okButton = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]; [alert addAction:okButton]; [self presentViewController:alert animated:true completion:nil]; return; } // TEMPORARY self.calendarJSON.text = [[NSString alloc] initWithData:data encoding:NSUTF8StringEncoding]; [self.calendarJSON sizeToFit]; }); }]; } @endExecute o aplicativo, entre e toque no item de navegação Calendário no menu. Você deve ver um despejo JSON dos eventos no aplicativo.
Exibir os resultados
Agora você pode substituir o despejo JSON por algo para exibir os resultados de maneira amigável. Nesta seção, você modificará a função para retornar objetos fortemente digitados e modificará para usar um exibição de tabela getCalendarViewStartingAt CalendarViewController para renderizar os eventos.
Atualizar getCalendarViewStartingAt
Abra GraphManager.h. Altere
GetCalendarViewCompletionBlocka definição de tipo para o seguinte.typedef void (^GetCalendarViewCompletionBlock)(NSArray<MSGraphEvent*>* _Nullable events, NSError* _Nullable error);Abra GraphManager.m. Substitua a função
getCalendarViewStartingAtexistente pelo código seguinte.- (void) getCalendarViewStartingAt: (NSString *) viewStart endingAt: (NSString *) viewEnd withCompletionBlock: (GetCalendarViewCompletionBlock) completion { // Set calendar view start and end parameters NSString* viewStartEndString = [NSString stringWithFormat:@"startDateTime=%@&endDateTime=%@", viewStart, viewEnd]; // GET /me/calendarview NSString* eventsUrlString = [NSString stringWithFormat:@"%@/me/calendarview?%@&%@&%@&%@", MSGraphBaseURL, viewStartEndString, // Only return these fields in results @"$select=subject,organizer,start,end", // Sort results by start time @"$orderby=start/dateTime", // Request at most 25 results @"$top=25"]; NSURL* eventsUrl = [[NSURL alloc] initWithString:eventsUrlString]; NSMutableURLRequest* eventsRequest = [[NSMutableURLRequest alloc] initWithURL:eventsUrl]; // Add the Prefer: outlook.timezone header to get start and end times // in user's time zone NSString* preferHeader = [NSString stringWithFormat:@"outlook.timezone=\"%@\"", self.graphTimeZone]; [eventsRequest addValue:preferHeader forHTTPHeaderField:@"Prefer"]; MSURLSessionDataTask* eventsDataTask = [[MSURLSessionDataTask alloc] initWithRequest:eventsRequest client:self.graphClient completion:^(NSData *data, NSURLResponse *response, NSError *error) { if (error) { completion(nil, error); return; } NSError* graphError; // Deserialize to an events collection MSCollection* eventsCollection = [[MSCollection alloc] initWithData:data error:&graphError]; if (graphError) { completion(nil, graphError); return; } // Create an array to return NSMutableArray* eventsArray = [[NSMutableArray alloc] initWithCapacity:eventsCollection.value.count]; for (id event in eventsCollection.value) { // Deserialize the event and add to the array MSGraphEvent* graphEvent = [[MSGraphEvent alloc] initWithDictionary:event]; [eventsArray addObject:graphEvent]; } completion(eventsArray, nil); }]; // Execute the request [eventsDataTask execute]; }
Atualizar CalendarViewController
Crie um novo arquivo da Classe Cocoa Touch no projeto GraphTutorial chamado
CalendarTableViewCell. Escolha UITableViewCell na Subclasse do campo.Abra CalendarTableViewCell.h e substitua seu conteúdo pelo código a seguir.
#import <UIKit/UIKit.h> NS_ASSUME_NONNULL_BEGIN @interface CalendarTableViewCell : UITableViewCell @property (nonatomic) NSString* subject; @property (nonatomic) NSString* organizer; @property (nonatomic) NSString* duration; @end NS_ASSUME_NONNULL_ENDAbra CalendarTableViewCell.m e substitua seu conteúdo pelo código a seguir.
#import "CalendarTableViewCell.h" @interface CalendarTableViewCell() @property (nonatomic) IBOutlet UILabel *subjectLabel; @property (nonatomic) IBOutlet UILabel *organizerLabel; @property (nonatomic) IBOutlet UILabel *durationLabel; @end @implementation CalendarTableViewCell - (void)awakeFromNib { [super awakeFromNib]; // Initialization code } - (void)setSelected:(BOOL)selected animated:(BOOL)animated { [super setSelected:selected animated:animated]; // Configure the view for the selected state } - (void) setSubject:(NSString *)subject { _subject = subject; self.subjectLabel.text = subject; [self.subjectLabel sizeToFit]; } - (void) setOrganizer:(NSString *)organizer { _organizer = organizer; self.organizerLabel.text = organizer; [self.organizerLabel sizeToFit]; } - (void) setDuration:(NSString *)duration { _duration = duration; self.durationLabel.text = duration; [self.durationLabel sizeToFit]; } @endCrie um novo arquivo da Classe Cocoa Touch no projeto GraphTutorial chamado
CalendarTableViewController. Escolha UITableViewController na Subclasse do campo.Abra CalendarTableViewController.h e substitua seu conteúdo pelo código a seguir.
#import <UIKit/UIKit.h> #import <MSGraphClientModels/MSGraphClientModels.h> NS_ASSUME_NONNULL_BEGIN @interface CalendarTableViewController : UITableViewController @property (nonatomic) NSArray<MSGraphEvent*>* events; @end NS_ASSUME_NONNULL_ENDAbra CalendarTableViewController.m e substitua seu conteúdo pelo código a seguir.
#import "CalendarTableViewController.h" #import "CalendarTableViewCell.h" #import <MSGraphClientModels/MSGraphClientModels.h> @interface CalendarTableViewController () @end @implementation CalendarTableViewController - (void)viewDidLoad { [super viewDidLoad]; self.tableView.rowHeight = UITableViewAutomaticDimension; self.tableView.estimatedRowHeight = 100; } - (NSInteger) numberOfSections:(UITableView*) tableView { return 1; } - (NSInteger) tableView:(UITableView*) tableView numberOfRowsInSection:(NSInteger) section { return self.events ? self.events.count : 0; } - (UITableViewCell*) tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath { CalendarTableViewCell* cell = [tableView dequeueReusableCellWithIdentifier:@"EventCell"]; // Get the event that corresponds to the row MSGraphEvent* event = self.events[indexPath.row]; // Configure the cell cell.subject = event.subject; cell.organizer = event.organizer.emailAddress.name; cell.duration = [NSString stringWithFormat:@"%@ to %@", [self formatGraphDateTime:event.start], [self formatGraphDateTime:event.end]]; return cell; } - (NSString*) formatGraphDateTime:(MSGraphDateTimeTimeZone*) dateTime { // Create a formatter to parse Graph's date format NSDateFormatter* isoFormatter = [[NSDateFormatter alloc] init]; isoFormatter.dateFormat = @"yyyy-MM-dd'T'HH:mm:ss.SSSSSSS"; // Specify the time zone isoFormatter.timeZone = [[NSTimeZone alloc] initWithName:dateTime.timeZone]; NSDate* date = [isoFormatter dateFromString:dateTime.dateTime]; // Output like 5/5/2019, 2:00 PM NSDateFormatter* dateFormatter = [[NSDateFormatter alloc] init]; dateFormatter.dateStyle = NSDateFormatterShortStyle; dateFormatter.timeStyle = NSDateFormatterShortStyle; NSString* dateString = [dateFormatter stringFromDate:date]; return dateString; } - (void) setEvents:(NSArray<MSGraphEvent *> *)events { _events = events; [self.tableView reloadData]; } @endAbra Main.storyboard e localize a Cena do Calendário. Exclua o exibição de rolagem do exibição raiz.
Usando a Biblioteca, adicione uma Barra de Navegação à parte superior do modo de exibição.
Clique duas vezes no Título na barra de navegação e atualize-o para
Calendar.Usando a Biblioteca, adicione um Item de Botão de Barra ao lado direito da barra de navegação.
Selecione o novo botão de barra e selecione o Inspetor de Atributos. Alterar Imagem para mais.
Adicione um Modo de Exibição de Contêiner da Biblioteca ao modo de exibição na barra de navegação. Resize o exibição de contêiner para tirar todo o espaço restante no exibição.
Definir restrições na barra de navegação e no modo de exibição de contêiner da seguinte forma.
- Barra de Navegação
- Adicionar restrição: Altura, valor: 44
- Adicionar restrição: Levando espaço Cofre Área, valor: 0
- Adicionar restrição: espaço à Cofre Área, valor: 0
- Adicionar restrição: Espaço superior Cofre Área, valor: 0
- Exibição de contêiner
- Adicionar restrição: Levando espaço Cofre Área, valor: 0
- Adicionar restrição: espaço à Cofre Área, valor: 0
- Adicionar restrição: Espaço superior à Barra de Navegação Inferior, valor: 0
- Adicionar restrição: Espaço inferior para Cofre Área, valor: 0
- Barra de Navegação
Localize o segundo controlador de exibição adicionado ao storyboard quando você adicionou o exibição de contêiner. Ele é conectado à Cena do Calendário por um segue de incorporar. Selecione este controlador e use o Inspetor de Identidade para alterar Class para CalendarTableViewController.
Exclua o View do Controlador de Exibição de Tabela de Calendário.
Adicione um Exibição de Tabela da Biblioteca ao Controlador de Exibição de Tabela de Calendário.
Selecione o exibição de tabela e selecione o Inspetor de Atributos. Definir células de protótipo como 1.
Arraste a borda inferior da célula protótipo para lhe dar uma área maior para trabalhar.
Use a Biblioteca para adicionar três Rótulos à célula protótipo.
Selecione a célula protótipo e selecione o Inspetor de Identidade. Alterar classe para CalendarTableViewCell.
Selecione o Inspetor de Atributos e de definir Identificador como
EventCell.Com o EventCell selecionado, selecione o Inspetor de Conexões e conecte - e aos rótulos adicionados à célula
durationLabelnoorganizerLabelsubjectLabelstoryboard.De definir as propriedades e restrições nos três rótulos da seguinte forma.
- Rótulo de assunto
- Adicionar restrição: Espaço à esquerda para a margem de exibição de conteúdo, valor: 0
- Adicionar restrição: espaço à direita à margem de exibição de conteúdo à direita, valor: 0
- Adicionar restrição: Espaço superior à Margem Superior de Exibição de Conteúdo, valor: 0
- Rótulo organizador
- Fonte: Sistema 12.0
- Adicionar restrição: Altura, valor: 15
- Adicionar restrição: Espaço à esquerda para a margem de exibição de conteúdo, valor: 0
- Adicionar restrição: espaço à direita à margem de exibição de conteúdo à direita, valor: 0
- Adicionar restrição: Espaço superior para Subject Label Bottom, valor: Standard
- Rótulo de Duração
- Fonte: Sistema 12.0
- Cor: Cor cinza escuro
- Adicionar restrição: Altura, valor: 15
- Adicionar restrição: Espaço à esquerda para a margem de exibição de conteúdo, valor: 0
- Adicionar restrição: espaço à direita à margem de exibição de conteúdo à direita, valor: 0
- Adicionar restrição: Espaço superior ao Rótulo do Organizador Inferior, valor: Padrão
- Adicionar restrição: Espaço inferior à margem inferior do exibição de conteúdo, valor: 0
- Rótulo de assunto
Selecione EventCell e selecione o Inspetor de Tamanhos. Habilitar Automático para Altura da Linha.

Abra CalendarViewController.h e remova a
calendarJSONpropriedade.Abra CalendarViewController.m e substitua seu conteúdo pelo código a seguir.
#import "CalendarViewController.h" #import "CalendarTableViewController.h" #import "SpinnerViewController.h" #import "GraphManager.h" #import "GraphToIana.h" #import <MSGraphClientModels/MSGraphClientModels.h> @interface CalendarViewController () @property SpinnerViewController* spinner; @property CalendarTableViewController* tableView; @end @implementation CalendarViewController - (void)viewDidLoad { [super viewDidLoad]; // Do any additional setup after loading the view. self.spinner = [SpinnerViewController alloc]; [self.spinner startWithContainer:self]; // Calculate the start and end of the current week NSString* timeZoneId = [GraphToIana getIanaIdentifierFromGraphIdentifier: [GraphManager.instance graphTimeZone]]; NSDate* now = [NSDate date]; NSCalendar* calendar = [NSCalendar calendarWithIdentifier:NSCalendarIdentifierGregorian]; NSTimeZone* timeZone = [NSTimeZone timeZoneWithName:timeZoneId]; [calendar setTimeZone:timeZone]; NSDateComponents* startOfWeekComponents = [calendar components:NSCalendarUnitCalendar | NSCalendarUnitYearForWeekOfYear | NSCalendarUnitWeekOfYear fromDate:now]; NSDate* startOfWeek = [startOfWeekComponents date]; NSDate* endOfWeek = [calendar dateByAddingUnit:NSCalendarUnitDay value:7 toDate:startOfWeek options:0]; // Convert start and end to ISO 8601 strings NSISO8601DateFormatter* isoFormatter = [[NSISO8601DateFormatter alloc] init]; NSString* viewStart = [isoFormatter stringFromDate:startOfWeek]; NSString* viewEnd = [isoFormatter stringFromDate:endOfWeek]; [GraphManager.instance getCalendarViewStartingAt:viewStart endingAt:viewEnd withCompletionBlock:^(NSArray<MSGraphEvent*>* _Nullable events, NSError * _Nullable error) { dispatch_async(dispatch_get_main_queue(), ^{ [self.spinner stop]; if (error) { // Show the error UIAlertController* alert = [UIAlertController alertControllerWithTitle:@"Error getting events" message:error.debugDescription preferredStyle:UIAlertControllerStyleAlert]; UIAlertAction* okButton = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]; [alert addAction:okButton]; [self presentViewController:alert animated:true completion:nil]; return; } [self.tableView setEvents:events]; }); }]; } - (void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender { // Save a reference to the contained table view so // we can pass the results of the Graph call to it if ([segue.destinationViewController isKindOfClass:[CalendarTableViewController class]]) { self.tableView = segue.destinationViewController; } } - (IBAction) showNewEventForm { [self performSegueWithIdentifier:@"showEventForm" sender:nil]; } @endExecute o aplicativo, entre e toque na guia Calendário. Você deve ver a lista de eventos.

Criar um novo evento
Nesta seção, você adicionará a capacidade de criar eventos no calendário do usuário.
Abra GraphManager.h e adicione o seguinte código acima da
@interfacedeclaração.typedef void (^CreateEventCompletionBlock)(MSGraphEvent* _Nullable event, NSError* _Nullable error);Adicione o código a seguir à
@interfacedeclaração.- (void) createEventWithSubject: (NSString*) subject andStart: (NSDate*) start andEnd: (NSDate*) end andAttendees: (NSArray<NSString*>* _Nullable) attendees andBody: (NSString* _Nullable) body andCompletionBlock: (CreateEventCompletionBlock) completion;Abra GraphManager.m e adicione a seguinte função para criar um novo evento no calendário do usuário.
- (void) createEventWithSubject: (NSString*) subject andStart: (NSDate*) start andEnd: (NSDate*) end andAttendees: (NSArray<NSString*>* _Nullable) attendees andBody: (NSString* _Nullable) body andCompletionBlock: (CreateEventCompletionBlock) completion { NSDateFormatter* isoFormatter = [[NSDateFormatter alloc] init]; isoFormatter.dateFormat = @"yyyy-MM-dd'T'HH:mm"; // Create a dictionary to represent the event // Current version of the Graph SDK models don't serialize properly // see https://github.com/microsoftgraph/msgraph-sdk-objc-models/issues/27 NSMutableDictionary* eventDict = [NSMutableDictionary dictionary]; [eventDict setObject:subject forKey:@"subject"]; NSDictionary* startDict = @{ @"dateTime": [isoFormatter stringFromDate:start], @"timeZone": self.graphTimeZone }; [eventDict setObject:startDict forKey:@"start"]; NSDictionary* endDict = @{ @"dateTime": [isoFormatter stringFromDate:end], @"timeZone": self.graphTimeZone }; [eventDict setObject:endDict forKey:@"end"]; if (attendees != nil && attendees.count > 0) { NSMutableArray* attendeeArray = [NSMutableArray array]; for (id email in attendees) { NSDictionary* attendeeDict = @{ @"type": @"required", @"emailAddress": @{ @"address": email } }; [attendeeArray addObject:attendeeDict]; } [eventDict setObject:attendeeArray forKey:@"attendees"]; } if (body != nil) { NSDictionary* bodyDict = @{ @"content": body, @"contentType": @"text" }; [eventDict setObject:bodyDict forKey:@"body"]; } NSError* error = nil; NSData* eventData = [NSJSONSerialization dataWithJSONObject:eventDict options:kNilOptions error:&error]; // Prepare Graph request NSString* eventsUrlString = [NSString stringWithFormat:@"%@/me/events", MSGraphBaseURL]; NSURL* eventsUrl = [[NSURL alloc] initWithString:eventsUrlString]; NSMutableURLRequest* eventsRequest = [[NSMutableURLRequest alloc] initWithURL:eventsUrl]; eventsRequest.HTTPMethod = @"POST"; eventsRequest.HTTPBody = eventData; [eventsRequest addValue:@"application/json" forHTTPHeaderField:@"Content-Type"]; MSURLSessionDataTask* createEventDataTask = [[MSURLSessionDataTask alloc] initWithRequest:eventsRequest client:self.graphClient completion:^(NSData *data, NSURLResponse *response, NSError *error) { if (error) { completion(nil, error); return; } NSError* graphError; // Deserialize to an event MSGraphEvent* event = [[MSGraphEvent alloc] initWithData:data error:&graphError]; if (graphError) { completion(nil, graphError); return; } completion(event, nil); }]; // Execute the request [createEventDataTask execute]; }Crie um novo arquivo da Classe Cocoa Touch na pasta GraphTutorial chamada
NewEventViewController. Escolha UIViewController na Subclasse do campo.Abra NewEventViewController.h e adicione o seguinte código dentro da
@interfacedeclaração.@property (nonatomic) IBOutlet UITextField* subject; @property (nonatomic) IBOutlet UITextField* attendees; @property (nonatomic) IBOutlet UIDatePicker* start; @property (nonatomic) IBOutlet UIDatePicker* end; @property (nonatomic) IBOutlet UITextView* body;Abra NewEventController.m e substitua seu conteúdo pelo código a seguir.
#import "NewEventViewController.h" #import "SpinnerViewController.h" #import "GraphManager.h" @interface NewEventViewController () @property SpinnerViewController* spinner; @end @implementation NewEventViewController - (void)viewDidLoad { [super viewDidLoad]; self.spinner = [SpinnerViewController alloc]; // Add border around text view UIColor* borderColor = [UIColor colorWithRed:0.85 green:0.85 blue:0.85 alpha:1.0]; self.body.layer.borderWidth = 0.5; self.body.layer.borderColor = [borderColor CGColor]; self.body.layer.cornerRadius = 5.0; // Set start picker to the next closest half-hour NSDate* now = [NSDate date]; NSCalendar* calendar = [NSCalendar currentCalendar]; NSInteger minutes = [calendar component:NSCalendarUnitMinute fromDate:now]; NSInteger offset = 30 - (minutes % 30); NSDate* start = [calendar dateByAddingUnit:NSCalendarUnitMinute value:offset toDate:now options:kNilOptions]; self.start.date = start; // Set end picker to start + 30 min NSDate* end = [calendar dateByAddingUnit:NSCalendarUnitMinute value:30 toDate:start options:kNilOptions]; self.end.date = end; } - (IBAction) createEvent { [self.spinner startWithContainer:self]; NSString* subject = self.subject.text; NSString* attendeeString = self.attendees.text; NSArray* attendees = nil; if (attendeeString != nil && attendeeString.length > 0) { attendees = [attendeeString componentsSeparatedByString:@";"]; } NSDate* start = self.start.date; NSDate* end = self.end.date; NSString* body = self.body.text; [GraphManager.instance createEventWithSubject:subject andStart:start andEnd:end andAttendees:attendees andBody:body andCompletionBlock:^(MSGraphEvent * _Nullable event, NSError * _Nullable error) { dispatch_async(dispatch_get_main_queue(), ^{ [self.spinner stop]; NSString* alertTitle = nil; NSString* alertMessage = nil; UIAlertAction* okButton = nil; if (error) { // Show the error alertTitle = @"Error creating event"; alertMessage = error.debugDescription; okButton = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:nil]; } else { alertTitle = @"Success"; alertMessage = @"Event created"; okButton = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault handler:^(UIAlertAction * action) { [self dismissViewControllerAnimated:true completion:nil]; }]; } UIAlertController* alert = [UIAlertController alertControllerWithTitle:alertTitle message:alertMessage preferredStyle:UIAlertControllerStyleAlert]; [alert addAction:okButton]; [self presentViewController:alert animated:true completion:nil]; }); }]; } - (IBAction) cancel { [self dismissViewControllerAnimated:true completion:nil]; } @endAbra Main.storyboard. Use a Biblioteca para arrastar um Controlador de Exibição para o storyboard.
Usando a Biblioteca, adicione uma Barra de Navegação ao controlador de exibição.
Clique duas vezes no Título na barra de navegação e atualize-o para
New Event.Usando a Biblioteca, adicione um Item de Botão de Barra ao lado esquerdo da barra de navegação.
Selecione o novo botão de barra e selecione o Inspetor de Atributos. Alterar Título para
Cancel.Usando a Biblioteca, adicione um Item de Botão de Barra ao lado direito da barra de navegação.
Selecione o novo botão de barra e selecione o Inspetor de Atributos. Alterar Título para
Create.Selecione o controlador de exibição e selecione o Inspetor de Identidade. Alterar classe para NewEventViewController.
Adicione os controles a seguir da Biblioteca à exibição.
- Adicione um Rótulo na barra de navegação. De definir seu texto como
Subject. - Adicione um Campo de Texto sob o rótulo. Definir seu atributo Placeholder como
Subject. - Adicione um Rótulo no campo de texto. De definir seu texto como
Attendees. - Adicione um Campo de Texto sob o rótulo. Definir seu atributo Placeholder como
Separate multiple entries with ;. - Adicione um Rótulo no campo de texto. De definir seu texto como
Start. - Adicione um Selador de Data sob o rótulo. Definir seu Estilo Preferencial como Compacto, seu Intervalo como 15 minutos e sua altura como 35.
- Adicione um Rótulo no selador de datas. De definir seu texto como
End. - Adicione um Selador de Data sob o rótulo. Definir seu Estilo Preferencial como Compacto, seu Intervalo como 15 minutos e sua altura como 35.
- Adicione um Text View no selador de datas.
- Adicione um Rótulo na barra de navegação. De definir seu texto como
Selecione o Novo Controlador de Exibição de Evento e use o Inspetor de Conexão para fazer as seguintes conexões.
- Conexão ação cancelar recebida no botão Cancelar barra.
- Conexão a ação createEvent recebida no botão Criar barra.
- Conexão saída de assunto para o primeiro campo de texto.
- Conexão a saída dos participantes para o segundo campo de texto.
- Conexão saída inicial para o selador de primeira data.
- Conexão saída final para o segundo se picker de data.
- Conexão saída do corpo para o exibição de texto.
Adicione as seguintes restrições.
- Barra de Navegação
- Espaço à Cofre Área, valor: 0
- Espaço à Cofre Área, valor: 0
- Espaço superior para Cofre Área, valor: 0
- Altura, valor: 44
- Rótulo de assunto
- Espaço à esquerda para Exibir margem, valor: 0
- Espaço à direita para Exibir margem, valor: 0
- Espaço superior para a Barra de Navegação, valor: 20
- Campo Texto do Assunto
- Espaço à esquerda para Exibir margem, valor: 0
- Espaço à direita para Exibir margem, valor: 0
- Espaço superior para Rótulo de Assunto, valor: Standard
- Rótulo de participantes
- Espaço à esquerda para Exibir margem, valor: 0
- Espaço à direita para Exibir margem, valor: 0
- Espaço superior para Campo de Texto de Assunto, valor: Standard
- Campo texto dos participantes
- Espaço à esquerda para Exibir margem, valor: 0
- Espaço à direita para Exibir margem, valor: 0
- Espaço superior para Rótulo de Participantes, valor: Standard
- Iniciar Rótulo
- Espaço à esquerda para Exibir margem, valor: 0
- Espaço à direita para Exibir margem, valor: 0
- Espaço superior para Campo de Texto de Assunto, valor: Standard
- Se picker de data de início
- Espaço à esquerda para Exibir margem, valor: 0
- Espaço à direita para Exibir margem, valor: 0
- Espaço superior para Rótulo de Participantes, valor: Standard
- Altura, valor: 35
- Rótulo final
- Espaço à esquerda para Exibir margem, valor: 0
- Espaço à direita para Exibir margem, valor: 0
- Espaço superior para o Se picker de data de início, valor: Standard
- Se picker de data de término
- Espaço à esquerda para Exibir margem, valor: 0
- Espaço à direita para Exibir margem, valor: 0
- Espaço superior para Rótulo Final, valor: Standard
- Altura: 35
- Body Text View
- Espaço à esquerda para Exibir margem, valor: 0
- Espaço à direita para Exibir margem, valor: 0
- Espaço superior para o Se picker de data de término, valor: Standard
- Espaço inferior para Exibir margem, valor: 0

- Barra de Navegação
Selecione a Cena do Calendário e selecione o Inspetor de Conexões.
Em Segues Disparados, arraste o círculo não preenchido ao lado do manual para o Novo Controlador de Modo de Exibição de Eventos no storyboard. Selecione Apresentar Modally no menu pop-up.
Selecione o segue que você acabou de adicionar e selecione o Inspetor de Atributos. Definir o campo Identificador como
showEventForm.Conexão a ação showNewEventForm recebida no botão da barra de + navegação.
Salve suas alterações e reinicie o aplicativo. Vá para a página de calendário e toque no + botão. Preencha o formulário e toque em Criar para criar um novo evento.

Parabéns!
Você concluiu o tutorial do microsoft Graph iOS Objective-C. Agora que você tem um aplicativo de trabalho que chama a Microsoft Graph, você pode experimentar e adicionar novos recursos. Visite a visão geral do microsoft Graph para ver todos os dados que você pode acessar com o Microsoft Graph.
Comentários
Forneça qualquer comentário sobre este tutorial no repositório GitHub .
Tem algum problema com essa seção? Se tiver, envie seus comentários para que possamos melhorar esta seção.