Création d’applications iOS Objective-C avec Microsoft Graph
Ce didacticiel vous apprend à créer une application React Native qui utilise l’API Microsoft Graph pour récupérer les informations de calendrier d’un utilisateur.
Conseil
Si vous préférez simplement télécharger le didacticiel terminé, vous pouvez télécharger ou cloner le GitHub complet.
Conditions préalables
Avant de commencer ce didacticiel, les instructions suivantes doivent être installées sur votre ordinateur de développement.
Vous devez également avoir un compte Microsoft personnel avec une boîte aux lettres sur Outlook.com, ou un compte scolaire ou scolaire Microsoft. Si vous n’avez pas de compte Microsoft, deux options s’offrent à vous pour obtenir un compte gratuit :
- Vous pouvez vous inscrire à un nouveau compte Microsoft personnel.
- Vous pouvez vous inscrire au programme Microsoft 365 développeur pour obtenir un abonnement Microsoft 365 gratuit.
Notes
Ce didacticiel a été écrit à l’aide de Xcode version 12.3 et CocoaPods version 1.10.1. Les étapes de ce guide peuvent fonctionner avec d’autres versions, mais elles n’ont pas été testées.
Commentaires
N’hésitez pas à nous faire part de vos commentaires sur ce didacticiel dans GitHub référentiel.
Créer une application iOS Objective-C
Commencez par créer un projet Objective-C.
Ouvrez Xcode. Dans le menu Fichier, sélectionnez Nouveau, puis Project.
Choisissez le modèle d’application et sélectionnez Suivant.
Définissez le nom du produit et la
GraphTutorial
langue sur Objective-C.Remplissez les champs restants et sélectionnez Suivant.
Choisissez un emplacement pour le projet, puis sélectionnez Créer.
Installer les dépendances
Avant de passer à autre chose, installez des dépendances supplémentaires que vous utiliserez ultérieurement.
- Bibliothèque d’authentification Microsoft (MSAL) pour iOS pour l’authentification auprès d’Azure AD.
- Microsoft Graph SDK pour l’Objectif C pour effectuer des appels à Microsoft Graph.
- Microsoft Graph SDK modèles pour l’objectif C pour les objets fortement typés représentant des ressources Microsoft Graph comme des utilisateurs ou des événements.
Quittez Xcode.
Ouvrez Terminal et modifiez le répertoire à l’emplacement de votre projet GraphTutorial.
Exécutez la commande suivante pour créer un podfile.
pod init
Ouvrez le podfile et ajoutez les lignes suivantes juste après la
use_frameworks!
ligne.pod 'MSAL', '~> 1.1.13' pod 'MSGraphClientSDK', ' ~> 1.0.0' pod 'MSGraphClientModels', '~> 1.3.0'
Enregistrez le podfile, puis exécutez la commande suivante pour installer les dépendances.
pod install
Une fois la commande terminée, ouvrez l’espace GraphTutorial.xcworkspace nouvellement créé dans Xcode.
Concevoir l’application
Dans cette section, vous allez créer les vues de l’application : une page de signature, un navigateur de barre d’onglets, une page d’accueil et une page de calendrier. Vous allez également créer une superposition d’indicateur d’activité.
Page Créer une signature
Développez le dossier GraphTutorial dans Xcode, puis sélectionnez le fichier ViewController.m.
Dans l’Inspecteur de fichier, modifiez le nom du fichier en
SignInViewController.m
.Ouvrez SignInViewController.m et remplacez son contenu par le code suivant.
#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
Sélectionnez le fichier ViewController.h.
Dans l’Inspecteur de fichier, modifiez le nom du fichier en
SignInViewController.h
.Ouvrez SignInViewController.h et modifiez toutes les instances
ViewController
deSignInViewController
.Ouvrez le fichier Main.storyboard.
Développez La scène du contrôleur de vue, puis sélectionnez Contrôleur de vue.
Sélectionnez l’Inspecteur d’identité, puis modifiez la classe de la catégorie à SignInViewController.
Sélectionnez la bibliothèque, puis faites glisser un bouton sur le contrôleur d’affichage de la signature.
Une fois le bouton sélectionné, sélectionnez l’Inspecteur d’attributs et modifiez le titre du bouton sur
Sign In
.Une fois le bouton sélectionné, sélectionnez le bouton Aligner en bas du storyboard. Sélectionnez les contraintes Horizontalement dans le conteneur et Verticalement dans les contraintes de conteneur, laissez leurs valeurs sous la forme 0, puis sélectionnez Ajouter 2 contraintes.
Sélectionnez le contrôleur d’affichage de connexion, puis l’inspecteur de connexions.
Sous Actions reçues, faites glisser le cercle non rempli en dessous de la signature sur le bouton. Sélectionnez Toucher à l’intérieur dans le menu déroulant.
Créer une barre d’onglets
Sélectionnez la bibliothèque, puis faites glisser un contrôleur de barre d’onglets vers le storyboard.
Sélectionnez le contrôleur d’affichage de connexion, puis l’inspecteur de connexions.
Sous Segues déclenchés, faites glisser le cercle non rempli à côté du manuel sur le contrôleur de barre de tabulation dans le storyboard. Sélectionnez Présenter en mode mod dans le menu déroulant.
Sélectionnez le segue que vous avez ajouté, puis sélectionnez l’inspecteur d’attributs. Définissez le champ identificateur sur , et
userSignedIn
définissez Présentation en plein écran.Sélectionnez la scène d’élément 1, puis l’inspecteur de connexions.
Sous Segues déclenchés, faites glisser le cercle non rempli en regard du manuel sur le contrôleur d’affichage de la signature dans le storyboard. Sélectionnez Présenter en mode mod dans le menu déroulant.
Sélectionnez le segue que vous avez ajouté, puis sélectionnez l’inspecteur d’attributs. Définissez le champ identificateur sur , et
userSignedOut
définissez Présentation en plein écran.
Créer une page d’accueil
Sélectionnez le fichier Assets.x vhs.
Dans le menu Éditeur, sélectionnez Ajouter un nouvel actif, puis Jeu d’images.
Sélectionnez la nouvelle image et utilisez l’inspecteur d’attributs pour définir son nom
DefaultUserPhoto
sur .Ajoutez n’importe quelle image que vous souhaitez servir de photo de profil utilisateur par défaut.
Créez un fichier de classe Cocoa Touch dans le dossier GraphTutorial nommé
WelcomeViewController
. Choisissez UIViewController dans la sous-classe du champ.Ouvrez WelcomeViewController.h et ajoutez le code suivant à l’intérieur de la
@interface
déclaration.@property (nonatomic) IBOutlet UIImageView *userProfilePhoto; @property (nonatomic) IBOutlet UILabel *userDisplayName; @property (nonatomic) IBOutlet UILabel *userEmail;
Ouvrez WelcomeViewController.m et remplacez son contenu par le code suivant.
#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
Ouvrez Main.storyboard. Sélectionnez la scène d’élément 1, puis l’inspecteur d’identité. Modifiez la valeur de classe en WelcomeViewController.
À l’aide de la bibliothèque, ajoutez les éléments suivants à la scène d’élément 1.
- Une vue d’image
- Deux étiquettes
- Un bouton
À l’aide de l’Inspecteur de connexions, établir les connexions suivantes.
- Lier la sortie userDisplayName à la première étiquette.
- Lier la sortie userEmail à la deuxième étiquette.
- Lier la sortie userProfilePhoto à la vue d’image.
- Lier l’action reçue de connexion à l’action Touch Up Inside du bouton.
Sélectionnez l’affichage d’image, puis l’Inspecteur de taille.
Définissez la largeur et la hauteur sur 196.
Utilisez le bouton Aligner pour ajouter la contrainte Horizontalement dans le conteneur avec la valeur 0.
Utilisez le bouton Ajouter de nouvelles contraintes (à côté du bouton Aligner) pour ajouter les contraintes suivantes :
- Aligner le haut sur : Coffre zone, valeur : 0
- Espace inférieur à : Nom d’affichage de l’utilisateur, valeur : Standard
- Hauteur, valeur : 196
- Largeur, valeur : 196
Sélectionnez la première étiquette, puis utilisez le bouton Aligner pour ajouter la contrainte Horizontalement dans le conteneur avec la valeur 0.
Utilisez le bouton Ajouter de nouvelles contraintes pour ajouter les contraintes suivantes :
- Espace supérieur vers : Photo de profil utilisateur, valeur : Standard
- Espace inférieur à : Courrier électronique de l’utilisateur, valeur : Standard
Sélectionnez la deuxième étiquette, puis l’inspecteur d’attributs.
Modifiez la couleur en gris foncé et modifiez la police sur System 12.0.
Utilisez le bouton Aligner pour ajouter la contrainte Horizontalement dans le conteneur avec la valeur 0.
Utilisez le bouton Ajouter de nouvelles contraintes pour ajouter les contraintes suivantes :
- Espace supérieur : Nom d’affichage de l’utilisateur, valeur : Standard
- Espace inférieur pour : Se sortir, valeur : 14
Sélectionnez le bouton, puis l’inspecteur d’attributs.
Changez le titre en
Sign Out
.Utilisez le bouton Aligner pour ajouter la contrainte Horizontalement dans le conteneur avec la valeur 0.
Utilisez le bouton Ajouter de nouvelles contraintes pour ajouter les contraintes suivantes :
- Espace supérieur pour : Courrier électronique de l’utilisateur, valeur : 14
Sélectionnez l’élément de barre d’onglets en bas de la scène, puis sélectionnez l’inspecteur d’attributs. Changez le titre en
Me
.Dans le menu Éditeur, sélectionnez Résoudre les problèmes de disposition automatique, puis sélectionnez Ajouter des contraintes manquantes sous Tous les affichages dans le contrôleur de vue d’accueil.
La scène d’accueil doit ressembler à ceci une fois que vous avez terminé.
Créer une page de calendrier
Créez un fichier de classe Cocoa Touch dans le dossier GraphTutorial nommé
CalendarViewController
. Choisissez UIViewController dans la sous-classe du champ.Ouvrez CalendarViewController.h et ajoutez le code suivant à l’intérieur de la
@interface
déclaration.@property (nonatomic) IBOutlet UITextView *calendarJSON;
Ouvrez CalendarViewController.m et remplacez son contenu par le code suivant.
#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
Ouvrez Main.storyboard. Sélectionnez la scène d’élément 2, puis l’inspecteur d’identité. Modifiez la valeur de classe en CalendarViewController.
À l’aide de la bibliothèque, ajoutez un affichage texte à la scène d’élément 2.
Sélectionnez l’affichage texte que vous avez ajouté. On the Editor, choose Embed In, then Scroll View.
À l’aide de l’Inspecteur de connexions, connectez la sortie calendarJSON à l’affichage texte.
Sélectionnez l’élément de barre d’onglets en bas de la scène, puis sélectionnez l’inspecteur d’attributs. Changez le titre en
Calendar
.Dans le menu Éditeur, sélectionnez Résoudre les problèmes de disposition automatique, puis sélectionnez Ajouter des contraintes manquantes sous Tous les affichages dans le contrôleur de vue d’accueil.
La scène de calendrier doit ressembler à ceci une fois que vous avez terminé.
Créer un indicateur d’activité
Créez un fichier de classe Cocoa Touch dans le dossier GraphTutorial nommé
SpinnerViewController
. Choisissez UIViewController dans la sous-classe du champ.Ouvrez SpinnerViewController.h et ajoutez le code suivant à l’intérieur de la
@interface
déclaration.- (void) startWithContainer:(UIViewController*) container; - (void) stop;
Ouvrez SpinnerViewController.m et remplacez son contenu par le code suivant.
#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
Tester l'application
Enregistrez vos modifications et lancez l’application. Vous devriez être en mesure de vous déplacer entre les écrans à l’aide des boutons Se connectez et se connectez et de la barre d’onglets.
Inscrire l’application sur le portail
Dans cet exercice, vous allez créer une application native Azure AD à l’aide Azure Active Directory centre d’administration.
Ouvrez un navigateur, accédez au Centre d’administration Azure Active Directory et connectez-vous à l’aide d’un compte personnel (ou compte Microsoft) ou d’un compte professionnel ou scolaire.
Sélectionnez Azure Active Directory dans le volet de navigation gauche, puis sélectionnez Inscriptions d’applications sous Gérer.
Sélectionnez Nouvelle inscription. Sur la page Inscrire une application, définissez les valeurs comme suit.
- Définissez le Nom sur
iOS Objective-C Graph Tutorial
. - Définissez les Types de comptes pris en charge sur Comptes dans un annuaire organisationnel et comptes personnels Microsoft.
- Laissez Redirect URI vide.
- Définissez le Nom sur
Sélectionner Inscription. Sur la page didacticiel Graph iOS Objective-C, copiez la valeur de l’ID d’application (client) et enregistrez-la. Vous en aurez besoin à l’étape suivante.
Sous Gérer, sélectionnez Authentification. Sélectionnez Ajouter une plateforme, puis iOS /macOS.
Entrez l’ID d’offre groupée de votre application, puis sélectionnez Configurer, puis sélectionnez Terminé.
Ajouter une authentification Azure AD
Dans cet exercice, vous allez étendre l’application de l’exercice précédent pour prendre en charge l’authentification avec Azure AD. Cette étape est nécessaire pour obtenir le jeton d’accès OAuth nécessaire pour appeler l’Graph Microsoft. Pour ce faire, vous allez intégrer la bibliothèque d’authentification Microsoft (MSAL) pour iOS dans l’application.
Créez un fichier de liste de propriétés dans le projet GraphTutorial nommé AuthSettings.plist.
Ajoutez les éléments suivants au fichier dans le dictionnaire racine.
Clé Type Valeur AppId
String ID d’application à partir du portail Azure GraphScopes
Tableau Trois valeurs de User.Read
chaîne :MailboxSettings.Read
, etCalendars.ReadWrite
Important
Si vous utilisez un contrôle source tel que Git, il est temps d’exclure le fichier AuthSettings.plist du contrôle source afin d’éviter toute fuite accidentelle de votre ID d’application.
Implémentation de la connexion
Dans cette section, vous allez configurer le projet pour MSAL, créer une classe de gestionnaire d’authentification et mettre à jour l’application pour se connecter et se sortir.
Configurer le projet pour MSAL
Ajoutez un nouveau groupe dechains aux fonctionnalités de votre projet.
- Sélectionnez le projet GraphTutorial, puis & fonctionnalités de signature.
- Sélectionnez + Fonctionnalité, puis double-cliquez sur Partage duchain.
- Ajoutez un groupe de chaînes de clés avec la valeur
com.microsoft.adalcache
.
Contrôlez cliquez sur Info.plist et sélectionnez Ouvrir sous, puis Code source.
Ajoutez ce qui suit à l’intérieur de
<dict>
l’élément.<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>
Ouvrez AppDelegate.m et ajoutez l’instruction import suivante en haut du fichier.
#import <MSAL/MSAL.h>
Ajoutez la fonction suivante à la classe
AppDelegate
.- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options { return [MSALPublicClientApplication handleMSALResponse:url sourceApplication:options[UIApplicationOpenURLOptionsSourceApplicationKey]]; }
Créer un gestionnaire d’authentification
Créez une classe Cocoa Touch dans le projet GraphTutorial nommé AuthenticationManager. Choisissez NSObject dans la sous-classe de champ.
Ouvrez AuthenticationManager.h et remplacez son contenu par le code suivant.
#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
Ouvrez AuthenticationManager.m et remplacez son contenu par le code suivant.
#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
Ajout de la signature et de la sortie
Ouvrez le fichier SignInViewController.m et remplacez son contenu par le code suivant.
#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
Ouvrez WelcomeViewController.m et ajoutez l’instruction
import
suivante en haut du fichier.#import "AuthenticationManager.h"
Remplacez la fonction
signOut
existante par ce qui suit.- (IBAction)signOut { [AuthenticationManager.instance signOut]; [self performSegueWithIdentifier: @"userSignedOut" sender: nil]; }
Enregistrez vos modifications et redémarrez l’application dans simulateur.
Si vous vous connectez à l’application, un jeton d’accès doit s’afficher dans la fenêtre de sortie dans Xcode.
Obtenir les détails de l’utilisateur
Dans cette section, vous allez créer une classe d’aide pour contenir tous les appels à Microsoft Graph et mettre à jour la classe pour utiliser cette nouvelle classe afin d’obtenir l’utilisateur WelcomeViewController
connecté.
Créez une classe Cocoa Touch dans le projet GraphTutorial nommé GraphManager. Choisissez NSObject dans la sous-classe de champ.
Ouvrez GraphManager.h et remplacez son contenu par le code suivant.
#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
Ouvrez GraphManager.m et remplacez son contenu par le code suivant.
#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
Ouvrez WelcomeViewController.m et ajoutez les
#import
instructions suivantes en haut du fichier.#import "SpinnerViewController.h" #import "GraphManager.h" #import <MSGraphClientModels/MSGraphClientModels.h>
Ajoutez la propriété suivante à la déclaration
WelcomeViewController
d’interface.@property SpinnerViewController* spinner;
Remplacez
viewDidLoad
l’existant par le code suivant.- (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 vous enregistrez vos modifications et redémarrez l’application maintenant, après la mise à jour de l’interface utilisateur avec le nom d’affichage et l’adresse e-mail de l’utilisateur.
Obtenir un affichage Calendrier
Dans cet exercice, vous allez incorporer l’Graph Microsoft dans l’application. Pour cette application, vous allez utiliser le SDK Microsoft Graph pour l’objectif C pour effectuer des appels à Microsoft Graph.
Récupérer les événements de calendrier à partir d’Outlook
Dans cette section, vous allez étendre la classe pour ajouter une fonction afin d’obtenir les événements de l’utilisateur pour la semaine en cours et mettre à jour pour GraphManager
CalendarViewController
utiliser cette nouvelle fonction.
Ouvrez GraphManager.h et ajoutez le code suivant au-dessus de la
@interface
déclaration.typedef void (^GetCalendarViewCompletionBlock)(NSData* _Nullable data, NSError* _Nullable error);
Ajoutez le code suivant à la
@interface
déclaration.- (void) getCalendarViewStartingAt: (NSString*) viewStart endingAt: (NSString*) viewEnd withCompletionBlock: (GetCalendarViewCompletionBlock) completion;
Ouvrez GraphManager.m et ajoutez la fonction suivante à la
GraphManager
classe.- (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]; }
Notes
Réfléchissez à ce que fait
getCalendarViewStartingAt
le code.- L’URL qui sera appelée est
/v1.0/me/calendarview
.- Les
startDateTime
endDateTime
paramètres de requête définissent le début et la fin de l’affichage Calendrier. - Le paramètre de requête limite les champs renvoyés pour chaque événement à ceux que
select
l’affichage utilisera réellement. - Le
orderby
paramètre de requête trie les résultats par heure de début. - Le
top
paramètre de requête demande 25 résultats par page. - L’en-tête entraîne le Graph Microsoft à renvoyer les heures de début et de fin de chaque événement dans le
Prefer: outlook.timezone
fuseau horaire de l’utilisateur.
- Les
- L’URL qui sera appelée est
Créez une classe Cocoa Touch dans le projet GraphTutorial nommé GraphToIana. Choisissez NSObject dans la sous-classe du champ.
Ouvrez GraphToIana.h et remplacez son contenu par le code suivant.
#import <Foundation/Foundation.h> NS_ASSUME_NONNULL_BEGIN @interface GraphToIana : NSObject + (NSString*) getIanaIdentifierFromGraphIdentifier: (NSString *) graphIdentifier; @end NS_ASSUME_NONNULL_END
Ouvrez GraphToIana.m et remplacez son contenu par le code suivant.
#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
Cette opération permet de rechercher simplement un identificateur de fuseau horaire IANA basé sur le nom de fuseau horaire renvoyé par Microsoft Graph.
Ouvrez CalendarViewController.m et remplacez tout son contenu par le code suivant.
#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
Exécutez l’application, connectez-vous et appuyez sur l’élément de navigation Calendrier dans le menu. Vous devriez voir un vidage JSON des événements dans l’application.
Afficher les résultats
Vous pouvez désormais remplacer le vidage JSON par un autre qui permet d’afficher les résultats de manière conviviale. Dans cette section, vous allez modifier la fonction pour renvoyer des objets fortement typés et modifier pour utiliser une vue de getCalendarViewStartingAt
tableau pour afficher les CalendarViewController
événements.
Update getCalendarViewStartingAt
Ouvrez GraphManager.h. Modifiez
GetCalendarViewCompletionBlock
la définition de type comme suit.typedef void (^GetCalendarViewCompletionBlock)(NSArray<MSGraphEvent*>* _Nullable events, NSError* _Nullable error);
Ouvrez GraphManager.m. Remplacez la fonction
getCalendarViewStartingAt
existante par le code suivant.- (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]; }
Mettre à jour CalendarViewController
Créez un fichier de classe Cocoa Touch dans le projet GraphTutorial nommé
CalendarTableViewCell
. Choisissez UITableViewCell dans la sous-classe du champ.Ouvrez CalendarTableViewCell.h et remplacez son contenu par le code suivant.
#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
Ouvrez CalendarTableViewCell.m et remplacez son contenu par le code suivant.
#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
Créez un fichier de classe Cocoa Touch dans le projet GraphTutorial nommé
CalendarTableViewController
. Choisissez UITableViewController dans la sous-classe du champ.Ouvrez CalendarTableViewController.h et remplacez son contenu par le code suivant.
#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
Ouvrez CalendarTableViewController.m et remplacez son contenu par le code suivant.
#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
Ouvrez Main.storyboard et recherchez la scène de calendrier. Supprimez le défilement de l’affichage racine.
À l’aide de la bibliothèque, ajoutez une barre de navigation en haut de l’affichage.
Double-cliquez sur le titre dans la barre de navigation et mettez-le à
Calendar
jour.À l’aide de la bibliothèque, ajoutez un élément de bouton de barre à droite de la barre de navigation.
Sélectionnez le nouveau bouton de barre, puis l’inspecteur d’attributs. Modifier l’image sur plus.
Ajoutez un affichage conteneur de la bibliothèque à l’affichage sous la barre de navigation. Resize the container view to take all of the remaining space in the view.
Définissez les contraintes sur la barre de navigation et l’affichage conteneur comme suit.
- Barre de navigation
- Ajouter une contrainte : Hauteur, valeur : 44
- Add constraint: Leading space to Coffre Area, value: 0
- Add constraint: Trailing space to Coffre Area, value: 0
- Add constraint: Top space to Coffre Area, value: 0
- Affichage conteneur
- Add constraint: Leading space to Coffre Area, value: 0
- Add constraint: Trailing space to Coffre Area, value: 0
- Ajouter une contrainte : espace supérieur dans la barre de navigation en bas, valeur : 0
- Add constraint: Bottom space to Coffre Area, value: 0
- Barre de navigation
Recherchez le deuxième contrôleur d’affichage ajouté au storyboard lorsque vous avez ajouté l’affichage conteneur. Il est connecté à la scène de calendrier par une ségue d’incorporation. Sélectionnez ce contrôleur et utilisez l’Inspecteur d’identité pour modifier la classe en CalendarTableViewController.
Supprimez l’affichage du contrôleur d’affichage de table de calendrier.
Ajoutez un affichage Table à partir de la bibliothèque au contrôleur d’affichage de table de calendrier.
Sélectionnez l’affichage Tableau, puis l’Inspecteur d’attributs. Définissez les cellules prototype sur 1.
Faites glisser le bord inférieur de la cellule prototype pour vous donner une zone plus grande à travailler.
Utilisez la bibliothèque pour ajouter trois étiquettes à la cellule prototype.
Sélectionnez la cellule prototype, puis l’inspecteur d’identité. Change Class to CalendarTableViewCell.
Sélectionnez l’inspecteur d’attributs et définissez l’identificateur sur
EventCell
.Une fois EventCell sélectionné, sélectionnez l’Inspecteur de connexions et connectez-vous, ainsi qu’aux étiquettes que vous avez ajoutées à la cellule
durationLabel
dans leorganizerLabel
subjectLabel
storyboard.Définissez les propriétés et les contraintes sur les trois étiquettes comme suit.
- Étiquette d’objet
- Add constraint: Leading space to Content View Leading Margin, value: 0
- Add constraint: Trailing space to Content View Trailing Margin, value: 0
- Ajouter une contrainte : espace supérieur à la marge supérieure de l’affichage de contenu, valeur : 0
- Étiquette d’organisateur
- Police : System 12.0
- Ajouter une contrainte : Hauteur, valeur : 15
- Add constraint: Leading space to Content View Leading Margin, value: 0
- Add constraint: Trailing space to Content View Trailing Margin, value: 0
- Add constraint: Top space to Subject Label Bottom, value: Standard
- Étiquette de durée
- Police : System 12.0
- Couleur : gris foncé
- Ajouter une contrainte : Hauteur, valeur : 15
- Add constraint: Leading space to Content View Leading Margin, value: 0
- Add constraint: Trailing space to Content View Trailing Margin, value: 0
- Add constraint: Top space to Organizer Label Bottom, value: Standard
- Ajouter une contrainte : espace inférieur à la marge inférieure de l’affichage de contenu, valeur : 0
- Étiquette d’objet
Sélectionnez EventCell, puis l’inspecteur de taille. Activer automatique pour la hauteur de ligne.
Ouvrez CalendarViewController.h et supprimez la
calendarJSON
propriété.Ouvrez CalendarViewController.m et remplacez son contenu par le code suivant.
#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
Exécutez l’application, connectez-vous et appuyez sur l’onglet Calendrier. Vous devriez voir la liste des événements.
Créer un événement
Dans cette section, vous allez ajouter la possibilité de créer des événements sur le calendrier de l’utilisateur.
Ouvrez GraphManager.h et ajoutez le code suivant au-dessus de la
@interface
déclaration.typedef void (^CreateEventCompletionBlock)(MSGraphEvent* _Nullable event, NSError* _Nullable error);
Ajoutez le code suivant à la
@interface
déclaration.- (void) createEventWithSubject: (NSString*) subject andStart: (NSDate*) start andEnd: (NSDate*) end andAttendees: (NSArray<NSString*>* _Nullable) attendees andBody: (NSString* _Nullable) body andCompletionBlock: (CreateEventCompletionBlock) completion;
Ouvrez GraphManager.m et ajoutez la fonction suivante pour créer un événement sur le calendrier de l’utilisateur.
- (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]; }
Créez un fichier de classe Cocoa Touch dans le dossier GraphTutorial nommé
NewEventViewController
. Choisissez UIViewController dans la sous-classe du champ.Ouvrez NewEventViewController.h et ajoutez le code suivant à l’intérieur de la
@interface
déclaration.@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;
Ouvrez NewEventController.m et remplacez son contenu par le code suivant.
#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
Ouvrez Main.storyboard. Utilisez la bibliothèque pour faire glisser un contrôleur de vue vers la storyboard.
À l’aide de la bibliothèque, ajoutez une barre de navigation au contrôleur de vue.
Double-cliquez sur le titre dans la barre de navigation et mettez-le à
New Event
jour.À l’aide de la bibliothèque, ajoutez un élément de bouton de barre sur le côté gauche de la barre de navigation.
Sélectionnez le nouveau bouton de barre, puis l’Inspecteur d’attributs. Change title to
Cancel
.À l’aide de la bibliothèque, ajoutez un élément de bouton de barre à droite de la barre de navigation.
Sélectionnez le nouveau bouton de barre, puis l’Inspecteur d’attributs. Change title to
Create
.Sélectionnez le contrôleur d’affichage, puis l’inspecteur d’identité. Modifiez la classe en NewEventViewController.
Ajoutez les contrôles suivants de la bibliothèque à l’affichage.
- Ajoutez une étiquette sous la barre de navigation. Définissez son texte sur
Subject
. - Ajoutez un champ de texte sous l’étiquette. Définissez son attribut d’espace réservé sur
Subject
. - Ajoutez une étiquette sous le champ de texte. Définissez son texte sur
Attendees
. - Ajoutez un champ de texte sous l’étiquette. Définissez son attribut d’espace réservé sur
Separate multiple entries with ;
. - Ajoutez une étiquette sous le champ de texte. Définissez son texte sur
Start
. - Ajoutez un s sélectionneur de dates sous l’étiquette. Définissez son style préféré sur Compact, son intervalle à 15 minutes et sa hauteur sur 35.
- Ajoutez une étiquette sous le s sélectionneur de dates. Définissez son texte sur
End
. - Ajoutez un s sélectionneur de dates sous l’étiquette. Définissez son style préféré sur Compact, son intervalle à 15 minutes et sa hauteur sur 35.
- Ajoutez un affichage texte sous le s sélectionneur de dates.
- Ajoutez une étiquette sous la barre de navigation. Définissez son texte sur
Sélectionnez le nouveau contrôleur d’affichage d’événements et utilisez l’Inspecteur de connexion pour établir les connexions suivantes.
- Connecter l’action d’annulation reçue sur le bouton de la barre Annuler.
- Connecter’action createEvent reçue sur le bouton Créer une barre.
- Connecter la sortie de l’objet vers le premier champ de texte.
- Connecter sortie des participants vers le deuxième champ de texte.
- Connecter la sortie de début au premier s picker de date.
- Connecter sortie de fin au deuxième s picker de date.
- Connecter la sortie du corps vers l’affichage texte.
Ajoutez les contraintes suivantes.
- Barre de navigation
- Espace de Coffre zone, valeur : 0
- Espace de fin vers Coffre zone, valeur : 0
- Espace supérieur pour Coffre zone, valeur : 0
- Hauteur, valeur : 44
- Étiquette d’objet
- Espace de tête vers la marge d’affichage, valeur : 0
- Espace à la fin de la marge d’affichage, valeur : 0
- Espace supérieur de la barre de navigation, valeur : 20
- Champ de texte de l’objet
- Espace de tête vers la marge d’affichage, valeur : 0
- Espace à la fin de la marge d’affichage, valeur : 0
- Espace supérieur à l’étiquette de l’objet, valeur : Standard
- Étiquette Attendees
- Espace de tête vers la marge d’affichage, valeur : 0
- Espace à la fin de la marge d’affichage, valeur : 0
- Espace supérieur au champ texte de l’objet, valeur : Standard
- Champ de texte Attendees
- Espace de tête vers la marge d’affichage, valeur : 0
- Espace à la fin de la marge d’affichage, valeur : 0
- Espace supérieur à l’étiquette Attendees, valeur : Standard
- Start Label
- Espace de tête vers la marge d’affichage, valeur : 0
- Espace à la fin de la marge d’affichage, valeur : 0
- Espace supérieur au champ texte de l’objet, valeur : Standard
- Socheur de date de début
- Espace de tête vers la marge d’affichage, valeur : 0
- Espace à la fin de la marge d’affichage, valeur : 0
- Espace supérieur à l’étiquette Attendees, valeur : Standard
- Hauteur, valeur : 35
- End Label
- Espace de tête vers la marge d’affichage, valeur : 0
- Espace à la fin de la marge d’affichage, valeur : 0
- Espace supérieur au s picker de date de début, valeur : Standard
- Socheur de date de fin
- Espace de tête vers la marge d’affichage, valeur : 0
- Espace à la fin de la marge d’affichage, valeur : 0
- Espace supérieur vers l’étiquette de fin, valeur : Standard
- Hauteur : 35
- Affichage corps de texte
- Espace de tête vers la marge d’affichage, valeur : 0
- Espace à la fin de la marge d’affichage, valeur : 0
- Espace supérieur au s picker de date de fin, valeur : Standard
- Espace inférieur pour afficher la marge, valeur : 0
- Barre de navigation
Sélectionnez la scène de calendrier, puis l’inspecteur de connexions.
Sous Segues déclenchés, faites glisser le cercle non rempli en regard du manuel sur le nouveau contrôleur d’affichage d’événements dans le storyboard. Sélectionnez Présenter en mode mod dans le menu déroulant.
Sélectionnez le segue que vous avez ajouté, puis sélectionnez l’inspecteur d’attributs. Définissez le champ identificateur sur
showEventForm
.Connecter’action showNewEventForm reçue sur le bouton de + barre de navigation.
Enregistrez vos modifications, puis redémarrez l’application. Go to the calendar page and tap the + button. Remplissez le formulaire et appuyez sur Créer pour créer un événement.
Félicitations !
Vous avez terminé le didacticiel microsoft iOS Objective-C Graph. Maintenant que vous disposez d’une application de travail qui appelle Microsoft Graph, vous pouvez expérimenter et ajouter de nouvelles fonctionnalités. Consultez la vue d’ensemble de Microsoft Graph pour voir toutes les données accessibles avec Microsoft Graph.
Commentaires
N’hésitez pas à nous faire part de vos commentaires sur ce didacticiel dans GitHub référentiel.
Vous avez un problème avec cette section ? Si c'est le cas, faites-nous part de vos commentaires pour que nous puissions l'améliorer.