Skip to content

Commit

Permalink
[auth-swift] Sample app Settings screen 2
Browse files Browse the repository at this point in the history
  • Loading branch information
paulb777 committed Sep 12, 2023
1 parent 1bc8a8e commit 450fe6c
Show file tree
Hide file tree
Showing 4 changed files with 182 additions and 30 deletions.
23 changes: 17 additions & 6 deletions FirebaseAuth/Sources/Swift/Auth/Auth.swift
Original file line number Diff line number Diff line change
Expand Up @@ -169,20 +169,29 @@ extension Auth: AuthInterop {
/** @property app
@brief Gets the `FirebaseApp` object that this auth object is connected to.
*/
@objc public weak var app: FirebaseApp?
@objc public internal(set) weak var app: FirebaseApp?

/** @property currentUser
@brief Synchronously gets the cached current user, or null if there is none.
*/
@objc public var currentUser: User?
@objc public internal(set) var currentUser: User?

/** @property languageCode
@brief The current user language code. This property can be set to the app's current language by
calling `useAppLanguage()`.

@remarks The string used to set this property must be a language code that follows BCP 47.
*/
@objc public var languageCode: String?
@objc public var languageCode: String? {
get {
return requestConfiguration.languageCode
}
set(val) {
kAuthGlobalWorkQueue.sync {
requestConfiguration.languageCode = val
}
}
}

/** @property settings
@brief Contains settings related to the auth object.
Expand All @@ -192,7 +201,7 @@ extension Auth: AuthInterop {
/** @property userAccessGroup
@brief The current user access group that the Auth instance is using. Default is nil.
*/
@objc public var userAccessGroup: String?
@objc public internal(set) var userAccessGroup: String?

/** @property shareAuthStateAcrossDevices
@brief Contains shareAuthStateAcrossDevices setting related to the auth object.
Expand Down Expand Up @@ -1458,8 +1467,10 @@ extension Auth: AuthInterop {
@brief Sets `languageCode` to the app's current language.
*/
@objc public func useAppLanguage() {
kAuthGlobalWorkQueue.async {
self.requestConfiguration.languageCode = Locale.preferredLanguages.first
kAuthGlobalWorkQueue.sync {
if let language = Locale.preferredLanguages.first {
self.requestConfiguration.languageCode = language
}
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
DEC2E5DD2A95331E0090260A /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEC2E5DC2A95331D0090260A /* SettingsViewController.swift */; };
DEC2E5DF2A9583CA0090260A /* AppManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = DEC2E5DE2A9583CA0090260A /* AppManager.swift */; };
DEC2E5E42A966DE20090260A /* GoogleService-Info_multi.plist in Resources */ = {isa = PBXBuildFile; fileRef = DEC2E5E32A966DE20090260A /* GoogleService-Info_multi.plist */; };
DED37F632AB0C4F7003A67E4 /* SettingsUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = DED37F622AB0C4F7003A67E4 /* SettingsUITests.swift */; };
EA02F68524A000E00079D000 /* UserActions.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA02F68424A000E00079D000 /* UserActions.swift */; };
EA02F68D24A063E90079D000 /* LoginDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = EA02F68C24A063E90079D000 /* LoginDelegate.swift */; };
EA062D5D24A0FEB6006714D3 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = EA062D5C24A0FEB6006714D3 /* README.md */; };
Expand Down Expand Up @@ -102,6 +103,7 @@
DEC2E5DC2A95331D0090260A /* SettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewController.swift; sourceTree = "<group>"; };
DEC2E5DE2A9583CA0090260A /* AppManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppManager.swift; sourceTree = "<group>"; };
DEC2E5E32A966DE20090260A /* GoogleService-Info_multi.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = "GoogleService-Info_multi.plist"; sourceTree = SOURCE_ROOT; };
DED37F622AB0C4F7003A67E4 /* SettingsUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsUITests.swift; sourceTree = "<group>"; };
EA02F68424A000E00079D000 /* UserActions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserActions.swift; sourceTree = "<group>"; };
EA02F68C24A063E90079D000 /* LoginDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LoginDelegate.swift; sourceTree = "<group>"; };
EA062D5C24A0FEB6006714D3 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = SOURCE_ROOT; };
Expand Down Expand Up @@ -327,6 +329,7 @@
children = (
EAE4CBE624855E3E00245E92 /* AuthenticationExampleUITests.swift */,
EAE4CBE824855E3E00245E92 /* Info.plist */,
DED37F622AB0C4F7003A67E4 /* SettingsUITests.swift */,
);
path = AuthenticationExampleUITests;
sourceTree = "<group>";
Expand Down Expand Up @@ -555,6 +558,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
DED37F632AB0C4F7003A67E4 /* SettingsUITests.swift in Sources */,
EAE4CBE724855E3E00245E92 /* AuthenticationExampleUITests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ enum SettingsAction: String {
case toggleSecureTokenAPI = "Secure Token"
case toggleActiveApp = "Active App"
case toggleAccessGroup = "Current Access Group"
case setAuthLanugage = "Auth Language"
case useAppLanguage = "Use App Language"
case togglePhoneAppVerification = "Disable App Verification (Phone)"
}

class SettingsViewController: UIViewController, DataSourceProviderDelegate {
Expand Down Expand Up @@ -61,6 +64,7 @@ class SettingsViewController: UIViewController, DataSourceProviderDelegate {
// The row tapped has no affiliated action.
return
}
let auth = AppManager.shared.auth()

switch action {
case .toggleSecureTokenAPI:
Expand All @@ -71,6 +75,15 @@ class SettingsViewController: UIViewController, DataSourceProviderDelegate {
AppManager.shared.toggle()
case .toggleAccessGroup:
toggleAccessGroup()
case .setAuthLanugage:
setAuthLanguage()
case .useAppLanguage:
auth.useAppLanguage()
case .togglePhoneAppVerification:
guard let settings = auth.settings else {
fatalError("Unset auth.settings")
}
settings.appVerificationDisabledForTesting = !settings.isAppVerificationDisabledForTesting
}
updateUI()
}
Expand Down Expand Up @@ -105,6 +118,19 @@ class SettingsViewController: UIViewController, DataSourceProviderDelegate {
}
}

private func setAuthLanguage() {
let prompt = UIAlertController(title: nil, message: "Enter Language Code For Auth:",
preferredStyle: .alert)
prompt.addTextField()
let okAction = UIAlertAction(title: "OK", style: .default) { action in
AppManager.shared.auth().languageCode = prompt.textFields?[0].text ?? ""
self.updateUI()
}
prompt.addAction(okAction)

present(prompt, animated: true)
}

// MARK: - Private Helpers

private func configureNavigationBar() {
Expand Down Expand Up @@ -134,29 +160,6 @@ class SettingsViewController: UIViewController, DataSourceProviderDelegate {
options: .transitionCrossDissolve,
animations: { tableView.reloadData() })
}

private func presentEditUserInfoController(for title: String,
to saveHandler: @escaping (String) -> Void) {
let editController = UIAlertController(
title: "Update \(title)",
message: nil,
preferredStyle: .alert
)
editController.addTextField { $0.placeholder = "New \(title)" }

let saveHandler1: (UIAlertAction) -> Void = { _ in
let text = editController.textFields!.first!.text!
saveHandler(text)
}

let cancel: (UIAlertAction) -> Void = { _ in
saveHandler("")
}

editController.addAction(UIAlertAction(title: "Save", style: .default, handler: saveHandler1))
editController.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: cancel))
present(editController, animated: true, completion: nil)
}
}

// MARK: - Extending a `AuthSettings` to conform to `DataSourceProvidable`
Expand Down Expand Up @@ -184,7 +187,48 @@ extension AuthSettings: DataSourceProvidable {
return Section(headerDescription: "Keychain Access Groups", items: items)
}

// TODO: Add ability to click and clear both of these fields.
private var phoneAuthSection: Section {
var tokenString = "No Token"
var credentialString = "No Credential"
if let token = AppManager.shared.auth().tokenManager.token {
let tokenType = token.type == .prod ? "Production" : "Sandbox"
tokenString = "token: \(token.string): type: \(tokenType)"
}
if let credential = AppManager.shared.auth().appCredentialManager.credential {
// TODO: Maybe use truncatedString like ObjC sample
credentialString = "\(credential.receipt)/\(credential.secret ?? "nil")"
}
let items = [Item(title: tokenString, detailTitle: "APNs Token"),
Item(title: credentialString, detailTitle: "App Credential")]
return Section(headerDescription: "Phone Auth - TODO toggle off", items: items)
}

private var languageSection: Section {
let languageCode = AppManager.shared.auth().languageCode
let items = [Item(title: languageCode ?? "[none]", detailTitle: "Auth Language"),
Item(title: "Click to Use App Language", detailTitle: "Use App Language")]
return Section(headerDescription: "Language", items: items)
}

private var disableSection: Section {
guard let settings = AppManager.shared.auth().settings else {
fatalError("Missing auth settings")
}
let disabling = settings.isAppVerificationDisabledForTesting ? "YES" : "NO"
let items = [Item(title: disabling, detailTitle: "Disable App Verification (Phone)")]
return Section(headerDescription: "Auth Settings", items: items)
}

var sections: [Section] {
[versionSection, apiHostSection, appsSection, keychainSection]
[
versionSection,
apiHostSection,
appsSection,
keychainSection,
phoneAuthSection,
languageSection,
disableSection,
]
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// Copyright 2023 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

import XCTest

class SettingsUITests: XCTestCase {
var app: XCUIApplication!

override func setUp() {
super.setUp()

continueAfterFailure = false

app = XCUIApplication()
app.launch()
}

func testSettings() {
app.staticTexts["Settings"].tap()

wait(forElement: app.navigationBars["Settings"], timeout: 5.0)
XCTAssertTrue(app.navigationBars["Settings"].exists)

// Test Identity toolkit
let identityCell = app.cells.containing(.staticText, identifier: "Identity Toolkit").element
XCTAssertTrue(identityCell.staticTexts["www.googleapis.com"].exists)
identityCell.tap()
XCTAssertTrue(identityCell.staticTexts["staging-www.sandbox.googleapis.com"].exists)
identityCell.tap()
XCTAssertTrue(identityCell.staticTexts["www.googleapis.com"].exists)

// Test Secure Token
let secureTokenCell = app.cells.containing(.staticText, identifier: "Secure Token").element
XCTAssertTrue(secureTokenCell.staticTexts["securetoken.googleapis.com"].exists)
secureTokenCell.tap()
XCTAssertTrue(secureTokenCell.staticTexts["staging-securetoken.sandbox.googleapis.com"].exists)
secureTokenCell.tap()
XCTAssertTrue(secureTokenCell.staticTexts["securetoken.googleapis.com"].exists)

// Swap Firebase App
let appCell = app.cells.containing(.staticText, identifier: "Active App").element
XCTAssertTrue(appCell.staticTexts["gcip-ios-test"].exists)
appCell.tap()
XCTAssertTrue(appCell.staticTexts["fb-sa-upgraded"].exists)
appCell.tap()
XCTAssertTrue(appCell.staticTexts["gcip-ios-test"].exists)

// Current Access Group
let accessCell = app.cells.containing(.staticText, identifier: "Current Access Group").element
XCTAssertTrue(accessCell.staticTexts["[none]"].exists)
accessCell.tap()
// XCTAssertTrue(accessCell.staticTexts["com.google.firebase.auth.keychainGroup1"].exists)
accessCell.tap()
XCTAssertTrue(accessCell.staticTexts["[none]"].exists)

// Auth Language
let languageCell = app.cells.containing(.staticText, identifier: "Auth Language").element
XCTAssertTrue(languageCell.staticTexts["[none]"].exists)
languageCell.tap()
app.typeText("abc")
app.buttons["OK"].tap()
XCTAssertTrue(languageCell.staticTexts["abc"].exists)

// TODO: PhoneAuth

// Click to Use App Language
let appLanguageCell = app.cells.containing(.staticText,
identifier: "Click to Use App Language").element
appLanguageCell.tap()
XCTAssertTrue(languageCell.staticTexts["en"].exists)

// Disable App Verification
let disabledCell = app.cells.containing(.staticText,
identifier: "Disable App Verification (Phone)")
.element
XCTAssertTrue(disabledCell.staticTexts["NO"].exists, "App verification should NOT be disabled")
disabledCell.tap()
XCTAssertTrue(disabledCell.staticTexts["YES"].exists, "App verification should NOW be disabled")
disabledCell.tap()
XCTAssertTrue(disabledCell.staticTexts["NO"].exists, "App verification should NOT be disabled")
}
}

0 comments on commit 450fe6c

Please sign in to comment.