How to customize drop-in UI with the AWSMobileClient? - swift

I currently have the correct code to customize the drop in UI straight from the iOS SDK docs. However it runs but is still the default AWS look. I have browsed profusely for the solution but to no avail.
import UIKit
import AWSAuthCore
import AWSAuthUI
import AWSMobileClient
class SignInViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
showSignIn()
//
AWSMobileClient.default().initialize { (userState, error) in
if let userState = userState {
switch(userState){
case .signedIn:
self.createAlert(title: "You are already signed in!", message: "Sign out first")
DispatchQueue.main.async {
// self.signInStateLabel.text = "Signed out"
}
case .signedOut:
AWSMobileClient.default().showSignIn(navigationController: self.navigationController!, { (userState, error) in
if(error == nil){ //Successful signin
DispatchQueue.main.async {
// self.signInStateLabel.text = "Logged In"
// self.signOutButton.isHidden = false
// self.tableView.reloadData()
}
}
})
default:
AWSMobileClient.default().signOut()
}
} else if let error = error {
print(error.localizedDescription)
}
}
// Do any additional setup after loading the view.
}
func showSignIn() {
if !AWSSignInManager.sharedInstance().isLoggedIn{
AWSAuthUIViewController.presentViewController(with: self.navigationController!, configuration: nil, completionHandler: {(provider: AWSSignInProvider, error: Error?) in
if error != nil {
print("Error occured: \(String(describing: error))")
}else{
//Sign in Successful
print("Logged in with provider \(provider.identityProviderName)with Token: \(provider.token())")
}
})
AWSMobileClient.default()
.showSignIn(navigationController: self.navigationController!,
signInUIOptions: SignInUIOptions(
canCancel: true,
logoImage: UIImage(named: "MyCustomLogo"),
backgroundColor: UIColor.red)) { (result, err) in
//handle results and errors
}
}
}
func createAlert (title: String, message: String){
let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertController.Style.actionSheet)
alert.addAction(UIAlertAction(title: "Ok", style: UIAlertAction.Style.default, handler: { (Action) in
alert.dismiss(animated: true, completion: nil)
}))
self.present(alert, animated: true, completion: nil)
}
}
I try to customize it in func showSignIn(). I would like to customize this UI to my own preferences.

Looks like you have the right idea with SignInUIOptions but might just need less code and in the right places. Here's what's worked for me:
In AppDelegate:
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Initialize AWSMobileClient singleton
AWSMobileClient.default().initialize { (userState, error) in
if let userState = userState {
print("UserState: \(userState.rawValue)")
} else if let error = error {
print("error: \(error.localizedDescription)")
}
}
return true
}
In ViewController:
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if !AWSMobileClient.default().isSignedIn {
showLoginScreen()
return
}
}
func showLoginScreen() {
AWSMobileClient.default().showSignIn(
navigationController: self.navigationController!,
signInUIOptions: SignInUIOptions(
canCancel: false,
logoImage: UIImage(named: "MyLogo"),
backgroundColor: UIColor.red,
secondaryBackgroundColor: UIColor.red,
disableSignUpButton: true)
) { (signInState, error) in
if let signInState = signInState {
print("Sign in flow completed: \(signInState)")
} else if let error = error {
print("error logging in: \(error.localizedDescription)")
}
}
}

Related

Google Auth: Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'presentingViewController must be set.'

