Skip to content

Commit de2102c

Browse files
committed
SwiftFirebase: initial import
0 parents  commit de2102c

31 files changed

+1648
-0
lines changed

.gitattributes

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
.gitattributes eol=lf
2+
.gitignore eol=lf
3+
*.hh eol=lf
4+
*.json eol=lf
5+
*.plist eol=lf
6+
*.swift eol=lf

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
*.sw?
2+
.build/
3+
.vscode/
4+
Examples/FireBaseUI/Resources/google-services-desktop.json
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
2+
<asmv1:assembly
3+
manifestVersion="1.0"
4+
xmlns:asmv1="urn:schemas-microsoft-com:asm.v1"
5+
xmlns:asmv3="urn:schemas-microsoft-com:asm.v3">
6+
7+
<!-- assembly identity -->
8+
<asmv1:assemblyIdentity
9+
name="org.compnerd.SwiftFirebase.FireBaseUI"
10+
processorArchitecture="*"
11+
type="win32"
12+
version="1.0.0.0"/>
13+
14+
<!-- application specific settings -->
15+
<asmv3:application xmlns="http://schemas.microsoft.com/SMI/2016/WindowsSettings">
16+
<windowsSettings>
17+
<dpiAwareness xmlns="">PerMonitorV2</dpiAwareness>
18+
<dpiAware>true</dpiAware>
19+
</windowsSettings>
20+
</asmv3:application>
21+
22+
<asmv1:description>FireBaseUI</asmv1:description>
23+
24+
<!-- Common Control Support -->
25+
<asmv1:dependency>
26+
<asmv1:dependentAssembly>
27+
<asmv1:assemblyIdentity
28+
language="*"
29+
name="Microsoft.Windows.Common-Controls"
30+
processorArchitecture="*"
31+
publicKeyToken="6595b64144ccf1df"
32+
type="win32"
33+
version="6.0.0.0"
34+
/>
35+
</asmv1:dependentAssembly>
36+
</asmv1:dependency>
37+
</asmv1:assembly>

