Crear aplicaciones de iOS Objective-C con Microsoft Graph
Este tutorial te enseña a crear una aplicación React Native que usa la API de Microsoft Graph para recuperar información de calendario para un usuario.
Sugerencia
Si prefiere descargar el tutorial completado, puede descargar o clonar el repositorio GitHub archivo.
Requisitos previos
Antes de iniciar este tutorial, debe tener lo siguiente instalado en el equipo de desarrollo.
También debe tener una cuenta personal de Microsoft con un buzón en Outlook.com, o una cuenta de Trabajo o escuela de Microsoft. Si no tienes una cuenta de Microsoft, hay un par de opciones para obtener una cuenta gratuita:
- Puedes suscribirte a una nueva cuenta personal de Microsoft.
- Puedes suscribirte al programa Microsoft 365 desarrolladores para obtener una suscripción Microsoft 365 gratuita.
Nota
Este tutorial se escribió con Xcode versión 12.3 y CocoaPods versión 1.10.1. Los pasos de esta guía pueden funcionar con otras versiones, pero eso no se ha probado.
Comentarios
Proporcione cualquier comentario sobre este tutorial en el repositorio GitHub usuario.
Crear una aplicación de iOS Objective-C
Comience creando un nuevo proyecto Objective-C.
Abra Xcode. En el menú Archivo, seleccione Nuevo y, a continuación, Project.
Elija la plantilla Aplicación y seleccione Siguiente.
Establezca el nombre del producto en y el
GraphTutorial
idioma en Objective-C.Rellene los campos restantes y seleccione Siguiente.
Elija una ubicación para el proyecto y seleccione Crear.
Instalar dependencias
Antes de seguir adelante, instale algunas dependencias adicionales que usará más adelante.
- Biblioteca de autenticación de Microsoft (MSAL) para iOS para autenticarse con Azure AD.
- Microsoft Graph SDK para objective C para realizar llamadas a Microsoft Graph.
- Sdk Graph modelos de Microsoft para objective C para objetos fuertemente typed que representan microsoft Graph recursos como usuarios o eventos.
Salga de Xcode.
Abra Terminal y cambie el directorio a la ubicación del proyecto GraphTutorial.
Ejecute el siguiente comando para crear un podfile.
pod init
Abra el podfile y agregue las siguientes líneas justo después de la
use_frameworks!
línea.pod 'MSAL', '~> 1.1.13' pod 'MSGraphClientSDK', ' ~> 1.0.0' pod 'MSGraphClientModels', '~> 1.3.0'
Guarde el podfile y, a continuación, ejecute el siguiente comando para instalar las dependencias.
pod install
Una vez completado el comando, abra el graphtutorial.xcworkspace recién creado en Xcode.
Diseñar la aplicación
En esta sección, crearás las vistas de la aplicación: una página de inicio de sesión, un navegador de barras de pestañas, una página de bienvenida y una página de calendario. También crearás una superposición de indicador de actividad.
Crear página de inicio de sesión
Expanda la carpeta GraphTutorial en Xcode y, a continuación, seleccione el archivo ViewController.m.
En el Inspector de archivos, cambie el nombre del archivo a
SignInViewController.m
.Abra SignInViewController.m y reemplace su contenido por el siguiente código.
#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]; } @end
Seleccione el archivo ViewController.h.
En el Inspector de archivos, cambie el nombre del archivo a
SignInViewController.h
.Abra SignInViewController.h y cambie todas las instancias de
ViewController
aSignInViewController
.Abra el archivo Main.storyboard.
Expanda Ver escena del controlador y, a continuación, seleccione Ver controlador.
Seleccione el Inspector de identidades y, a continuación, cambie el desplegable Clase a SignInViewController.
Seleccione la biblioteca y, a continuación, arrastre un botón al controlador de vista de inicio de sesión.
Con el botón seleccionado, seleccione el Inspector de atributos y cambie el Título del botón a
Sign In
.Con el botón seleccionado, selecciona el botón Alinear en la parte inferior del guión gráfico. Seleccione horizontalmente en contenedor y verticalmente en restricciones de contenedor, deje sus valores como 0 y, a continuación, seleccione Agregar 2 restricciones.
Seleccione el controlador de vista Iniciar sesión y, a continuación, seleccione el Inspector de conexiones.
En Acciones recibidas, arrastre el círculo sin rellenar junto a signIn al botón. Seleccione Retoco dentro en el menú emergente.
Crear barra de pestañas
Seleccione la biblioteca y, a continuación, arrastre un controlador de barra de pestañas al guión gráfico.
Seleccione el controlador de vista Iniciar sesión y, a continuación, seleccione el Inspector de conexiones.
En Segues desencadenados, arrastre el círculo no rellenado junto al manual al controlador de barra de pestañas en el guión gráfico. Seleccione Presentar modalmente en el menú emergente.
Seleccione el segue que acaba de agregar y, a continuación, seleccione el Inspector de atributos. Establezca el campo Identificador en y
userSignedIn
establezca Presentation en Pantalla completa.Seleccione la escena elemento 1 y, a continuación, seleccione el Inspector de conexiones.
En Segues desencadenados, arrastre el círculo no rellenado junto a manual al controlador de vista de inicio de sesión en el guión gráfico. Seleccione Presentar modalmente en el menú emergente.
Seleccione el segue que acaba de agregar y, a continuación, seleccione el Inspector de atributos. Establezca el campo Identificador en y
userSignedOut
establezca Presentation en Pantalla completa.
Crear página de bienvenida
Seleccione el archivo Assets.xcassets.
En el menú Editor, seleccione Agregar nuevo activo y, a continuación, Conjunto de imágenes.
Seleccione el nuevo activo Image y use el Inspector de atributos para establecer su nombre en
DefaultUserPhoto
.Agrega cualquier imagen que quieras que sirva como foto de perfil de usuario predeterminada.
Cree un nuevo archivo cocoa touch class en la carpeta GraphTutorial denominada
WelcomeViewController
. Elija UIViewController en la subclase del campo.Abra WelcomeViewController.h y agregue el siguiente código dentro de la
@interface
declaración.@property (nonatomic) IBOutlet UIImageView *userProfilePhoto; @property (nonatomic) IBOutlet UILabel *userDisplayName; @property (nonatomic) IBOutlet UILabel *userEmail;
Abra WelcomeViewController.m y reemplace su contenido por el siguiente código.
#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]; } @end
Abra Main.storyboard. Seleccione la escena elemento 1 y, a continuación, seleccione el Inspector de identidades. Cambie el valor class a WelcomeViewController.
Mediante la biblioteca, agregue los siguientes elementos a la escena del elemento 1.
- Una vista de imagen
- Dos etiquetas
- Un botón
Con el Inspector de conexiones, realice las siguientes conexiones.
- Vincula la salida userDisplayName a la primera etiqueta.
- Vincula la salida userEmail a la segunda etiqueta.
- Vincula la salida userProfilePhoto a la vista de imagen.
- Vincule la acción signOut recibida al botón Touch Up Inside.
Seleccione la vista de imagen y, a continuación, seleccione el Inspector de tamaño.
Establezca width y height en 196.
Use el botón Alinear para agregar la restricción Horizontalmente en contenedor con un valor de 0.
Use el botón Agregar nuevas restricciones (junto al botón Alinear) para agregar las siguientes restricciones:
- Alinear arriba a: Caja fuerte área, valor: 0
- Espacio inferior a: Nombre para mostrar del usuario, valor: Estándar
- Alto, valor: 196
- Width, value: 196
Seleccione la primera etiqueta y, a continuación, use el botón Alinear para agregar la restricción Horizontalmente en contenedor con un valor de 0.
Use el botón Agregar nuevas restricciones para agregar las siguientes restricciones:
- Espacio superior a: Foto de perfil de usuario, valor: Estándar
- Espacio inferior a: Correo electrónico del usuario, valor: Estándar
Seleccione la segunda etiqueta y, a continuación, seleccione el Inspector de atributos.
Cambie el color a color gris oscuro y cambie la fuente a Sistema 12.0.
Use el botón Alinear para agregar la restricción Horizontalmente en contenedor con un valor de 0.
Use el botón Agregar nuevas restricciones para agregar las siguientes restricciones:
- Espacio superior a: Nombre para mostrar del usuario, valor: Estándar
- Espacio inferior a: Cerrar sesión, valor: 14
Seleccione el botón y, a continuación, seleccione el Inspector de atributos.
Cambie el título a
Sign Out
.Use el botón Alinear para agregar la restricción Horizontalmente en contenedor con un valor de 0.
Use el botón Agregar nuevas restricciones para agregar las siguientes restricciones:
- Espacio superior a: Correo electrónico del usuario, valor: 14
Seleccione el elemento de la barra de pestañas en la parte inferior de la escena y, a continuación, seleccione el Inspector de atributos. Cambie el título a
Me
.En el menú Editor, seleccione Resolver problemas de diseño automático y, a continuación, seleccione Agregar restricciones que faltan debajo de Todas las vistas en el controlador de vista de bienvenida.
La escena de bienvenida debe ser similar a esta una vez que haya terminado.
Crear página de calendario
Cree un nuevo archivo cocoa touch class en la carpeta GraphTutorial denominada
CalendarViewController
. Elija UIViewController en la subclase del campo.Abra CalendarViewController.h y agregue el siguiente código dentro de la
@interface
declaración.@property (nonatomic) IBOutlet UITextView *calendarJSON;
Abra CalendarViewController.m y reemplace su contenido por el código siguiente.
#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]; } @end
Abra Main.storyboard. Seleccione la escena del elemento 2 y, a continuación, seleccione el Inspector de identidades. Cambie el valor class a CalendarViewController.
Mediante la biblioteca, agregue una vista de texto a la escena del elemento 2.
Seleccione la vista de texto que acaba de agregar. En el Editor, elija Insertar en y, a continuación, Vista de desplazamiento.
Con el Inspector de conexiones, conecte la salida calendarJSON a la vista de texto.
Seleccione el elemento de la barra de pestañas en la parte inferior de la escena y, a continuación, seleccione el Inspector de atributos. Cambie el título a
Calendar
.En el menú Editor, seleccione Resolver problemas de diseño automático y, a continuación, seleccione Agregar restricciones que faltan debajo de Todas las vistas en el controlador de vista de bienvenida.
La escena del calendario debe ser similar a esta una vez que haya terminado.
Crear indicador de actividad
Cree un nuevo archivo cocoa touch class en la carpeta GraphTutorial denominada
SpinnerViewController
. Elija UIViewController en la subclase del campo.Abra SpinnerViewController.h y agregue el siguiente código dentro de la
@interface
declaración.- (void) startWithContainer:(UIViewController*) container; - (void) stop;
Abra SpinnerViewController.m y reemplace su contenido por el siguiente código.
#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
Probar la aplicación
Guarda los cambios e inicia la aplicación. Debería poder moverse entre las pantallas con los botones Iniciar sesión y Cerrar sesión y la barra de pestañas.
Registrar la aplicación en el portal
En este ejercicio, creará una nueva aplicación nativa de Azure AD con el centro Azure Active Directory administración.
Abra un explorador y vaya al centro de administración de Azure Active Directory e inicie sesión con una cuenta personal (también conocida como: cuenta Microsoft) o una cuenta profesional o educativa.
Seleccione Azure Active Directory en el panel de navegación izquierdo y, a continuación, seleccione Registros de aplicaciones en Administrar.
Seleccione Nuevo registro. En la página Registrar una aplicación, establezca los valores siguientes.
- Establezca Nombre como
iOS Objective-C Graph Tutorial
. - Establezca Tipos de cuenta admitidos en Cuentas en cualquier directorio de organización y cuentas personales de Microsoft.
- Deje URI de redireccionamiento vacía.
- Establezca Nombre como
Seleccione Registrar. En la página Tutorial de Graph objective-C de iOS, copie el valor del identificador de aplicación (cliente) y guárdelo, lo necesitará en el paso siguiente.
Seleccione Autenticación en Administrar. Seleccione Agregar una plataforma y, a continuación, iOS /macOS.
Escribe el id. de agrupación de la aplicación y selecciona Configurar y, a continuación, selecciona Listo.
Agregar autenticación de Azure AD
En este ejercicio, extenderá la aplicación desde el ejercicio anterior para admitir la autenticación con Azure AD. Esto es necesario para obtener el token de acceso OAuth necesario para llamar a Microsoft Graph. Para ello, integrará la Biblioteca de autenticación de Microsoft (MSAL) para iOS en la aplicación.
Cree un nuevo archivo de lista de propiedades en el proyecto GraphTutorial denominado AuthSettings.plist.
Agregue los siguientes elementos al archivo en el diccionario raíz.
Key Tipo Valor AppId
String El identificador de aplicación de Azure Portal GraphScopes
Matriz Tres valores de cadena: User.Read
MailboxSettings.Read
, yCalendars.ReadWrite
Importante
Si usas el control de código fuente como git, ahora sería un buen momento para excluir el archivo AuthSettings.plist del control de código fuente para evitar la pérdida involuntaria del identificador de la aplicación.
Implementar el inicio de sesión
En esta sección, configurará el proyecto para MSAL, creará una clase de administrador de autenticación y actualizará la aplicación para iniciar sesión y cerrar sesión.
Configurar el proyecto para MSAL
Agregue un nuevo grupo de llaves a las capacidades del proyecto.
- Seleccione el proyecto GraphTutorial y, a continuación, & funcionalidades.
- Seleccione + Funcionalidad y, a continuación, haga doble clic en Compartir cadena de teclas.
- Agregue un grupo de llaves con el valor
com.microsoft.adalcache
.
Control click Info.plist and select Open As, then Source Code.
Agregue lo siguiente dentro del
<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 y agregue la siguiente instrucción import en la parte superior del archivo.
#import <MSAL/MSAL.h>
Agregue la siguiente función a la clase
AppDelegate
.- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options { return [MSALPublicClientApplication handleMSALResponse:url sourceApplication:options[UIApplicationOpenURLOptionsSourceApplicationKey]]; }
Crear administrador de autenticación
Cree una nueva clase de cocoa táctil en el proyecto GraphTutorial denominado AuthenticationManager. Elija NSObject en la subclase del campo.
Abra AuthenticationManager.h y reemplace su contenido por el siguiente código.
#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_END
Abra AuthenticationManager.m y reemplace su contenido por el código siguiente.
#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
Agregar inicio de sesión y salida
Abra el archivo SignInViewController.m y reemplace su contenido por el siguiente código.
#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]; }); }]; } @end
Abra WelcomeViewController.m y agregue la siguiente
import
instrucción a la parte superior del archivo.#import "AuthenticationManager.h"
Reemplace la función
signOut
existente por lo siguiente.- (IBAction)signOut { [AuthenticationManager.instance signOut]; [self performSegueWithIdentifier: @"userSignedOut" sender: nil]; }
Guarde los cambios y reinicie la aplicación en Simulador.
Si inicias sesión en la aplicación, deberías ver un token de acceso que se muestra en la ventana de salida en Xcode.
Obtener detalles del usuario
En esta sección, creará una clase auxiliar para contener todas las llamadas a Microsoft Graph y actualizar la para usar esta nueva clase para obtener el usuario que ha WelcomeViewController
iniciado sesión.
Cree una nueva clase de cocoa táctil en el proyecto GraphTutorial denominado GraphManager. Elija NSObject en la subclase del campo.
Abra GraphManager.h y reemplace su contenido por el siguiente código.
#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_END
Abra GraphManager.m y reemplace su contenido por el código siguiente.
#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]; } @end
Abra WelcomeViewController.m y agregue las siguientes
#import
instrucciones en la parte superior del archivo.#import "SpinnerViewController.h" #import "GraphManager.h" #import <MSGraphClientModels/MSGraphClientModels.h>
Agregue la siguiente propiedad a la
WelcomeViewController
declaración de interfaz.@property SpinnerViewController* spinner;
Reemplace el existente
viewDidLoad
por el código siguiente.- (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")]; }); }]; }
Si guardas los cambios y reinicias la aplicación ahora, después de iniciar sesión, la interfaz de usuario se actualiza con el nombre para mostrar y la dirección de correo electrónico del usuario.
Obtener una vista de calendario
En este ejercicio, incorporará el Graph Microsoft en la aplicación. Para esta aplicación, usará el SDK de Microsoft Graph para el objetivo C para realizar llamadas a Microsoft Graph.
Obtener eventos del calendario desde Outlook
En esta sección, extenderá la clase para agregar una función para obtener los eventos del usuario de la semana actual y actualizar para usar GraphManager
CalendarViewController
esta nueva función.
Abra GraphManager.h y agregue el siguiente código encima de la
@interface
declaración.typedef void (^GetCalendarViewCompletionBlock)(NSData* _Nullable data, NSError* _Nullable error);
Agregue el siguiente código a la
@interface
declaración.- (void) getCalendarViewStartingAt: (NSString*) viewStart endingAt: (NSString*) viewEnd withCompletionBlock: (GetCalendarViewCompletionBlock) completion;
Abra GraphManager.m y agregue la siguiente función a la
GraphManager
clase.- (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]; }
Nota
Tenga en cuenta lo que está
getCalendarViewStartingAt
haciendo el código.- La dirección URL a la que se llamará es
/v1.0/me/calendarview
.- Los
startDateTime
parámetros y de consulta definen el inicio y el final de la vista deendDateTime
calendario. - El
select
parámetro de consulta limita los campos devueltos para cada evento a solo aquellos que la vista usará realmente. - El
orderby
parámetro query ordena los resultados por hora de inicio. - El
top
parámetro de consulta solicita 25 resultados por página. - el encabezado hace que el Graph Microsoft devuelva las horas de inicio y finalización de cada evento en la
Prefer: outlook.timezone
zona horaria del usuario.
- Los
- La dirección URL a la que se llamará es
Cree una nueva clase de cocoa táctil en el proyecto GraphTutorial denominado GraphToIana. Elija NSObject en la subclase del campo.
Abra GraphToIana.h y reemplace su contenido por el siguiente código.
#import <Foundation/Foundation.h> NS_ASSUME_NONNULL_BEGIN @interface GraphToIana : NSObject + (NSString*) getIanaIdentifierFromGraphIdentifier: (NSString *) graphIdentifier; @end NS_ASSUME_NONNULL_END
Abra GraphToIana.m y reemplace su contenido por el siguiente código.
#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; } @end
Esto hace una búsqueda sencilla para buscar un identificador de zona horaria IANA en función del nombre de zona horaria devuelto por Microsoft Graph.
Abra CalendarViewController.m y reemplace todo su contenido por el siguiente código.
#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]; }); }]; } @end
Ejecuta la aplicación, inicia sesión y pulsa el elemento de navegación Calendario en el menú. Debería ver un volcado JSON de los eventos en la aplicación.
Mostrar los resultados
Ahora puede reemplazar el volcado JSON por algo para mostrar los resultados de una manera fácil de usar. En esta sección, modificará la función para devolver objetos fuertemente especificados y modificará para usar una vista de tabla getCalendarViewStartingAt
CalendarViewController
para representar los eventos.
Actualizar getCalendarViewStartingAt
Abra GraphManager.h. Cambie la
GetCalendarViewCompletionBlock
definición de tipo a la siguiente.typedef void (^GetCalendarViewCompletionBlock)(NSArray<MSGraphEvent*>* _Nullable events, NSError* _Nullable error);
Abra GraphManager.m. Reemplace la función
getCalendarViewStartingAt
existente por el siguiente código.- (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]; }
Actualizar CalendarViewController
Cree un nuevo archivo de clase táctil de Cocoa en el proyecto GraphTutorial denominado
CalendarTableViewCell
. Elija UITableViewCell en la subclase del campo.Abra CalendarTableViewCell.h y reemplace su contenido por el siguiente código.
#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_END
Abra CalendarTableViewCell.m y reemplace su contenido por el siguiente código.
#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]; } @end
Cree un nuevo archivo de clase táctil de Cocoa en el proyecto GraphTutorial denominado
CalendarTableViewController
. Elija UITableViewController en la subclase del campo.Abra CalendarTableViewController.h y reemplace su contenido por el código siguiente.
#import <UIKit/UIKit.h> #import <MSGraphClientModels/MSGraphClientModels.h> NS_ASSUME_NONNULL_BEGIN @interface CalendarTableViewController : UITableViewController @property (nonatomic) NSArray<MSGraphEvent*>* events; @end NS_ASSUME_NONNULL_END
Abra CalendarTableViewController.m y reemplace su contenido por el siguiente código.
#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]; } @end
Abra Main.storyboard y busque la escena del calendario. Elimine la vista de desplazamiento de la vista raíz.
Con la biblioteca, agregue una barra de navegación a la parte superior de la vista.
Haga doble clic en el título de la barra de navegación y actualíquelo a
Calendar
.Mediante la biblioteca, agregue un elemento de botón de barra al lado derecho de la barra de navegación.
Seleccione el nuevo botón de barra y, a continuación, seleccione el Inspector de atributos. Cambiar imagen a más.
Agregue una vista contenedora desde la biblioteca a la vista debajo de la barra de navegación. Cambie el tamaño de la vista contenedora para tomar todo el espacio restante de la vista.
Establezca las restricciones en la barra de navegación y la vista contenedora de la siguiente manera.
- Barra de navegación
- Restricción Add: Height, value: 44
- Agregar restricción: espacio inicial a Caja fuerte area, valor: 0
- Agregar restricción: espacio final a Caja fuerte area, valor: 0
- Agregar restricción: Espacio superior a Caja fuerte área, valor: 0
- Vista contenedora
- Agregar restricción: espacio inicial a Caja fuerte area, valor: 0
- Agregar restricción: espacio final a Caja fuerte area, valor: 0
- Agregar restricción: Espacio superior a la parte inferior de la barra de navegación, valor: 0
- Restricción Agregar: espacio inferior a Caja fuerte Area, value: 0
- Barra de navegación
Busque el segundo controlador de vista agregado al guión gráfico cuando agregó la vista contenedora. Se conecta a la escena del calendario mediante un segue de inserción. Seleccione este controlador y use el Inspector de identidades para cambiar Clase a CalendarTableViewController.
Elimine la vista del controlador de vista de tabla de calendario.
Agregue una vista de tabla de la biblioteca al controlador de vista de tabla de calendario.
Seleccione la vista de tabla y, a continuación, seleccione el Inspector de atributos. Establecer celdas prototipo en 1.
Arrastre el borde inferior de la celda prototipo para darle un área más grande con la que trabajar.
Use la biblioteca para agregar tres etiquetas a la celda prototipo.
Seleccione la celda prototipo y, a continuación, seleccione el Inspector de identidades. Cambiar clase a CalendarTableViewCell.
Seleccione el Inspector de atributos y establezca Identifier en
EventCell
.Con eventcell seleccionado, seleccione el Inspector de conexiones y conecte , y a las etiquetas que agregó a la celda en
durationLabel
elorganizerLabel
subjectLabel
guión gráfico.Establezca las propiedades y restricciones de las tres etiquetas de la siguiente manera.
- Etiqueta de sujeto
- Agregar restricción: Espacio inicial a margen inicial de la vista de contenido, valor: 0
- Agregar restricción: espacio final a margen final de la vista de contenido, valor: 0
- Agregar restricción: Espacio superior a margen superior de la vista de contenido, valor: 0
- Etiqueta de organizador
- Fuente: System 12.0
- Restricción Add: Height, value: 15
- Agregar restricción: Espacio inicial a margen inicial de la vista de contenido, valor: 0
- Agregar restricción: espacio final a margen final de la vista de contenido, valor: 0
- Restricción Agregar: Espacio superior a La parte inferior de la etiqueta del sujeto, valor: Estándar
- Etiqueta de duración
- Fuente: System 12.0
- Color: color gris oscuro
- Restricción Add: Height, value: 15
- Agregar restricción: Espacio inicial a margen inicial de la vista de contenido, valor: 0
- Agregar restricción: espacio final a margen final de la vista de contenido, valor: 0
- Restricción Agregar: Espacio superior a La parte inferior de la etiqueta del organizador, valor: Estándar
- Restricción agregar: espacio inferior a la vista de contenido Margen inferior, valor: 0
- Etiqueta de sujeto
Seleccione eventcell y, a continuación, seleccione el Inspector de tamaño. Habilitar Automático para altura de fila.
Abra CalendarViewController.h y quite la
calendarJSON
propiedad.Abra CalendarViewController.m y reemplace su contenido por el código siguiente.
#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]; } @end
Ejecuta la aplicación, inicia sesión y pulsa la pestaña Calendario. Debería ver la lista de eventos.
Crear un nuevo evento
En esta sección, agregará la capacidad de crear eventos en el calendario del usuario.
Abra GraphManager.h y agregue el siguiente código encima de la
@interface
declaración.typedef void (^CreateEventCompletionBlock)(MSGraphEvent* _Nullable event, NSError* _Nullable error);
Agregue el siguiente código a la
@interface
declaración.- (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 y agregue la siguiente función para crear un nuevo evento en el calendario del usuario.
- (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]; }
Cree un nuevo archivo cocoa touch class en la carpeta GraphTutorial denominada
NewEventViewController
. Elija UIViewController en la subclase del campo.Abra NewEventViewController.h y agregue el siguiente código dentro de la
@interface
declaración.@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 y reemplace su contenido por el siguiente código.
#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]; } @end
Abra Main.storyboard. Use la biblioteca para arrastrar un controlador de vista al guión gráfico.
Mediante la biblioteca, agregue una barra de navegación al controlador de vista.
Haga doble clic en el título de la barra de navegación y actualíquelo a
New Event
.Con la biblioteca, agregue un elemento de botón de barra al lado izquierdo de la barra de navegación.
Seleccione el nuevo botón de barra y, a continuación, seleccione el Inspector de atributos. Cambie Title a
Cancel
.Mediante la biblioteca, agregue un elemento de botón de barra al lado derecho de la barra de navegación.
Seleccione el nuevo botón de barra y, a continuación, seleccione el Inspector de atributos. Cambie Title a
Create
.Seleccione el controlador de vista y, a continuación, seleccione el Inspector de identidad. Cambie clase a NewEventViewController.
Agregue los siguientes controles de la biblioteca a la vista.
- Agregue una etiqueta debajo de la barra de navegación. Establezca su texto en
Subject
. - Agregue un campo de texto bajo la etiqueta. Establezca su atributo Placeholder en
Subject
. - Agregue una etiqueta bajo el campo de texto. Establezca su texto en
Attendees
. - Agregue un campo de texto bajo la etiqueta. Establezca su atributo Placeholder en
Separate multiple entries with ;
. - Agregue una etiqueta bajo el campo de texto. Establezca su texto en
Start
. - Agregue un selector de fecha bajo la etiqueta. Establece su estilo preferido en Compacto, su intervalo en 15 minutos y su altura en 35.
- Agregue una etiqueta en el selector de fecha. Establezca su texto en
End
. - Agregue un selector de fecha bajo la etiqueta. Establece su estilo preferido en Compacto, su intervalo en 15 minutos y su altura en 35.
- Agregue una vista de texto en el selector de fecha.
- Agregue una etiqueta debajo de la barra de navegación. Establezca su texto en
Seleccione el nuevo controlador de vista de eventos y use el Inspector de conexión para realizar las siguientes conexiones.
- Conectar la acción cancelar recibida al botón Cancelar barra.
- Conectar la acción createEvent recibida en el botón Crear barra.
- Conectar salida del asunto al primer campo de texto.
- Conectar la salida de los asistentes al segundo campo de texto.
- Conectar la salida de inicio al primer selector de fecha.
- Conectar la salida final al segundo selector de fechas.
- Conectar la salida del cuerpo a la vista de texto.
Agregue las siguientes restricciones.
- Barra de navegación
- Espacio inicial al Caja fuerte, valor: 0
- Espacio final a Caja fuerte area, valor: 0
- Espacio superior para Caja fuerte area, valor: 0
- Alto, valor: 44
- Etiqueta de sujeto
- Espacio inicial a Margen de vista, valor: 0
- Espacio final a Margen de vista, valor: 0
- Espacio superior a barra de navegación, valor: 20
- Campo texto del asunto
- Espacio inicial a Margen de vista, valor: 0
- Espacio final a Margen de vista, valor: 0
- Espacio superior a Etiqueta de sujeto, valor: Estándar
- Etiqueta de asistentes
- Espacio inicial a Margen de vista, valor: 0
- Espacio final a Margen de vista, valor: 0
- Espacio superior al campo texto del sujeto, valor: Estándar
- Campo de texto de los asistentes
- Espacio inicial a Margen de vista, valor: 0
- Espacio final a Margen de vista, valor: 0
- Espacio superior a Etiqueta de asistentes, valor: Estándar
- Etiqueta de inicio
- Espacio inicial a Margen de vista, valor: 0
- Espacio final a Margen de vista, valor: 0
- Espacio superior al campo texto del sujeto, valor: Estándar
- Selector de fecha de inicio
- Espacio inicial a Margen de vista, valor: 0
- Espacio final a Margen de vista, valor: 0
- Espacio superior a Etiqueta de asistentes, valor: Estándar
- Alto, valor: 35
- Etiqueta de finalización
- Espacio inicial a Margen de vista, valor: 0
- Espacio final a Margen de vista, valor: 0
- Espacio superior al Selector de fecha de inicio, valor: Estándar
- Selector de fecha de finalización
- Espacio inicial a Margen de vista, valor: 0
- Espacio final a Margen de vista, valor: 0
- Espacio superior a Etiqueta final, valor: Estándar
- Alto: 35
- Vista texto del cuerpo
- Espacio inicial a Margen de vista, valor: 0
- Espacio final a Margen de vista, valor: 0
- Espacio superior a Selector de fecha de finalización, valor: Estándar
- Espacio inferior a Ver margen, valor: 0
- Barra de navegación
Seleccione la escena del calendario y, a continuación, seleccione el Inspector de conexiones.
En Segues desencadenados, arrastre el círculo no rellenado junto a manual al nuevo controlador de vista de eventos en el guión gráfico. Seleccione Presentar modalmente en el menú emergente.
Seleccione el segue que acaba de agregar y, a continuación, seleccione el Inspector de atributos. Establezca el campo Identificador en
showEventForm
.Conectar la acción showNewEventForm recibió una acción en el botón + de la barra de navegación.
Guarde los cambios y reinicie la aplicación. Ve a la página del calendario y pulsa el + botón. Rellene el formulario y pulse Crear para crear un nuevo evento.
¡Enhorabuena!
Has completado el tutorial de Microsoft Objective-C Graph iOS. Ahora que tienes una aplicación de trabajo que llama a Microsoft Graph, puedes experimentar y agregar nuevas características. Visite la información general de Microsoft Graph para ver todos los datos a los que puede acceder con Microsoft Graph.
Comentarios
Proporcione cualquier comentario sobre este tutorial en el repositorio GitHub archivo.
¿Tiene algún problema con esta sección? Si es así, envíenos sus comentarios para que podamos mejorarla.