I have incorporated Google sign in into my app. It was working well this whole time and then suddenly stopped working, now giving me this error in the debugger:
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'presentingViewController must be set.'
Here is my code for the login view controller (edited to show only necessary code):
class WelcomeViewController: UIViewController, GIDSignInDelegate {
#IBOutlet weak var signInGoogleButton: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
setUpGoogleButton()
GIDSignIn.sharedInstance()?.presentingViewController = self
}
// MARK: - SIGN UP WITH GOOGLE
func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error!) {
if let err = error {
print("Failed to log into Google: ", err)
return
}
print("Successfully logged into Google")
guard let authentication = user.authentication else { return }
let credential = GoogleAuthProvider.credential(withIDToken: authentication.idToken, accessToken: authentication.accessToken)
// sign user in with Firebase
Auth.auth().signIn(with: credential, completion: { (user, error) in
let firstName = user?.user.displayName
let email = user?.user.email
let lastName = ""
let uid = user?.user.uid
if let err = error {
print("Failed to create a Firebase User with Google account: ", err)
return
} else {
// Successfully logged in
print("Successfully logged into Firebase with Google email: ", email ?? "", "Now add user to Firestore if user is new.")
// check if user already exists
self.addUserToFirestore(firstName ?? "", lastName, email ?? "", uid ?? "", "Google")
}
})
}
fileprivate func setUpGoogleButton() {
Utilities.styleLightFilledButton(signInGoogleButton)
signInGoogleButton!.addTarget(self, action:
#selector(handleCustomGoogleSignIn), for: .touchUpInside)
GIDSignIn.sharedInstance()?.delegate = self
}
#objc func handleCustomGoogleSignIn() {
GIDSignIn.sharedInstance().signIn()
}
// MARK: - Other functions
func addUserToFirestore(_ firstName:String, _ lastName:String, _ email:String, _ uid:String, _ signInMethod:String) {
let db = Firestore.firestore()
let docRef = db.collection("users").document(uid)
// check if user exists in firestore
docRef.getDocument { (document, error) in
if let document = document {
if document.exists {
let message = "Good news! You already have a Coal account that uses " + email + ".\nPlease sign in to your existing account. Then you will be able to link your " + signInMethod + " profile from your Account Settings page."
// user exists. send to chats screen.
print("User already exists. Document data: \(String(describing: document.data()))")
self.showError("You're already a member!", message)
} else {
// user does not exist. create a new user
print("Document does not exist. Create new user.")
docRef.setData(["firstname":firstName, "lastname":lastName, "email":email]) { err in
if err != nil {
// Show error message
print("Error saving user data to Firestore")
} else {
print("New user created in Firestore")
self.transitionToConvo()
}
}
}
}
}
}
func showError(_ title:String, _ message:String) {
let alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertController.Style.alert)
alert.addAction(UIAlertAction(title: "Ok", style: UIAlertAction.Style.default, handler: nil))
self.present(alert, animated: true, completion: nil)
}
func transitionToConvo() {
let tabBarC = self.storyboard?.instantiateViewController(withIdentifier: "mainTabBarController") as! TabBarController
tabBarC.modalPresentationStyle = .fullScreen
self.present(tabBarC, animated: true, completion: nil)
print("Switched to TabBarController")
}
} // end
Here is the app delegate:
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, GIDSignInDelegate {
func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error!) {
if let err = error {
print("Failed to log into Google: ", err)
return
}
print("Successfully logged into Google")
guard let authentication = user.authentication else { return }
let credential = GoogleAuthProvider.credential(withIDToken: authentication.idToken, accessToken: authentication.accessToken)
Auth.auth().signIn(with: credential, completion: { (user, error) in
if let err = error {
print("Failed to create a Firebase User with Google account: ", err)
return
}
guard let uid = user?.user.uid else { return }
print("Successfully logged into Firebase with Google", uid)
})
}
func application(
_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
) -> Bool {
FirebaseApp.configure()
GIDSignIn.sharedInstance()?.clientID = FirebaseApp.app()?.options.clientID
GIDSignIn.sharedInstance()?.delegate = self
ApplicationDelegate.shared.application(
application,
didFinishLaunchingWithOptions: launchOptions
)
return true
}
#available(iOS 9.0, *)
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
ApplicationDelegate.shared.application(
app,
open: url,
sourceApplication: options[UIApplication.OpenURLOptionsKey.sourceApplication] as? String,
annotation: options[UIApplication.OpenURLOptionsKey.annotation]
)
return GIDSignIn.sharedInstance().handle(url)
}
}
The error occurs every time I click the signInGoogleButton. Any help is appreciated, thank you!!

Firebase Re-Authenticate Thread 1: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value error

