Skip to content

Sign up view #76

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions SwiftUIBasics.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,9 @@

/* Begin PBXBuildFile section */
30CA734E2B24AE460025B2EB /* SymbolsAnimations.swift in Sources */ = {isa = PBXBuildFile; fileRef = 30CA734D2B24AE460025B2EB /* SymbolsAnimations.swift */; };
5544EF492B52003C0070BA56 /* AuthenticationViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5544EF482B52003C0070BA56 /* AuthenticationViewModel.swift */; };
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

File not needed on PR

5544EF4B2B5202A60070BA56 /* TestSignUp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5544EF4A2B5202A60070BA56 /* TestSignUp.swift */; };
5544EF4D2B5204A00070BA56 /* SignUpViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5544EF4C2B5204A00070BA56 /* SignUpViewModel.swift */; };
557042892B24CB310048C81C /* ProfileListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 557042882B24CB310048C81C /* ProfileListView.swift */; };
5570428B2B24D2F90048C81C /* TabBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5570428A2B24D2F90048C81C /* TabBarView.swift */; };
5570428D2B24D8050048C81C /* SignUpView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5570428C2B24D8050048C81C /* SignUpView.swift */; };
Expand All @@ -24,6 +27,9 @@

/* Begin PBXFileReference section */
30CA734D2B24AE460025B2EB /* SymbolsAnimations.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SymbolsAnimations.swift; sourceTree = "<group>"; };
5544EF482B52003C0070BA56 /* AuthenticationViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AuthenticationViewModel.swift; sourceTree = "<group>"; };
5544EF4A2B5202A60070BA56 /* TestSignUp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TestSignUp.swift; sourceTree = "<group>"; };
5544EF4C2B5204A00070BA56 /* SignUpViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignUpViewModel.swift; sourceTree = "<group>"; };
557042882B24CB310048C81C /* ProfileListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileListView.swift; sourceTree = "<group>"; };
5570428A2B24D2F90048C81C /* TabBarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabBarView.swift; sourceTree = "<group>"; };
5570428C2B24D8050048C81C /* SignUpView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SignUpView.swift; sourceTree = "<group>"; };
Expand Down Expand Up @@ -54,6 +60,8 @@
isa = PBXGroup;
children = (
5598D3252B2404FC00A6AFD3 /* ProfileCardVM.swift */,
5544EF482B52003C0070BA56 /* AuthenticationViewModel.swift */,
5544EF4C2B5204A00070BA56 /* SignUpViewModel.swift */,
);
path = ViewModels;
sourceTree = "<group>";
Expand All @@ -77,6 +85,7 @@
isa = PBXGroup;
children = (
5598D3212B2403CE00A6AFD3 /* CircularImage.swift */,
5544EF4A2B5202A60070BA56 /* TestSignUp.swift */,
);
path = Components;
sourceTree = "<group>";
Expand Down Expand Up @@ -188,10 +197,13 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
5544EF492B52003C0070BA56 /* AuthenticationViewModel.swift in Sources */,
5570428B2B24D2F90048C81C /* TabBarView.swift in Sources */,
5598D3162B23DF6D00A6AFD3 /* ContentView.swift in Sources */,
5598D3262B2404FC00A6AFD3 /* ProfileCardVM.swift in Sources */,
5570428F2B24E50E0048C81C /* RatingView.swift in Sources */,
5544EF4B2B5202A60070BA56 /* TestSignUp.swift in Sources */,
5544EF4D2B5204A00070BA56 /* SignUpViewModel.swift in Sources */,
30CA734E2B24AE460025B2EB /* SymbolsAnimations.swift in Sources */,
5598D3142B23DF6D00A6AFD3 /* SwiftUIBasicsApp.swift in Sources */,
5598D3242B24049B00A6AFD3 /* ProfileCardView.swift in Sources */,
Expand Down
Binary file not shown.
68 changes: 68 additions & 0 deletions SwiftUIBasics/ViewModels/AuthenticationViewModel.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
//
// AuthenticationViewModel.swift
// SwiftUIBasics
//
// Created by Diplomado on 12/01/24.
//

