Facebook api swift current user is nil - ios

I have a problem with Facebook api. After login and success unwrapping user token I have a problem with taking user profile: it's nil.
func updateLoginStatus() {
if let _ = AccessToken.current {
let currentUser = UserProfile.current
userName.text = currentUser?.fullName
userPictureView = UserProfile.PictureView(frame: blankUserImage.frame, profile: currentUser)
userPictureView!.clipsToBounds = true
userPictureView!.layer.cornerRadius = userPictureView!.bounds.width/2
popUpView.addSubview(userPictureView!)
isLogged = true
updateLabels()
} else {
isLogged = false
userPictureView?.removeFromSuperview()
updateLabels()
}
}
Even if I try to get profile manual by using let userProfile = UserProfile(userId: (AccessToken.current?.userId)!) it also take nil at all parameters, but it have initialized user. Funny part is that with that UserProfile.current it have take user picture. What it can be? How I can get full user name by other methods?

This problem happend in android too.
You should load profile (listen profile change in android) after you got access token.
I write a sample and just tested in swift 3, Facebook iOS SDK 4.21.0.
Then you can got updatedUser.
if let _ = FBSDKAccessToken.current() {
if let currentUser = FBSDKProfile.current() {
print("current user got: \(currentUser)")
} else {
print("current user not got")
FBSDKProfile.loadCurrentProfile(completion: {
profile, error in
if let updatedUser = profile {
print("finally got user: \(updatedUser)")
} else {
print("current user still not got")
}
})
}
}

Related

How to resolve totally app freeze after DocuSign login