The user needs to be authenticated again to change the email address. When I write the following code, I get the error: user.reauthenticate (with: credential) {_ in the line of error Thread 1: Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value.
var credential: AuthCredential! I have also defined this
if let user = Auth.auth().currentUser {
// re authenticate the user
user.reauthenticate(with: credential) { _,error in
if let error = error {
print(error)
} else {
// User re-authenticated.
user.updateEmail(to: self.emailField.text!) { (error) in
}
}
}
}
You need to prompt the user for their credentials, otherwise that property will be nil which will show the error you are seeing
let user = Auth.auth().currentUser
var credential: AuthCredential
// *** Prompt the user to re-provide their sign-in credentials ***
// populate the credential var with that data so it's not nil
//
user?.reauthenticate(with: credential) { error in
if let error = error {
// An error happened.
} else {
// User re-authenticated.
}
}
You can follow this example step by step
Configuration:
-> Select your project
-> Go to TARGETS
-> Select your project icon
-> Click info tab
-> Add new URL Types(REVERSED_CLIENT_ID from GoogleService-Info.plist)
1. You need to setup your pre requisite configuration into your AppDelegate class
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
// Pass device token to auth
Auth.auth().setAPNSToken(deviceToken, type: AuthAPNSTokenType.unknown)
}
// For iOS 9+
func application(_ application: UIApplication, open url: URL,
options: [UIApplication.OpenURLOptionsKey : Any]) -> Bool {
if Auth.auth().canHandle(url) {
return true
}
// URL not auth related, developer should handle it.
return ApplicationDelegate.shared.application(application, open: url, options: options)
}
// For iOS 8-
func application(_ application: UIApplication,
open url: URL,
sourceApplication: String?,
annotation: Any) -> Bool {
if Auth.auth().canHandle(url) {
return true
}
// URL not auth related, developer should handle it.
return ApplicationDelegate.shared.application(application, open: url)
}
func application(_ application: UIApplication, didReceiveRemoteNotification notification: [AnyHashable : Any], fetchCompletionHandler completionHandler: #escaping (UIBackgroundFetchResult) -> Void) {
if Auth.auth().canHandleNotification(notification) {
completionHandler(UIBackgroundFetchResult.noData)
return
}else{
completionHandler(UIBackgroundFetchResult.newData)
}
// This notification is not auth related, developer should handle it.
}
2. It's your ViewModel class
class FirebaseSignin: NSObject {
public func firebaseSigninWith(phoneNumber: String?, completion: #escaping (Bool, String?, Error?)->()) {
//SVProgressHUD.show()
if let phoneNumber = phoneNumber {
print("firebaseSigninWith phoneNumber: ", phoneNumber)
Auth.auth().languageCode = "fr";
PhoneAuthProvider.provider().verifyPhoneNumber(phoneNumber, uiDelegate: nil) { [weak self] (verificationID, error) in
//SVProgressHUD.dismiss()
if let error = error {
completion(false, verificationID, error)
return
}else{
UserDefaults.standard.set(verificationID, forKey: "authVerificationID")
completion(true, verificationID, error)
}
}
}else{
completion(false, nil, nil)
}
}
public func otpConfirmation(verificationCode: String?, completion: #escaping (Bool, Any?, Error?)->()) {
//SVProgressHUD.show()
if let verificationCode = verificationCode {
if let verificationID = UserDefaults.standard.string(forKey: "authVerificationID") {
let credential = PhoneAuthProvider.provider().credential(withVerificationID: verificationID, verificationCode: verificationCode)
Auth.auth().signIn(with: credential) { (authResult, error) in
//SVProgressHUD.dismiss()
if let error = error {
completion(false, verificationID, error)
return
}else{
completion(true, verificationID, error)
}
}
}else{
completion(false, nil, nil)
}
}else{
completion(false, nil, nil)
}
}
}
3. you call your submitBttonAction function from LoginClass
func submitBttonAction() {
let mobile_no = mobileNumberTextField.getFormattedPhoneNumber(format: .E164)
self.firebaseSignin.firebaseSigninWith(phoneNumber: mobile_no) { [weak self] (isSuccess, verificationID, error) in
if isSuccess {
//GlobalVariable.showToastWith(view: GlobalVariable.getRootViewController()?.view, message: "OTP send successfully.")
//RootViewController.selectViewController(_viewController: .OTPConfrimationViewController, ["delegate": self])
// you open your OTPConfrimationViewController
}else{
//GlobalVariable.showToastWith(view: GlobalVariable.getRootViewController()?.view, message: "OTP sending fail \(error?.localizedDescription ?? "")")
}
}
}
4. confirm your OTP from TOPViewController class
func otpConfirmation() {
if let otp = self.otpTextField.text {
self.firebaseSignin.otpConfirmation(verificationCode: otp) { [weak self] (isSuccess, authResult, error) in
if isSuccess {
//GlobalVariable.showToastWith(view: GlobalVariable.getRootViewController()?.view, message: "OTP varified successfully.")
//self?.handleHeaderBackAction(nil)
//self?.delegate?.otpConfrimationCallBack(isSuccess)
}else{
//GlobalVariable.showToastWith(view: GlobalVariable.getRootViewController()?.view, message: "OTP varification fail \(error?.localizedDescription ?? "")")
}
}
}
}