Examples/FireBaseUI/FireBaseUI.swift

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// SPDX-License-Identifier: BSD-3-Clause
2+
3+
import SwiftWin32
4+
import Foundation
5+
6+
import firebase
7+
import FirebaseCore
8+
import FirebaseAuth
9+
10+
extension Foundation.Bundle {
11+
internal static var resources: URL {
12+
Bundle.module.bundleURL.appendingPathComponent("Resources")
13+
}
14+
}
15+
16+
@main
17+
final class FireBaseUI: ApplicationDelegate {
18+
func application(_ application: Application,
19+
didFinishLaunchingWithOptions: [Application.LaunchOptionsKey:Any]?)
20+
-> Bool {
21+
#if _runtime(_ObjC)
22+
firebase.App.SetDefaultConfigPath(Bundle.resources.fileSystemRepresentation)
23+
#else
24+
Bundle.resources.withUnsafeFileSystemRepresentation(firebase.App.SetDefaultConfigPath)
25+
#endif
26+
27+
FirebaseApp.configure()
28+
return true
29+
}
30+
}
Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
// SPDX-License-Identifier: BSD-3-Clause
2+
3+
import firebase
4+
import FirebaseCore
5+
import FirebaseAuth
6+
import SwiftWin32
7+
8+
private final class FireBaseLogLevelPickerHandler {
9+
private let levels = ["Default", "Verbose", "Debug", "Info", "Warning", "Error", "Assert"]
10+
}
11+
12+
extension FireBaseLogLevelPickerHandler: PickerViewDataSource {
13+
public func numberOfComponents(in pickerView: PickerView) -> Int {
14+
1
15+
}
16+
17+
public func pickerView(_ pickerView: PickerView,
18+
numberOfRowsInComponent component: Int) -> Int {
19+
self.levels.count
20+
}
21+
}
22+
23+
extension FireBaseLogLevelPickerHandler: PickerViewDelegate {
24+
public func pickerView(_ pickerView: PickerView, titleForRow row: Int,
25+
forComponent component: Int) -> String? {
26+
self.levels[row]
27+
}
28+
29+
public func pickerView(_ pickerView: PickerView, didSelectRow row: Int,
30+
inComponent component: Int) {
31+
guard row > 0 else { return }
32+
FirebaseConfiguration.shared.setLoggerLevel(FirebaseLoggerLevel(rawValue: CInt(row - 1)))
33+
}
34+
}
35+
36+
// MARK: - FireBaseUIViewController
37+
38+
internal final class FireBaseUIViewController: ViewController {
39+
fileprivate let firebaseLogHandler = FireBaseLogLevelPickerHandler()
40+
41+
var cboLogLevel = PickerView(frame: Rect(x: 136, y: 8, width: 256, height: 24))
42+
var txtEmail = TextField(frame: Rect(x: 136, y: 46, width: 512, height: 24))
43+
var txtPassword = TextField(frame: Rect(x: 136, y: 78, width: 512, height: 24))
44+
var btnSignIn = Button(frame: Rect(x: 8, y: 116, width: 120, height: 32),
45+
title: "Sign In")
46+
var btnToken = Button(frame: Rect(x: 8, y: 180, width: 120, height: 32),
47+
title: "Get Token")
48+
var chkRefresh = Switch(frame: Rect(x: 8, y: 212, width: 648, height: 32),
49+
title: "Force Token Refresh")
50+
var txtToken: TextView = TextView(frame: Rect(x: 8, y: 244, width: 640, height: 48))
51+
52+
var btnCreate = Button(frame: Rect(x: 8, y: 324, width: 120, height: 32),
53+
title: "Create User")
54+
var btnVerify = Button(frame: Rect(x: 132, y: 324, width: 120, height: 32),
55+
title: "Verify Email")
56+
var btnReset = Button(frame: Rect(x: 256, y: 324, width: 156, height: 32),
57+
title: "Reset Password")
58+
59+
override func viewDidLoad() {
60+
super.viewDidLoad()
61+
self.title = "FireBase UI"
62+
configureView()
63+
64+
if let user = Auth.auth().currentUser {
65+
txtEmail.text = user.email
66+
try? Auth.auth().signOut()
67+
}
68+
}
69+
70+
private func configureView() {
71+
let lblLogLevel = Label(frame: Rect(x: 8, y: 8, width: 128, height: 24),
72+
title: "Log Level:")
73+
self.view?.addSubview(lblLogLevel)
74+
75+
cboLogLevel.dataSource = firebaseLogHandler
76+
cboLogLevel.delegate = firebaseLogHandler
77+
self.view?.addSubview(cboLogLevel)
78+
cboLogLevel.reloadAllComponents()
79+
cboLogLevel.selectRow(0, inComponent: 0, animated: false)
80+
81+
let lblEmail = Label(frame: Rect(x: 8, y: 46, width: 128, height: 20),
82+
title: "Email Address:")
83+
self.view?.addSubview(lblEmail)
84+
self.view?.addSubview(txtEmail)
85+
86+
let lblPassword = Label(frame: Rect(x: 8, y: 78, width: 128, height: 20),
87+
title: "Password:")
88+
self.view?.addSubview(lblPassword)
89+
90+
txtPassword.isSecureTextEntry = true
91+
self.view?.addSubview(txtPassword)
92+
93+
btnSignIn.addTarget(self, action: FireBaseUIViewController.signIn,
94+
for: .primaryActionTriggered)
95+
self.view?.addSubview(btnSignIn)
96+
97+
btnToken.addTarget(self, action: FireBaseUIViewController.getToken,
98+
for: .primaryActionTriggered)
99+
self.view?.addSubview(btnToken)
100+
101+
self.view?.addSubview(chkRefresh)
102+
103+
txtToken.editable = false
104+
txtToken.font = .systemFont(ofSize: 9)
105+
self.view?.addSubview(txtToken)
106+
107+
btnCreate.addTarget(self, action: FireBaseUIViewController.createUser,
108+
for: .primaryActionTriggered)
109+
self.view?.addSubview(btnCreate)
110+
111+
btnVerify.addTarget(self, action: FireBaseUIViewController.verifyEmail,
112+
for: .primaryActionTriggered)
113+
self.view?.addSubview(btnVerify)
114+
115+
btnReset.addTarget(self, action: FireBaseUIViewController.resetPassword,
116+
for: .primaryActionTriggered)
117+
self.view?.addSubview(btnReset)
118+
}
119+
120+
private func signIn() {
121+
guard let email = txtEmail.text, let password = txtPassword.text else {
122+
print("email and password are required")
123+
return
124+
}
125+
126+
Task {
127+
do {
128+
_ = try await Auth.auth().signIn(withEmail: email, password: password)
129+
} catch {
130+
print("Error signing in: \(error.localizedDescription)")
131+
}
132+
}
133+
}
134+
135+
private func createUser() {
136+
guard let email = txtEmail.text, let password = txtPassword.text else {
137+
print("email and password are required")
138+
return
139+
}
140+
141+
Task {
142+
do {
143+
_ = try await Auth.auth().createUser(withEmail: email, password: password)
144+
} catch {
145+
print("Error signing in: \(error.localizedDescription)")
146+
}
147+
}
148+
}
149+
150+
private func getToken() {
151+
Task {
152+
guard var user = Auth.auth().currentUser else {
153+
print("user not logged in")
154+
return
155+
}
156+
157+
if chkRefresh.isOn {
158+
do {
159+
let result = try await user.getIDTokenResult(forcingRefresh: true)
160+
txtToken.text = result.token
161+
} catch {
162+
print("Error refreshing token: \(error.localizedDescription)")
163+
}
164+
} else {
165+
do {
166+
txtToken.text = try await user.getIDToken()
167+
} catch {
168+
print("Error refreshing token: \(error.localizedDescription)")
169+
}
170+
}
171+
}
172+
}
173+
174+
private func verifyEmail() {
175+
Task {
176+
guard var user = Auth.auth().currentUser else {
177+
print("user not logged in")
178+
return
179+
}
180+
181+
try await user.sendEmailVerification()
182+
}
183+
}
184+
185+
private func resetPassword() {
186+
guard let email = txtEmail.text else {
187+
print("email is required")
188+
return
189+
}
190+
191+
Task {
192+
do {
193+
_ = try await Auth.auth().sendPasswordReset(withEmail: email)
194+
} catch {
195+
print("Error sending password reset: \(error.localizedDescription)")
196+
}
197+
}
198+
}
199+
}

