I am trying to get the CloudKit User's first and last name.
Here is the code:
container.fetchUserRecordID { (recordID, error) in
guard error == nil else { return }
guard let recordID = recordID else { return }
self.container.discoverUserInfo(withUserRecordID: recordID) { (info, fetchError) in
// use info.firstName and info.lastName however you need
print(info?.displayContact?.givenName)
}
}
I am getting the following message when running the print line: [LogFacilityCK] Got a user discovery progress callback with no user identity: {
FetchInfo = ">";
}
The info variable is showing as nil when debugging.
Any thoughts?
Here's how I got the users' name:
CKContainer.default().requestApplicationPermission(.userDiscoverability) { (status, error) in
CKContainer.default().fetchUserRecordID { (record, error) in
CKContainer.default().discoverUserIdentity(withUserRecordID: record!, completionHandler: { (userID, error) in
userName = (userID?.nameComponents?.givenName)! + " " + (userID?.nameComponents?.familyName)!
print("CK User Name: " + userName)
})
}
}
Related
hello, i need some help with function and or possibly closures in that function. I want my function to check the firestore users collection for duplicate documents (usernames). If a duplicate is found i want to display a message, if a duplicate is not found, create a new user. i have folowing code:
func checkIfUserExists(username: String, completion: #escaping (Bool) -> Void) {
let docRef = db.collection("users").document(username)
docRef.getDocument { (document, error) in
if error != nil {
print(error)
} else {
if let document = document {
if document.exists {
completion(true)
} else {
completion(false)
}
}
}
}
}
and i call the function with:
if let username = textField.text, username.count > 8 { // Username needs to be more then 8 Char
checkIfUserExists(username: username) { (doesExist) in
if doesExist {
print("user exists")
} else {
print("new User can be created")
}
}
} else {
print("Username needs to be more then 8 char")
}
}
It works, but i have the feeling it is not good practice and i'm making detours. Is this the right way to do it ?
I think the way you're doing it now should work well, but another option to prevent you from having to do a read of the database before writing is to use security rules. For example, if this is the structure of your users collection...
users: [
username1: { // doc ID is the username
userid: abcFirebaseUserId, // a field for the uid of the owner of the username
//...etc
}
]
...then you can use the following rules:
match /users/{username} {
allow create: if request.auth.uid != null;
allow update, delete: if resource.data.userId = request.auth.uid;
}
This allows any authenticated user to create a new username, but only the owner of that username can update it or delete it. If you aren't allowing users to change their username, you wouldn't even have to worry about the second rule. Then, in the client, you go right to creating a username, like so:
func createUsername(username: String, completion: #escaping (String?) -> Void) {
guard let userId = Auth.auth().currentUser.uid else {
completion("no current user")
return
}
let docRef = db.collection("users").document(username)
docRef.setData(data:[userId: userId]) { error in
if let error = error {
completion(error.debugDescription)
} else {
completion(nil)
}
}
}
This would write the new username to the database and pass an error to the closure if there is one. If the username already exists, an insufficient permissions error would be present. When checking if the user exists, you could display the error or alert the user however you wanted.
createUsername(username: username) { err in
if let err = err {
print("user exists")
} else {
print("new User has been created")
}
}
Just a suggestion though. I think they way you're doing it now is fine, too!
How do I get the current UserId of user? I'm using the following code:
CKContainer.default().requestApplicationPermission(.userDiscoverability) { (status, error) in
CKContainer.default().fetchUserRecordID { (record, error) in
if #available(iOS 10.0, *) {
CKContainer.default().discoverUserIdentity(withUserRecordID: record!, completionHandler: { (userID, error) in
self.currentuserID = userID?.userRecordID
print("user record id")
print(userID)
})
} else {
// Fallback on earlier versions
}
}
However at the end currentUserID is set to nill? Does anyone know how to successfully gain permission and get the current user id?
As far as I know, you don't need permission from requestApplicationPermission to get the CloudKit user's unique ID. You can do this...
CKContainer.default().fetchUserRecordID() { recordID, error in
//...
}
Without ever calling this...
CKContainer.default().requestApplicationPermission(.userDiscoverability){ status, error in
//...
}
The .userDiscoverability is just so other app users can locate your unique user record through an email or phone number.
I hope that helps. :)
You should check the status value before fetch user information. It could be something like this...
CKContainer.default().requestApplicationPermission(.userDiscoverability) { (status, error) in
switch status
{
case .granted:
print("iCloud is OK")
case .initialState:
print("The user has not yet decided whether to grant this permission")
return
case .couldNotComplete:
print("An error occurred during the getting or setting of the app permission")
if let error = error
{
print("err # \(#function) -> \(error.localizedDescription)")
}
return
case .denied:
print("The user denied access to the permission.")
return
}
CKContainer.default().fetchUserRecordID { (record, error) in
if #available(iOS 10.0, *)
{
CKContainer.default().discoverUserIdentity(withUserRecordID: record!, completionHandler: { (userID, error) in
self.currentuserID = userID?.userRecordID
print("user record id")
print(userID)
})
}
else
{
// Fallback on earlier versions
}
}
During SignUp on my app, I want to retrieve information, such as first name, from iCloud,I then want to store this in my own cloud kit database. How do I access user information from iCloud, without having to ask the user themselves for these relevant fields?
I was able to get it working with this in XCode 8 iOS 10 beta 2:
CKContainer.default().requestApplicationPermission(.userDiscoverability) { (status, error) in
CKContainer.default().fetchUserRecordID { (record, error) in
CKContainer.default().discoverUserIdentity(withUserRecordID: record!, completionHandler: { (userID, error) in
print(userID?.hasiCloudAccount)
print(userID?.lookupInfo?.phoneNumber)
print(userID?.lookupInfo?.emailAddress)
print((userID?.nameComponents?.givenName)! + " " + (userID?.nameComponents?.familyName)!)
})
}
}
Use CKContainer.discoverUserIdentity(withUserRecordID:) in combination with CKContainer.fetchUserRecordID to get a CKUserIdentity object for the current user. You can then use a PersonNameComponentsFormatter to get their name from the nameComponents property of the identity.
let container = CKContainer.defaultContainer()
container.fetchUserRecordIDWithCompletionHandler { (recordId, error) in
if error != nil {
print("Handle error)")
}else{
self.container.discoverUserInfoWithUserRecordID(
recordId!, completionHandler: { (userInfo, error) in
if error != nil {
print("Handle error")
}else{
if let userInfo = userInfo {
print("givenName = \(userInfo.displayContact?.givenName)")
print("familyName = \(userInfo.displayContact?.familyName)")
}else{
print("no user info")
}
}
})
}
}
Im trying to update a variable inside a class "var CurrentStatus:status! " status is an enum. I have a firebase function that will update the variable. the variable get update inside the firebase function but it will not update the variable outside of the firebase function
class signUpClass:UIViewController {
// check to see if form is empty
let ihelpController = UIViewController()
var CurrentStatus:status!
func signUp(var formArray: [String:String]) -> status{
var formStatus:status = ihelpController.checkIfFormIsEmpty(formArray)
if (formStatus == status.success){
//form is ok to process
// check DOB
//TODO: create date calculation function
let DateOfBirth:Int = 18
if DateOfBirth < 18 {
//user is not 18 they can not register
alertError("oops", message: "You must be 18 to register", comfirm: "Ok")
} else {
//Proceed with registration
let firebaseController = Firebase()
var email = "asdf#afd.com"
var password = "1234"
firebaseController.refPath("users").createUser(email, password: password, withValueCompletionBlock: {error, result in
if error != nil {
print("registration Error")
self.alertError("oops", message: "That email is registered already", comfirm: "OK")
} else {
let vc =
print("user can register")
firebaseController.firebaseRefUrl().authUser(email, password: password, withCompletionBlock:{
error, authdata in
if error != nil {
print("login Error")
}else{
let userId = firebaseController.firebaseRefUrl().authData.uid
formArray["userId"] = userId
firebaseController.refPath("users/\(userId)").updateChildValues(formArray)
print("user is register and can proceed to dashBoard")
//Proceed to dashboard
self.CurrentStatus = status.success
}
})
}
})
}
}
return CurrentStatus
}
Agreed with Jay's comment. You cannot return the status like that because Firebases works asynchronously... what I would do, is add a closure parameter that executions on completion like so:
class signUpClass:UIViewController {
// check to see if form is empty
let ihelpController = UIViewController()
var CurrentStatus:status!
func signUp(var formArray: [String:String], complete:(CurrentStatus)->()){
var formStatus:status = ihelpController.checkIfFormIsEmpty(formArray)
if (formStatus == status.success){
//form is ok to process
// check DOB
//TODO: create date calculation function
let DateOfBirth:Int = 18
if DateOfBirth < 18 {
//user is not 18 they can not register
alertError("oops", message: "You must be 18 to register", comfirm: "Ok")
} else {
//Proceed with registration
let firebaseController = Firebase()
var email = "asdf#afd.com"
var password = "1234"
firebaseController.refPath("users").createUser(email, password: password, withValueCompletionBlock: {error, result in
if error != nil {
print("registration Error")
self.alertError("oops", message: "That email is registered already", comfirm: "OK")
} else {
let vc =
print("user can register")
firebaseController.firebaseRefUrl().authUser(email, password: password, withCompletionBlock:{
error, authdata in
if error != nil {
print("login Error")
}else{
let userId = firebaseController.firebaseRefUrl().authData.uid
formArray["userId"] = userId
firebaseController.refPath("users/\(userId)").updateChildValues(formArray)
print("user is register and can proceed to dashBoard")
//Send status to callback to handle
complete(status.success)
}
})
}
})
}
}
}
After a user has logged in and if they later decide they want to change their username they have the ability to do so. when I save the record I catch the parse errors for username already exists and email already exists.
The issue I have is if the user decides not change their info after that the app is keeping the "BAD Username" in the cache and shows it all over the app.
I have tried calling PFUser.currentUser().fetch() as you will see below to refresh it from what is in the DB but to no avail.
The only thing that works is signing out and back in to get it back to whats in the DB.
Any ideas?
heres my code to catch the username error and attempt to refresh the PFUser
func saveUser() {
let username = nameField.text
let firstname = firstNameField.text! as String
let lastname = lastNameField.text! as String
let profilemsg = profileMessage.text! as String
let email = emailField.text! as String
if username!.characters.count > 0 {
let user = PFUser.currentUser()
if let user = user {
user["username"] = username
user["first"] = firstname
user["last"] = lastname
user["greeting"] = profilemsg
user["email"] = email
}
user!.saveInBackgroundWithBlock {(success: Bool, error: NSError?) -> Void in
if (success) {
ProgressHUD.showSuccess("Saved")
} else {
let errorCode = error!.code
switch errorCode {
case 202:
ProgressHUD.showError("Username " + username! + " already taken")
self.nameField.text = nil
do
{
try PFUser.currentUser()!.fetch()
print("User refreshed")
}
catch{
}
break
case 203:
ProgressHUD.showError("E-mail " + email + " already taken")
self.emailField.text = nil
do
{
try PFUser.currentUser()!.fetch()
print("User refreshed")
}
catch{
}
break
default:
ProgressHUD.showError("Network Error")
break
}
}
}
} else {
ProgressHUD.showError("Userame field must not be empty")
}
}
There is no need on creating a new instance of user to save the currentUser.
Just make the changes on the currentUser as it follows:
PFUser.currentUser()!["username"] = username
...
and then save it:
PFUser.currentUser()!.saveInBackgroundWithBlock {(...)}