swift 4 shared.URLS Session from other class?

I got a getJSON Function with url parameter:
func getJsons(jsonUrl: String) {
guard let url = URL(string: jsonUrl) else { return }
URLSession.shared.dataTask(with: url:) { (data, response, err) in
if err != nil {
print("ERROR: \(err!.localizedDescription)")
let alertController = UIAlertController(title: "Error", message:
err!.localizedDescription, preferredStyle: UIAlertControllerStyle.alert)
alertController.addAction(UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.default,handler: nil))
var topController:UIViewController = UIApplication.shared.keyWindow!.rootViewController!
while ((topController.presentedViewController) != nil) {
topController = topController.presentedViewController!;
}
topController.present(alertController, animated: true, completion: nil)
}
guard let data = data else { return }
do {
let test = try JSONDecoder().decode([ArticleStruct].self, from: data)
DispatchQueue.main.async {
self.myArticles = test
print(self.myArticles?.count ?? 0 )
self.myTableView.reloadData()
}
} catch let jsonErr {
print("Error:", jsonErr)
}
}.resume()
}
now i want to move the function to another class (network class).
what must I do to add a completionHandler to the function and how do I call it from other classes.
i want to return the json to the caller class.
My plan:
in MainActivity -> viewDidLoad: call network completionHandler(getJsons(192.168.178.100/getPicture.php))
on completion -> myJsonDataMainActivity = (json data from completionHandler)
-> MainActivity.TableView.reload
in otherClass -> call network completionHandler(getJsons(192.168.178.100/getData.php))
on completion -> myJsonDataOtherClass = (json data from completionHandler)
-> otherClass.TableView.reload
Thanks for your help!
You can use delegate.
myJsonDataOtherClass:
protocol NetworkDelegate {
func didFinish(result: Data)
}
class myJsonDataOtherClass {
var delegate: NetworkDelegate? = nil
...
func getJsons(jsonUrl: String) {
...
URLSession.shared.dataTask(with: url:) { (data, response, err) in
...
delegate?.didFinish(data)
}.resume()
}
}
and set delegate at MainActivity
class MainActivity: UIViewController, NetworkDelegate{
...
let jsonClass = myJsonDataOtherClass()
jsonClass.delegate = self
jsonClass.getJsons(jsonUrl:url)
func didFinish(result:Data) {
// process data
}
}
You should add a completion handler in your function and pass the JSON object.
func getJsons(jsonUrl: String, completion:#escaping (_ success: Bool,_ json: [String: Any]?) -> Void) {
...
completion(true, json)
}

how do I perform segue after log in with facebook account?

Currently I am attempting to perform a segue to a second view controller after a user logs in with Facebook using firebase
I was able to sort of get this to work. My problem is I have to actually log-in twice before the Segue is activated. Any suggestions?
see my CODE below
private var fbLoginSuccess = false //This is gobal
override func viewDidAppear(_ animated: Bool) {
if (FBSDKAccessToken.current() != nil && fbLoginSuccess == true)
{
performSegue(withIdentifier: "Home", sender: self)
}
}
#IBAction func facebookLogin(sender: UIButton) {
let facebookLogin = FBSDKLoginManager()
facebookLogin.logIn(withReadPermissions: ["public_profile", "email"], from: self, handler: {
(facebookResult, facebookError) -> Void in
if facebookError != nil {
print("Facebook login failed. Error \(String(describing: facebookError))")
} else if (facebookResult?.isCancelled)! {
print("Facebook login was cancelled.")
} else {
let credential = FacebookAuthProvider.credential(withAccessToken: FBSDKAccessToken.current().tokenString)
Auth.auth().signIn(with: credential) { (user, error) in
if error != nil {
print("Login failed. \(String(describing: error))")
} else {
fbLoginSuccess = true
print("Logged in!")
if (facebookResult?.grantedPermissions.contains("email"))! {
}
}
}
}
})
}
It appears that you're only calling performSegue(withIdentifier:) in viewDidAppear. If you want the segue to occur after signing in, then you need to include it there.
let facebookLogin = FBSDKLoginManager()
facebookLogin.logIn(withReadPermissions: ["public_profile", "email"], from: self, handler: {
(facebookResult, facebookError) -> Void in
if facebookError != nil {
print("Facebook login failed. Error \(String(describing: facebookError))")
} else if (facebookResult?.isCancelled)! {
print("Facebook login was cancelled.")
} else {
let credential = FacebookAuthProvider.credential(withAccessToken: FBSDKAccessToken.current().tokenString)
Auth.auth().signIn(with: credential) { (user, error) in
if error != nil {
print("Login failed. \(String(describing: error))")
} else {
fbLoginSuccess = true
print("Logged in!")
if (facebookResult?.grantedPermissions.contains("email"))! {
}
performSegue(withIdentifier: "Home", sender: self)
}
}
}
})