import Foundation
import Combine

final class AuthenticationViewModel: ObservableObject {
// MARK: - Password input
@Published var password = ""
@Published var confirmPassword = ""

// MARK: - Password requirements
@Published var hasEightChar = false
@Published var hasSpacialChar = false
@Published var hasOneDigit = false
@Published var hasOneUpperCaseChar = false
@Published var confirmationMatch = false
@Published var areAllFieldsValid = false

init() {
validateSignUpFields()
}

private func validateSignUpFields() {
/// Check password has minimum 8 characters
$password
.map { password in
password.count >= 8
}
.assign(to: &$hasEightChar)
/// Check password has minimum 1 special character
$password
.map { password in
password.rangeOfCharacter(from: CharacterSet(charactersIn: "!@#$%^&*()_+-=[]{}|:\"';<>,.?/~`")) != nil
}
.assign(to: &$hasSpacialChar)
/// Check password has minimum 1 digit
$password
.map { password in
password.contains { $0.isNumber }
}
.assign(to: &$hasOneDigit)
/// Check password has minimum 1 uppercase letter
$password
.map { password in
password.contains { $0.isUppercase }
}
.assign(to: &$hasOneUpperCaseChar)
/// Check confirmation match password
Publishers.CombineLatest($password, $confirmPassword)
.map { [weak self] _, _ in
guard let self else { return false}
return self.password == self.confirmPassword
}
.assign(to: &$confirmationMatch)
/// Check all fields match
Publishers.CombineLatest($password, $confirmPassword)
.map { [weak self] _, _ in
guard let self else { return false}
return self.hasEightChar && self.hasSpacialChar && self.hasOneDigit && self.hasOneUpperCaseChar && self.confirmationMatch
}
.assign(to: &$areAllFieldsValid)
}
}
80 changes: 80 additions & 0 deletions SwiftUIBasics/ViewModels/SignUpViewModel.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
//
// SignUpViewModel.swift
// SwiftUIBasics
//
// Created by Diplomado on 12/01/24.
//

import Foundation
import Combine


final class SignUpViewModel: ObservableObject {

// Input values from view
@Published var userName = ""
@Published var userEmail = ""
@Published var userPassword = ""
@Published var userRepeatedPassword = ""

// Output subscribers
@Published var formIsValid = false

private var publishers = Set<AnyCancellable>()

init() {
isSignupFormValidPublisher
.receive(on: RunLoop.main)
.assign(to: \.formIsValid, on: self)
.store(in: &publishers)
}

}

private extension SignUpViewModel {

var isUserNameValidPublisher: AnyPublisher<Bool, Never> {
$userName
.map { name in
return name.count >= 3
}
.eraseToAnyPublisher()
}

var isUserEmailValidPublisher: AnyPublisher<Bool, Never> {
$userPassword
.map { password in
return password.count >= 8
}
.eraseToAnyPublisher()
}

var isPasswordValidPublisher: AnyPublisher<Bool, Never> {
$userPassword
.map { password in
return password.count >= 8
}
.eraseToAnyPublisher()
}

var passwordMatchesPublisher: AnyPublisher<Bool, Never> {
Publishers.CombineLatest($userPassword, $userRepeatedPassword)
.map { password, repeated in
return password == repeated
}
.eraseToAnyPublisher()
}

var isSignupFormValidPublisher: AnyPublisher<Bool, Never> {
Publishers.CombineLatest4(
isUserNameValidPublisher,
isUserEmailValidPublisher,
isPasswordValidPublisher,
passwordMatchesPublisher)
.map { isNameValid, isEmailValid, isPasswordValid, passwordMatches in
return isNameValid && isEmailValid && isPasswordValid && passwordMatches
}
.eraseToAnyPublisher()
}

}
50 changes: 50 additions & 0 deletions SwiftUIBasics/Views/Components/TestSignUp.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//
// TestSignUp.swift
// SwiftUIBasics
//
// Created by Diplomado on 12/01/24.
//

import SwiftUI

struct UserFormTextField: View {




}



