Erstellen von iOS Objective-C-Apps mit Microsoft Graph
In diesem Lernprogramm erfahren Sie, wie Sie eine React Native-App erstellen, die die Microsoft Graph-API zum Abrufen von Kalenderinformationen für einen Benutzer verwendet.
Tipp
Wenn Sie es vorziehen, nur das abgeschlossene Lernprogramm herunterzuladen, können Sie das GitHub Repository herunterladen oder klonen.
Voraussetzungen
Bevor Sie mit diesem Lernprogramm beginnen, sollten Sie Folgendes auf Ihrem Entwicklungscomputer installiert haben.
Sie sollten auch über ein persönliches Microsoft-Konto mit einem Postfach auf Outlook.com oder ein Microsoft-Geschäfts-, Schul- oder Unikonto verfügen. Wenn Sie kein Microsoft-Konto haben, gibt es einige Optionen, um ein kostenloses Konto zu erhalten:
- Sie können sich für ein neues persönliches Microsoft-Konto registrieren.
- Sie können sich für das Microsoft 365-Entwicklerprogramm registrieren, um ein kostenloses Microsoft 365 Abonnement zu erhalten.
Hinweis
Dieses Lernprogramm wurde mit Xcode Version 12.3 und CocoaPods Version 1.10.1 geschrieben. Die Schritte in diesem Handbuch funktionieren möglicherweise mit anderen Versionen, die jedoch nicht getestet wurden.
Feedback
Bitte geben Sie Feedback zu diesem Lernprogramm im GitHub Repository.
Erstellen einer iOS Objective-C-App
Erstellen Sie zunächst ein neues Objective-C-Projekt.
Öffnen Sie Xcode. Wählen Sie im Menü "Datei" die Option "Neu" und dann Project aus.
Wählen Sie die App-Vorlage aus, und wählen Sie "Weiter" aus.
Legen Sie den Produktnamen
GraphTutorial
und die Sprache auf Objective-C fest.Füllen Sie die verbleibenden Felder aus, und wählen Sie "Weiter" aus.
Wählen Sie einen Speicherort für das Projekt aus, und wählen Sie "Erstellen" aus.
Installieren von Abhängigkeiten
Bevor Sie fortfahren, installieren Sie einige zusätzliche Abhängigkeiten, die Sie später verwenden werden.
- Microsoft Authentication Library (MSAL) für iOS für die Authentifizierung bei Azure AD.
- Microsoft Graph SDK für Objective C für Anrufe an Microsoft Graph.
- Microsoft Graph Models SDK für Objective C für stark typisierte Objekte, die Microsoft Graph Ressourcen wie Benutzer oder Ereignisse darstellen.
Beenden Sie Xcode.
Öffnen Sie Terminal, und ändern Sie das Verzeichnis an den Speicherort Ihres GraphTutorial-Projekts.
Führen Sie den folgenden Befehl aus, um eine Podfile-Datei zu erstellen.
pod init
Öffnen Sie die Podfile-Datei, und fügen Sie die folgenden Zeilen direkt hinter der
use_frameworks!
Zeile hinzu.pod 'MSAL', '~> 1.1.13' pod 'MSGraphClientSDK', ' ~> 1.0.0' pod 'MSGraphClientModels', '~> 1.3.0'
Speichern Sie die Podfile-Datei, und führen Sie dann den folgenden Befehl aus, um die Abhängigkeiten zu installieren.
pod install
Öffnen Sie nach Abschluss des Befehls den neu erstellten GraphTutorial.xcworkspace in Xcode.
Entwerfen der App
In diesem Abschnitt erstellen Sie die Ansichten für die App: eine Anmeldeseite, einen Navigator auf der Registerkartenleiste, eine Willkommensseite und eine Kalenderseite. Außerdem erstellen Sie eine Aktivitätsindikatorüberlagerung.
Anmeldeseite erstellen
Erweitern Sie den Ordner "GraphTutorial" in Xcode, und wählen Sie dann die Datei "ViewController.m" aus.
Ändern Sie im Dateiinspektor den Namen der Datei in
SignInViewController.m
.Öffnen Sie SignInViewController.m, und ersetzen Sie den Inhalt durch den folgenden Code.
#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
Wählen Sie die Datei "ViewController.h" aus.
Ändern Sie im Dateiinspektor den Namen der Datei in
SignInViewController.h
.Öffnen Sie SignInViewController.h, und ändern Sie alle Instanzen von
ViewController
inSignInViewController
.Öffnen Sie die Datei "Main.storyboard".
Erweitern Sie die Ansichtscontroller-Szene, und wählen Sie dann Ansichtscontroller aus.
Wählen Sie den Identitätsinspektor aus, und ändern Sie dann die Dropdownliste "Klasse" in "SignInViewController".
Wählen Sie die Bibliothek aus, und ziehen Sie dann eine Schaltfläche auf den Anmeldeansichtscontroller.
Wenn die Schaltfläche ausgewählt ist, wählen Sie den Attributes Inspector aus, und ändern Sie den Titel der Schaltfläche in
Sign In
.Wenn die Schaltfläche ausgewählt ist, wählen Sie die Schaltfläche "Ausrichten" am unteren Rand des Storyboards aus. Wählen Sie horizontal im Container und vertikal in Containereinschränkungen aus, lassen Sie deren Werte 0, und wählen Sie dann Add 2 constraints aus.
Wählen Sie den Controller für die Anmeldeansicht aus, und wählen Sie dann den Connections Inspector aus.
Ziehen Sie unter "Empfangene Aktionen" den ungefüllten Kreis neben "SignIn" auf die Schaltfläche. Klicken Sie im Popupmenü auf "Innen berühren".
Registerkartenleiste erstellen
Wählen Sie die Bibliothek aus, und ziehen Sie dann einen Tableistencontroller in das Storyboard.
Wählen Sie den Controller für die Anmeldeansicht aus, und wählen Sie dann den Connections Inspector aus.
Ziehen Sie unter "Ausgelöste Segues" den ungefüllten Kreis neben manuell auf den Tab Bar Controller im Storyboard. Wählen Sie "Modal präsentieren" im Popupmenü aus.
Wählen Sie die Segue aus, die Sie soeben hinzugefügt haben, und wählen Sie dann den Attributes Inspector aus. Legen Sie das Feld "Bezeichner" auf
userSignedIn
und legen Sie "Präsentation" auf "Vollbild" fest.Wählen Sie die Szene "Element 1" aus, und wählen Sie dann den Connections Inspector aus.
Ziehen Sie unter "Ausgelöste Segues" den ungefüllten Kreis neben manuell auf den Controller für die Anmeldeansicht im Storyboard. Wählen Sie "Modal präsentieren" im Popupmenü aus.
Wählen Sie die Segue aus, die Sie soeben hinzugefügt haben, und wählen Sie dann den Attributes Inspector aus. Legen Sie das Feld "Bezeichner" auf
userSignedOut
und legen Sie "Präsentation" auf "Vollbild" fest.
Willkommensseite erstellen
Wählen Sie die Datei Assets.xcassets aus.
Wählen Sie im Menü "Editor" die Option "Neues Objekt hinzufügen" und dann "Bildsatz" aus.
Wählen Sie die neue Image-Ressource aus, und verwenden Sie den Attributinspektor, um den Namen auf
DefaultUserPhoto
festzulegen.Fügen Sie ein beliebiges Bild hinzu, das als Standardmäßiges Benutzerprofilfoto dienen soll.
Erstellen Sie eine neue Cocoa Touch Class-Datei im Ordner "GraphTutorial" mit dem Namen
WelcomeViewController
. Wählen Sie "UIViewController" in der Unterklasse des Felds aus.Öffnen Sie WelcomeViewController.h, und fügen Sie den folgenden Code innerhalb der
@interface
Deklaration hinzu.@property (nonatomic) IBOutlet UIImageView *userProfilePhoto; @property (nonatomic) IBOutlet UILabel *userDisplayName; @property (nonatomic) IBOutlet UILabel *userEmail;
Öffnen Sie WelcomeViewController.m, und ersetzen Sie den Inhalt durch den folgenden Code.
#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
Öffnen Sie Main.storyboard. Wählen Sie die Szene "Element 1" aus, und wählen Sie dann den Identitätsinspektor aus. Ändern Sie den Klassenwert in WelcomeViewController.
Fügen Sie mithilfe der Bibliothek der Szene "Element 1" die folgenden Elemente hinzu.
- One Image View
- Zwei Bezeichnungen
- One Button
Stellen Sie mithilfe des Connections Inspector die folgenden Verbindungen.
- Verknüpfen Sie das UserDisplayName-Outlet mit der ersten Bezeichnung.
- Verknüpfen Sie das userEmail-Outlet mit der zweiten Bezeichnung.
- Verknüpfen Sie das UserProfilePhoto-Outlet mit der Bildansicht.
- Verknüpfen Sie die empfangene SignOut-Aktion mit der Touch-Up-Aktion der Schaltfläche.
Wählen Sie die Bildansicht und dann den Größeninspektor aus.
Legen Sie die Breite und Höhe auf 196 fest.
Verwenden Sie die Schaltfläche "Ausrichten", um die Containereinschränkung horizontal mit dem Wert 0 hinzuzufügen.
Verwenden Sie die Schaltfläche "Neue Einschränkungen hinzufügen" (neben der Schaltfläche "Ausrichten"), um die folgenden Einschränkungen hinzuzufügen:
- Oben ausrichten an: Tresor Bereich, Wert: 0
- Unteres Leerzeichen bis: Benutzeranzeigename, Wert: Standard
- Höhe, Wert: 196
- Breite, Wert: 196
Wählen Sie die erste Beschriftung aus, und verwenden Sie dann die Schaltfläche "Ausrichten", um die Containereinschränkung horizontal mit dem Wert 0 hinzuzufügen.
Verwenden Sie die Schaltfläche "Neue Einschränkungen hinzufügen", um die folgenden Einschränkungen hinzuzufügen:
- Top Space to: User Profile Photo, value: Standard
- Unterer Speicherplatz bis: Benutzer-E-Mail, Wert: Standard
Wählen Sie die zweite Beschriftung aus, und wählen Sie dann den Attributes Inspector aus.
Ändern Sie die Farbe in Dunkelgrau, und ändern Sie die Schriftart in System 12.0.
Verwenden Sie die Schaltfläche "Ausrichten", um die Containereinschränkung horizontal mit dem Wert 0 hinzuzufügen.
Verwenden Sie die Schaltfläche "Neue Einschränkungen hinzufügen", um die folgenden Einschränkungen hinzuzufügen:
- Top Space to: User Display Name, value: Standard
- Unterer Abstand zu: Abmelden, Wert: 14
Wählen Sie die Schaltfläche aus, und wählen Sie dann den Attributes Inspector aus.
Ändern Sie den Titel in
Sign Out
.Verwenden Sie die Schaltfläche "Ausrichten", um die Containereinschränkung horizontal mit dem Wert 0 hinzuzufügen.
Verwenden Sie die Schaltfläche "Neue Einschränkungen hinzufügen", um die folgenden Einschränkungen hinzuzufügen:
- Top Space to: User Email, value: 14
Wählen Sie das Registerkartenleistenelement am unteren Rand der Szene aus, und wählen Sie dann das Attributes Inspector aus. Ändern Sie den Titel in
Me
.Wählen Sie im Menü "Editor" die Option "Probleme mit dem automatischen Layout beheben" und dann "Fehlende Einschränkungen hinzufügen" unter "Alle Ansichten" im Willkommensansichtscontroller aus.
Die Willkommens-Szene sollte etwa wie folgt aussehen, sobald Sie fertig sind.
Kalenderseite erstellen
Erstellen Sie eine neue Cocoa Touch Class-Datei im Ordner "GraphTutorial" mit dem Namen
CalendarViewController
. Wählen Sie "UIViewController" in der Unterklasse des Felds aus.Öffnen Sie CalendarViewController.h, und fügen Sie den folgenden Code innerhalb der
@interface
Deklaration hinzu.@property (nonatomic) IBOutlet UITextView *calendarJSON;
Öffnen Sie CalendarViewController.m, und ersetzen Sie den Inhalt durch den folgenden Code.
#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
Öffnen Sie Main.storyboard. Wählen Sie die Szene "Element 2" aus, und wählen Sie dann den Identitätsinspektor aus. Ändern Sie den Klassenwert in CalendarViewController.
Fügen Sie mithilfe der Bibliothek der Szene "Element 2" eine Textansicht hinzu.
Wählen Sie die Textansicht aus, die Sie soeben hinzugefügt haben. Klicken Sie im Editor auf "Einbetten" und dann auf "Bildlaufansicht".
Verbinden Sie mit dem Connections Inspector die calendarJSON-Outlet mit der Textansicht.
Wählen Sie das Registerkartenleistenelement am unteren Rand der Szene aus, und wählen Sie dann das Attributes Inspector aus. Ändern Sie den Titel in
Calendar
.Wählen Sie im Menü "Editor" die Option "Probleme mit dem automatischen Layout beheben" und dann "Fehlende Einschränkungen hinzufügen" unter "Alle Ansichten" im Willkommensansichtscontroller aus.
Die Kalender-Szene sollte etwa wie folgt aussehen, sobald Sie fertig sind.
Aktivitätsindikator erstellen
Erstellen Sie eine neue Cocoa Touch Class-Datei im Ordner "GraphTutorial" mit dem Namen
SpinnerViewController
. Wählen Sie "UIViewController" in der Unterklasse des Felds aus.Öffnen Sie SpinnerViewController.h, und fügen Sie den folgenden Code innerhalb der
@interface
Deklaration hinzu.- (void) startWithContainer:(UIViewController*) container; - (void) stop;
Öffnen Sie SpinnerViewController.m, und ersetzen Sie den Inhalt durch den folgenden Code.
#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
Testen der App
Speichern Sie Ihre Änderungen, und starten Sie die App. Sie sollten zwischen den Bildschirmen mithilfe der Schaltflächen "Anmelden" und "Abmelden" und der Registerkartenleiste wechseln können.
Registrieren der App im Portal
In dieser Übung erstellen Sie eine neue systemeigene Azure AD-Anwendung mithilfe des Azure Active Directory Admin Centers.
Öffnen Sie einen Browser, und navigieren Sie zum Azure Active Directory Admin Center. Melden Sie sich mit einem persönlichen Konto (auch: Microsoft-Konto) oder einem Geschäfts- oder Schulkonto an.
Wählen Sie in der linken Navigationsleiste Azure Active Directory aus, und wählen Sie dann App-Registrierungen unter Verwalten aus.
Wählen Sie Neue Registrierung aus. Legen Sie auf der Seite Anwendung registrieren die Werte wie folgt fest.
- Legen Sie Name auf
iOS Objective-C Graph Tutorial
fest. - Legen Sie Unterstützte Kontotypen auf Konten in allen Organisationsverzeichnissen und persönliche Microsoft-Konten fest.
- Lassen Sie URI umleiten leer.
- Legen Sie Name auf
Wählen Sie Registrieren aus. Kopieren Sie auf der Seite "iOS Objective-C Graph Tutorial" den Wert der Anwendungs-ID (Client-ID), und speichern Sie ihn. Sie benötigen ihn im nächsten Schritt.
Wählen Sie unter Verwalten die Option Authentifizierung aus. Wählen Sie "Plattform hinzufügen" und dann "iOS/macOS" aus.
Geben Sie die Bundle-ID Ihrer App ein, und wählen Sie "Konfigurieren" und dann "Fertig" aus.
Hinzufügen der Azure AD-Authentifizierung
In dieser Übung erweitern Sie die Anwendung aus der vorherigen Übung, um die Authentifizierung mit Azure AD zu unterstützen. Dies ist erforderlich, um das erforderliche OAuth-Zugriffstoken zum Aufrufen der Microsoft Graph abzurufen. Zu diesem Zweck integrieren Sie die Microsoft-Authentifizierungsbibliothek (MSAL) für iOS in die Anwendung.
Erstellen Sie eine neue Eigenschaftslistendatei im GraphTutorial-Projekt mit dem Namen AuthSettings.plist.
Fügen Sie der Datei im Stammverzeichnis die folgenden Elemente hinzu.
Key Typ Wert AppId
String Die Anwendungs-ID aus dem Azure-Portal GraphScopes
Array Drei Zeichenfolgenwerte: User.Read
MailboxSettings.Read
, undCalendars.ReadWrite
Wichtig
Wenn Sie die Quellcodeverwaltung wie Git verwenden, wäre jetzt ein guter Zeitpunkt, um die Datei "AuthSettings.plist" aus der Quellcodeverwaltung auszuschließen, um zu vermeiden, dass versehentlich Ihre App-ID offengelegt wird.
Implementieren der Anmeldung
In diesem Abschnitt konfigurieren Sie das Projekt für MSAL, erstellen eine Authentifizierungs-Manager-Klasse und aktualisieren die App so, dass sie sich anmeldet und abmeldet.
Konfigurieren des Projekts für MSAL
Fügen Sie den Funktionen Ihres Projekts eine neue Schlüsselbundgruppe hinzu.
- Wählen Sie das GraphTutorial-Projekt aus, und signieren Sie dann & Funktionen.
- Wählen Sie +Funktion aus, und doppelklicken Sie dann auf "Schlüsselbundfreigabe".
- Fügen Sie eine Schlüsselbundgruppe mit dem Wert
com.microsoft.adalcache
hinzu.
Steuerelement klicken Sie auf "Info.plist", und wählen Sie "Öffnen unter" und dann "Quellcode" aus.
Fügen Sie Folgendes in das
<dict>
Element ein.<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>
Öffnen Sie "AppDelegate.m", und fügen Sie die folgende Import-Anweisung am Anfang der Datei hinzu.
#import <MSAL/MSAL.h>
Fügen Sie die folgende Funktion zur
AppDelegate
-Klasse hinzu:- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options { return [MSALPublicClientApplication handleMSALResponse:url sourceApplication:options[UIApplicationOpenURLOptionsSourceApplicationKey]]; }
Erstellen des Authentifizierungs-Managers
Erstellen Sie eine neue Cocoa Touch-Klasse im GraphTutorial-Projekt mit dem Namen AuthenticationManager. Wählen Sie in der Unterklasse des Felds NSObject aus.
Öffnen Sie AuthenticationManager.h, und ersetzen Sie den Inhalt durch den folgenden Code.
#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
Öffnen Sie AuthenticationManager.m, und ersetzen Sie den Inhalt durch den folgenden Code.
#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
Hinzufügen von Anmeldung und Abmelden
Öffnen Sie die Datei SignInViewController.m, und ersetzen Sie den Inhalt durch den folgenden Code.
#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
Öffnen Sie WelcomeViewController.m, und fügen Sie die folgende
import
Anweisung am Anfang der Datei hinzu.#import "AuthenticationManager.h"
Ersetzen Sie die vorhandene
signOut
-Funktion durch Folgendes.- (IBAction)signOut { [AuthenticationManager.instance signOut]; [self performSegueWithIdentifier: @"userSignedOut" sender: nil]; }
Speichern Sie Ihre Änderungen, und starten Sie die Anwendung in Simulator neu.
Wenn Sie sich bei der App anmelden, sollte ein Zugriffstoken im Ausgabefenster in Xcode angezeigt werden.
Benutzerdetails abrufen
In diesem Abschnitt erstellen Sie eine Hilfsklasse, die alle Aufrufe von Microsoft Graph enthält, und aktualisieren die Klasse WelcomeViewController
so, dass diese neue Klasse verwendet wird, um den angemeldeten Benutzer abzurufen.
Erstellen Sie eine neue Cocoa Touch-Klasse im GraphTutorial-Projekt mit dem Namen GraphManager. Wählen Sie in der Unterklasse des Felds NSObject aus.
Öffnen Sie GraphManager.h, und ersetzen Sie den Inhalt durch den folgenden Code.
#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
Öffnen Sie GraphManager.m, und ersetzen Sie den Inhalt durch den folgenden Code.
#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
Öffnen Sie WelcomeViewController.m, und fügen Sie die folgenden
#import
Anweisungen am Anfang der Datei hinzu.#import "SpinnerViewController.h" #import "GraphManager.h" #import <MSGraphClientModels/MSGraphClientModels.h>
Fügen Sie der Schnittstellendeklaration die folgende Eigenschaft
WelcomeViewController
hinzu.@property SpinnerViewController* spinner;
Ersetzen Sie den vorhandenen
viewDidLoad
durch den folgenden Code.- (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")]; }); }]; }
Wenn Sie Ihre Änderungen speichern und die App jetzt neu starten, wird die Benutzeroberfläche nach der Anmeldung mit dem Anzeigenamen und der E-Mail-Adresse des Benutzers aktualisiert.
Kalenderansicht erhalten
In dieser Übung integrieren Sie die Microsoft Graph in die Anwendung. Für diese Anwendung verwenden Sie das Microsoft Graph SDK für Objective C, um Aufrufe an Microsoft Graph zu tätigen.
Abrufen von Kalenderereignissen von Outlook
In diesem Abschnitt erweitern Sie die GraphManager
Klasse, um eine Funktion hinzuzufügen, um die Ereignisse des Benutzers für die aktuelle Woche abzurufen und CalendarViewController
diese neue Funktion zu verwenden.
Öffnen Sie GraphManager.h, und fügen Sie den folgenden Code über der
@interface
Deklaration hinzu.typedef void (^GetCalendarViewCompletionBlock)(NSData* _Nullable data, NSError* _Nullable error);
Fügen Sie der Deklaration den folgenden Code
@interface
hinzu.- (void) getCalendarViewStartingAt: (NSString*) viewStart endingAt: (NSString*) viewEnd withCompletionBlock: (GetCalendarViewCompletionBlock) completion;
Öffnen Sie GraphManager.m, und fügen Sie der Klasse die folgende Funktion
GraphManager
hinzu.- (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]; }
Hinweis
Überlegen Sie, was der Code
getCalendarViewStartingAt
macht.- Die URL, die aufgerufen wird, lautet
/v1.0/me/calendarview
.- Die
startDateTime
Parameter undendDateTime
Abfrageparameter definieren den Anfang und das Ende der Kalenderansicht. - Der
select
Abfrageparameter beschränkt die für jedes Ereignis zurückgegebenen Felder auf die Felder, die tatsächlich von der Ansicht verwendet werden. - Der
orderby
Abfrageparameter sortiert die Ergebnisse nach Startzeit. - Der
top
Abfrageparameter fordert 25 Ergebnisse pro Seite an. - Der
Prefer: outlook.timezone
Header bewirkt, dass die Microsoft Graph die Start- und Endzeiten jedes Ereignisses in der Zeitzone des Benutzers zurückgibt.
- Die
- Die URL, die aufgerufen wird, lautet
Erstellen Sie eine neue Cocoa Touch-Klasse im GraphTutorial-Projekt mit dem Namen GraphToIana. Wählen Sie in der Unterklasse des Felds NSObject aus.
Öffnen Sie GraphToIana.h, und ersetzen Sie den Inhalt durch den folgenden Code.
#import <Foundation/Foundation.h> NS_ASSUME_NONNULL_BEGIN @interface GraphToIana : NSObject + (NSString*) getIanaIdentifierFromGraphIdentifier: (NSString *) graphIdentifier; @end NS_ASSUME_NONNULL_END
Öffnen Sie GraphToIana.m, und ersetzen Sie den Inhalt durch den folgenden Code.
#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
Dadurch wird ein einfacher Nachschlagevorgang ausgeführt, um einen IANA-Zeitzonenbezeichner basierend auf dem von Microsoft Graph zurückgegebenen Zeitzonennamen zu finden.
Öffnen Sie CalendarViewController.m, und ersetzen Sie den gesamten Inhalt durch den folgenden Code.
#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
Führen Sie die App aus, melden Sie sich an, und tippen Sie im Menü auf das Navigationselement "Kalender". Es sollte ein JSON-Dump der Ereignisse in der App angezeigt werden.
Anzeigen der Ergebnisse
Jetzt können Sie das JSON-Dump durch etwas ersetzen, um die Ergebnisse auf benutzerfreundliche Weise anzuzeigen. In diesem Abschnitt ändern Sie die Funktion so, dass getCalendarViewStartingAt
stark typisierte Objekte zurückgegeben werden, und ändern, CalendarViewController
um die Ereignisse mithilfe einer Tabellenansicht zu rendern.
getCalendarViewStartingAt aktualisieren
Öffnen Sie GraphManager.h. Ändern Sie die
GetCalendarViewCompletionBlock
Typdefinition wie folgt.typedef void (^GetCalendarViewCompletionBlock)(NSArray<MSGraphEvent*>* _Nullable events, NSError* _Nullable error);
Öffnen Sie GraphManager.m. Ersetzen Sie die vorhandene
getCalendarViewStartingAt
-Funktion durch den folgenden Code.- (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]; }
CalendarViewController aktualisieren
Erstellen Sie eine neue Cocoa Touch Class-Datei im GraphTutorial-Projekt mit dem Namen
CalendarTableViewCell
. Wählen Sie UITableViewCell in der Unterklasse des Felds aus.Öffnen Sie CalendarTableViewCell.h, und ersetzen Sie den Inhalt durch den folgenden Code.
#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
Öffnen Sie CalendarTableViewCell.m, und ersetzen Sie den Inhalt durch den folgenden Code.
#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
Erstellen Sie eine neue Cocoa Touch Class-Datei im GraphTutorial-Projekt mit dem Namen
CalendarTableViewController
. Wählen Sie "UITableViewController" in der Unterklasse des Felds aus.Öffnen Sie CalendarTableViewController.h, und ersetzen Sie den Inhalt durch den folgenden Code.
#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
Öffnen Sie CalendarTableViewController.m, und ersetzen Sie den Inhalt durch den folgenden Code.
#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
Öffnen Sie "Main.storyboard", und suchen Sie die Kalender-Szene. Löschen Sie die Bildlaufansicht aus der Stammansicht.
Fügen Sie mithilfe der Bibliothek oben in der Ansicht eine Navigationsleiste hinzu.
Doppelklicken Sie auf den Titel in der Navigationsleiste, und aktualisieren Sie ihn auf
Calendar
.Fügen Sie mithilfe der Bibliothek ein Balkenschaltflächenelement auf der rechten Seite der Navigationsleiste hinzu.
Wählen Sie die neue Schaltfläche "Leiste" und dann "Attributes Inspector" aus. Ändern Sie das Bild in plus.
Fügen Sie der Ansicht unter der Navigationsleiste eine Containeransicht aus der Bibliothek hinzu. Ändern Sie die Größe der Containeransicht, um den gesamten verbleibenden Platz in der Ansicht zu übernehmen.
Legen Sie Einschränkungen für die Navigationsleiste und die Containeransicht wie folgt fest.
- Navigationsleiste
- Einschränkung hinzufügen: Höhe, Wert: 44
- Einschränkung hinzufügen: Führendes Leerzeichen zu Tresor Bereich, Wert: 0
- Einschränkung hinzufügen: Nachgestellter Abstand zu Tresor Bereich, Wert: 0
- Einschränkung hinzufügen: Oberer Abstand zu Tresor Bereich, Wert: 0
- Containeransicht
- Einschränkung hinzufügen: Führendes Leerzeichen zu Tresor Bereich, Wert: 0
- Einschränkung hinzufügen: Nachgestellter Abstand zu Tresor Bereich, Wert: 0
- Einschränkung hinzufügen: Oberer Platz in Navigationsleiste unten, Wert: 0
- Add constraint: Bottom space to Tresor Area, value: 0
- Navigationsleiste
Suchen Sie den zweiten Ansichtscontroller, der dem Storyboard hinzugefügt wurde, wenn Sie die Containeransicht hinzugefügt haben. Sie ist über eine Einbettungs-Segue mit der Kalender-Szene verbunden. Wählen Sie diesen Controller aus, und verwenden Sie den Identitätsinspektor, um die Klasse in CalendarTableViewController zu ändern.
Löschen Sie die Ansicht aus dem Kalendertabellen-Ansichtscontroller.
Fügen Sie dem Kalendertabellenansichtscontroller eine Tabellenansicht aus der Bibliothek hinzu.
Wählen Sie die Tabellenansicht aus, und wählen Sie dann den Attributes Inspector aus. Legen Sie Prototypzellen auf 1 fest.
Ziehen Sie den unteren Rand der Prototypzelle, um ihnen einen größeren Bereich für die Arbeit zu geben.
Verwenden Sie die Bibliothek, um der Prototypzelle drei Bezeichnungen hinzuzufügen.
Wählen Sie die Prototypzelle aus, und wählen Sie dann den Identitätsinspektor aus. Ändern Sie die Klasse in CalendarTableViewCell.
Wählen Sie den Attributes Inspector aus, und legen Sie den Bezeichner auf
EventCell
fest.Wählen Sie bei ausgewählter EventCell das Connections Inspector und connect sowie die Beschriftungen aus,
durationLabel
die Sie der Zelle imorganizerLabel
subjectLabel
Storyboard hinzugefügt haben.Legen Sie die Eigenschaften und Einschränkungen für die drei Beschriftungen wie folgt fest.
- Betreffbezeichnung
- Einschränkung hinzufügen: Führendes Leerzeichen zum führenden Rand der Inhaltsansicht, Wert: 0
- Einschränkung hinzufügen: Nachgestellter Speicherplatz zum nachgestellten Rand der Inhaltsansicht, Wert: 0
- Einschränkung hinzufügen: Oberer Abstand zum oberen Rand der Inhaltsansicht, Wert: 0
- Organisatorbezeichnung
- Schriftart: System 12.0
- Einschränkung hinzufügen: Höhe, Wert: 15
- Einschränkung hinzufügen: Führendes Leerzeichen zum führenden Rand der Inhaltsansicht, Wert: 0
- Einschränkung hinzufügen: Nachgestellter Speicherplatz zum nachgestellten Rand der Inhaltsansicht, Wert: 0
- Einschränkung hinzufügen: Oberer Platz zu Betreffbezeichnung unten, Wert: Standard
- Duration-Bezeichnung
- Schriftart: System 12.0
- Farbe: Dunkelgrau
- Einschränkung hinzufügen: Höhe, Wert: 15
- Einschränkung hinzufügen: Führendes Leerzeichen zum führenden Rand der Inhaltsansicht, Wert: 0
- Einschränkung hinzufügen: Nachgestellter Speicherplatz zum nachgestellten Rand der Inhaltsansicht, Wert: 0
- Einschränkung hinzufügen: Oberster Platz für Organisatorbezeichnung unten, Wert: Standard
- Einschränkung hinzufügen: Unterer Platz in der Inhaltsansicht am unteren Rand, Wert: 0
- Betreffbezeichnung
Wählen Sie die EventCell aus, und wählen Sie dann den Größeninspektor aus. Aktivieren Sie "Automatisch für Zeilenhöhe".
Öffnen Sie CalendarViewController.h, und entfernen Sie die
calendarJSON
Eigenschaft.Öffnen Sie CalendarViewController.m, und ersetzen Sie den Inhalt durch den folgenden Code.
#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
Führen Sie die App aus, melden Sie sich an, und tippen Sie auf die Registerkarte "Kalender". Die Liste der Ereignisse sollte angezeigt werden.
Erstellen eines neuen Ereignisses
In diesem Abschnitt fügen Sie die Möglichkeit hinzu, Ereignisse im Kalender des Benutzers zu erstellen.
Öffnen Sie GraphManager.h, und fügen Sie den folgenden Code über der
@interface
Deklaration hinzu.typedef void (^CreateEventCompletionBlock)(MSGraphEvent* _Nullable event, NSError* _Nullable error);
Fügen Sie der Deklaration den folgenden Code
@interface
hinzu.- (void) createEventWithSubject: (NSString*) subject andStart: (NSDate*) start andEnd: (NSDate*) end andAttendees: (NSArray<NSString*>* _Nullable) attendees andBody: (NSString* _Nullable) body andCompletionBlock: (CreateEventCompletionBlock) completion;
Öffnen Sie GraphManager.m, und fügen Sie die folgende Funktion hinzu, um ein neues Ereignis im Kalender des Benutzers zu erstellen.
- (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]; }
Erstellen Sie eine neue Cocoa Touch Class-Datei im Ordner "GraphTutorial" mit dem Namen
NewEventViewController
. Wählen Sie "UIViewController" in der Unterklasse des Felds aus.Öffnen Sie "NewEventViewController.h", und fügen Sie den folgenden Code in der
@interface
Deklaration hinzu.@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;
Öffnen Sie "NewEventController.m", und ersetzen Sie den Inhalt durch den folgenden Code.
#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
Öffnen Sie Main.storyboard. Verwenden Sie die Bibliothek, um einen Ansichtscontroller in das Storyboard zu ziehen.
Fügen Sie mithilfe der Bibliothek dem Ansichtscontroller eine Navigationsleiste hinzu.
Doppelklicken Sie auf den Titel in der Navigationsleiste, und aktualisieren Sie ihn auf
New Event
.Fügen Sie mithilfe der Bibliothek der linken Seite der Navigationsleiste ein Balkenschaltflächenelement hinzu.
Wählen Sie die neue Schaltfläche "Leiste" und dann "Attributes Inspector" aus. Ändern Sie den Titel in
Cancel
.Fügen Sie mithilfe der Bibliothek ein Balkenschaltflächenelement auf der rechten Seite der Navigationsleiste hinzu.
Wählen Sie die neue Schaltfläche "Leiste" und dann "Attributes Inspector" aus. Ändern Sie den Titel in
Create
.Wählen Sie den Ansichtscontroller und dann den Identitätsinspektor aus. Ändern Sie die Klasse in NewEventViewController.
Fügen Sie die folgenden Steuerelemente aus der Bibliothek zur Ansicht hinzu.
- Fügen Sie eine Bezeichnung unter der Navigationsleiste hinzu. Legen Sie den Text auf
Subject
. - Fügen Sie ein Textfeld unter der Beschriftung hinzu. Legen Sie das Platzhalterattribut auf
Subject
. - Fügen Sie eine Beschriftung unter dem Textfeld hinzu. Legen Sie den Text auf
Attendees
. - Fügen Sie ein Textfeld unter der Beschriftung hinzu. Legen Sie das Platzhalterattribut auf
Separate multiple entries with ;
. - Fügen Sie eine Beschriftung unter dem Textfeld hinzu. Legen Sie den Text auf
Start
. - Fügen Sie eine Datumsauswahl unter der Bezeichnung hinzu. Legen Sie die bevorzugte Formatvorlage auf "Komprimiert", das Intervall auf 15 Minuten und die Höhe auf 35 fest.
- Fügen Sie eine Bezeichnung unter der Datumsauswahl hinzu. Legen Sie den Text auf
End
. - Fügen Sie eine Datumsauswahl unter der Bezeichnung hinzu. Legen Sie die bevorzugte Formatvorlage auf "Komprimiert", das Intervall auf 15 Minuten und die Höhe auf 35 fest.
- Fügen Sie unter der Datumsauswahl eine Textansicht hinzu.
- Fügen Sie eine Bezeichnung unter der Navigationsleiste hinzu. Legen Sie den Text auf
Wählen Sie den Controller für die neue Ereignisansicht aus, und verwenden Sie den Verbindungsinspektor, um die folgenden Verbindungen herzustellen.
- Verbinden die Aktion "Abbrechen empfangen" auf die Schaltfläche "Leiste abbrechen" ein.
- Verbinden die Aktion "createEvent received" an die Schaltfläche "Balken erstellen".
- Verbinden dem Betreff zum ersten Textfeld.
- Verbinden die Teilnehmer zum zweiten Textfeld.
- Verbinden der Startauslauf zur ersten Datumsauswahl.
- Verbinden das Ende der zweiten Datumsauswahl.
- Verbinden der Textauslassung zur Textansicht.
Fügen Sie die folgenden Einschränkungen hinzu.
- Navigationsleiste
- Führendes Leerzeichen zu Tresor Bereich, Wert: 0
- Nachgestellter Abstand zu Tresor Bereich, Wert: 0
- Oberer Abstand zu Tresor Bereich, Wert: 0
- Höhe, Wert: 44
- Betreffbezeichnung
- Führendes Leerzeichen zum Ansichtsrand, Wert: 0
- Nachgestellter Abstand zum Ansichtsrand, Wert: 0
- Oberer Abstand zur Navigationsleiste, Wert: 20
- Betrefftextfeld
- Führendes Leerzeichen zum Ansichtsrand, Wert: 0
- Nachgestellter Abstand zum Ansichtsrand, Wert: 0
- Top space to Subject Label, value: Standard
- Teilnehmerbezeichnung
- Führendes Leerzeichen zum Ansichtsrand, Wert: 0
- Nachgestellter Abstand zum Ansichtsrand, Wert: 0
- Oberer Abstand zum Betrefftextfeld, Wert: Standard
- Teilnehmertextfeld
- Führendes Leerzeichen zum Ansichtsrand, Wert: 0
- Nachgestellter Abstand zum Ansichtsrand, Wert: 0
- Top space to Attendees Label, value: Standard
- Bezeichnung starten
- Führendes Leerzeichen zum Ansichtsrand, Wert: 0
- Nachgestellter Abstand zum Ansichtsrand, Wert: 0
- Oberer Abstand zum Betrefftextfeld, Wert: Standard
- Startdatumsauswahl
- Führendes Leerzeichen zum Ansichtsrand, Wert: 0
- Nachgestellter Abstand zum Ansichtsrand, Wert: 0
- Top space to Attendees Label, value: Standard
- Höhe, Wert: 35
- End Label
- Führendes Leerzeichen zum Ansichtsrand, Wert: 0
- Nachgestellter Abstand zum Ansichtsrand, Wert: 0
- Top space to Start Date Picker, value: Standard
- Enddatumsauswahl
- Führendes Leerzeichen zum Ansichtsrand, Wert: 0
- Nachgestellter Abstand zum Ansichtsrand, Wert: 0
- Top space to End Label, value: Standard
- Höhe: 35
- Textkörperansicht
- Führendes Leerzeichen zum Ansichtsrand, Wert: 0
- Nachgestellter Abstand zum Ansichtsrand, Wert: 0
- Top space to End Date Picker, value: Standard
- Unteres Leerzeichen bis Ansichtsrand, Wert: 0
- Navigationsleiste
Wählen Sie die Kalender-Szene aus, und wählen Sie dann den Connections Inspector aus.
Ziehen Sie unter "Ausgelöste Segues" den ungefüllten Kreis neben manuell auf den Neuen Ereignisansichtscontroller im Storyboard. Wählen Sie "Modal präsentieren" im Popupmenü aus.
Wählen Sie die Segue aus, die Sie soeben hinzugefügt haben, und wählen Sie dann den Attributes Inspector aus. Legen Sie das Bezeichnerfeld auf
showEventForm
.Verbinden die Aktion "showNewEventForm" auf die Schaltfläche der + Navigationsleiste empfangen.
Speichern Sie die Änderungen, und starten Sie die App neu. Wechseln Sie zur Kalenderseite, und tippen Sie auf die + Schaltfläche. Füllen Sie das Formular aus, und tippen Sie auf "Erstellen", um ein neues Ereignis zu erstellen.
Herzlichen Glückwunsch!
Sie haben das iOS Objective-C Microsoft Graph Lernprogramm abgeschlossen. Nachdem Sie nun über eine funktionierende App verfügen, die Microsoft Graph aufruft, können Sie experimentieren und neue Features hinzufügen. Besuchen Sie die Übersicht über Microsoft Graph, um alle Daten anzuzeigen, auf die Sie mit Microsoft Graph zugreifen können.
Feedback
Bitte geben Sie Feedback zu diesem Lernprogramm im GitHub Repository.
Liegt ein Problem mit diesem Abschnitt vor? Wenn ja, senden Sie uns Feedback, damit wir den Abschnitt verbessern können.