{"componentChunkName":"component---src-templates-article-js","path":"/article/swiftui-login-signup-and-password-recovery-uis","result":{"data":{"contentfulArticle":{"title":"Login, Signup and Password recovery UIs","image":"https://res.cloudinary.com/liquidcoder/image/upload/v1597491261/habitApp/f1i7dn6oeppp1badvz26.jpg","createdDate":"Tuesday, 17 December 2019","videoUrl":"5pOCx5aXbsk","updatedDate":"3 years ago","isNewLayout":null,"previews":null,"slug":"swiftui-login-signup-and-password-recovery-uis","author":{"email":"john@liquidcoder.com","name":"John"},"tags":[{"title":"Article"},{"title":"Free"},{"title":"Swift UI"},{"title":"UI Design"}],"allChapters":null,"code":null,"explanation":null,"downloads":[{"name":"Project source code","url":"https://github.com/liquidcoder1/OnboardingScreen/archive/master.zip"}],"resources":[{"name":"Apple developer documentation","url":"https://developer.apple.com/documentation/swiftui"},{"name":"Hacking with swift","url":"https://www.hackingwithswift.com/example-code/system/what-is-the-first-responder"},{"name":"Swift by sundell","url":"https://www.swiftbysundell.com"},{"name":"Responder chain","url":"https://developer.apple.com/documentation/uikit/touches_presses_and_gestures/using_responders_and_the_responder_chain_to_handle_events"},{"name":"Understanding ios responder chain","url":"https://swiftrocks.com/understanding-the-ios-responder-chain.html"}],"body":{"childMdx":{"tableOfContents":{"items":[{"url":"#introduction","title":"Introduction"},{"url":"#requirements","title":"Requirements"},{"url":"#get-started","title":"Get started"},{"url":"#login-view","title":"Login View."},{"url":"#signup-view","title":"Signup View."},{"url":"#password-recovery-view","title":"Password recovery View."},{"url":"#putting-it-all-together","title":"Putting it all together"},{"url":"#further-keyboard-management","title":"Further Keyboard management"}]},"body":"function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }\n\nfunction _objectWithoutProperties(source, excluded) { if (source == null) return {}; var target = _objectWithoutPropertiesLoose(source, excluded); var key, i; if (Object.getOwnPropertySymbols) { var sourceSymbolKeys = Object.getOwnPropertySymbols(source); for (i = 0; i < sourceSymbolKeys.length; i++) { key = sourceSymbolKeys[i]; if (excluded.indexOf(key) >= 0) continue; if (!Object.prototype.propertyIsEnumerable.call(source, key)) continue; target[key] = source[key]; } } return target; }\n\nfunction _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; }\n\n/* @jsxRuntime classic */\n\n/* @jsx mdx */\nvar _frontmatter = {};\nvar layoutProps = {\n  _frontmatter: _frontmatter\n};\nvar MDXLayout = \"wrapper\";\nreturn function MDXContent(_ref) {\n  var components = _ref.components,\n      props = _objectWithoutProperties(_ref, [\"components\"]);\n\n  return mdx(MDXLayout, _extends({}, layoutProps, props, {\n    components: components,\n    mdxType: \"MDXLayout\"\n  }), mdx(\"h2\", null, \"Introduction\"), mdx(\"p\", null, \"Last week, I showed how to create an onboarding screen using the UIViewControllerRepresentable protocol. In this week\\u2019s swift iOS app tutorial, I will extend the onboarding screen to include login, signup and password recovery screens which is a normal iOS app flow or any kind of app. With that intro out of the way, let\\u2019s now write some swift code.\"), mdx(\"p\", null, \"First, make sure to download. Take a look at what we want to build. You can continue with the previous iOS project if you were following along.\"), mdx(\"h2\", null, \"Requirements\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Xcode 11 or greater\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"MacOS Catalina or greater\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Swift 5.0 or greater\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"iOS 12 or greater\")), mdx(\"h2\", null, \"Get started\"), mdx(\"ul\", null, mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Open Xcode\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Create a new Xcode project\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Select single view app and click next\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Name your app and make sure the user interface is Swift UI\"), mdx(\"li\", {\n    parentName: \"ul\"\n  }, \"Last, click Finish.\")), mdx(\"h2\", null, \"Login View.\"), mdx(\"p\", null, \"Once  the user has scrolled to the last \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"PageViewController\"), \", we want to show the Swift UI Login View when is clicks the \", mdx(\"strong\", {\n    parentName: \"p\"\n  }, mdx(\"em\", {\n    parentName: \"strong\"\n  }, \"Get started\")), \" button. \"), mdx(\"p\", null, \"Now in your Xcode project, add a swift file named \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"LoginView\"), \" in the Views folder containing the following swift code:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-swift\"\n  }), \"\\nimport SwiftUI\\n\\nstruct LoginView: View {\\n    \\n    @State private var email = \\\"\\\"\\n    @State private var password = \\\"\\\"\\n    @State private var formOffset: CGFloat = 0\\n    @State private var presentSignupSheet = false\\n    @State private var presentPasswordRecoverySheet = false\\n\\n    \\n    var body: some View {\\n        VStack(spacing: 40) {\\n            Image(\\\"Logo\\\")\\n            Text(\\\"Login\\\").font(.title).bold()\\n            VStack {\\n                LCTextfield(value: self.$email, placeholder: \\\"Email\\\", icon: Image(systemName: \\\"at\\\"), onEditingChanged: { flag in\\n                    withAnimation {\\n                        self.formOffset = flag ? -150 : 0\\n                    }\\n                })\\n                LCTextfield(value: self.$password, placeholder: \\\"Password\\\", icon: Image(systemName: \\\"lock\\\"), isSecure: true)\\n                LCButton(text: \\\"Login\\\") {\\n                    \\n                }\\n            }\\n            \\n            Button(action: {\\n                self.presentSignupSheet.toggle()\\n            }) {\\n              HStack {\\n                Text(\\\"Don't have an account? Sign up.\\\").accentColor(Color.accentColor)\\n                  }\\n              }.sheet(isPresented: self.$presentSignupSheet) {\\n                  SignupView()\\n              }\\n            \\n            Button(action: {\\n                self.presentPasswordRecoverySheet.toggle()\\n            }) {\\n              HStack {\\n                Text(\\\"Forgot your password?\\\").accentColor(Color.purple)\\n                  }\\n              }.sheet(isPresented: self.$presentPasswordRecoverySheet) {\\n                RecoverPasswordView(presentPasswordRecoverySheet: self.$presentPasswordRecoverySheet)\\n              }\\n            \\n        }.padding().offset(y: self.formOffset)\\n    }\\n}\\n\\nstruct LoginView_Previews: PreviewProvider {\\n    static var previews: some View {\\n        LoginView()\\n    }\\n}\\n\\n\")), mdx(\"p\", null, \"The above code does the following:\"), mdx(\"ol\", null, mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Put all the views into a \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"VStack\"), \" view container\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Handles  the animation of the entire \", mdx(\"inlineCode\", {\n    parentName: \"li\"\n  }, \"VStack\"), \" container when in editing mode to accommodate the keyboard using the formOffset state property.\"), mdx(\"li\", {\n    parentName: \"ol\"\n  }, \"Shows the sheets for the sign up view and password recovery view.\")), mdx(\"p\", null, \"You will get some compiler errors because you haven\\u2019t created the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"LCTextfield\"), \" , \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"SignupView\"), \" and \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"PasswordRecoveryView\"), \" swift UI view yet. So let\\u2019s do that. In the same \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"Views\"), \", add a swift ui file named \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"LCTextfield\"), \" with the following swift code inside:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-swift\"\n  }), \"\\nimport SwiftUI\\n\\nstruct LCTextfield: View {\\n    \\n    @Binding var value: String\\n    var placeholder = \\\"Placeholder\\\"\\n    var icon = Image(systemName: \\\"person.crop.circle\\\")\\n    var color = Color(\\\"offColor\\\")\\n    var isSecure = false\\n    var onEditingChanged: ((Bool)->()) = {_ in }\\n    \\n    var body: some View {\\n        HStack {\\n            \\n            if isSecure{\\n                SecureField(placeholder, text: self.$value, onCommit: {\\n                    self.onEditingChanged(false)\\n                }).padding()\\n            } else {\\n                TextField(placeholder, text: self.$value, onEditingChanged: { flag in\\n                    self.onEditingChanged(flag)\\n                }).padding()\\n            }\\n            \\n            icon.imageScale(.large)\\n                .padding()\\n                .foregroundColor(color)\\n        }.background(color.opacity(0.2)).clipShape(Capsule())\\n    }\\n}\\n\\nstruct LCTextfield_Previews: PreviewProvider {\\n    static var previews: some View {\\n        LCTextfield(value: .constant(\\\"\\\"))\\n    }\\n}\\n\\n\")), mdx(\"p\", null, \"The above swift code creates a reusable swift ui Textfield which can also be used as a password Secure field depending on the isSecure flag. \"), mdx(\"h2\", null, \"Signup View.\"), mdx(\"p\", null, \"You will not be able to run the app until we create the 2 remaining screens. In the \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"Views\"), \" folder, add a swift ui file named \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"SignupView.swift\"), \" and put the following swift code inside:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-swift\"\n  }), \" \\nimport SwiftUI\\n\\nstruct SignupView: View {\\n       \\n    @State private var email = \\\"\\\"\\n    @State private var password = \\\"\\\"\\n    @State private var confirmedPassword = \\\"\\\"\\n    \\n    @State private var formOffset: CGFloat = 0\\n    \\n    var body: some View {\\n        \\n       return VStack(spacing: 40) {\\n            Image(\\\"Logo\\\")\\n            Text(\\\"Sign Up\\\").font(.title).bold()\\n            VStack {\\n                LCTextfield(value: self.$email, placeholder: \\\"Email\\\", icon: Image(systemName: \\\"at\\\"), onEditingChanged: { flag in\\n                    withAnimation {\\n                        self.formOffset = flag ? -150 : 0\\n                    }\\n                    \\n                })\\n                LCTextfield(value: self.$password, placeholder: \\\"Password\\\", icon: Image(systemName: \\\"lock\\\"), isSecure: true)\\n                LCTextfield(value: self.$confirmedPassword, placeholder: \\\"Confirm Password\\\", icon: Image(systemName: \\\"lock.rotation\\\"), isSecure: true)\\n                LCButton(text: \\\"Sign up\\\") {\\n                    \\n                }\\n            }\\n            \\n            Button(action: {\\n            }) {\\n              HStack {\\n                Text(\\\"Already have an account?\\\").accentColor(Color.accentColor)\\n                  }\\n              }\\n            \\n       }.padding().offset(y: self.formOffset)\\n    }\\n}\\n\\nstruct SignupView_Previews: PreviewProvider {\\n    static var previews: some View {\\n        SignupView()\\n    }\\n}\\n\\n\")), mdx(\"p\", null, \"The following code does the same thing as the \", mdx(\"inlineCode\", {\n    parentName: \"p\"\n  }, \"LoginView\"), \".\"), mdx(\"h2\", null, \"Password recovery View.\"), mdx(\"p\", null, \"Create a new swift ui file in the same folder named \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"RecoverPasswordView\"), \" and put the following code inside:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-swift\"\n  }), \"import SwiftUI\\n\\n\\nstruct RecoverPasswordView: View {\\n    @State private var email = \\\"\\\"\\n    @Binding var presentPasswordRecoverySheet: Bool\\n    \\n    var body: some View {\\n        VStack(spacing: 40) {\\n            Image(\\\"Logo\\\")\\n            Text(\\\"Recover Password\\\").font(.title).bold()\\n            VStack {\\n                LCTextfield(value: self.$email, placeholder: \\\"Email\\\", icon: Image(systemName: \\\"at\\\"))\\n                LCButton(text: \\\"Reset Password\\\") {}\\n            }\\n            \\n            Button(action: {\\n                self.presentPasswordRecoverySheet.toggle()\\n            }) {\\n              HStack {\\n                Text(\\\"Cancel\\\").accentColor(Color.accentColor)\\n                  }\\n              }\\n            \\n        }.padding()\\n    }\\n\\n}\\n\\nstruct RecoverEmailView_Previews: PreviewProvider {\\n    static var previews: some View {\\n        RecoverPasswordView(presentPasswordRecoverySheet: .constant(false))\\n    }\\n}\\n\\n\")), mdx(\"h2\", null, \"Putting it all together\"), mdx(\"p\", null, \"Now open the \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"ContentView.swift\"), \" and add the following properties to the top one the struct:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-swift\"\n  }), \"  @State var show = false\\n  private let initialLaunchKey = \\\"isInitialLaunch\\\"\\n\")), mdx(\"p\", null, \"The first property will handle the animation of the login view and the second will keep track of whether it\\u2019s the first we launch the app. In a normal iOS app, we wouldn\\u2019t want to show the onboarding screen every time the user opens our app. So we will use the UserDefault class to persist a boolean value indicating whether this is the first launch on this device or not.\"), mdx(\"p\", null, \"Replace the body with the following:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-swift\"\n  }), \"    \\n    var body: some View {\\n        VStack {\\n            if show || UserDefaults.standard.bool(forKey: initialLaunchKey){\\n                LoginView().transition(.move(edge: .bottom))\\n            } else {\\n                PageViewContainer( viewControllers: Page.getAll.map({  UIHostingController(rootView: PageView(page: $0) ) }), presentSignupView: {\\n                    withAnimation {\\n                        self.show = true\\n                    }\\n                    UserDefaults.standard.set(true, forKey: self.initialLaunchKey)\\n                }).transition(.scale)\\n            }\\n        }.frame(maxHeight: .infinity).background(Color.backgroundColor).edgesIgnoringSafeArea(.all)\\n    }\\n\")), mdx(\"p\", null, \"Now run the app twice and the second time will not show the onboarding screen.\"), mdx(\"h2\", null, \"Further Keyboard management\"), mdx(\"p\", null, \"Although the user can dismiss the keyboard by clicking the return key, he might also want to do the most natural thing that is to click anywhere on the screen to dismiss the keyboard.  So let me show how to dismiss the keyboard in swift UI when the user click anywhere on the screen.\"), mdx(\"p\", null, \"In the \", mdx(\"em\", {\n    parentName: \"p\"\n  }, \"Extensions\"), \" folder, create a swift file named UIApplicationExt.swift and put the following code inside:\"), mdx(\"pre\", null, mdx(\"code\", _extends({\n    parentName: \"pre\"\n  }, {\n    \"className\": \"language-swift\"\n  }), \"\\nextension UIApplication {\\n    func endEditing() {\\n        sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)\\n    }\\n}\\n\\n\")), mdx(\"p\", null, \"In the above code, we extend the UIApplication class , then send a resign first responder event to the \", mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"https://developer.apple.com/documentation/uikit/touches_presses_and_gestures/using_responders_and_the_responder_chain_to_handle_events\",\n    \"title\": \"Using Responders and the Responder Chain to Handle Events\"\n  }), \"responder chain\"), \" to be picked up and handled by the textfield.  Read more about the responder chain  in iOS app development\", mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"https://swiftrocks.com/understanding-the-ios-responder-chain.html\",\n    \"title\": \"Reponder chain in ios app - swift \"\n  }), \"here\"), \" and \", mdx(\"a\", _extends({\n    parentName: \"p\"\n  }, {\n    \"href\": \"https://www.hackingwithswift.com/example-code/system/what-is-the-first-responder\",\n    \"title\": \"Reponder chain\"\n  }), \"here\"), \". \"), mdx(\"p\", null, \"That\\u2019s it guys\\u2026 Feel free to share this article to whoever may need it and subscribe for more. \"));\n}\n;\nMDXContent.isMDXComponent = true;","excerpt":"Introduction Last week, I showed how to create an onboarding screen using the UIViewControllerRepresentable protocol. In this week’s swift iOS app tutorial, I will extend the onboarding screen to include login, signup and password recovery screens which is a normal iOS app flow or any kind of app…"}}}},"pageContext":{"slug":"swiftui-login-signup-and-password-recovery-uis"}},"staticQueryHashes":["1047155515","1278771865","2517639938","2517639938","2537039069","3452127246","3566879862","3891201089","439617394","670651827"]}