struct SignUpPasswordScreen: View {
@StateObject private var authVM = AuthenticationViewModel()

var body: some View {
VStack(spacing: 0) {
VStack {
VStack(alignment: .leading, spacing: 10) {
Text("Password")
.font(.title)
.bold()
Text("Password must have more than 8 characters, contain some special character, one digit, one uppercase letter")
.font(.caption)
}
Group {
UserFormTextField(text: $authVM.password, type: .password)
VStack(alignment: .leading) {
RequirementsPickerView(type: .eightChar, toggleState: $authVM.hasEightChar)
RequirementsPickerView(type: .spacialChar, toggleState: $authVM.hasSpacialChar)
RequirementsPickerView(type: .oneDigit, toggleState: $authVM.hasOneDigit)
RequirementsPickerView(type: .upperCaseChar, toggleState: $authVM.hasOneUpperCaseChar)
}
UserFormTextField(text: $authVM.confirmPassword, type: .repeatPassword)
RequirementsPickerView(type: .confirmation, toggleState: $authVM.confirmationMatch)
}
}
}
}
}

#Preview {
SignUpPasswordScreen()
}
59 changes: 58 additions & 1 deletion SwiftUIBasics/Views/PlansView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,69 @@

import SwiftUI

struct Plans: View{
let hasImg: Bool
let imgName: String
let planName: String
let planCost: Double
let hasDesc: Bool
let description: String
let backColor: Color
let fontColor: Color
let sizesBack: CGSize
var body: some View{
ZStack{
RoundedRectangle(cornerSize: CGSize(width: 20, height: 20))
.frame(width: sizesBack.width, height: sizesBack.height)
.aspectRatio(contentMode: .fill)
.foregroundStyle(backColor)
VStack{
if hasImg{
Image(systemName: imgName)

.font(.largeTitle)
}
Text(planName)
.font(.title.bold())
.foregroundStyle(fontColor)
Text("$\(planCost)")
.font(.title.bold())
.foregroundStyle(fontColor)
Text("per month")
.font(.footnote)
.foregroundStyle(fontColor)
if hasDesc{
Text(description)
.font(.footnote)
.padding(5)
.foregroundStyle(.white)
.background(.orange)

}
}
}
}
}

struct PlansView: View {
var body: some View {
Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)

Text("Choose \n ur plan 🐥")
.font(.title.bold())
.foregroundStyle(.black)

VStack{
HStack{
Plans(hasImg: false, imgName: "", planName: "Basic", planCost: 9.9, hasDesc: false, description: "", backColor: .purple, fontColor: .white, sizesBack: CGSize(width: 177, height: 177))
Plans(hasImg: false, imgName: "", planName: "Pro", planCost: 19, hasDesc: true, description: "Best for designer", backColor: .gray, fontColor: .black, sizesBack: CGSize(width: 177, height: 177))
}

Plans(hasImg: true, imgName: "wand.and.stars", planName: "Team", planCost: 299, hasDesc: true, description: "Perfect for teams with 20 members", backColor: .gray, fontColor: .white, sizesBack: CGSize(width: 300, height: 177))
}
}
}

#Preview {
PlansView()
}

32 changes: 29 additions & 3 deletions SwiftUIBasics/Views/RatingView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,39 @@
//
// Created by Diplomado on 09/12/23.
//
//

import SwiftUI

struct RatingView: View {
var body: some View {
Text(/*@START_MENU_TOKEN@*/"Hello, World!"/*@END_MENU_TOKEN@*/)
}
@State var rating: Int? = 0
@State private var animate = false

private func starType(index: Int) -> String {

if let rating = self.rating {
return index <= rating ? "star.fill" : "star"
} else {
return "star"
}
}

var body: some View {
HStack {
ForEach(1...5, id: \.self) { index in
Image(systemName: self.starType(index: index))
.symbolEffect(.bounce, value: animate)
.contentTransition(.symbolEffect(.replace))
.foregroundColor(Color.orange)
.onTapGesture {
UIImpactFeedbackGenerator(style: .medium).impactOccurred()
if self.rating != index{
self.rating = index
} else { self.rating = index-1}
}
}
}
}
}

#Preview {
Expand Down
Loading