@ellieN1235
Unfortunately there are missing files. You need to compress the project at the very top level folder as shown in the following screenshot.
The zip file needs to include the City Sights App.xcodeproj
file and the folder containing all the project files named City Sights App
Hi Chris,
Let me know if that doesnāt work
Thank you
@ellieN1235
Hi Ellie,
Yes, thatās good. I have opened the project so now I have to create a project on Firebase link it to. Iāll get back to you when I have got it working.
There is no GoogleService-Info.plist file included so I assume that you removed your copy before you compressed it?
@ellieN1235
Hi Ellie,
Iāve got the project to build and run on a simulator and the first screen presented is the Login Sign Up View.
which looks OK to me.
There is no code in the actionButton
as yet to either log the user in or create a new user (depending on which mode the View is in) so that is something for you to figure out. For the moment I have added a Binding to isLoggedIn
from the LaunchView and tapping that button sets isLoggedIn to true which dismisses the LoginSignupView
and takes the user to the OnBoardingView
.
OK so what did I do?.
- I added a GoogleService-Info.plist file
- Fixed the error in LaunchView which was causing the error in this screenshot you posted
The problem was related to this in LaunchView:
LoginSignupView(onLoginSuccess: { isLoggedIn in
self.isLoggedIn = isLoggedIn
})
This part of the above code:
onLoginSuccess: { isLoggedIn in
self.isLoggedIn = isLoggedIn
}
was causing the error you were seeing. Xcode sometimes is very bad at pointing to the actual error. I removed that code since it did not make any sense and didnāt line up with anything in LoginSignupView
and that got rid of the error.
The only changes I have made to the project are in LaunchView and LoginSignupView so if you modify your copies so that they match these you should see your project working.
LaunchView:
struct LaunchView: View {
@EnvironmentObject var model: ContentModel
@State private var isLoggedIn = false
var body: some View {
Group {
if isLoggedIn {
if model.authorizationState == .notDetermined {
OnboardingView()
} else if model.authorizationState == .authorizedAlways || model.authorizationState == .authorizedWhenInUse {
HomeView()
} else {
LocationDeniedView()
}
} else {
LoginSignupView(isLoggedIn: $isLoggedIn)
}
}
}
}
struct LaunchView_Previews: PreviewProvider {
static var previews: some View {
LaunchView()
.environmentObject(ContentModel())
}
}
LoginSignupView:
struct LoginSignupView: View {
@ObservedObject var viewModel = LoginSignupViewModel()
@Binding var isLoggedIn: Bool
var emailTextField: some View {
TextField("Email", text: $viewModel.emailText)
.textFieldStyle(.roundedBorder)
.autocorrectionDisabled(true)
.autocapitalization(.none)
}
var passwordTextField: some View {
SecureField("Password", text: $viewModel.passwordText)
.textFieldStyle(.roundedBorder)
.autocorrectionDisabled(true)
.autocapitalization(.none)
}
var actionButton: some View {
Button(viewModel.buttonTitle) {
//action
isLoggedIn = true
}.padding()
.frame(maxWidth: .infinity)
.foregroundColor(.white)
.background(Color(.systemPink))
.cornerRadius(16)
.padding()
}
var body: some View {
VStack {
Text(viewModel.title)
.font(.largeTitle)
.fontWeight(.bold)
Text(viewModel.subtitle)
.font(.title2)
.fontWeight(.semibold)
.foregroundColor(Color(.systemGray2))
Spacer().frame(height: 50)
emailTextField
passwordTextField
actionButton
Button {
if viewModel.mode == .login {
viewModel.mode = .signup
} else {
viewModel.mode = .login
}
} label: {
Text("Switch to \(viewModel.mode == .signup ? "Login" : "Sign-Up")")
}
Spacer()
}
.padding(.horizontal)
}
}
struct LoginSignupView_Previews: PreviewProvider {
static var viewModel = LoginSignupViewModel()
static var previews: some View {
NavigationView {
LoginSignupView(isLoggedIn: .constant(false))
}
.environment(\.colorScheme, .dark)
.environmentObject(viewModel)
}
}
Cheers
1 Like
Hi Chris,
Thanks for pointing that out for me. I fixed it and it worked now!
I just added some codes for LoginSignupView and ContentModel to log users in and create new users. Iām not receiving any errors this time, but itās still not working as expected. I tried to sign up/login with my email but Iām not seeing itās going to the Onboarding View after and user data also not appearing on the Firebase page. Although it says I created a new user on Xcode console.
If you have some time to take a look this is the link to the project: City Sights App.zip - Google Drive
Hi Ellie,
I donāt see any code that is interacting with the Authentication section of Firebase to either log in an existing user or create a new user.
Example code to sign a user in (existing user) could be something like this:
Auth.auth().signIn(withEmail: email, password: password) { result, error in
// Check for errors
guard error == nil else {
print(error!.localizedDescription)
return
}
// Example of other code here as a result of a successful login
isLoggedIn = true
}
Example code to create a new user could be something like this:
Auth.auth().createUser(withEmail: email, password: password) { result, error in
guard error == nil else {
print(error!.localizedDescription)
return
}
// If you were collecting the name of the new user as part of the
// log in credentials then what you could so is save the name
// to Firebase using the users UID as the document ID.
let firebaseUser = Auth.auth().currentUser
let db = Firestore.firestore()
let ref = db.collection("users").document(firebaseUser!.uid)
ref.setData(["name": name], merge: true)
// Example of other code here as a result of successfully creating a new user
isLoggedIn = true
}
These code segments require the swift file, in which they might be used, to import the following:
import Firebase
import FirebaseAuth
You would most likely do this in the LoginSignupView by adding the respective code to the performLogin()
and createUser()
functions.
There is a Button coded into the HomeView below the VStack inside the if !isMapShowing {
statement. The Button will never be visible since it is outside of the VStack. That said, itās not clear what you had intended to do with that Button anyway since the LogIn or SignUp is occuring before the HomeView is made visible.
The other thing that comes to mind is there needs to be provision for the user to log out.
Hi Chris,
I implemented the respective codes into LoginSignupView under performLogin()
and createUser()
functions. I received this āCannot find ānameā in scopeā error. Therefore I tried to define the name with " let name = viewModel.name" and received this error again -āReferencing subscript āsubscript(dynamicMember:)ā requires wrapper āObservedObject.Wrapperāā
Iām not sure what I should do next. Do you have any advice?
My project: City Sights App.zip - Google Drive
Thank you
@ellieN1235
Hi Ellie,
I think I have confused you. In your createUser function, replace the code with the following. In other words ignore the references to name since your SignUp code does not capture the persons name.
func createUser() {
// Perform create user logic here
// Use the `viewModel.emailText` and `viewModel.passwordText` to create a new user
let email = viewModel.emailText
let password = viewModel.passwordText
Auth.auth().createUser(withEmail: email, password: password) { result, error in
guard error == nil else {
print(error!.localizedDescription)
return
}
// Example of other code here as a result of successfully creating a new user
isLoggedIn = true
}
}
Hi Chris,
That works! Now I can load the userās data on Firebase whenever I try to log in or sign up for a new email. However, I can go to OnboardingScreen as long as I put an email and a random password in so I donāt feel like Iām correctly implying the logic of authentication.
Can you give me guidelines to implement that logic?
Also, I have a question regarding SwiftUI vs. UIKit but I thought I would ask it here in 1 response (do let me know if I should put it out into a separate thread since it is off-topic). I started learning IOS back in February with the course Learn IOS in 90 Days and so Iām most comfortable in SwiftUI. Now I realized that most apps out there are probably written in UIKit and so I thought if I do want to land my first job in IOS I might want to go back and learn UIKit. What are your thoughts on this? Should I continue with SwiftUI or go back and learn UIKit?
Vi
At the moment there is no validation of user inputs when creating a new user to make sure that they are supplying a valid email address that has the correct format.
The other thing is that there is no validation of the password to ensure that it follows some kind of standard such as at least 8 characters and it must have at least 1 capital letter and at least 1 lowercase letter and at least 1 other non alphabetic and non numeric character.
There is tons of validation code available online that can do both of those tasks to ensure that there is consistency.
When logging is as an existing user the same validation can be applied before the email and password is used in the Auth.auth().signIn(.....)
process. At the moment, If an error occurs when logging in, the console will display what the error is because there is a print statement which gives feedback.
guard error == nil else {
print(error!.localizedDescription)
return
}
Thatās no good for a user since he/she canāt see that.
What you need is feedback in the LoginSignUpView by use of an additional Text() element to show an error message to the user (which you populate) so they know what is going on in the background after they tap SignIn or Sign up.
Yes, create a separate thread.