Swift: ActionExtension doesn't open when declare Database.database().reference() - swift

Hello I'm currently working on ActionExtension using Firebase in order to share information through firebase realtime database.
My problem is Whenever I declare "Database.database().reference()" Application doesn't let me open the extension.
I have a code and error log underneath
import UIKit
import MobileCoreServices
import Firebase
import FirebaseAuth
import FirebaseCore
import FirebaseDatabase
class TextToClipViewController: UIViewController {
#IBOutlet weak var TextToClip: UITextView!
var convertedString: String?
var uid: String?
var ref = Database.database().reference() //<-- this is problem
override func viewDidLoad() {
super.viewDidLoad()
let textItem = self.extensionContext!.inputItems[0] as! NSExtensionItem
let textItemProvider = textItem.attachments![0]
if textItemProvider.hasItemConformingToTypeIdentifier(kUTTypeText as String) {
textItemProvider.loadItem(forTypeIdentifier: kUTTypeText as String,
options: nil,
completionHandler: {
(result, error) in
self.convertedString = result as? String
if self.convertedString != nil {
//self.convertedString = self.convertedString!.appending(uid)
DispatchQueue.main.async {
self.TextToClip.text = self.convertedString!
}
}
})
}
FirebaseApp.configure()
let user = Auth.auth().currentUser
let defaults = UserDefaults(suiteName: "group.JS.TossSync.share")
let email = defaults?.string(forKey: "email") ?? "nothing"
let password = defaults?.string(forKey: "password") ?? "nothing"
if let user = user {
uid = user.uid
if textItemProvider.hasItemConformingToTypeIdentifier(kUTTypeText as String) {
textItemProvider.loadItem(forTypeIdentifier: kUTTypeText as String,
options: nil,
completionHandler: {
(result, error) in
self.convertedString = result as? String
if self.convertedString != nil {
//self.convertedString = self.convertedString!.appending(uid)
DispatchQueue.main.async {
self.TextToClip.text = self.convertedString!
}
}
})
}
}
else{
if(email == "nothing" && password == "nothing"){
TextToClip.text = "login to main app"
}
else{
Auth.auth().signIn(withEmail: email, password: password) { user, error in
if let error = error, user == nil {
let alert = UIAlertController(title: "Sign In Failed",
message: error.localizedDescription,
preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default))
self.present(alert, animated: true, completion: nil)
}
else{}
}
}
}
}
#IBAction func done() {
let returnProvider =
NSItemProvider(item: convertedString as NSSecureCoding?,
typeIdentifier: kUTTypeText as String)
let returnItem = NSExtensionItem()
returnItem.attachments = [returnProvider]
self.extensionContext!.completeRequest(
returningItems: [returnItem], completionHandler: nil)
}
#IBAction func share(_ sender: Any) {
let pusher: [String : String] = ["content/10": convertedString!, "flag": "0"]
ref.child(uid!).child("clipboard").updateChildValues(pusher)
}
}
2020-04-18 01:12:51.284690+0900 TossSync_ForApple[8976:2834488] [lifecycle ] [u EFC68F17-B2CF-4CB0-8994-C50A12180DA3:m (null)] [JS.TossSync-Apple.Copy(1.0)] Connection to plugin interrupted while in use.
2020-04-18 01:12:51.285353+0900 TossSync_ForApple[8976:2834488] [lifecycle ] [u EFC68F17-B2CF-4CB0-8994-C50A12180DA3:m (null)] [JS.TossSync-Apple.Copy(1.0)] Connection to plugin invalidated while in use.
2020-04-18 01:12:51.425523+0900 TossSync_ForApple[8976:2834495] [assertion] Error acquiring assertion: <NSError: 0x283b3a580; domain: RBSAssertionErrorDomain; code: 2; reason: "Specified target process does not exist">
2020-04-18 01:12:51.484291+0900 TossSync_ForApple[8976:2834495] [lifecycle ] [u EFC68F17-B2CF-4CB0-8994-C50A12180DA3:m (null)] [JS.TossSync-Apple.Copy(1.0)] Connection to plugin interrupted while in use.
2020-04-18 01:12:51.484772+0900 TossSync_ForApple[8976:2834319] [ShareSheet] cancelled request - error: Extension cancelled by host.
2020-04-18 01:12:51.485174+0900 TossSync_ForApple[8976:2834319] [ShareSheet] Cannot connect to view controller in JS.TossSync-Apple.Copy - Error Domain=NSCocoaErrorDomain Code=4099 "The connection to service on pid 8983 named JS.TossSync-Apple.Copy.viewservice was interrupted, but the message was sent over an additional proxy and therefore this proxy has become invalid." UserInfo={NSDebugDescription=The connection to service on pid 8983 named JS.TossSync-Apple.Copy.viewservice was interrupted, but the message was sent over an additional proxy and therefore this proxy has become invalid.} info {
NSDebugDescription = "The connection to service on pid 8983 named JS.TossSync-Apple.Copy.viewservice was interrupted, but the message was sent over an additional proxy and therefore this proxy has become invalid.";
}

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!!