Examples/FireBaseUI/Info.plist

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
3+
<plist version="1.0">
4+
<dict>
5+
<key>CFBundleIdentifier</key>
6+
<string>org.compnerd.SwiftFirebase.FireBaseUI</string>
7+
<key>ApplicationSceneManifest</key>
8+
<dict>
9+
<key>ApplicationSupportsMultipleScenes</key>
10+
<false/>
11+
<key>SceneConfigurations</key>
12+
<dict>
13+
<key>UIWindowSceneSessionRoleApplication</key>
14+
<array>
15+
<dict>
16+
<key>SceneConfigurationName</key>
17+
<string>Default Configuration</string>
18+
<key>SceneDelegateClassName</key>
19+
<string>FireBaseUI.SceneDelegate</string>
20+
</dict>
21+
</array>
22+
</dict>
23+
</dict>
24+
</dict>
25+
</plist>
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
{
2+
"project_info": {
3+
"project_id": "test-do-not-use",
4+
"firebase_url": "https://test-do-not-use.firebaseio.com",
5+
"storage_bucket": "test-do-not-use.firebaseio.com"
6+
},
7+
"client": [
8+
{
9+
"api_key": [
10+
{ "current_key": "000000000000000000000000000000000000000" }
11+
],
12+
"client_info": {
13+
"mobilesdk_app_id": "1:999999999999:ios:0000000000000000",
14+
"android_client_info": {
15+
"package_name": "com.firebaseio.test-do-not-use"
16+
}
17+
}
18+
}
19+
]
20+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// SPDX-License-Identifier: BSD-3-Clause
2+
3+
import SwiftWin32
4+
5+
final class SceneDelegate: WindowSceneDelegate {
6+
var window: Window?
7+
8+
func scene(_ scene: Scene, willConnectTo session: SceneSession,
9+
options: Scene.ConnectionOptions) {
10+
guard let windowScene = scene as? WindowScene else { return }
11+
12+
self.window = Window(windowScene: windowScene)
13+
self.window?.rootViewController = FireBaseUIViewController()
14+
self.window?.makeKeyAndVisible()
15+
}
16+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// SPDX-License-Identifier: BSD-3-Clause
2+
3+
import SwiftWin32
4+
5+
extension Button {
6+
internal convenience init(frame: Rect, title: String) {
7+
self.init(frame: frame)
8+
self.setTitle(title, forState: .normal)
9+
}
10+
}
11+
12+
extension Label {
13+
internal convenience init(frame: Rect, title: String) {
14+
self.init(frame: frame)
15+
self.text = title
16+
}
17+
}
18+
19+
extension Switch {
20+
internal convenience init(frame: Rect, title: String) {
21+
self.init(frame: frame)
22+
self.title = title
23+
}
24+
}

LICENSE

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
BSD 3-Clause License
2+
3+
Copyright (c) 2023, Saleem Abdulrasool
4+
5+
Redistribution and use in source and binary forms, with or without
6+
modification, are permitted provided that the following conditions are met:
7+
8+
1. Redistributions of source code must retain the above copyright notice, this
9+
list of conditions and the following disclaimer.
10+
11+
2. Redistributions in binary form must reproduce the above copyright notice,
12+
this list of conditions and the following disclaimer in the documentation
13+
and/or other materials provided with the distribution.
14+
15+
3. Neither the name of the copyright holder nor the names of its
16+
contributors may be used to endorse or promote products derived from
17+
this software without specific prior written permission.
18+
19+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
22+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
23+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
25+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
26+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
27+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
28+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

0 commit comments

Comments
 (0)