My app totally freeze after successful login to DocuSign
Here my code:
#IBAction private func signDocument(_ sender: UIButton) {
guard let hostURL = URL(string: Environment.current.docuSignHost) else { return }
isLoading = true
DSMManager.login(withEmail: Environment.current.docuSignUserID,
password: Environment.current.docuSignPass,
integratorKey: Environment.current.docuSignIntegratorKey,
host: hostURL) { [weak self] info, error in
self?.isLoading = false
if let error = error {
self?.error = error
} else {
self?.showDocuSign(info: info)
}
}
}
// MARK: - Helpers
private func showDocuSign(info: DSMAccountInfo?) {
guard let info = info else { return }
envelopManager.perform(with: info, presentingController: self)
}
final class EnvelopeManager {
private let envelopesManager = DSMEnvelopesManager()
private let templateManager = DSMTemplatesManager()
// MARK: - Lifecycle
func sync() {
envelopesManager.syncEnvelopes()
}
func perform(with config: DSMAccountInfo, presentingController: UIViewController) {
guard let path = Bundle.main.path(forResource: R.file.tgkCapitalPortfolioBAgreementPdf.name, ofType: "pdf") else { return }
let envelopDefinition = DSMEnvelopeDefinition()
envelopDefinition.envelopeName = "Some name"
envelopDefinition.emailSubject = "Please Sign Envelope on Document"
envelopDefinition.emailBlurb = "Hello, Please sign my Envelope"
let builder = DSMDocumentBuilder()
builder.addDocumentId("1")
builder.addName(R.file.tgkCapitalPortfolioBAgreementPdf.name)
builder.addFilePath(Bundle.main.path(forResource: R.file.tgkCapitalPortfolioBAgreementPdf.name,
ofType: "pdf")!)
let document = builder.build()
envelopDefinition.documents = [document]
let signHere = DSMSignHere()
signHere.documentId = document.documentId
signHere.pageNumber = 1
signHere.recipientId = "1"
signHere.frame = .init(originX: 100,
originY: 100,
width: 100,
height: 100,
originYOffsetApplied: 50)
signHere.tabId = "1"
let tabs = DSMTabs()
tabs.signHereTabs = [signHere]
let signer = DSMSigner()
signer.email = config.email
signer.name = config.userName
signer.userId = config.userId
signer.clientUserId = config.userId
signer.routingOrder = 1
signer.recipientId = "1"
signer.tabs = tabs
let signers: [DSMSigner] = [signer]
let recipients = DSMRecipients()
recipients.signers = signers
envelopDefinition.recipients = recipients
envelopDefinition.status = "created"
envelopesManager.composeEnvelope(with: envelopDefinition, signingMode: .offline) { [weak self] envelopID, error in
if let envelopID = envelopID {
print(envelopID)
self?.presentSigning(presenter: presentingController,
envelopeID: envelopID)
} else {
print(error.localizedDescription)
}
}
}
private func presentSigning(presenter: UIViewController, envelopeID: String) {
envelopesManager.resumeSigningEnvelope(withPresenting: presenter,
envelopeId: envelopeID) { (viewController, error) in
if let viewController = viewController {
print(viewController)
}
if let error = error {
print(error.localizedDescription)
}
}
}
}
All closures have a success status but the app freezes and any DocuSign view controllers are not showing.
But if in the second attempt I add the calling the logout before login - all works as expected
#IBAction private func signDocument(_ sender: UIButton) {
guard let hostURL = URL(string: Environment.current.docuSignHost) else { return }
isLoading = true
DSMManager.logout()
DSMManager.login(withEmail: Environment.current.docuSignUserID,
password: Environment.current.docuSignPass,
integratorKey: Environment.current.docuSignIntegratorKey,
host: hostURL) { [weak self] info, error in
self?.isLoading = false
if let error = error {
self?.error = error
} else {
self?.showDocuSign(info: info)
}
}
}
How to resolve this issue? How to make that my app will not freeze after any successful login?
Edit1
I have figured out that the total app freeze is occurring by the first app install. When I open an app that was already installed and I many times opened it earlier and go to DocuSign flow, call logout and finally call login- all works as expected - I don't have any app freeze. But if I don't call the logout method in this chain then my app freezes.
The problem was consistent in that DSMManager.setup() called earlier than applicationDidLaunch. After I moved it to applicationDidLaunch the problem was resolved
Thanks for posting the solution #igdev.
Yes, correct sequence to perform SDK setup is during applicationDidLaunch or afterwards, say during viewDidLoad() of any of the viewControllers (lazy sdk setup & initialization). As long as setup() is done prior to any of the sdk-calls such as login(withAccessToken:...) or cacheEnvelope(...). DocuSign SDK setup() establishes the core data and other essential components required for its functionality.
Typical sequence of calls made with DocuSign SDK.
Application Launch
Initialize DocuSign SDK with DocuSignSDK setup()
Login with DocuSignSDK login(with...). Login could be skipped if a prior SDK session is already established, for e.g. when getting signatures in offline-envelopes using downloaded templates.
Accessing TemplatesManager or EnvelopesManager functionality [Download templates, Sign Envelopes, Sync offline envelopes, etc] and other tasks such as handling DocuSign-SDK notifications to track events (e.g. Signing Completed Envelope Cached, etc).
Logout, it's not required, if needed can perform DSMManager.logout() when client user is logged out.

Customize keychain alert actions