How to perform segue ONLY if token not nil swift 5

As the title is saying, im working on a login. The tokenHandler is already working and im using a KeychainAccess.
Here my tokenHandler class:
import KeychainAccess
class TokenHandler {
func saveUsernameToKeyChain(username: String) {
do {
try keychain.set(username, key: "myUsername")
} catch let error {
print(error)
}
}
func getUsernameFromKeyChain() -> String? {
return keychain[string: "myUsername" ]
}
func saveUserPasswordToKeyChain(password: String) {
do {
try keychain.set(password, key: "UserPassword")
} catch let error {
print(error)
}
}
func getUserPasswordFromKeyChain() -> String? {
return keychain[string: "UserPassword"]
}
let keychain = Keychain(service: "com.mybackendpage")
func getTokenFromKeyChain() -> String? {
return keychain[string: "myToken"]
}
func saveTokenToKeyChain(token: String) {
do {
try keychain.set(token, key: "myToken")
}
catch let error {
print(error)
}
}
func saveRefreshTokenToKeyChain(refreshToken: String) {
do {
try keychain.set(refreshToken, key: "myRefreshToken")
}
catch let error {
print(error)
}
}
func loginToAPI(username: String, password: String) -> Any {
guard let url = URL(string: "https:mypage.com") else
{
return ""
}
let jsonData = try? JSONSerialization.data(withJSONObject: [
"email": username,
"password": password
])
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
// insert json data to the request
request.httpBody = jsonData
URLSession.shared.dataTask(with: request) { (data, response, error) in
guard error == nil else { print(error!.localizedDescription); return }
guard let data = data else { print("Empty data"); return }
if let str = String(data: data, encoding: .utf8) {
print(str)
}
}.resume()
return "TOKENSTRING"
}
}
And here my LoginVC class:
class LoginViewController: UIViewController {
let tokenHandler = TokenHandler()
#IBOutlet weak var usernameTextField: UITextField!
#IBOutlet weak var passwordTextField: UITextField!
#IBOutlet weak var activityIndicator: UIActivityIndicatorView!
override func viewDidLoad() {
super.viewDidLoad()
let username = tokenHandler.getUsernameFromKeyChain()
let userPassword = tokenHandler.getUserPasswordFromKeyChain()
#IBAction func unwindToLogin(_ unwindSegue: UIStoryboardSegue) {
print("-unwinding success-")
}
// func for the login button:
#IBAction func loginButton(_ sender: UIButton) {
activityIndicator.startAnimating()
loginWithCredentials()
let token = tokenHandler.getTokenFromKeyChain()
if token != nil {
performSegue(withIdentifier: "segueToNavigation", sender: self)
} else if ( token == nil ){
// create the alert:
let alert = UIAlertController(title: "Wrong login data", message: "Please try again.", preferredStyle: UIAlertController.Style.alert)
// add an action to the button:
alert.addAction(UIAlertAction( title: "Ok", style: UIAlertAction.Style.default, handler: nil ))
// show the alert:
self.presentingViewController
print("-Token could not be created.-")
}
else {
// create the alert:
let alert = UIAlertController(title: "Wrong login data", message: "Please try again.", preferredStyle: UIAlertController.Style.alert)
// add an action to the button:
alert.addAction(UIAlertAction( title: "Ok", style: UIAlertAction.Style.default, handler: nil ))
// show the alert:
self.presentingViewController
print("-Token could not be created.-")
}
}
func loginWithCredentials() {
let username: String = usernameTextField.text!
let password: String = passwordTextField.text!
let authResponse = tokenHandler.loginToAPI(username: username, password: password)
}
}
Im still not skilled swift programmer, so I will be happy if any of you could give me some good advices. I was reading and trying to work with the delegate principle, but frankly, my guts are telling me, that this is not what I need.
I was reading about
PerformSegueWithIdentifier
but not really understand how to transform it into my code...
The segues which I have included storyboardwise are working, but unfortunately also, if the test user didn't do the login. So, im pressing the login button w/o any username and userpwd and im getting anyway to the next View. Not cool, so help me please :)
EDIT: I changed performSegue to shouldPerformSegue but im still getting access to the next View w/o any permission.
EDIT: Im getting:
-Token could not be created.-
{"message":"The given data was invalid.","errors":{"email":["The email field is required."],"password":["The password field is required."]}}
So the error is correct, but by pressing on the "Login" Button im still getting to the next View.
EDIT:
Ive tried a few changes, now I have for eg:
if tokenHandler.getTokenFromKeyChain() != nil
instead of
let token = tokenHandler.getTokenFromKeyChain()
if token != nil
Apparently, nothing what im doing in this IBAction for the LoginButton does anything different. What am I missing?
Well, it looks to me, that if you did once call saveTokenToKeyChain() ever since you've been running the app on your device/simulator, then the KeyChain will hold some string there, as I can't see where you set it to nil (I can't see where you set it at all, but let's suppose that you deleted the code that saves the token). So what your current logic does is that it performs the segue if you have some string saved as token (no matter if it's empty string, random string or an expired token). Whatever was left there, getTokenFromKeyChain() will return you a string so your if token != nil will always evaluate to true.
On the other hand, if you clear the KeyChain data, as I can't find any piece of code that saves the token, the UI will always say that the login fails, even if it actually succeeds.
So you should handle the login success/failure with properly writing/deleting the token to/from KeyChain.

Issue with signup page using clean swift architecture in Xcode

I am currently implementing a signup feature into a chat application I am working on. In my 'SignupViewController' I want to implement a function named 'signupButtonPressed' which routes me from a signup viewcontroller to a 'ListContacts' viewcontroller. If the signup fails, then a function called 'showValidationError' will execute. Code excerpt from my SignupViewController below:
#IBAction func signupButtonPressed(_ sender: Any) {
let request = Signup.Request(
name: fullNameTextField.text!,
email: emailTextField.text!,
password: passwordTextField.text!
)
interactor?.createAccount(request: request)
}
func showValidationError(_ message: String) {
let alertCtrl = UIAlertController(title: "Oops! An error occurred", message: message, preferredStyle: .alert)
alertCtrl.addAction(UIAlertAction(title: "Ok", style: .cancel, handler: nil))
self.show(alertCtrl, sender: self)
}
I am using Swift Clean Architecture, so I will link the code to my Signup Router, Model, and Interactor files also:
1) Signupinteractor.swift:
import Foundation
protocol SignupBusinessLogic {
func createAccount(request: Signup.Request)
}
class SignupInteractor: SignupBusinessLogic {
var viewController: SignupFormErrorLogic?
var router: (NSObjectProtocol & SignupRoutingLogic)?
var worker = UsersWorker()
func createAccount(request: Signup.Request) -> Void {
self.worker.signup(request: request) { user, error in
guard error == nil else {
print(error!)
self.viewController?.showValidationError("Error creating account!")
return
}
self.router?.routeToListContacts()
}
}
}
2) SignupModels.swift:
import Foundation
enum Signup {
struct Request {
var name: String
var email: String
var password: String
}
struct Response {
var user: User?
init(data: [String:Any]) {
self.user = User(
id: data["id"] as! Int,
name: data["name"] as! String,
email: data["email"] as! String,
chatkit_id: data["chatkit_id"] as! String
)
}
}
}
3) SignupRouter.swift:
import UIKit
#objc protocol SignupRoutingLogic {
func routeToListContacts()
}
class SignupRouter: NSObject, SignupRoutingLogic {
weak var viewController: SignupViewController?
func routeToListContacts() {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let destinationVC = storyboard.instantiateViewController(withIdentifier: "MainNavigator") as! UINavigationController
viewController!.show(destinationVC, sender: nil)
}
}
The signup function in my UsersWorker.swift file:
func signup(request: Signup.Request, completionHandler: #escaping (User?, UsersStoreError?) -> Void) {
let params: Parameters = [
"name": request.name,
"email": request.email,
"password": request.password
]
postRequest("/api/users/signup", params: params, headers: nil) { data in
guard data != nil else {
return completionHandler(nil, UsersStoreError.CannotSignup)
}
let response = Signup.Response(data: data!)
CurrentUserIDDataStore().setID(CurrentUserID(id: response.user?.chatkit_id))
let request = Login.Account.Request(
email: request.email,
password: request.password
)
self.login(request: request) { token, error in
guard error == nil else {
return completionHandler(nil, UsersStoreError.CannotLogin)
}
DispatchQueue.main.async {
completionHandler(response.user, nil)
}
}
}
}
When I enter signup details into the signup UITextfields (fullNameTextField; emailTextField; passwordTextField) and press the signup button, an error called 'CannotSignup' triggers. Unsure why however. This case can also be found in my UsersWorker.swift file:
enum UsersStoreError: Error {
case CannotLogin
case CannotSignup
case CannotFetchChatkitToken
}
Would be great if anyone is able to look over the code to get an idea for what the issue might be, and how I might resolve it? If any further info is required just ask!
Most probably the API call is failing, Please make a check on return HTTP status code instead of data. In some cases the API call can be success without any response data
Ideally send an Error instance as well along with data back from postRequest method