use of unresolved identifier 'result' swift 3

I'm writing a login page for my app and i'm getting this error
social media app , xcode 8.3.2 , swift 3
i've tried target membership in file inspector and nothing changed
also I removed test units (UITest and Test) and renew them , it didn't worked either.
at the line 41 I'm getting this error "use of unresolved identifier 'result'"
the picture below explains the code
Picture
import UIKit
class LoginViewController : UIViewController
{
#IBOutlet weak var txt_username: UITextField!
#IBOutlet weak var txt_password: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
#IBAction func btn_log_in_click(_ sender: Any){
let server=MultipartUtility (Url:"http://x.x.x.x/appname-api/user/login/")
//I.m hiding the real ip and details for posting this
server.AddFormField("username", value: txt_username.text)
server.AddFormField("password", value: txt_password.text)
let task = URLSession.shared.dataTask(with: server.execute())
{Data,URLResponse,error in
if error != nil{
print(error as Any)
return
}
do{
let json = try JSONSerialization.jsonObject(with: Data!, options: .allowFragments)
if let json_result = json as? [String: Any]{
let result = json_result ["result"] as? String
if result == "0"
{
DispatchQueue.main.async {
let alert = UIAlertController(title:"Incorrect Username",message : "The username you entered doesn't appear to belong to an account. Please check your username and try again", preferredStyle : .alert)
let alert_action = UIAlertAction(title: "Try Again", style: .default, handler: nil)
alert.addAction(alert_action)
self.present(alert, animated: true, completion: nil)
}
}
}
else{
DispatchQueue.main.async {
UserDefaults.standard.set(result!, forKey: "user_id")
//" use of unresolved identifier 'result' "
let current_view=UIApplication.shared.windows[0] as UIWindow
let new_view=(self.storyboard? .instantiateViewController(withIdentifier: "tab_bar"))! as UIViewController
UIView.transition(from: (current_view.rootViewController? .view)!, to:new_view.view , duration: 0.65, options: .transitionFlipFromRight, completion: {(action) in current_view.rootViewController=new_view
})
}
}
}
catch{
}
}
task.resume()
}
}
if let json_result = json as? [String: Any]
{
let result = json_result ["result"] as? String
if result == "0"
{
DispatchQueue.main.async {
let alert = UIAlertController(title:"Incorrect Username",message : "The username you entered doesn't appear to belong to an account. Please check your username and try again", preferredStyle : .alert)
let alert_action = UIAlertAction(title: "Try Again", style: .default, handler: nil)
alert.addAction(alert_action)
self.present(alert, animated: true, completion: nil)
}
}
else
{
DispatchQueue.main.async {
UserDefaults.standard.set(result!, forKey: "user_id")
//" use of unresolved identifier 'result' "
let current_view=UIApplication.shared.windows[0] as UIWindow
let new_view=(self.storyboard? .instantiateViewController(withIdentifier: "tab_bar"))! as UIViewController
UIView.transition(from: (current_view.rootViewController? .view)!, to:new_view.view , duration: 0.65, options: .transitionFlipFromRight, completion: {(action) in current_view.rootViewController=new_view
})
}
}
}
else{
// Error in jsonSerialization
}