I'm wondering if customization of the keychain alert is possible? Below you can find the image:
What I'm trying to achieve:
When the authentication with FaceID/TouchID fails and pops above shown alert, I want user to tap Enter Passcode and show my custom Application Passcode UI, instead of device passcode (system ui). Basically, I want to customize the fallback action.
Context:
I know it is possible with using LAContext, however due to the reasons that we can bypass it, I don't feel comfortable in implementing that, so I chose Keychain accessControlFlags.
Snapshot code, what I currently have:
func data(forAccount account: String, service: String, accessGroup: String?) throws -> Data? {
guard let accessControl = createAccessControl(with: .userPresence) else { return nil }
var query = self.query(forAccount: account, service: service, accessGroup: accessGroup)
var authContext = LAContext()
authContext.localizedFallbackTitle = "Enter Passcode"
authContext.localizedCancelTitle = "Cancel"
query[KeychainConstants.matchLimit] = KeychainConstants.matchLimitOne
query[KeychainConstants.returnData] = kCFBooleanTrue
query[KeychainConstants.authenticationContext] = authContext
query[KeychainConstants.accessControl] = accessControl
var result: AnyObject?
let status = withUnsafeMutablePointer(to: &result) {
securityItemManager.copyMatching(query, result: UnsafeMutablePointer($0))
}
if let error = error(fromStatus: status), error != .itemNotFound {
throw error
}
guard result != nil else { return nil }
guard let resultData = result as? Data else { throw AccessError.invalidQueryResult }
return resultData
}
The only customization you can do is change the dialog buttons labels. You can do this using the following:
let context = LAContext()
context.localizedFallbackTitle = ""
context.localizedCancelTitle = "Manual connection"
By setting the localizedFallbackTitle property to an empty string removes the fallback button (enter password).
Unfortunately, there is no "delegate" kind of callback for this dialog. You need to manage the user interaction using errors. Therefore, LAError.userCancel is triggered when user has pressed the cancel button and LAError.userFallback is triggered when the fallback button has been pressed.
Here's an example:
let context = LAContext()
context.localizedFallbackTitle = "Facebook"
context.localizedCancelTitle = "Web Connection"
[...]
context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics,
localizedReason: "My reason") { (success, errorOrNil) in
if let error = errorOrNil {
if success {
// The evaluation succeeded
} else {
if let error = errorOrNil {
switch error {
case .userCancel:
// Execute the custom actions you want to do. Here it would be
// to start a web login process.
self.webLogin()
case .userFallback:
self.fabebookAuthentication()
default:
// Other error thrown by the framework.
}
}
}
}

Login credentials missing in swift UserDefaults

I have an application which uses a rest api for authentication. The problem I am facing now is that I save user's token in my UserDefaults and username too because those are the two main parameters needed to get user details. so if the application is closed by the user he should still be able to view the view his profile when he opens the application back but instead the profile returns empty details. this is the UserDefaults codes that I have
let defaults = UserDefaults.standard
var isLoggedIn : Bool {
get {
return defaults.bool(forKey: LOGGED_IN_KEY)
}
set {
defaults.set(newValue, forKey: LOGGED_IN_KEY)
}
}
//Auth Token
var authToken: String {
get {
return defaults.value(forKey: TOKEN_KEY) as? String ?? ""
}
set {
defaults.set(newValue, forKey: TOKEN_KEY)
}
}
var userUsername: String {
get {
return defaults.value(forKey: USERNAME_KEY) as? String ?? ""
}
set {
defaults.set(newValue, forKey: USERNAME_KEY)
}
}
I have no idea why it isnt retrieving the user data.
My second question is when I logout the user, all the users details are cleared as expected but the moment I try loging in with a different user, the new user's authToken and details gets printed in the console but the user profile returns the profile of the previous person. which is not supposed to be. my code is shown below
func logoutUser() -> Void {
pk = 0
username = ""
email = ""
firstName = ""
lastName = ""
AuthService.instance.isLoggedIn = false
AuthService.instance.authToken = ""
AuthService.instance.userUsername = ""
}
#IBAction func logoutPressed(_ sender: Any) {
UserDataService.instance.logoutUser()
dismiss(animated: true, completion: nil)
}
I would also like to add that when i run the api using postman i get a response that "detail": "Signature has expired." so i had to input the new token in the header so it displays the user details again
enum SettingKeys: String {
case authToken
//...
}
struct Settings {
static var authToken: String? {
get { return UserDefaults.standard.string(forKey: SettingKeys.authToken.rawValue) }
set(value) { UserDefaults.standard.set(value, forKey: SettingKeys.authToken.rawValue) }
}
static func deleteAll(exclude: [SettingKeys] = []) {
let saveKeys = exclude.map({ $0.rawValue })
for key in UserDefaults.standard.dictionaryRepresentation().keys {
if !saveKeys.contains(key) {
UserDefaults.standard.removeObject(forKey: key)
}
}
}
}
I recommend storing keys as Enum, because then u can use it like that:
//Read
if let token = Settings.authToken {
//do something
}
//Write
Settings.authToken = "123456"
//Delete settings
Settings.deleteAll()
//or choose what to leave
Settings.deleteAll(exclude: [.authToken])
And it's worth to mention that defaults.synchronize() is deprecated.

"cannot convert value of type" - error in Firebase Swift