What is the correct way to log in with facebook on firebase? swift

When I log in with a facebook account in a view, I pass it a second view, in the second view I want a fetch query but in the view log I get permission denied and I dont see the info.
I have a normal firebase account, application test facebook.
this is the code view log in
#IBAction func InicioSesionFacebook(_ sender: Any)
{
esperaSesion.isHidden = false
esperaSesion.startAnimating()
let fbLoginManager = FBSDKLoginManager()
fbLoginManager.logIn(withReadPermissions: ["public_profile", "email"], from: self) { (result, error) in
if let error = error {
print("Failed to login: \(error.localizedDescription)")
self.esperaSesion.stopAnimating()
return
}
guard let accessToken = FBSDKAccessToken.current() else {
print("Failed to get access token")
self.esperaSesion.stopAnimating()
return
}
let credential = FacebookAuthProvider.credential(withAccessToken: accessToken.tokenString)
// Perform login by calling Firebase APIs
Auth.auth().signIn(with: credential, completion: { (user, error) in
if let error = error
{
self.esperaSesion.stopAnimating()
print("Login error: \(error.localizedDescription)")
let alertController = UIAlertController(title: "Login Error", message: error.localizedDescription, preferredStyle: .alert)
let okayAction = UIAlertAction(title: "OK", style: .cancel, handler: nil)
alertController.addAction(okayAction)
self.present(alertController, animated: true, completion: nil)
return
}
else
{
let fbloginresult : FBSDKLoginManagerLoginResult = result!
if (result?.isCancelled)!
{
return
}
else
{
// Present the main view
self.esperaSesion.stopAnimating()
if let viewController = self.storyboard?.instantiateViewController(withIdentifier: "NavigationMasterController")
{
UIApplication.shared.keyWindow?.rootViewController = viewController
self.dismiss(animated: true, completion: nil)
}
}
}
})
}
}
this is the code in the second view, a query
import FirebaseAuth
import FirebaseDatabase
import FBSDKLoginKit
var refDB: DatabaseReference!
override func viewDidLoad()
{
super.viewDidLoad()
refDB = Database.database().reference()
CerrarSesion.layer.cornerRadius = 8
imagenPerfil.layer.cornerRadius = imagenPerfil.frame.height/2
imagenPerfil.clipsToBounds = true
verDatos()
// Do any additional setup after loading the view.
}
func verDatos()
{
let userID = Auth.auth().currentUser?.uid
refDB.child("users").child(userID!).observeSingleEvent(of: .value, with: { (snapshot) in
// Get user value
let value = snapshot.value as? NSDictionary
let nombre = value?["nombre"] as? String ?? ""
let apellido = value?["apellido"] as? String ?? ""
self.nombreUsuario.text = nombre
self.apellidoUsuario.text = apellido
// ...
}) { (error) in
print(error.localizedDescription)
}
}
and the button log out
#IBAction func CerrarSesion(_ sender: Any)
{
do
{
try Auth.auth().signOut()
self.view.window?.rootViewController?.dismiss(animated: true, completion: borrarUserDefaults)
}
catch let error as NSError
{
print (error.localizedDescription)
}
}
how is the correct form for log out when I logged in with facebook account?
You can check out my YouTube Tutorial on this exact topic !
https://www.youtube.com/watch?v=BfwNf-W-R4U
The version of the Facebook API that you are using is dated. The Login function should look something like this
let loginManager = LoginManager()
loginManager.logIn(readPermissions: [.publicProfile], viewController: self) {loginResult in
switch loginResult {
case .failed(let error):
print("error: \(error)")
case .cancelled:
print("User cancelled login.")
case .success(let grantedPermissions, let declinedPermissions, let accessToken):
print(grantedPermissions)
print(declinedPermissions)
fbAccessToken = accessToken
let credential = FacebookAuthProvider.credential(withAccessToken: (fbAccessToken?.authenticationToken)!)
Auth.auth().signIn(with: credential) { (user, error) in
if let error = error {
print(error)
return
}
currentUser = Auth.auth().currentUser
moveToHomeScreen()
print("Logged in!")
}
}
}
I think that you are getting a permissions error because the parameter name from the AccessToken changed and you are passing the wrong value. (Sorry I cant recall what the change was).
If you are following the Facebook API instructions on the facebook developer portal they are horrendously out of date iOS 9 I think.

