@@ -46,6 +46,9 @@ class ViewController: UIViewController, UITextFieldDelegate, URLSessionDelegate
4646 var loggingText : UITextView !
4747 var signOutButton : UIButton !
4848 var callGraphButton : UIButton !
49+ var usernameLabel : UILabel !
50+
51+ var currentAccount : MSALAccount ?
4952
5053 /**
5154 Setup public client application in viewDidLoad
@@ -62,12 +65,28 @@ class ViewController: UIViewController, UITextFieldDelegate, URLSessionDelegate
6265 } catch let error {
6366 self . updateLogging ( text: " Unable to create Application Context \( error) " )
6467 }
68+
69+ self . loadCurrentAccount ( )
70+ self . platformViewDidLoadSetup ( )
71+ }
72+
73+ func platformViewDidLoadSetup( ) {
74+
75+ NotificationCenter . default. addObserver ( self ,
76+ selector: #selector( appCameToForeGround ( notification: ) ) ,
77+ name: UIApplication . willEnterForegroundNotification,
78+ object: nil )
79+
6580 }
6681
6782 override func viewWillAppear( _ animated: Bool ) {
6883
6984 super. viewWillAppear ( animated)
70- self . updateSignOutButton ( enabled: !self . accessToken. isEmpty)
85+ self . loadCurrentAccount ( )
86+ }
87+
88+ @objc func appCameToForeGround( notification: Notification ) {
89+ self . loadCurrentAccount ( )
7190 }
7291}
7392
@@ -103,15 +122,38 @@ extension ViewController {
103122
104123 let msalConfiguration = MSALPublicClientApplicationConfig ( clientId: kClientID, redirectUri: nil , authority: authority)
105124 self . applicationContext = try MSALPublicClientApplication ( configuration: msalConfiguration)
106-
107- self . webViewParamaters = MSALWebviewParameters ( parentViewController: self )
125+ self . initWebViewParams ( )
108126 }
109127
110128 func initWebViewParams( ) {
111129 self . webViewParamaters = MSALWebviewParameters ( parentViewController: self )
112130 }
113131}
114132
133+ // MARK: Shared device
134+
135+ extension ViewController {
136+
137+ @objc func getDeviceMode( _ sender: UIButton ) {
138+
139+ if #available( iOS 13 . 0 , * ) {
140+ self . applicationContext? . getDeviceInformation ( with: nil , completionBlock: { ( deviceInformation, error) in
141+
142+ guard let deviceInfo = deviceInformation else {
143+ self . updateLogging ( text: " Device info not returned. Error: \( String ( describing: error) ) " )
144+ return
145+ }
146+
147+ let isSharedDevice = deviceInfo. deviceMode == . shared
148+ let modeString = isSharedDevice ? " shared " : " private "
149+ self . updateLogging ( text: " Received device info. Device is in the \( modeString) mode. " )
150+ } )
151+ } else {
152+ self . updateLogging ( text: " Running on older iOS. GetDeviceInformation API is unavailable. " )
153+ }
154+ }
155+ }
156+
115157
116158// MARK: Acquiring and using token
117159
@@ -123,14 +165,18 @@ extension ViewController {
123165
124166 @objc func callGraphAPI( _ sender: UIButton ) {
125167
126- guard let currentAccount = self . currentAccount ( ) else {
127- // We check to see if we have a current logged in account.
128- // If we don't, then we need to sign someone in.
129- acquireTokenInteractively ( )
130- return
168+ self . loadCurrentAccount { ( account) in
169+
170+ guard let currentAccount = account else {
171+
172+ // We check to see if we have a current logged in account.
173+ // If we don't, then we need to sign someone in.
174+ self . acquireTokenInteractively ( )
175+ return
176+ }
177+
178+ self . acquireTokenSilently ( currentAccount)
131179 }
132-
133- acquireTokenSilently ( currentAccount)
134180 }
135181
136182 func acquireTokenInteractively( ) {
@@ -139,7 +185,7 @@ extension ViewController {
139185 guard let webViewParameters = self . webViewParamaters else { return }
140186
141187 let parameters = MSALInteractiveTokenParameters ( scopes: kScopes, webviewParameters: webViewParameters)
142- parameters. promptType = . selectAccount;
188+ parameters. promptType = . selectAccount
143189
144190 applicationContext. acquireToken ( with: parameters) { ( result, error) in
145191
@@ -157,7 +203,7 @@ extension ViewController {
157203
158204 self . accessToken = result. accessToken
159205 self . updateLogging ( text: " Access token is \( self . accessToken) " )
160- self . updateSignOutButton ( enabled : true )
206+ self . updateCurrentAccount ( account : result . account )
161207 self . getContentWithToken ( )
162208 }
163209 }
@@ -262,27 +308,47 @@ extension ViewController {
262308// MARK: Get account and removing cache
263309
264310extension ViewController {
265- func currentAccount( ) -> MSALAccount ? {
266-
267- guard let applicationContext = self . applicationContext else { return nil }
311+
312+ typealias AccountCompletion = ( MSALAccount ? ) -> Void
313+
314+ func loadCurrentAccount( completion: AccountCompletion ? = nil ) {
268315
269- // We retrieve our current account by getting the first account from cache
270- // In multi-account applications, account should be retrieved by home account identifier or username instead
316+ guard let applicationContext = self . applicationContext else { return }
271317
272- do {
318+ let msalParameters = MSALParameters ( )
319+ msalParameters. completionBlockQueue = DispatchQueue . main
320+
321+ // Note that this sample showcases an app that signs in a single account at a time
322+ // If you're building a more complex app that signs in multiple accounts at the same time, you'll need to use a different account retrieval API that specifies account identifier
323+ // For example, see "accountsFromDeviceForParameters:completionBlock:" - https://azuread.github.io/microsoft-authentication-library-for-objc/Classes/MSALPublicClientApplication.html#/c:objc(cs)MSALPublicClientApplication(im)accountsFromDeviceForParameters:completionBlock:
324+ applicationContext. getCurrentAccount ( with: msalParameters, completionBlock: { ( currentAccount, previousAccount, error) in
273325
274- let cachedAccounts = try applicationContext. allAccounts ( )
326+ if let error = error {
327+ self . updateLogging ( text: " Couldn't query current account with error: \( error) " )
328+ return
329+ }
275330
276- if !cachedAccounts. isEmpty {
277- return cachedAccounts. first
331+ if let currentAccount = currentAccount {
332+
333+ self . updateLogging ( text: " Found a signed in account \( String ( describing: currentAccount. username) ) . Updating data for that account... " )
334+
335+ self . updateCurrentAccount ( account: currentAccount)
336+
337+ if let completion = completion {
338+ completion ( self . currentAccount)
339+ }
340+
341+ return
278342 }
279343
280- } catch let error as NSError {
344+ self . updateLogging ( text: " Account signed out. Updating UX " )
345+ self . accessToken = " "
346+ self . updateCurrentAccount ( account: nil )
281347
282- self . updateLogging ( text : " Didn't find any accounts in cache: \( error ) " )
283- }
284-
285- return nil
348+ if let completion = completion {
349+ completion ( nil )
350+ }
351+ } )
286352 }
287353
288354 /**
@@ -293,7 +359,7 @@ extension ViewController {
293359
294360 guard let applicationContext = self . applicationContext else { return }
295361
296- guard let account = self . currentAccount ( ) else { return }
362+ guard let account = self . currentAccount else { return }
297363
298364 do {
299365
@@ -303,14 +369,21 @@ extension ViewController {
303369 - account: The account to remove from the cache
304370 */
305371
306- try applicationContext. remove ( account)
307- self . updateLogging ( text: " " )
308- self . updateSignOutButton ( enabled: false )
309- self . accessToken = " "
372+ let signoutParameters = MSALSignoutParameters ( webviewParameters: self . webViewParamaters!)
373+ signoutParameters. signoutFromBrowser = false
310374
311- } catch let error as NSError {
375+ applicationContext. signout ( with: account, signoutParameters: signoutParameters, completionBlock: { ( success, error) in
376+
377+ if let error = error {
378+ self . updateLogging ( text: " Couldn't sign out account with error: \( error) " )
379+ return
380+ }
381+
382+ self . updateLogging ( text: " Sign out completed successfully " )
383+ self . accessToken = " "
384+ self . updateCurrentAccount ( account: nil )
385+ } )
312386
313- self . updateLogging ( text: " Received error signing account out: \( error) " )
314387 }
315388 }
316389}
@@ -320,6 +393,20 @@ extension ViewController {
320393extension ViewController {
321394
322395 func initUI( ) {
396+
397+ usernameLabel = UILabel ( )
398+ usernameLabel. translatesAutoresizingMaskIntoConstraints = false
399+ usernameLabel. text = " "
400+ usernameLabel. textColor = . darkGray
401+ usernameLabel. textAlignment = . right
402+
403+ self . view. addSubview ( usernameLabel)
404+
405+ usernameLabel. topAnchor. constraint ( equalTo: view. topAnchor, constant: 50.0 ) . isActive = true
406+ usernameLabel. rightAnchor. constraint ( equalTo: view. rightAnchor, constant: - 10.0 ) . isActive = true
407+ usernameLabel. widthAnchor. constraint ( equalToConstant: 300.0 ) . isActive = true
408+ usernameLabel. heightAnchor. constraint ( equalToConstant: 50.0 ) . isActive = true
409+
323410 // Add call Graph button
324411 callGraphButton = UIButton ( )
325412 callGraphButton. translatesAutoresizingMaskIntoConstraints = false
@@ -329,7 +416,7 @@ extension ViewController {
329416 self . view. addSubview ( callGraphButton)
330417
331418 callGraphButton. centerXAnchor. constraint ( equalTo: view. centerXAnchor) . isActive = true
332- callGraphButton. topAnchor. constraint ( equalTo: view. topAnchor, constant: 50 .0) . isActive = true
419+ callGraphButton. topAnchor. constraint ( equalTo: view. topAnchor, constant: 120 .0) . isActive = true
333420 callGraphButton. widthAnchor. constraint ( equalToConstant: 300.0 ) . isActive = true
334421 callGraphButton. heightAnchor. constraint ( equalToConstant: 50.0 ) . isActive = true
335422
@@ -347,16 +434,28 @@ extension ViewController {
347434 signOutButton. widthAnchor. constraint ( equalToConstant: 150.0 ) . isActive = true
348435 signOutButton. heightAnchor. constraint ( equalToConstant: 50.0 ) . isActive = true
349436
437+ let deviceModeButton = UIButton ( )
438+ deviceModeButton. translatesAutoresizingMaskIntoConstraints = false
439+ deviceModeButton. setTitle ( " Get device info " , for: . normal) ;
440+ deviceModeButton. setTitleColor ( . blue, for: . normal) ;
441+ deviceModeButton. addTarget ( self , action: #selector( getDeviceMode ( _: ) ) , for: . touchUpInside)
442+ self . view. addSubview ( deviceModeButton)
443+
444+ deviceModeButton. centerXAnchor. constraint ( equalTo: view. centerXAnchor) . isActive = true
445+ deviceModeButton. topAnchor. constraint ( equalTo: signOutButton. bottomAnchor, constant: 10.0 ) . isActive = true
446+ deviceModeButton. widthAnchor. constraint ( equalToConstant: 150.0 ) . isActive = true
447+ deviceModeButton. heightAnchor. constraint ( equalToConstant: 50.0 ) . isActive = true
448+
350449 // Add logging textfield
351450 loggingText = UITextView ( )
352451 loggingText. isUserInteractionEnabled = false
353452 loggingText. translatesAutoresizingMaskIntoConstraints = false
354453
355454 self . view. addSubview ( loggingText)
356455
357- loggingText. topAnchor. constraint ( equalTo: signOutButton . bottomAnchor, constant: 10.0 ) . isActive = true
456+ loggingText. topAnchor. constraint ( equalTo: deviceModeButton . bottomAnchor, constant: 10.0 ) . isActive = true
358457 loggingText. leftAnchor. constraint ( equalTo: self . view. leftAnchor, constant: 10.0 ) . isActive = true
359- loggingText. rightAnchor. constraint ( equalTo: self . view. rightAnchor, constant: 10.0 ) . isActive = true
458+ loggingText. rightAnchor. constraint ( equalTo: self . view. rightAnchor, constant: - 10.0 ) . isActive = true
360459 loggingText. bottomAnchor. constraint ( equalTo: self . view. bottomAnchor, constant: 10.0 ) . isActive = true
361460 }
362461
@@ -380,4 +479,20 @@ extension ViewController {
380479 }
381480 }
382481 }
482+
483+ func updateAccountLabel( ) {
484+
485+ guard let currentAccount = self . currentAccount else {
486+ self . usernameLabel. text = " Signed out "
487+ return
488+ }
489+
490+ self . usernameLabel. text = currentAccount. username
491+ }
492+
493+ func updateCurrentAccount( account: MSALAccount ? ) {
494+ self . currentAccount = account
495+ self . updateAccountLabel ( )
496+ self . updateSignOutButton ( enabled: account != nil )
497+ }
383498}
0 commit comments