I am trying to create a user in Firebase with an email & password and upon successful creation of the user record, I would go and update tree with additional attributes as child nodes of the user's UID. Here is what I have in code:
#IBAction func registerPressed(sender: AnyObject) {
let connection = Firebase(url:"https://something.firebaseio.com")
connection.createUser(self.emailTxtField.text, password: self.passwordTxtField.text,
withValueCompletionBlock: { error, result in
if error != nil {
// There was an error creating the account
print("Error Creating Account...Please try again")
} else {
let uid = result["uid"] as? String
print("Successfully created user account with uid: \(uid)")
/*After creating the user, we want to update the tree with other attributes*/
let userAdditionalDetails = ["name": self.nameTxtField.text, "mobile": self.mobileTxtField.text, "username": self.usernameTxtField.text]
let usersRef = connection.childByAppendingPath("users")
let users = ["\(uid)": userAdditionalDetails]
usersRef.setValue(users)
}
})
}
Although am not sure if the above will work or not (since this my first work with Firebase SDK), I am getting the following error:
cannot convert value of type “[String: [String : String?]]” to
expected argument type "AnyObject!"
I am unable of course to build the project due to the above error being triggered at line:
usersRef.setValue(users)
Any idea why this is happening and how to solve it?
Thanks in advance.
Found the solution and just having it here for someone else's future reference:
#IBAction func registerPressed(sender: AnyObject) {
let connection = Firebase(url:"https://something.firebaseio.com")
connection.createUser(self.emailTxtField.text, password: self.passwordTxtField.text,
withValueCompletionBlock: { error, result in
if error != nil {
// There was an error creating the account
print("Error Creating Account...Please try again")
} else {
let uid = result["uid"] as? String
print("Successfully created user account with uid: \(uid)")
/*After creating the user, we want to update the tree with other attributes*/
let userAdditionalDetails = ["name": "\(self.nameTxtField.text!)", "mobile": "\(self.mobileTxtField.text!)", "username": "\(self.usernameTxtField.text!)"]
let usersRef = connection.childByAppendingPath("users")
let users = ["\(uid)": userAdditionalDetails]
usersRef.setValue(users)
}
})
}
The catch is that when initializing userAdditionalDetails, should include the outlets variables in string format. E.g.
"username": "\(username.text!)"
This worked and all objects reflected correctly in backend.
In the call to usersRef.setValue(users), the error message states that the parameter is expected to be of type AnyObject!. Therefore, you must cast users to be of type AnyObject!:
let users = ["\(uid)": userAdditionalDetails]
usersRef.setValue(users as? AnyObject)

integrating security for user objects in swift

I am relatively new to iOS software development.
I am trying to add security for user objects in my parse app.
But have no idea on how to add it to the project.
Should it be like this?
func logInViewController() {
PFUser.logInWithUsernameInBackground("myname", password: "mypass") {
(user: PFUser?, error: NSError?) -> Void in
if user != nil {
// Do stuff after successful login.
let user = PFUser.logInWithUsername("my_username", password: "my_password")
user.username = "my_new_username" // attempt to change username
user.save() // This succeeds, since the user was authenticated on the device.
// Get the user from a non-authenticated method.
let query = PFUser.query()
let userAgain = query!.getObjectWithId(user.objectId!) as! PFUser
userAgain.username = "another_username"
// This will crash, sinse the PFUser is not authenticated
userAgain.save()
} else {
// The login failed. Check error to see why.
}
let currentUser = PFUser.currentUser()
if currentUser != nil {
// Do stuff with the user
} else {
// Show the signup or login screen.
}
}
}
or like this?
func userOnlyNameChange() {
let user = PFUser.logInWithUsername("my_username", password: "my_password")
user.username = "my_new_username" // attempt to change username
user.save() // This succeeds, since the user was authenticated on the device.
// Get the user from a non-authenticated method.
let query = PFUser.query()
let userAgain = query!.getObjectWithId(user.objectId!) as! PFUser
userAgain.username = "another_username"
// This will crash, sinse the PFUser is not authenticated
userAgain.save()
}
or have I just done it completely wrong?

Resources