Microsoft Graph で iOS Swift アプリを構築する
このチュートリアルでは、Microsoft Graph API を使用してユーザーの予定表情報を取得する Swift を使用して iOS アプリを構築する方法について説明します。
ヒント
完了したチュートリアルをダウンロードする場合は、リポジトリをダウンロードまたは複製GitHubできます。
前提条件
このチュートリアルを開始する前に、開発マシンに次の手順をインストールする必要があります。
また、Outlook.com 上のメールボックスを持つ個人用 Microsoft アカウント、または Microsoft の仕事用または学校用のアカウントを持っている必要があります。 Microsoft アカウントをお持ちでない場合は、無料アカウントを取得するためのオプションが 2 つご利用できます。
- 新しい 個人用 Microsoft アカウントにサインアップできます。
- 開発者プログラムにサインアップして、Microsoft 365サブスクリプションをMicrosoft 365できます。
注意
このチュートリアルは、Xcode バージョン 12.3 および CocoaPods バージョン 1.10.1 を使用して作成されました。このガイドの手順は他のバージョンでは動作しますが、テストされていない場合があります。
フィードバック
このチュートリアルに関するフィードバックは、リポジトリのGitHubしてください。
iOS Swift アプリを作成する
まず、新しい Swift プロジェクトを作成します。
Xcode を開きます。 [ファイル]メニューの[新規]を選択 し、[Project]をクリックします。
[アプリ テンプレート] を選択し 、[次へ] を 選択します。
[製品名 ] に設定し
GraphTutorial
、[言語] を [Swift] に 設定します。残りのフィールドに入力し、[次へ] を 選択します。
プロジェクトの場所を選択し、[作成] を 選択します。
依存関係のインストール
次に進む前に、後で使用する追加の依存関係をインストールします。
- Microsoft Authentication Library (MSAL) for iOS for authenticationing to Azure AD。
- Microsoft Graphを呼び出す目的 C用の SDK Graph。
- Microsoft Graphユーザーやイベントなどのリソースを表す、強く型指定されたオブジェクトGraphモデル SDK for Objective C。
Xcode を終了します。
ターミナルを開き、ディレクトリを GraphTutorial プロジェクトの場所に変更 します。
ポッドファイルを作成するには、次のコマンドを実行します。
pod init
Podfile を開き、行の直後に次の行を追加
use_frameworks!
します。pod 'MSAL', '~> 1.1.13' pod 'MSGraphClientSDK', ' ~> 1.0.0' pod 'MSGraphClientModels', '~> 1.3.0'
Podfile を保存し、次のコマンドを実行して依存関係をインストールします。
pod install
コマンドが完了したら、Xcode で新しく作成した GraphTutorial.xcworkspace を 開きます。
アプリを設計する
このセクションでは、サインイン ページ、タブ バー ナビゲーター、ウェルカム ページ、予定表ページなど、アプリのビューを作成します。 また、アクティビティ インジケーター オーバーレイも作成します。
[サインイン ページの作成]
Xcode で GraphTutorial フォルダーを展開し 、[ViewController.swift] を選択します。
[ファイル インスペクター] で、ファイル の名前を に 変更します
SignInViewController.swift
。SignInViewController.swift を開き、その内容を次のコードに置き換えます。
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) } }
Main.storyboard を開きます。 [コントローラー シーンの表示] を展開 し、[コントローラーの表示 ] を選択します。
[Identity Inspector] を選択 し、[クラス] ドロップダウンを SignInViewController に変更します。
ライブラリを 選択し、ボタン をサインイン ビュー コントローラー にドラッグします。
ボタンを選択した後、属性インスペクタ ーを選択し 、ボタン のタイトルを に変更します
Sign In
。ボタンを選択して、ストーリーボード の 下部にある [配置] ボタンを選択します。 [コンテナー内の 水平方向] と[コンテナーの垂直方向] の両方の制約を選択し、その値を 0 のままにしてから、[2 制約の追加]を選択します。
[サインイン ビュー コントローラー] を選択し、[接続] インスペクタ を選択します。
[ 受信したアクション] で、次に入力されていない円をドラッグして 、signIn をボタン に移動します。 ポップアップ メニューの [内側に タッチアップ] を選択します。
タブ バーの作成
ライブラリを 選択し、タブ バー コントローラーをストーリーボード にドラッグします。
[サインイン ビュー コントローラー] を選択し、[接続] インスペクタ を選択します。
[トリガーされたセグエ] で、手動の横にある塗り付けされていない円をストーリーボードの タブ バー コントローラー にドラッグします。 ポップアップ メニューで [モーダルの 表示] を選択します。
追加したセグエを選択し、[属性] インスペクタ を選択します。 [識別子] フィールドを
userSignedIn
に設定し、[プレゼンテーション] を [フルスクリーン] に設定します。アイテム 1 シーンを選択し、[接続] インスペクタ を選択します。
[トリガーされたセグ エ] で、手動の横にある塗り付けされていない円をストーリーボードの サインイン ビュー コントローラー にドラッグします。 ポップアップ メニューで [モーダルの 表示] を選択します。
追加したセグエを選択し、[属性] インスペクタ を選択します。 [識別子] フィールドを
userSignedOut
に設定し、[プレゼンテーション] を [フルスクリーン] に設定します。
ようこそページを作成する
Assets.xcassets ファイルを選択 します。
[エディター] メニューの [新しい アセットの追加] 、 次に [イメージ セット] の順に選択します。
新しい Image アセットを選択 し、属性インスペクタ ーを使用して Name を に設定 します
DefaultUserPhoto
。既定のユーザー プロファイル写真として使用する画像を追加します。
という名前の GraphTutorial フォルダーに新しいココア タッチ クラス ファイルを作成します
WelcomeViewController
。 [ サブクラス] フィールドで [UIViewController] を選択 します。WelcomeViewController.swift を開き、その内容を次のコードに置き換えます。
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) } }
Main.storyboard を開きます。 アイテム 1 シーンを選択し、[Identity Inspector] を選択します。 クラスの 値を WelcomeViewController に変更します。
ライブラリを 使用して、アイテム 1 シーンに 次のアイテムを追加します。
- 1 つのイメージ ビュー
- 2 つのラベル
- 1 つのボタン
接続インスペクタ ーを使用して、次の接続を行います。
- userDisplayName アウトレットを 最初のラベルにリンクします。
- userEmail アウトレットを 2 番目のラベルにリンクします。
- userProfilePhoto アウトレットを イメージ ビューにリンクします。
- signOut 受信アクションを ボタンの Touch Up Inside にリンクします。
イメージ ビューを選択し、[サイズインスペクター] を選択します。
[幅]**と [**高さ] を 196 に設定します。
[配置 ] ボタン を使用して、 コンテナーの水平方向 の制約に値 0 を追加します。
[新しい 制約の追加] ボタン ([配置] ボタンの横) を 使用 して、次の制約を追加します。
- [上の位置を揃える]: セーフ領域、値: 0
- 下のスペース: ユーザー表示名、値: Standard
- 高さ、値: 196
- 幅、値: 196
1 つ目のラベルを選択し、[配置]ボタンを使用して、コンテナーの水平方向の制約を 0 の値で追加します。
[新しい 制約の追加] ボタン を使用して、次の制約を追加します。
- Top Space to: User Profile Photo, value: Standard
- 下のスペース: ユーザーの電子メール、値: Standard
2 番目のラベルを選択し、[属性] インスペクタ を選択します。
[色 ] を [濃い灰色] に変更し、[フォント] を [システム 12.0] に変更します。
[配置 ] ボタン を使用して、 コンテナーの水平方向 の制約に値 0 を追加します。
[新しい 制約の追加] ボタン を使用して、次の制約を追加します。
- 上のスペース: ユーザー表示名、値: Standard
- 下のスペース: サインアウト、値: 14
ボタンを選択し、[属性] インスペクタ を選択します。
[タイトル] をに変更 します
Sign Out
。[配置 ] ボタン を使用して、 コンテナーの水平方向 の制約に値 0 を追加します。
[新しい 制約の追加] ボタン を使用して、次の制約を追加します。
- Top Space to: User Email, value: 14
シーンの下部にあるタブ バー項目を選択し、[属性] インスペクタを 選択します。 [タイトル] をに変更 します
Me
。
完了したら、ウェルカム シーンは次のように表示されます。
[予定表の作成] ページ
という名前の GraphTutorial フォルダーに新しいココア タッチ クラス ファイルを作成します
CalendarViewController
。 [ サブクラス] フィールドで [UIViewController] を選択 します。CalendarViewController.swift を開き、その内容を次のコードに置き換えます。
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() } }
Main.storyboard を開きます。 アイテム 2 シーンを選択し、[Identity Inspector] を選択します。 クラスの 値を CalendarViewController に変更します。
ライブラリを 使用して、アイテム 2 シーンに****テキスト ビューを追加します。
追加したテキスト ビューを選択します。 [エディター] メニューの [埋め込み ] 、[ スクロール ビュー] の順に選択します。
スクロール ビューとテキスト ビューのサイズを変更して、画面全体を表示します。
接続インスペクタ ーを使用して**、calendarJSON アウトレットを** テキスト ビューに接続します。
シーンの下部にあるタブ バー項目を選択し、[属性] インスペクタを 選択します。 [タイトル] をに変更 します
Calendar
。[エディター]メニューの[自動レイアウトの問題の解決] を選択し、[予定表ビュー コントローラーのすべてのビュー] の下にある [不足している制約の追加]を選択します。
完了したら、カレンダー シーンは次のように表示されます。
アクティビティ インジケーターの作成
という名前の GraphTutorial フォルダーに新しいココア タッチ クラス ファイルを作成します
SpinnerViewController
。 [ サブクラス] フィールドで [UIViewController] を選択 します。SpinnerViewController.swift を開き、その内容を次のコードに置き換えます。
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() } }
アプリのテスト
変更を保存し、アプリを起動します。 [サインイン] ボタンと [サイン アウト] ボタンとタブ バーを使用して、画面間を移動できる必要があります。
ポータルでアプリを登録する
この演習では、新しい Azure AD管理センターを使用Azure Active Directoryします。
ブラウザーを開き、Azure Active Directory 管理センターへ移動して、個人用アカウント (別名: Microsoft アカウント)、または 職場/学校アカウント を使用してログインします。
左側のナビゲーションで [Azure Active Directory] を選択し、それから [管理] で [アプリの登録] を選択します。
[新規登録] を選択します。 [アプリケーションを登録] ページで、次のように値を設定します。
iOS Swift Graph Tutorial
に [名前] を設定します。- [サポートされているアカウントの種類] を [任意の組織のディレクトリ内のアカウントと個人用の Microsoft アカウント] に設定します。
- [リダイレクト URI] を空のままにします。
[登録] を選択します。 [iOS Swift Graph チュートリアル] ページで、アプリケーション (クライアント) ID の値をコピーして保存します。次の手順で必要になります。
[管理] の下の [認証] を選択します。 [ プラットフォームの追加] を 選択し 、[iOS/ macOS] を選択します。
アプリのバンドル ID を入力し、[構成] を 選択し、[完了] を 選択します。
Azure AD 認証を追加する
この演習では、前の演習からアプリケーションを拡張して、Azure サーバーでの認証をサポートAD。 これは、Microsoft サーバーを呼び出す必要がある OAuth アクセス トークンを取得するために必要Graph。 これを行うには 、iOS 用 Microsoft 認証ライブラリ (MSAL) をアプリケーションに統合します。
AuthSettings.plist という名前の GraphTutorial プロジェクトに新しいプロパティ リスト ファイルを作成します。
ルート ディクショナリのファイルに次の項目 を 追加します。
キー 型 値 AppId
String Azure portal からのアプリケーション ID GraphScopes
配列 3 つの文字列値: User.Read
MailboxSettings.Read
、、およびCalendars.ReadWrite
重要
git などのソース管理を使用している場合は 、AuthSettings.plist ファイルをソース管理から除外して、アプリ ID が誤って漏洩しないようにする良い時期です。
サインインの実装
このセクションでは、MSAL のプロジェクトを構成し、認証マネージャー クラスを作成し、サインインしてサインアウトするアプリを更新します。
MSAL のプロジェクトを構成する
プロジェクトの機能に新しいキーチェーン グループを追加します。
- GraphTutorial プロジェクトを選択 し、次に [署名&機能] を選択します。
- [+ 機能] を 選択し、[キーチェーン共有] をダブルクリックします。
- 値を持つキーチェーン グループを追加します
com.microsoft.adalcache
。
[コントロール] をクリックして [Info.plist] をクリック し、[ ファイル名を指定して開く] を選択し、[ソース コード] を選択します。
要素内に次の項目を
<dict>
追加します。<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>
AppDelegate.swift を開 き、ファイルの上部に次のインポート ステートメントを追加します。
import MSAL
次の関数を
AppDelegate
クラスに追加します。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) }
認証マネージャーの作成
AuthenticationManager.swift という名前 の GraphTutorial プロジェクトで新しい Swift ファイルを作成します。 次のコードをファイルに追加します。
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))") } } }
サインインとサインアウトの追加
SignInViewController.swift を開き、その内容を次のコードに置き換えます。
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) } } } }
WelcomeViewController.swift を開き、既存の関数を次に
signOut
置き換える。@IBAction func signOut() { AuthenticationManager.instance.signOut() self.performSegue(withIdentifier: "userSignedOut", sender: nil) }
変更を保存し、シミュレーターでアプリケーションを再起動します。
アプリにサインインすると、Xcode の出力ウィンドウにアクセス トークンが表示されます。
ユーザーの詳細情報を取得する
このセクションでは、Microsoft Graph へのすべての呼び出しを保持するヘルパー クラスを作成し、この新しいクラスを使用してログイン ユーザーを取得するために更新します WelcomeViewController
。
GraphManager.swift という名前 の GraphTutorial プロジェクトで新しい Swift ファイルを作成します。 次のコードをファイルに追加します。
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() } }
WelcomeViewController.swift を 開き、ファイルの上部に
import
次のステートメントを追加します。import MSGraphClientModels
次のプロパティを
WelcomeViewController
クラスに追加します。private let spinner = SpinnerViewController()
既存のコードを次
viewDidLoad
のコードに置き換えます。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" } } }
変更を保存してアプリを再起動すると、サインイン後に UI がユーザーの表示名と電子メール アドレスで更新されます。
予定表ビューを取得する
この演習では、アプリケーションに Microsoft Graphを組み込む必要があります。 このアプリケーションでは、目的 C 用の Microsoft Graph SDKを使用して、Microsoft サーバーに呼び出Graph。
Outlook からカレンダー イベントを取得する
このセクションでは、クラスを拡張して、現在の週のユーザーのイベントを取得し、これらの新しい関数を使用するために更新する関数 GraphManager
CalendarViewController
を追加します。
GraphManager.swift を開 き、次のメソッドをクラスに追加
GraphManager
します。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() }
注意
コードの実行を
getCalendarView
検討します。- 呼び出される URL は
/v1.0/me/calendarview
です。- クエリ
startDateTime
パラメーターendDateTime
とクエリ パラメーターは、予定表ビューの開始と終了を定義します。 - クエリ
select
パラメーターは、各イベントで返されるフィールドを、ビューが実際に使用するフィールドに限定します。 - クエリ
orderby
パラメーターは、開始時刻で結果を並べ替える。 - クエリ
top
パラメーターは、ページごとに 25 の結果を要求します。 - ヘッダーを使用すると、Microsoft Graphユーザーのタイム ゾーン内の各イベントの開始時刻と終了時刻
Prefer: outlook.timezone
を返します。
- クエリ
- 呼び出される URL は
GraphToIana.swift という名前 の****GraphTutorial プロジェクトで新しい Swift ファイルを作成します。 次のコードをファイルに追加します。
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 } }
これは、Microsoft が返すタイム ゾーン名に基づいて IANA タイム ゾーン識別子を検索する簡単なGraph。
CalendarViewController.swift を開き、その内容全体を次のコードに置き換えます。
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() } } } }
これで、アプリを実行し、サインインし、メニューの [予定表 ] ナビゲーション アイテムをタップできます。 アプリにイベントの JSON ダンプが表示されます。
結果の表示
これで、JSON ダンプを何かに置き換え、結果をユーザーフレンドリーに表示できます。 このセクションでは、関数を変更して、強く型指定されたオブジェクトを返し、テーブル ビューを使用してイベント getCalendarView
CalendarViewController
をレンダリングするために変更します。
GraphManager.swift を開きます。 既存の
getCalendarView
関数を、以下の関数で置き換えます。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() }
という名前の GraphTutorial プロジェクトで新しいココア タッチ クラス ファイルを作成します
CalendarTableViewController.swift
。 [ サブクラス] フィールドで [UITableViewController] を選択 します。CalendarTableViewController.swift を開き、 その内容を次に置き換えてください。
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() } }
という名前の GraphTutorial プロジェクトで新しいココア タッチ クラス ファイルを作成します
CalendarTableViewCell.swift
。 [ サブクラス] フィールドで [UITableViewCell] を選択 します。CalendarTableViewCell.swift を開 き、次のプロパティをクラスに追加
CalendarTableViewCell
します。@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 } }
Main.storyboard を開き、カレンダー シーン を探します。 ルート ビューからスクロール ビューを削除します。
ライブラリを 使用して、ビュー の上部に ナビゲーション バーを追加します。
ナビゲーション バーの [タイトル] をダブルクリックし、に更新します
Calendar
。ライブラリを 使用して、ナビゲーション バー の 右側にバー ボタン 項目を追加します。
新しいバー ボタンを選択し、[属性] インスペクタ を選択します。 イメージ をプラスに****変更します。
ライブラリから ナビゲーション バー の 下の ビューにコンテナー ビューを追加します。 コンテナー ビューのサイズを変更して、ビュー内の残りのすべての領域を取得します。
ナビゲーション バーとコンテナー ビューの制約を次のように設定します。
- ナビゲーション バー
- 制約の追加: 高さ、値: 44
- 制約の追加: 領域の先頭にスペースセーフ、値: 0
- 制約の追加: 領域に末尾セーフ、値: 0
- 制約の追加: 領域にトップ スペースセーフ値: 0
- コンテナー ビュー
- 制約の追加: 領域の先頭にスペースセーフ、値: 0
- 制約の追加: 領域に末尾セーフ、値: 0
- 制約の追加: ナビゲーション バーの下部に上のスペース、値: 0
- 制約の追加: 領域に下セーフ、値: 0
- ナビゲーション バー
コンテナー ビューを追加するときに、ストーリーボードに追加された 2 番目のビュー コントローラーを探します。 埋め込みセ グによってカレンダー シーンに接続されます。 このコントローラーを選択し 、Identity Inspector を使用してクラス****を CalendarTableViewController に変更します。
予定表テーブル ビュー コントローラー****からビューを削除します。
ライブラリから 予定表テーブル ビュー コントローラーに テーブル ビュー を追加します。
テーブル ビューを選択し、[属性] インスペクタ を選択します。 [ プロトタイプ セル] を 1 に設定します。
プロトタイプ セルの下端をドラッグして、操作する領域を大きくします。
ライブラリを 使用して、 プロトタイプ セルに 3 つのラベル を追加します。
[プロトタイプ] セルを選択し、[Identity Inspector] を選択します。 クラス を CalendarTableViewCell に変更します。
[属性] インスペクタを選択し 、[ 識別子] をに 設定します
EventCell
。EventCell が選択 されている場合は、[ 接続 ] インスペクタを選択して接続し、ストーリーボードのセルに追加したラベル
durationLabel
organizerLabel
subjectLabel
に接続します。3 つのラベルのプロパティと制約を次のように設定します。
- 件名ラベル
- 制約の追加: コンテンツ ビューの先頭余白に先頭のスペース、値: 0
- 制約の追加: コンテンツ ビューの末尾余白に末尾のスペース、値: 0
- 制約の追加: コンテンツ ビューの上余白にトップスペース、値: 0
- オーガナイザー ラベル
- フォント: システム 12.0
- 制約の追加: 高さ、値: 15
- 制約の追加: コンテンツ ビューの先頭余白に先頭のスペース、値: 0
- 制約の追加: コンテンツ ビューの末尾余白に末尾のスペース、値: 0
- 制約の追加: 件名ラベルの下に上のスペース、値: Standard
- 期間ラベル
- フォント: システム 12.0
- 色: 濃い灰色
- 制約の追加: 高さ、値: 15
- 制約の追加: コンテンツ ビューの先頭余白に先頭のスペース、値: 0
- 制約の追加: コンテンツ ビューの末尾余白に末尾のスペース、値: 0
- 制約を追加する: [オーガナイザー ラベルの下部] に上部スペース、値: Standard
- 制約の追加: コンテンツ ビューの下余白に下のスペース、値: 0
- 件名ラベル
EventCell を選択し、[サイズインスペクター] を選択します。 行の 高さに 対して 自動を有効にする。
CalendarViewController.swift を開き、その内容を次のコードに置き換えます。
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) } }
アプリを実行し、サインインし、[予定表] タブ をタップ します。イベントの一覧が表示されます。
新しいイベントを作成する
このセクションでは、ユーザーの予定表にイベントを作成する機能を追加します。
GraphManager.swift を開 き、次の関数を追加して、ユーザーの予定表に新しいイベントを作成します。
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() }
という名前の GraphTutorial フォルダーに新しいココア タッチ クラス ファイルを作成します
NewEventViewController
。 [ サブクラス] フィールドで [UIViewController] を選択 します。NewEventViewController.swift を開き、その内容を次に置き換えてください。
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) } }
Main.storyboard を開きます。 ライブラリを 使用して 、ビュー コントローラーをストーリーボード にドラッグします。
ライブラリを 使用して、ビュー コントローラーにナビゲーション バー を追加します。
ナビゲーション バーの [タイトル] をダブルクリックし、に更新します
New Event
。ライブラリを 使用して、ナビゲーション バー の 左側にバー ボタン 項目を追加します。
新しいバー ボタンを選択し、[属性] インスペクタ を選択します。 [タイトル ] をに変更 します
Cancel
。ライブラリを 使用して、ナビゲーション バー の 右側にバー ボタン 項目を追加します。
新しいバー ボタンを選択し、[属性] インスペクタ を選択します。 [タイトル ] をに変更 します
Create
。ビュー コントローラーを選択し、[Identity Inspector] を選択します。 クラス を NewEventViewController に変更します。
ライブラリからビューに次の コントロール を追加します。
- ナビゲーション バー の下にラベル を追加します。 テキストをに設定します
Subject
。 - ラベルの 下にテキスト フィールド を追加します。 プレースホルダー属性 をに設定 します
Subject
。 - テキスト フィールド の下にラベル を追加します。 テキストをに設定します
Attendees
。 - ラベルの 下にテキスト フィールド を追加します。 プレースホルダー属性 をに設定 します
Separate multiple entries with ;
。 - テキスト フィールド の下にラベル を追加します。 テキストをに設定します
Start
。 - ラベルの 下に日付ピッカー を追加します。 [優先 スタイル] を [コンパクト] に 設定し、 間隔 を 15 分に設定し、高さを 35 に設定します。
- 日付ピ ッカーの下 にラベルを追加します。 テキストをに設定します
End
。 - ラベルの 下に日付ピッカー を追加します。 [優先 スタイル] を [コンパクト] に 設定し、 間隔 を 15 分に設定し、高さを 35 に設定します。
- 日付ピ ッカーの下に テキスト ビューを追加します。
- ナビゲーション バー の下にラベル を追加します。 テキストをに設定します
[新しい イベント ビュー コントローラー] を 選択し、[ 接続インスペクター] を使用して 次の接続を行います。
- Connect取り 消しアクション を [キャンセル]バー ボタンに 移動します。
- Connectアクション を [バーの 作成] ボタン に 移動します。
- Connectを 最初の テキスト フィールドに設定します。
- Connect 参加者の出口を 2 番目のテキスト フィールドに設定します。
- Connect を最初の 日付ピッカーに設定します。
- Connectを 2 番目の日付ピッカーに設定します。
- Connectに 本文の 出口を表示します。
次の制約を追加します。
- ナビゲーション バー
- 領域を先頭にセーフ領域、値: 0
- 領域の末尾にセーフ領域、値: 0
- [領域] のセーフ、 値: 0
- 高さ、値: 44
- 件名ラベル
- 余白を表示する先頭のスペース、値: 0
- 余白を表示する末尾のスペース、値: 0
- ナビゲーション バーの上のスペース、値: 20
- 件名テキスト フィールド
- 余白を表示する先頭のスペース、値: 0
- 余白を表示する末尾のスペース、値: 0
- 件名ラベルの上のスペース、値: Standard
- 出席者ラベル
- 余白を表示する先頭のスペース、値: 0
- 余白を表示する末尾のスペース、値: 0
- 件名テキスト フィールドの上のスペース、値: Standard
- 出席者テキスト フィールド
- 余白を表示する先頭のスペース、値: 0
- 余白を表示する末尾のスペース、値: 0
- 出席者ラベルの上部スペース、値: Standard
- ラベルの開始
- 余白を表示する先頭のスペース、値: 0
- 余白を表示する末尾のスペース、値: 0
- 件名テキスト フィールドの上のスペース、値: Standard
- 開始日ピッカー
- 余白を表示する先頭のスペース、値: 0
- 余白を表示する末尾のスペース、値: 0
- 出席者ラベルの上部スペース、値: Standard
- 高さ、値: 35
- End Label
- 余白を表示する先頭のスペース、値: 0
- 余白を表示する末尾のスペース、値: 0
- 開始日ピッカーの上のスペース、値: Standard
- 終了日ピッカー
- 余白を表示する先頭のスペース、値: 0
- 余白を表示する末尾のスペース、値: 0
- 上のスペースから終了ラベル、値: Standard
- 高さ: 35
- 本文の表示
- 余白を表示する先頭のスペース、値: 0
- 余白を表示する末尾のスペース、値: 0
- トップ スペースから終了日ピッカー、値: Standard
- 余白を表示する下のスペース、値: 0
- ナビゲーション バー
[カレンダー シーン ] を選択 し、[接続] インスペクタ を選択します。
[トリガーされたセグエ] で、手動の横にある塗り付けされていない円をストーリーボードの新しいイベント ビュー コントローラー にドラッグします。 ポップアップ メニューで [モーダルの 表示] を選択します。
追加したセグエを選択し、[属性] インスペクタ を選択します。 [識別子] フィールドを に設定します
showEventForm
。Connectボタン に showNewEventForm 受信アクション + を追加します。
変更内容を保存し、アプリを再起動します。 予定表ページに移動し、ボタンをタップ + します。 フォームに入力し、[作成] を タップして 新しいイベントを作成します。
おめでとうございます。
iOS Swift Microsoft のチュートリアルをGraphしました。 Microsoft Graphを呼び出す作業アプリが作成されたので、新しい機能を試して追加できます。 Microsoft Graphの概要を参照して、Microsoft Graphでアクセスできるすべてのデータを確認Graph。
フィードバック
このチュートリアルに関するフィードバックは、GitHubしてください。
このセクションに問題がある場合 このセクションを改善できるよう、フィードバックをお送りください。