Password is getting cached somewhere, need to clear it

I am working on an app that accesses a REST API Webservice. Everything is working great, except I recently started working on the the ability to logout and switch users and I've run into a strange situation. If I log out, and then click login again without entering the password it's working. I've even debugged the code and see that the password is blank, but the authentication is still working. Here is the code:
import UIKit
import LocalAuthentication
var userName = String()
var password = String()
var server = String()
var port = String()
var myUser = User()
var myExtensions = [ExtensionListItem]()
var myDevices = [Device]()
class LoginViewController: UIViewController, NSURLSessionDelegate, UITextFieldDelegate {
let authContext: LAContext = LAContext()
var logOutUser = Bool()
#IBOutlet weak var activityIndicator: UIActivityIndicatorView!
#IBOutlet weak var serverNameField: UITextField!
#IBOutlet weak var usernameField: UITextField!
#IBOutlet weak var passwordField: UITextField!
#IBOutlet var loginEnable: UIButton!
var userPasswordString = NSString()
let userRequest = NSMutableURLRequest()
var userSession = NSURLSession()
override func viewDidLoad() {
super.viewDidLoad()
NSNotificationCenter.defaultCenter().addObserver(self, selector: #selector(logoff), name: "logoff", object: nil)
if logOutUser {
NSUserDefaults.standardUserDefaults().setValue("", forKey: "password")
NSURLCache.sharedURLCache().removeAllCachedResponses()
userPasswordString = NSString()
}
//Determine if the user has a stored Username and populate the usernameField if possible
if NSUserDefaults.standardUserDefaults().objectForKey("userName") != nil{
usernameField.text = NSUserDefaults.standardUserDefaults().objectForKey("userName") as? String}
//Determine if the user has a stored ServerName and populate the serverNameField if possible.
if NSUserDefaults.standardUserDefaults().objectForKey("serverName") != nil{
serverNameField.text = NSUserDefaults.standardUserDefaults().objectForKey("serverName") as? String}
//Determin if the user has requested to use Touch ID
if (NSUserDefaults.standardUserDefaults().objectForKey("useTouchID") != nil) {
if NSUserDefaults.standardUserDefaults().valueForKey("useTouchID") as! Bool == true && CheckTouchIDCapable(){
//Trigger Touch ID
usernameField.enabled = false
passwordField.enabled = false
serverNameField.enabled = false
activityIndicator.startAnimating()
TouchIDCall()
}
}
// Do any additional setup after loading the view.
}
func logoff(){
NSURLCache.sharedURLCache().removeAllCachedResponses()
userSession.invalidateAndCancel()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
NSURLCache.sharedURLCache().removeAllCachedResponses()
// Dispose of any resources that can be recreated.
}
#IBAction func loginButton(sender: AnyObject) {
NSUserDefaults.standardUserDefaults().setObject(usernameField.text, forKey: "userName")
NSUserDefaults.standardUserDefaults().setObject(passwordField.text, forKey: "password")
NSUserDefaults.standardUserDefaults().setObject(serverNameField.text, forKey: "serverName")
if NSUserDefaults.standardUserDefaults().valueForKey("touchIDPreferenceSet") == nil && CheckTouchIDCapable() {
DisplayTouchIDQuestion("Use Touch ID?", message: "Would you like to use touch ID to login?")
}else{
usernameField.enabled = false
passwordField.enabled = false
serverNameField.enabled = false
activityIndicator.startAnimating()
CheckUser()
}
print("Password: \(password)")
print("Stored Password: \(NSUserDefaults.standardUserDefaults().valueForKey("password"))")
print("?? \(NSUserDefaults.standardUserDefaults().objectForKey("password"))")
}
func CheckUser(){
userName = (NSUserDefaults.standardUserDefaults().objectForKey("userName") as? String)!
if !logOutUser{
password = (NSUserDefaults.standardUserDefaults().objectForKey("password") as? String)!
}
server = (NSUserDefaults.standardUserDefaults().objectForKey("serverName") as? String)!
port = "8443"
// set up the base64-encoded credentials
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
userPasswordString = NSString(format: "%#:%#", userName, password)
let userPasswordData = userPasswordString.dataUsingEncoding(NSUTF8StringEncoding)
let base64EncodedCredential = userPasswordData!.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.Encoding64CharacterLineLength)
let authString = "Basic \(base64EncodedCredential)"
config.HTTPAdditionalHeaders?.removeAll()
config.HTTPAdditionalHeaders = ["Authorization" : authString]
config.timeoutIntervalForRequest = 10.0
// create the user request
let userUrlString = NSString(format: "https://%#:%#/webserver/user/%#", server, port, userName)
let userUrl = NSURL(string: userUrlString as String)
userRequest.cachePolicy = .ReloadIgnoringLocalAndRemoteCacheData
userRequest.URL = userUrl!
userRequest.HTTPMethod = "GET"
userRequest.setValue("Basic \(base64EncodedCredential)", forHTTPHeaderField: "Authorization")
userSession = NSURLSession(configuration: config, delegate: self, delegateQueue:NSOperationQueue.mainQueue())
//Send User Request to the server and populate labels with response.
_ = userSession.dataTaskWithRequest(userRequest) { (data, response, error) in
dispatch_async(dispatch_get_main_queue(), { () -> Void in
if error?.code != nil{
print("ERROR: \(error!.localizedDescription)")
self.DisplayAlert("Error", message: error!.localizedDescription)
}else{
_ = NSString (data: data!, encoding: NSUTF8StringEncoding)
let dataString = NSString(data: data!, encoding: NSUTF8StringEncoding)
let accessDenied = Bool(dataString?.rangeOfString("HTTP Status 403").location != NSNotFound)
let authFailure = Bool(dataString?.rangeOfString("HTTP Status 401").location != NSNotFound)
if (authFailure || accessDenied) {
print("\(NSDate()): Unsuccessful Password Authentication Attempt for user: \(NSUserDefaults.standardUserDefaults().valueForKey("userName")!)")
self.DisplayAlert("Access Denied", message: "Please Verify Your Credentials")
}else{
print("\(NSDate()): Successful Password Authentication for user: \(NSUserDefaults.standardUserDefaults().valueForKey("userName")!)")
self.performSegueWithIdentifier("authenticated", sender: self)
}
}
})
}.resume()
}
func URLSession(session: NSURLSession, didReceiveChallenge challenge: NSURLAuthenticationChallenge, completionHandler: (NSURLSessionAuthChallengeDisposition, NSURLCredential?) -> Void) {
completionHandler(NSURLSessionAuthChallengeDisposition.UseCredential, NSURLCredential(forTrust: challenge.protectionSpace.serverTrust!))
}
override func prefersStatusBarHidden() -> Bool {
return true
}
// MARK: - Keyboard Functions
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
self.view.endEditing(true)
}
func textFieldShouldReturn(textField: UITextField) -> Bool {
textField.resignFirstResponder()
if textField == passwordField && usernameField.text != "" && serverNameField.text != ""{
loginButton(self)
}
return true
}
func ReEnableLogin(){
self.activityIndicator.hidesWhenStopped = true
self.activityIndicator.stopAnimating()
self.usernameField.enabled = true
self.passwordField.enabled = true
self.serverNameField.enabled = true
}
func DisplayAlert(title: String, message: String){
let alertController = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.Alert)
alertController.addAction(UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.Default, handler: nil))
self.presentViewController(alertController, animated: true, completion: nil)
self.ReEnableLogin()
}
func DisplayTouchIDQuestion(title: String, message: String){
let alertControllerQuestion = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.Alert)
alertControllerQuestion.addAction(UIAlertAction(title: "Yes", style: UIAlertActionStyle.Default, handler: { (action:UIAlertAction) in
NSUserDefaults.standardUserDefaults().setValue(true, forKey: "useTouchID")
NSUserDefaults.standardUserDefaults().setValue(true, forKey: "touchIDPreferenceSet")
NSUserDefaults.standardUserDefaults().setValue(self.passwordField.text, forKey: "touchIDCachedCredential")
self.CheckUser()
}))
alertControllerQuestion.addAction(UIAlertAction(title: "No", style: UIAlertActionStyle.Default, handler: { (action:UIAlertAction) in
NSUserDefaults.standardUserDefaults().setValue(false, forKey: "useTouchID")
NSUserDefaults.standardUserDefaults().setValue(true, forKey: "touchIDPreferenceSet")
self.CheckUser()
}))
self.presentViewController(alertControllerQuestion, animated: true, completion: nil)
}
func CheckTouchIDCapable()-> Bool {
var error: NSError?
var touchEnabledDevice: Bool = false
if authContext.canEvaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, error: &error){
touchEnabledDevice = true
}
return touchEnabledDevice
}
func TouchIDCall(){
authContext.evaluatePolicy(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, localizedReason: "Place your finger on the Home button to log into Collaboration User Tools", reply: { (wasSuccessful, error) in
if wasSuccessful{
print("\(NSDate()): Successful Biometric Authentication for user: \(NSUserDefaults.standardUserDefaults().valueForKey("userName")!)")
NSUserDefaults.standardUserDefaults().setValue(NSUserDefaults.standardUserDefaults().valueForKey("touchIDCachedCredential"), forKey: "password")
self.CheckUser()
}else{
print("\(NSDate()): Error: \(error!.code)")
print("\(NSDate()): Unsuccessful Biometric Authentication for user: \(NSUserDefaults.standardUserDefaults().valueForKey("userName")!)")
let qualityOfServiceClass = QOS_CLASS_USER_INTERACTIVE
let backgroundQueue = dispatch_get_global_queue(qualityOfServiceClass, 0)
dispatch_async(backgroundQueue, {
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.activityIndicator.stopAnimating()
})
})
self.ReEnableLogin()
}
})
}
}
I've tried:
NSURLCache.sharedURLCache().removeAllCachedResponses()
userSession.invalidatedAndCancel()
The logout table view controller calls this method:
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
switch indexPath.row{
case 0:
myUser = User()
myExtensions.removeAll()
myDevices.removeAll()
NSUserDefaults.standardUserDefaults().setObject("", forKey: "password")
userName = ""
password = ""
NSURLCache.sharedURLCache().removeAllCachedResponses()
NSNotificationCenter.defaultCenter().postNotificationName("logoff", object: nil)
performSegueWithIdentifier("logout", sender: self)
default:
break
}
}
I don't know where the password is being cached. Any ideas?
I think your issue lies on part of your logic when handling your password. First, in your func CheckUser() you assign the value of var password if !logOutUser from your user defaults, however this property (password) is not getting cleared at any point, so if !logOutUser is false (which it looks like always is, as its not getting set anywhere), and your input fields are empty, then it will use the previous value that you had store in there.
So if I am correct, and the culprit here is that the password field is storing its data, a quick, dirty fix would be to simply clear these fields (username and password) after you perform your request. A better fix would be to directly pass the usernameField.text value directly and clear it after its done. (I'm unsure why you are storing your user's password in your app, which I would recommend reconsidering, if you need it for validating a session you should be using authentication tokens)
// set up the base64-encoded credentials
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
userPasswordString = NSString(format: "%#:%#", userName, password)
userName = ""
password = ""
I would also like to point out that you should be using optionals for these fields, you should be using optional unwrapping for your user defaults and also adding a validation mechanism so that you do not submit requests with empty fields, ideally your button would be disabled or you would alert your user of an invalid field.
Good Luck!
EDIT
Try this approach: rather than using a class property for the values of password username and server. Use method variables to set your values, this way these values will get disposed when the method terminates. (This is a bit of a shot in the dark as i dont have a compiler at hand so there might be some minor syntax errors). - note the method signature
func CheckUser(username: String, password: String, server: String){
let port = "8443"
let config = NSURLSessionConfiguration.defaultSessionConfiguration()
let userPasswordString = NSString(format: "%#:%#", username, password)
let userPasswordData = userPasswordString.dataUsingEncoding(NSUTF8StringEncoding)
let base64EncodedCredential = userPasswordData!.base64EncodedStringWithOptions(NSDataBase64EncodingOptions.Encoding64CharacterLineLength)
let authString = "Basic \(base64EncodedCredential)"
config.HTTPAdditionalHeaders?.removeAll()
config.HTTPAdditionalHeaders = ["Authorization" : authString]
config.timeoutIntervalForRequest = 10.0
// create the user request
let userUrlString = NSString(format: "https://%#:%#/webserver/user/%#", server, port, username)
let userUrl = NSURL(string: userUrlString as String)
userRequest.cachePolicy = .ReloadIgnoringLocalAndRemoteCacheData
userRequest.URL = userUrl!
userRequest.HTTPMethod = "GET"
userRequest.setValue("Basic \(base64EncodedCredential)", forHTTPHeaderField: "Authorization")
userSession = NSURLSession(configuration: config, delegate: self, delegateQueue:NSOperationQueue.mainQueue())
//Send User Request to the server and populate labels with response.
let task = userSession.dataTaskWithRequest(userRequest) { (data, response, error) in
dispatch_async(dispatch_get_main_queue(), { () -> Void in
if error?.code != nil{
print("ERROR: \(error!.localizedDescription)")
self.DisplayAlert("Error", message: error!.localizedDescription)
}else{
_ = NSString (data: data!, encoding: NSUTF8StringEncoding)
let dataString = NSString(data: data!, encoding: NSUTF8StringEncoding)
let accessDenied = Bool(dataString?.rangeOfString("HTTP Status 403").location != NSNotFound)
let authFailure = Bool(dataString?.rangeOfString("HTTP Status 401").location != NSNotFound)
if (authFailure || accessDenied) {
print("\(NSDate()): Unsuccessful Password Authentication Attempt for user: \(NSUserDefaults.standardUserDefaults().valueForKey("userName")!)")
self.DisplayAlert("Access Denied", message: "Please Verify Your Credentials")
}else{
print("\(NSDate()): Successful Password Authentication for user: \(NSUserDefaults.standardUserDefaults().valueForKey("userName")!)")
self.performSegueWithIdentifier("authenticated", sender: self)
}
}
})
}.resume()
}
Then you can call your method as so:
if NSUserDefaults.standardUserDefaults().valueForKey("touchIDPreferenceSet") == nil && CheckTouchIDCapable() {
DisplayTouchIDQuestion("Use Touch ID?", message: "Would you like to use touch ID to login?")
}else{
usernameField.enabled = false
passwordField.enabled = false
serverNameField.enabled = false
activityIndicator.startAnimating()
CheckUser(usernameField.text, password: passwordField.text, server: serverNameField.text)
}
My problem was never related to password caching. Most likely due to my relatively amateur experience level, I never considered the fact that it might be COOKIES keeping the session up. That's exactly what it ended up being. I solved the situation simply by deleting all cookies during the logout procedure. I added the following to my logout code and it's working perfectly.
print("\(NSDate()): Logout Requested, Deleting Cookies")
let cookieStorage = NSHTTPCookieStorage.sharedHTTPCookieStorage()
let cookies = cookieStorage.cookies! as [NSHTTPCookie]
print("\(NSDate()): Cookie count: \(cookies.count)")
for cookie in cookies{
print("\(NSDate()): Deleting Cookie name: \(cookie.name) value: \(cookie.value)")
NSHTTPCookieStorage.sharedHTTPCookieStorage().deleteCookie(cookie)
}