Skip to content

Commit b07ae65

Browse files
committed
(chore): Add SignUpViewModel
1 parent 00c7530 commit b07ae65

File tree

4 files changed

+202
-4
lines changed

4 files changed

+202
-4
lines changed

ScribbleLab/App/ScribbleLabApp.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,10 @@ struct ScribbleLabApp: App {
4141
init() {
4242
/* SCRLog */
4343
logger = SCRLog.shared
44-
SCRLog.configureShared(subsystem: "com.nhsystems.scribblelab", category: .log)
44+
SCRLog.configureShared(
45+
subsystem: "com.nhsystems.scribblelab",
46+
category: .log
47+
)
4548
logger.log("SCRLogStream successfully initialized")
4649
}
4750

ScribbleLab/Core/Authentication/Models/SignInViewModel.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ class SignInViewModel: ObservableObject {
7575
return !email.isEmpty && validateEmail(withEmail: email) && password.count >= 8
7676
}
7777

78-
func validateEmail(withEmail email: String) -> Bool {
78+
private func validateEmail(withEmail email: String) -> Bool {
7979
return Validation.isValidEmail(email)
8080
}
8181

ScribbleLab/Core/Authentication/Models/SignUpViewModel.swift

Lines changed: 76 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,13 @@ import ScribbleFoundation
3737

3838
class SignUpViewModel: ObservableObject {
3939

40+
/// Published property holding the entered email.
4041
@Published var email: String = ""
42+
43+
/// Published property holding the entered password.
4144
@Published var password: String = ""
45+
46+
/// Published property holding the entered username.
4247
@Published var username: String = ""
4348

4449
/// Published property determining if the sign-up button is enabled.
@@ -50,10 +55,79 @@ class SignUpViewModel: ObservableObject {
5055

5156
private var cancellables = Set<AnyCancellable>()
5257

58+
init() {
59+
setUpValidation()
60+
}
61+
62+
// That's the most ugliest code I've ever written - please don't blame me
63+
// I'm just tired - Thank you for your understanding!
64+
private func setUpValidation() {
65+
Publishers.CombineLatest3($email, $password, $username)
66+
.sink { [weak self] email, password, username in
67+
guard let self = self else { return }
68+
69+
let isInputValid = self.validateInput(
70+
email: email,
71+
password: password,
72+
username: username
73+
)
74+
75+
if isInputValid {
76+
Task {
77+
if let isAvailable = try? await self.checkUsernameAvailability(for: username) {
78+
DispatchQueue.main.async {
79+
self.isSignUpButtonEnabled = isAvailable
80+
}
81+
}
82+
}
83+
} else {
84+
self.isSignUpButtonEnabled = false
85+
}
86+
}
87+
.store(in: &cancellables)
88+
}
5389

90+
private func validateInput(
91+
email: String,
92+
password: String,
93+
username: String
94+
) -> Bool {
95+
guard Validation.isValidEmail(email),
96+
Validation.isStrongPassword(password),
97+
!username.isEmpty
98+
else {
99+
return false
100+
}
101+
102+
return true
103+
}
54104

55-
init() {}
105+
private func checkUsernameAvailability(for username: String) async throws -> Bool {
106+
// swiftlint:disable:next identifier_name
107+
let db = Firestore.firestore()
108+
109+
let usersRef = db.collection("users")
110+
let query = usersRef.whereField("username", isEqualTo: username)
111+
112+
let snapshot = try await query.getDocuments()
113+
114+
return snapshot.isEmpty
115+
}
56116

117+
func createScribbleID() async throws {
118+
try await SCIDAuthService.shared.createUser(
119+
email: email,
120+
password: password,
121+
username: username
122+
)
123+
124+
// Resetting published properties after user creation
125+
username = ""
126+
password = ""
127+
email = ""
128+
}
57129

58-
deinit {}
130+
deinit {
131+
cancellables.removeAll()
132+
}
59133
}

ScribbleLab/Misc/SCIDError.swift

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,121 @@
1+
//
2+
// SCIDError.swift
3+
// ScribbleLab
4+
//
5+
// Copyright (c) 2024 ScribbleLabApp LLC. - All rights reserved.
6+
//
7+
// Redistribution and use in source and binary forms, with or without
8+
// modification, are permitted provided that the following conditions are met:
9+
//
10+
// 1. Redistributions of source code must retain the above copyright notice, this
11+
// list of conditions and the following disclaimer.
12+
//
13+
// 2. Redistributions in binary form must reproduce the above copyright notice,
14+
// this list of conditions and the following disclaimer in the documentation
15+
// and/or other materials provided with the distribution.
16+
//
17+
// 3. Neither the name of the copyright holder nor the names of its
18+
// contributors may be used to endorse or promote products derived from
19+
// this software without specific prior written permission.
20+
//
21+
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22+
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23+
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24+
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25+
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26+
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27+
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28+
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29+
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30+
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31+
32+
import Foundation
33+
34+
/// A comprehensive list of errors that can occur within the ScribbleLabApp identity and user services.
35+
///
36+
/// The `SCIDError` enum categorizes errors into those related to Google authentication processes, such as
37+
/// OAuth flows and user sign-ins, and errors encountered in user data retrieval and handling. Each error case
38+
/// provides contextual information and a standardized error code to support debugging and enhance error
39+
/// tracking throughout the app.
40+
///
41+
/// - Note: These error codes can be used in logging and alerting systems to quickly identify the
42+
/// error source and cause.
43+
internal enum SCIDError: Error {
44+
45+
/// The client ID required for Google Sign-In is missing.
46+
///
47+
/// This error occurs if the app is unable to retrieve the necessary client ID to initiate Google OAuth
48+
/// authentication. The absence of this ID prevents the app from making the initial connection with
49+
/// Google’s authentication services.
50+
///
51+
/// - Error Code: `GID-E115`
52+
case missingClientID
53+
54+
/// Failed to present the OAuth consent sheet for Google Sign-In.
55+
///
56+
/// This error is encountered when the app cannot display the OAuth consent sheet, which is
57+
/// required for users to grant permissions to their Google account. This may happen due to
58+
/// UI-related constraints or network issues.
59+
///
60+
/// - Error Code: `GID-E110`
61+
case failedOAuthSheet
62+
63+
64+
/// The signed-in user or the ID token is unavailable after an attempt to sign in.
65+
///
66+
/// This error is returned if the app fails to retrieve the expected user profile or ID token
67+
/// following a successful OAuth flow, potentially due to session timeouts or network interruptions.
68+
///
69+
/// - Error Code: `GID-E116`
70+
case missingUserOrToken
71+
72+
/// Google Sign-In failed due to an unknown error.
73+
///
74+
/// This error generally occurs when the sign-in process does not complete successfully, either due
75+
/// to connectivity issues or unhandled exceptions during authentication.
76+
///
77+
/// - Error Code: `GID-E118`
78+
case signInFailed
79+
80+
/// The signed-in user object is unavailable after Google authentication completes.
81+
///
82+
/// This error occurs when the user object is not retrievable even after a successful Google Sign-In.
83+
/// It is likely related to session handling or data parsing issues during the sign-in process.
84+
///
85+
/// - Error Code: `GID-E119`
86+
case missingSignedInUser
87+
88+
/// The user’s login attempt failed.
89+
///
90+
/// This error represents a general failure in the login process. It may be caused by invalid credentials,
91+
/// connectivity issues, or other authentication-related errors.
92+
///
93+
/// - Error Code: `AUT-E101`
94+
case logInFailed
95+
96+
/// Failed to create a new user account.
97+
///
98+
/// This error occurs when the app fails to establish a new user account, potentially due to server-side
99+
/// issues, invalid data inputs, or connectivity problems during account creation.
100+
///
101+
/// - Error Code: `AUT-E102`
102+
case userCreationFailed
103+
104+
/// The requested user data could not be found or was invalid.
105+
///
106+
/// This error is encountered when attempting to fetch user data from the database, but the returned data
107+
/// is either missing or improperly formatted. This may occur if the user’s record does not exist or if data
108+
/// parsing fails.
109+
///
110+
/// - Error Code: `SLUSR-E201`
111+
case userDataNotFound
112+
113+
/// Required user fields are missing or incomplete in the retrieved data.
114+
///
115+
/// This error is thrown when essential fields such as user ID, username, or email are missing in the
116+
/// retrieved user data, leading to incomplete user profiles. This may happen due to improper data
117+
/// entry or issues in database management.
118+
///
119+
/// - Error Code: `SLUSR-E202`
120+
case missingRequiredUserFields
121+
}

0 commit comments

Comments
 (0)