Erstellen von iOS Swift-Apps mit Microsoft Graph
In diesem Lernprogramm erfahren Sie, wie Sie eine iOS-App mit Swift 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 Swift-App
Erstellen Sie zunächst ein neues Swift-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 Swift 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 "ViewController.swift" aus.
Ändern Sie im Dateiinspektor den Namen der Datei in
SignInViewController.swift
.Öffnen Sie SignInViewController.swift, und ersetzen Sie den Inhalt durch den folgenden Code.
import UIKit class SignInViewController: UIViewController { override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. } @IBAction func signIn() { self.performSegue(withIdentifier: "userSignedIn", sender: nil) } }
Öffnen Sie 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.swift, und ersetzen Sie den Inhalt durch den folgenden Code.
import UIKit class WelcomeViewController: UIViewController { @IBOutlet var userProfilePhoto: UIImageView! @IBOutlet var userDisplayName: UILabel! @IBOutlet var userEmail: UILabel! override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. // TEMPORARY self.userProfilePhoto.image = UIImage(imageLiteralResourceName: "DefaultUserPhoto") self.userDisplayName.text = "Default User" self.userEmail.text = "default@contoso.com" } @IBAction func signOut() { self.performSegue(withIdentifier: "userSignedOut", sender: nil) } }
Ö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 sicher.
- 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
.
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.swift, und ersetzen Sie den Inhalt durch den folgenden Code.
import UIKit class CalendarViewController: UIViewController { @IBOutlet var calendarJSON: UITextView! override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. // TEMPORARY calendarJSON.text = "Calendar" calendarJSON.sizeToFit() } }
Ö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. Wählen Sie im Menü "Editor" die Option "Einbetten" und dann "Bildlaufansicht" aus.
Ändern Sie die Größe der Bildlauf- und Textansicht, um den gesamten Bildschirm aufzunehmen.
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 Kalenderansichtscontroller 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.swift, und ersetzen Sie den Inhalt durch den folgenden Code.
import UIKit class SpinnerViewController: UIViewController { var spinner = UIActivityIndicatorView(style: .large) override func loadView() { view = UIView() view.backgroundColor = UIColor(white: 0, alpha: 0.7) spinner.translatesAutoresizingMaskIntoConstraints = false spinner.startAnimating() view.addSubview(spinner) spinner.centerXAnchor.constraint(equalTo: view.centerXAnchor).isActive = true spinner.centerYAnchor.constraint(equalTo: view.centerYAnchor).isActive = true } public func start(container: UIViewController) { container.addChild(self) self.view.frame = container.view.frame container.view.addSubview(self.view) self.didMove(toParent: container) } public func stop() { self.willMove(toParent: nil) self.view.removeFromSuperview() self.removeFromParent() } }
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 Swift 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 Lernprogrammseite von iOS Swift Graph 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.swift", und fügen Sie die folgende Import-Anweisung am Anfang der Datei hinzu.
import MSAL
Fügen Sie die folgende Funktion zur
AppDelegate
-Klasse hinzu:func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool { guard let sourceApplication = options[UIApplication.OpenURLOptionsKey.sourceApplication] as? String else { return false } return MSALPublicClientApplication.handleMSALResponse(url, sourceApplication: sourceApplication) }
Erstellen des Authentifizierungs-Managers
Erstellen Sie eine neue Swift-Datei im GraphTutorial-Projekt mit dem Namen AuthenticationManager.swift. Fügen Sie den folgenden Code in die Datei ein:
import Foundation import MSAL import MSGraphClientSDK // Implement the MSAuthenticationProvider interface so // this class can be used as an auth provider for the Graph SDK class AuthenticationManager: NSObject, MSAuthenticationProvider { // Implement singleton pattern static let instance = AuthenticationManager() private let publicClient: MSALPublicClientApplication? private let appId: String private let graphScopes: Array<String> private override init() { // Get app ID and scopes from AuthSettings.plist let bundle = Bundle.main let authConfigPath = bundle.path(forResource: "AuthSettings", ofType: "plist")! let authConfig = NSDictionary(contentsOfFile: authConfigPath)! self.appId = authConfig["AppId"] as! String self.graphScopes = authConfig["GraphScopes"] as! Array<String> do { // Create the MSAL client try self.publicClient = MSALPublicClientApplication(clientId: self.appId) } catch { print("Error creating MSAL public client: \(error)") self.publicClient = nil } } // Required function for the MSAuthenticationProvider interface func getAccessToken(for authProviderOptions: MSAuthenticationProviderOptions!, andCompletion completion: ((String?, Error?) -> Void)!) { getTokenSilently(completion: completion) } public func getTokenInteractively(parentView: UIViewController, completion: @escaping(_ accessToken: String?, Error?) -> Void) { let webParameters = MSALWebviewParameters(authPresentationViewController: parentView) let interactiveParameters = MSALInteractiveTokenParameters(scopes: self.graphScopes, webviewParameters: webParameters) interactiveParameters.promptType = MSALPromptType.selectAccount // Call acquireToken to open a browser so the user can sign in publicClient?.acquireToken(with: interactiveParameters, completionBlock: { (result: MSALResult?, error: Error?) in guard let tokenResult = result, error == nil else { print("Error getting token interactively: \(String(describing: error))") completion(nil, error) return } print("Got token interactively: \(tokenResult.accessToken)") completion(tokenResult.accessToken, nil) }) } public func getTokenSilently(completion: @escaping(_ accessToken: String?, Error?) -> Void) { // Check if there is an account in the cache var userAccount: MSALAccount? do { userAccount = try publicClient?.allAccounts().first } catch { print("Error getting account: \(error)") } if (userAccount != nil) { // Attempt to get token silently let silentParameters = MSALSilentTokenParameters(scopes: self.graphScopes, account: userAccount!) publicClient?.acquireTokenSilent(with: silentParameters, completionBlock: { (result: MSALResult?, error: Error?) in guard let tokenResult = result, error == nil else { print("Error getting token silently: \(String(describing: error))") completion(nil, error) return } print("Got token silently: \(tokenResult.accessToken)") completion(tokenResult.accessToken, nil) }) } else { print("No account in cache") completion(nil, NSError(domain: "AuthenticationManager", code: MSALError.interactionRequired.rawValue, userInfo: nil)) } } public func signOut() -> Void { do { // Remove all accounts from the cache let accounts = try publicClient?.allAccounts() try accounts!.forEach({ (account: MSALAccount) in try publicClient?.remove(account) }) } catch { print("Sign out error: \(String(describing: error))") } } }
Hinzufügen von Anmeldung und Abmelden
Öffnen Sie SignInViewController.swift, und ersetzen Sie den Inhalt durch den folgenden Code.
import UIKit class SignInViewController: UIViewController { private let spinner = SpinnerViewController() override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. // See if a user is already signed in spinner.start(container: self) AuthenticationManager.instance.getTokenSilently { (token: String?, error: Error?) in DispatchQueue.main.async { self.spinner.stop() guard let _ = token, error == nil else { // 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, a user is signed in // Go to welcome page self.performSegue(withIdentifier: "userSignedIn", sender: nil) } } } @IBAction func signIn() { spinner.start(container: self) // Do an interactive sign in AuthenticationManager.instance.getTokenInteractively(parentView: self) { (token: String?, error: Error?) in DispatchQueue.main.async { self.spinner.stop() guard let _ = token, error == nil else { // Show the error and stay on the sign-in page let alert = UIAlertController(title: "Error signing in", message: error.debugDescription, preferredStyle: .alert) alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil)) self.present(alert, animated: true) return } // Signed in successfully // Go to welcome page self.performSegue(withIdentifier: "userSignedIn", sender: nil) } } } }
Öffnen Sie "WelcomeViewController.swift", und ersetzen Sie die vorhandene
signOut
Funktion durch Folgendes.@IBAction func signOut() { AuthenticationManager.instance.signOut() self.performSegue(withIdentifier: "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 Swift-Datei im GraphTutorial-Projekt mit dem Namen GraphManager.swift. Fügen Sie den folgenden Code in die Datei ein:
import Foundation import MSGraphClientSDK import MSGraphClientModels class GraphManager { // Implement singleton pattern static let instance = GraphManager() private let client: MSHTTPClient? public var userTimeZone: String private init() { client = MSClientFactory.createHTTPClient(with: AuthenticationManager.instance) userTimeZone = "UTC" } public func getMe(completion: @escaping(MSGraphUser?, Error?) -> Void) { // GET /me let select = "$select=displayName,mail,mailboxSettings,userPrincipalName" let meRequest = NSMutableURLRequest(url: URL(string: "\(MSGraphBaseURL)/me?\(select)")!) let meDataTask = MSURLSessionDataTask(request: meRequest, client: self.client, completion: { (data: Data?, response: URLResponse?, graphError: Error?) in guard let meData = data, graphError == nil else { completion(nil, graphError) return } do { // Deserialize response as a user let user = try MSGraphUser(data: meData) completion(user, nil) } catch { completion(nil, error) } }) // Execute the request meDataTask?.execute() } }
Öffnen Sie WelcomeViewController.swift, und fügen Sie die folgende
import
Anweisung am Anfang der Datei hinzu.import MSGraphClientModels
Fügen Sie der
WelcomeViewController
-Klasse die folgende Eigenschaft hinzu.private let spinner = SpinnerViewController()
Ersetzen Sie den vorhandenen
viewDidLoad
durch den folgenden Code.override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. self.spinner.start(container: self) // Get the signed-in user self.userProfilePhoto.image = UIImage(imageLiteralResourceName: "DefaultUserPhoto") GraphManager.instance.getMe { (user: MSGraphUser?, error: Error?) in DispatchQueue.main.async { self.spinner.stop() guard let currentUser = user, error == nil else { print("Error getting user: \(String(describing: error))") return } // Set display name self.userDisplayName.text = currentUser.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 = currentUser.mail ?? currentUser.userPrincipalName ?? "" self.userEmail.sizeToFit() // Save the user's time zone GraphManager.instance.userTimeZone = currentUser.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
zu aktualisieren, um diese neuen Funktionen zu verwenden.
Öffnen Sie GraphManager.swift, und fügen Sie der Klasse die folgende Methode
GraphManager
hinzu.public func getCalendarView(viewStart: String, viewEnd: String, completion: @escaping(Data?, Error?) -> Void) { // GET /me/calendarview // Set start and end of the view let start = "startDateTime=\(viewStart)" let end = "endDateTime=\(viewEnd)" // Only return these fields in results let select = "$select=subject,organizer,start,end" // Sort results by when they were created, newest first let orderBy = "$orderby=start/dateTime" // Request at most 25 results let top = "$top=25" let eventsRequest = NSMutableURLRequest(url: URL(string: "\(MSGraphBaseURL)/me/calendarview?\(start)&\(end)&\(select)&\(orderBy)&\(top)")!) // Add the Prefer: outlook.timezone header to get start and end times // in user's time zone eventsRequest.addValue("outlook.timezone=\"\(self.userTimeZone)\"", forHTTPHeaderField: "Prefer") let eventsDataTask = MSURLSessionDataTask(request: eventsRequest, client: self.client, completion: { (data: Data?, response: URLResponse?, graphError: Error?) in guard let eventsData = data, graphError == nil else { completion(nil, graphError) return } // TEMPORARY completion(eventsData, nil) }) // Execute the request eventsDataTask?.execute() }
Hinweis
Überlegen Sie, was der Code
getCalendarView
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 Swift-Datei im GraphTutorial-Projekt mit dem Namen GraphToIana.swift. Fügen Sie den folgenden Code in die Datei ein:
import Foundation // 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 class GraphToIana { private static let 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" ] public static func getIanaIdentifier(graphIdentifer: String) -> String { // If a mapping was not found, assume the value passed // was already an IANA identifier return timeZoneMap[graphIdentifer] ?? graphIdentifer } }
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.swift, und ersetzen Sie den gesamten Inhalt durch den folgenden Code.
import UIKit import MSGraphClientModels class CalendarViewController: UIViewController { @IBOutlet var calendarJSON: UITextView! private let spinner = SpinnerViewController() override func viewDidLoad() { super.viewDidLoad() // Do any additional setup after loading the view. self.spinner.start(container: self) // Calculate the start and end of the current week let timeZone = GraphToIana.getIanaIdentifier(graphIdentifer: GraphManager.instance.userTimeZone) let now = Date() var calendar = Calendar(identifier: .gregorian) calendar.timeZone = TimeZone(identifier: timeZone)! let startOfWeek = calendar.dateComponents([.calendar, .yearForWeekOfYear, .weekOfYear], from: now).date! let endOfWeek = calendar.date(byAdding: .day, value: 7, to: startOfWeek)! // Convert start and end to ISO 8601 strings let isoFormatter = ISO8601DateFormatter() let viewStart = isoFormatter.string(from: startOfWeek) let viewEnd = isoFormatter.string(from: endOfWeek) GraphManager.instance.getCalendarView(viewStart: viewStart, viewEnd: viewEnd) { (data: Data?, error: Error?) in DispatchQueue.main.async { self.spinner.stop() // TEMPORARY guard let eventsData = data, error == nil else { self.calendarJSON.text = error.debugDescription return } let jsonString = String(data: eventsData, encoding: .utf8) self.calendarJSON.text = jsonString self.calendarJSON.sizeToFit() } } } }
Sie können jetzt die App ausführen, sich anmelden und im Menü auf das Navigationselement "Kalender" tippen. 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 getCalendarView
stark typisierte Objekte zurückgegeben werden, und ändern, CalendarViewController
um die Ereignisse mithilfe einer Tabellenansicht zu rendern.
Öffnen Sie GraphManager.swift. Ersetzen Sie die vorhandene
getCalendarView
-Funktion durch Folgendes.public func getCalendarView(viewStart: String, viewEnd: String, completion: @escaping([MSGraphEvent]?, Error?) -> Void) { // GET /me/calendarview // Set start and end of the view let start = "startDateTime=\(viewStart)" let end = "endDateTime=\(viewEnd)" // Only return these fields in results let select = "$select=subject,organizer,start,end" // Sort results by when they were created, newest first let orderBy = "$orderby=start/dateTime" // Request at most 25 results let top = "$top=25" let eventsRequest = NSMutableURLRequest(url: URL(string: "\(MSGraphBaseURL)/me/calendarview?\(start)&\(end)&\(select)&\(orderBy)&\(top)")!) // Add the Prefer: outlook.timezone header to get start and end times // in user's time zone eventsRequest.addValue("outlook.timezone=\"\(self.userTimeZone)\"", forHTTPHeaderField: "Prefer") let eventsDataTask = MSURLSessionDataTask(request: eventsRequest, client: self.client, completion: { (data: Data?, response: URLResponse?, graphError: Error?) in guard let eventsData = data, graphError == nil else { completion(nil, graphError) return } do { // Deserialize response as events collection let eventsCollection = try MSCollection(data: eventsData) var eventArray: [MSGraphEvent] = [] eventsCollection.value.forEach({ (rawEvent: Any) in // Convert JSON to a dictionary guard let eventDict = rawEvent as? [String: Any] else { return } // Deserialize event from the dictionary let event = MSGraphEvent(dictionary: eventDict)! eventArray.append(event) }) // Return the array completion(eventArray, nil) } catch { completion(nil, error) } }) // Execute the request eventsDataTask?.execute() }
Erstellen Sie eine neue Cocoa Touch Class-Datei im GraphTutorial-Projekt mit dem Namen
CalendarTableViewController.swift
. Wählen Sie "UITableViewController" in der Unterklasse des Felds aus.Öffnen Sie CalendarTableViewController.swift, und ersetzen Sie den Inhalt durch Folgendes.
import UIKit import MSGraphClientModels class CalendarTableViewController: UITableViewController { private let tableCellIdentifier = "EventCell" private var events: [MSGraphEvent]? override func viewDidLoad() { super.viewDidLoad() tableView.rowHeight = UITableView.automaticDimension tableView.estimatedRowHeight = 100 } // Number of sections, always 1 override func numberOfSections(in tableView: UITableView) -> Int { return 1 } // Return the number of events in the table override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { return events?.count ?? 0 } override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: tableCellIdentifier, for: indexPath) as! CalendarTableViewCell // Get the event that corresponds to the row let event = events?[indexPath.row] // Configure the cell cell.subject = event?.subject cell.organizer = event?.organizer?.emailAddress?.name // Build a duration string let duration = "\(self.formatGraphDateTime(dateTime: event?.start)) to \(self.formatGraphDateTime(dateTime: event?.end))" cell.duration = duration return cell } private func formatGraphDateTime(dateTime: MSGraphDateTimeTimeZone?) -> String { guard let graphDateTime = dateTime else { return "" } // Create a formatter to parse Graph's date format let isoFormatter = DateFormatter() isoFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSSSSS" let date = isoFormatter.date(from: graphDateTime.dateTime) // Output like 5/5/2019, 2:00 PM let dateFormatter = DateFormatter() dateFormatter.dateStyle = .short dateFormatter.timeStyle = .short return dateFormatter.string(from: date!) } public func setEvents(events: [MSGraphEvent]?) -> Void { self.events = events self.tableView.reloadData() } }
Erstellen Sie eine neue Cocoa Touch Class-Datei im GraphTutorial-Projekt mit dem Namen
CalendarTableViewCell.swift
. Wählen Sie UITableViewCell in der Unterklasse des Felds aus.Öffnen Sie CalendarTableViewCell.swift, und fügen Sie der Klasse die folgenden Eigenschaften
CalendarTableViewCell
hinzu.@IBOutlet var subjectLabel: UILabel! @IBOutlet var organizerLabel: UILabel! @IBOutlet var durationLabel: UILabel! var subject: String? { didSet { subjectLabel.text = subject } } var organizer: String? { didSet { organizerLabel.text = organizer } } var duration: String? { didSet { durationLabel.text = duration } }
Ö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 belegen.
Legen Sie Einschränkungen für die Navigationsleiste und die Containeransicht wie folgt fest.
- Navigationsleiste
- Einschränkung hinzufügen: Höhe, Wert: 44
- Add constraint: Leading space to Tresor Area, value: 0
- Einschränkung hinzufügen: Nachgestellter Abstand zu Tresor Bereich, Wert: 0
- Einschränkung hinzufügen: Oberer Platz zu Tresor Bereich, Wert: 0
- Containeransicht
- Add constraint: Leading space to Tresor Area, value: 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.swift, und ersetzen Sie den Inhalt durch den folgenden Code.
import UIKit import MSGraphClientModels class CalendarViewController: UIViewController { private let spinner = SpinnerViewController() private var tableViewController: CalendarTableViewController? override func viewDidLoad() { super.viewDidLoad() self.spinner.start(container: self) // Calculate the start and end of the current week let timeZone = GraphToIana.getIanaIdentifier(graphIdentifer: GraphManager.instance.userTimeZone) let now = Date() var calendar = Calendar(identifier: .gregorian) calendar.timeZone = TimeZone(identifier: timeZone)! let startOfWeek = calendar.dateComponents([.calendar, .yearForWeekOfYear, .weekOfYear], from: now).date! let endOfWeek = calendar.date(byAdding: .day, value: 7, to: startOfWeek)! // Convert start and end to ISO 8601 strings let isoFormatter = ISO8601DateFormatter() let viewStart = isoFormatter.string(from: startOfWeek) let viewEnd = isoFormatter.string(from: endOfWeek) GraphManager.instance.getCalendarView(viewStart: viewStart, viewEnd: viewEnd) { (eventArray: [MSGraphEvent]?, error: Error?) in DispatchQueue.main.async { self.spinner.stop() guard let events = eventArray, error == nil else { // Show the error let alert = UIAlertController(title: "Error getting events", message: error.debugDescription, preferredStyle: .alert) alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil)) self.present(alert, animated: true) return } self.tableViewController?.setEvents(events: events) } } } internal override func prepare(for segue: UIStoryboardSegue, sender: Any?) { // Save reference to the contained table view if segue.destination is CalendarTableViewController { self.tableViewController = segue.destination as? CalendarTableViewController } } @IBAction func showNewEventForm() { self.performSegue(withIdentifier: "showEventForm", sender: self) } }
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.swift, und fügen Sie die folgende Funktion hinzu, um ein neues Ereignis im Kalender des Benutzers zu erstellen.
public func createEvent(subject: String, start: Date, end: Date, attendees: [Substring]?, body: String?, completion: @escaping(MSGraphEvent?, Error?) -> Void) { let isoFormatter = DateFormatter() isoFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss" // 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 var newEventDict: [String: Any] = [ "subject": subject, "start": [ "dateTime": isoFormatter.string(from: start), "timeZone": self.userTimeZone ], "end": [ "dateTime": isoFormatter.string(from: end), "timeZone": self.userTimeZone ] ] if attendees?.count ?? 0 > 0 { var attendeeArray: [Any] = [] for attendee in attendees! { let attendeeDict: [String: Any] = [ "type": "required", "emailAddress": [ "address": String(attendee) ] ] attendeeArray.append(attendeeDict) } newEventDict["attendees"] = attendeeArray } if !(body?.isEmpty ?? false) { newEventDict["body"] = [ "content": body, "contentType": "text" ] } let eventData = try? JSONSerialization.data(withJSONObject: newEventDict) let createEventRequest = NSMutableURLRequest(url: URL(string: "\(MSGraphBaseURL)/me/events")!) createEventRequest.httpMethod = "POST" createEventRequest.httpBody = eventData createEventRequest.addValue("application/json", forHTTPHeaderField: "Content-Type") let createEventTask = MSURLSessionDataTask(request: createEventRequest, client: self.client, completion: { (data: Data?, response: URLResponse?, graphError: Error?) in guard let eventData = data, graphError == nil else { completion(nil, graphError) return } do { // Deserialize response as event let returnedEvent = try MSGraphEvent(data: eventData) // Return the event completion(returnedEvent, nil) } catch { completion(nil, error) } }) // Execute the task createEventTask?.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.swift", und ersetzen Sie den Inhalt durch Folgendes.
import UIKit import MSGraphClientModels class NewEventViewController: UIViewController { @IBOutlet var subject: UITextField! @IBOutlet var attendees: UITextField! @IBOutlet var start: UIDatePicker! @IBOutlet var end: UIDatePicker! @IBOutlet var body: UITextView! private let spinner = SpinnerViewController() override func viewDidLoad() { super.viewDidLoad() // Add border around text view let borderColor : UIColor = UIColor(red: 0.85, green: 0.85, blue: 0.85, alpha: 1.0) body.layer.borderWidth = 0.5 body.layer.borderColor = borderColor.cgColor body.layer.cornerRadius = 5.0 // Set start picker to the next closest half-hour let now = Date() let calendar = Calendar.current let components = calendar.dateComponents([.minute], from:now) let offset = 30 - (components.minute! % 30) let start = calendar.date(byAdding: .minute, value: offset, to: now) self.start.date = start! // Set end picker to start + 30 min let end = calendar.date(byAdding: .minute, value: 30, to: start!) self.end.date = end! } @IBAction func createEvent() { self.spinner.start(container: self) // Do create let subject = self.subject.text ?? "" let attendees = self.attendees.text?.split(separator: ";") let start = self.start.date let end = self.end.date let body = self.body.text ?? "" GraphManager.instance.createEvent(subject: subject, start: start, end: end, attendees: attendees, body: body) { (event: MSGraphEvent?, error: Error?) in DispatchQueue.main.async { self.spinner.stop() guard let _ = event, error == nil else { // Show the error let alert = UIAlertController(title: "Error creating event", message: error.debugDescription, preferredStyle: .alert) alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil)) self.present(alert, animated: true) return } let alert = UIAlertController(title: "Success", message: "Event created", preferredStyle: .alert) alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { (action: UIAlertAction?) in self.dismiss(animated: true, completion: nil) })) self.present(alert, animated: true) } } } @IBAction func cancel() { self.dismiss(animated: true, completion: nil) } }
Ö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 Lernprogramm für iOS Swift Microsoft Graph 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.