Accessing user profile picture from iCloud account - icloud

I'm attempting to pull a user's profile picture from their iCloud account. I'm using CloudKit and am verifying the user has an iCloud account as well as requesting discoverability permissions. I'm not sure this is possible but if it is I'd like to know how. Here is the relevant code:
To verify iCloud account:
[[CKContainer defaultContainer] accountStatusWithCompletionHandler:^(CKAccountStatus accountStatus, NSError *error) {
if (accountStatus == CKAccountStatusAvailable)
{
self.shouldLogin = YES;
}
else
{
self.shouldLogin = NO;
}
}];
To pull their profile image out of their iCloud account:
[[CKContainer defaultContainer] requestApplicationPermission:CKApplicationPermissionUserDiscoverability completionHandler:^(CKApplicationPermissionStatus applicationPermissionStatus, NSError * _Nullable error) {
if (applicationPermissionStatus == CKApplicationPermissionStatusGranted)
{
[[CKContainer defaultContainer] discoverUserInfoWithUserRecordID:self.dataStore.user.userID completionHandler:^(CKDiscoveredUserInfo * _Nullable userInfo, NSError * _Nullable error) {
NSData *imageData = userInfo.displayContact.imageData;
UIImage *profileImage = [UIImage imageWithData:imageData];
self.profileImageView.image = profileImage;
}];
}
}];

You can not get more than an unique ID plus the first and last name of an iCloud user. If you want more information, then you have to ask all users for that data.

Related

Where in Firebase login process, do I check/migrate existing content?

I have a SwiftUI/Firebase project, where I allow users to create and upload content while logged in with anonymous. I also have a Firebase rule that prevent editing data that isn't tagged with the same UID as you're logged in with.
My problem is that, when users log in with Google or Apple login, I don't know where to insert any logic for migrating their content from their old anonymous UID to their Apple/Google UID. (Update: Yes, I can link accounts, but that only works if they haven't previously used their account on a different device).
As far as I can tell, I don't get their new Apple/Google UID until after they're authenticated, and by then, they can no longer modify data tagged with the Anonymous UID.
I've tried linking the accounts, but I get an "Account is already linked" error, so I'm assuming that approach is a dead end?
As an example, here is my code for the Google login with a note where I'm trying to insert my migration logic:
import SwiftUI
import Firebase
import GoogleSignIn
struct GoogleSignInButton: View {
#EnvironmentObject var viewModel: GoogleSignInViewModel
var body: some View {
Button("Sign in with Google") {
viewModel.signIn()
}
.foregroundColor(Color.greyZ)
.padding()
.frame(maxWidth: .infinity)
.background(Color.greyB)
.cornerRadius(5)
.padding()
}
}
struct GoogleSignInButton_Previews: PreviewProvider {
static var previews: some View {
GoogleSignInButton()
}
}
class GoogleSignInViewModel: NSObject, ObservableObject {
enum SignInState {
case signedIn
case signedOut
}
#Published var state: SignInState = .signedOut
override init() {
super.init()
setupGoogleSignIn()
}
func signIn() {
if GIDSignIn.sharedInstance().currentUser == nil {
GIDSignIn.sharedInstance().presentingViewController = UIApplication.shared.windows.first?.rootViewController
GIDSignIn.sharedInstance().signIn()
}
}
func signOut() {
GIDSignIn.sharedInstance().signOut()
do {
try Auth.auth().signOut()
state = .signedOut
} catch let signOutError as NSError {
print(signOutError.localizedDescription)
}
}
private func setupGoogleSignIn() {
GIDSignIn.sharedInstance().delegate = self
}
}
extension GoogleSignInViewModel: GIDSignInDelegate {
func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error!) {
if error == nil {
// Get UID of existing user
if let previousUID:String = Auth.auth().currentUser?.uid {
// migrate Firestore data for old uid to new uid
// Firebase rule prevent modifying data if you're logged in with different uid so it has to be before logging in with Google
// But I don't seem to have the new Google UID yet, so what do I migrate it to?
}
// Log in with new user
firebaseAuthentication(withUser: user)
} else {
print(error.debugDescription)
}
}
private func firebaseAuthentication(withUser user: GIDGoogleUser) {
if let authentication = user.authentication {
let credential = GoogleAuthProvider.credential(withIDToken: authentication.idToken, accessToken: authentication.accessToken)
Auth.auth().signIn(with: credential) { (_, error) in
if let error = error {
print(error.localizedDescription)
self.state = .signedOut
} else {
self.state = .signedIn
}
}
}
}
}
UPDATE: As requested, here is the Link-function that invariably results in a "This credential is already associated with a different user account" error. I have checked the account in Firebase, and the account already exists, so that is why I assumed the "link" approach is a dead end, and tried migrating the data instead.
private func firebaseAuthentication(withUser user: GIDGoogleUser) {
if let authentication = user.authentication {
let credential = GoogleAuthProvider.credential(withIDToken: authentication.idToken, accessToken: authentication.accessToken)
if let currentUser = Auth.auth().currentUser {
// User already logged in
currentUser.link(with: credential) { result, error in
if let error = error {
print(error.localizedDescription)
} else {
print(result ?? "Success")
}
}
} else {
// User not logged in (shouldn't happen as they're always anonymous
Auth.auth().signIn(with: credential) { (_, error) in
if let error = error {
print(error.localizedDescription)
self.state = .signedOut
} else {
self.state = .signedIn
}
}
}
}
}
Instead of migrating the data, consider linking the user's new Google or Apple credentials to their existing Firebase account by filling the process outlines in linking multiple Auth providers to an account on iOS.

How to translate the object c into rubymotion to fetch facebook user’s information

I tried to translate part of the objective-c but I still stuck at part of them
any idea ? Thanks so much
objective c version
if ([FBSDKAccessToken currentAccessToken]) {
NSMutableDictionary *params = [NSMutableDictionary dictionaryWithObjectsAndKeys:#"picture",#"fields",nil];
FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc]
initWithGraphPath:#"me"
parameters:params
HTTPMethod:#"GET"];
[request startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection,
id result,
NSError *error) {
UIImage * downloadedImage = [UIImage imageWithData:pictureData];
dispatch_async(dispatch_get_main_queue(), ^{
self.profilePictureImageView.image = downloadedImage;
});
}];
}
ruby motion version
if (FBSDKAccessToken.currentAccessToken) {
request = FBSDKGraphRequest.alloc.initWithGraphPath("me", parameters:nil, HTTPMethod: "GET")
}
Finally, I came up with the corresponding code in rubymotion
Please correct me directly if anything wrong, it works for me now
part of original objective-c
if ([FBSDKAccessToken currentAccessToken]) {
FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc]
initWithGraphPath:#"me"
parameters:params
HTTPMethod:#"GET"];
[request startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection,
id result,
NSError *error) {
}];
}
convert version by ruby_motion_query
def loginButton(loginButton, didCompleteWithResult: result, error: error)
puts result
puts error
if not error
request = FBSDKGraphRequest.alloc.initWithGraphPath("me", parameters:nil, HTTPMethod: "GET")
request.startWithCompletionHandler( lambda{ |connection, user, error|
#DO_ANYTHING_YOU_WANT_FATE_LOGINING_SUCESSFULLY
rmq(#fb_login_button).animate { |btn| btn.move(b: 400) }
#name_label = rmq.append(UILabel, :label_name).get
#name_label.text = "#{user['first_name']} #{user['last_name']}"
rmq(#name_label).animations.fade_in
})
end
end
def loginButtonDidLogOut(loginButton)
#DO_ANYTHING_YOU_WANT_HERE
end

FBSDKGraphRequest FBSDKGraphRequestConnection only in iOS 9 return error

this is the login success with iOS 9.0.1.
this is the login failed with iOS 9 iPhone .
and in iOS 9 iPhone i have this warning :
the sdk update to parse 1.8.5 and Facebook to 4.6 from here :https://developers.facebook.com/docs/ios/ios9
what can I do to solved this ?
the Facebook login is with PFFacebookUtils logInInBackgroundWithReadPermissions :
NSArray *permissions = [NSArray arrayWithObjects:#"email",#"user_friends", nil];
[PFFacebookUtils logInInBackgroundWithReadPermissions:permissions block:^(PFUser *user, NSError *error)
{
if (!user) // The user cancelled the Facebook login
{
NSLog(#"Uh oh. The user cancelled the Facebook login.");
}
else if (user.isNew)
{
}
else if (user) // the user is exist at DB
{
}
else if (error)
{
}
}];
Have u added this params in yout plist?
<key>FacebookAppID</key>
<string>YourFBKey</string>
<key>FacebookDisplayName</key>
<string>moodoo</string>
<key>LSApplicationQueriesSchemes</key>
<array>
<string>fbapi</string>
<string>fbapi20130214</string>
<string>fbapi20130410</string>
<string>fbapi20130702</string>
<string>fbapi20131010</string>
<string>fbapi20131219</string>
<string>fbapi20140410</string>
<string>fbapi20140116</string>
<string>fbapi20150313</string>
<string>fbapi20150629</string>
<string>fbauth</string>
<string>fbauth2</string>
<string>fb-messenger-api20140430</string>
<string>fb-messenger-platform-20150128</string>
<string>fb-messenger-platform-20150218</string>
<string>fb-messenger-platform-20150305</string>

Can not get friends name, pic_quare from Facebook in iOS?

When I am Fetching name and pic_square from my friend list the it shows the following error.
Error: The operation couldn’t be completed. (com.facebook.sdk error 5.)
FBSDKLog: Error: HTTP status code: 400
FBSDKLog: Response <#1386> <Error>:
The operation couldn’t be completed. (com.facebook.sdk error 5.)
{
"com.facebook.sdk:ErrorSessionKey" = "<FBSession: 0x146f1600, state: FBSessionStateOpen, loginHandler: 0x146cca50, appID: 293072694193895, urlSchemeSuffix: , tokenCachingStrategy:<FBSessionTokenCachingStrategy: 0x1468b170>, expirationDate: 2014-07-17 07:47:12 +0000, refreshDate: 2014-05-18 11:14:42 +0000, attemptedRefreshDate: 0001-12-30 00:00:00 +0000, permissions:(\n status,\n permission\n)>";
"com.facebook.sdk:HTTPStatusCode" = 400;
"com.facebook.sdk:ParsedJSONResponseKey" = (
{
body = {
error = {
code = 606;
message = "(#606) The global ID 100003190599973 is not allowed. Please use the application specific ID instead.";
type = OAuthException;
};
};
code = 400;
}
);
}
here I used the code for retrieving the the required information
NSString *query = [NSString stringWithFormat:#"select name, pic_square from user where uid = %#", curId];
NSDictionary *queryParam = [NSDictionary dictionaryWithObjectsAndKeys:query, #"q", nil];
// Make the API request that uses FQL
[FBRequestConnection startWithGraphPath:#"/fql" parameters:queryParam HTTPMethod:#"GET"
completionHandler:^(FBRequestConnection *connection, id result, NSError *error)
{
if (error)
NSLog(#"Error: %#", [error localizedDescription]);
else
{
[namePicArray addObject:result[#"data"]];
}
}];
Thanks in advance.

ios Facebook Login not working;

Sandbox Mode=on
Bundle ID =same FBid=Ok ..Anything else does not matter
-(void) askForPublishPermission
{ BOOL isSessionActive = [self isFacebookSessionActive];
//BOOL useUI = !isSessionActive;
//useUI = YES;
BOOL publishPermissionAvailable = NO;
if (isSessionActive)
{
NSArray* validPermission = [[FBSession activeSession] permissions];
for (int i=0; i<[validPermission count]; i++)
{
NSObject* permission = [validPermission objectAtIndex:i];
if ([permission isKindOfClass:[NSString class]])
{
NSString* validPermission = (NSString*)permission;
//NSLog(#"Valid Permissions = %#", validPermission);
if ([validPermission isEqualToString:#"publish_actions"])
{
publishPermissionAvailable = YES;
break;
}
}
}
}
if (publishPermissionAvailable == YES)
{
//NSLog(#"------------------ CALL AT TWO --------------");
//NSLog(#"Login Success");
}
else // Request for publish permission.
{
NSArray* permissionArray = [NSArray arrayWithObjects:
#"publish_actions",nil];
[FBSession openActiveSessionWithPublishPermissions:permissionArray
defaultAudience:FBSessionDefaultAudienceEveryone
allowLoginUI:YES
completionHandler:^(FBSession *session,
FBSessionState state,
NSError *error) {
[self sessionStateChanged:session
state:state
error:error];
}];
}
}
and
-(void) askForPublishPermission
{
BOOL isSessionActive = [self isFacebookSessionActive];
//BOOL useUI = !isSessionActive;
//useUI = YES;
BOOL publishPermissionAvailable = NO;
if (isSessionActive)
{
NSArray* validPermission = [[FBSession activeSession] permissions];
for (int i=0; i<[validPermission count]; i++)
{
NSObject* permission = [validPermission objectAtIndex:i];
if ([permission isKindOfClass:[NSString class]])
{
NSString* validPermission = (NSString*)permission;
//NSLog(#"Valid Permissions = %#", validPermission);
if ([validPermission isEqualToString:#"publish_actions"])
{
publishPermissionAvailable = YES;
break;
}
}
}
}
if (publishPermissionAvailable == YES)
{
//NSLog(#"------------------ CALL AT TWO --------------");
//NSLog(#"Login Success");
}
else // Request for publish permission.
{
NSArray* permissionArray = [NSArray arrayWithObjects:
#"publish_actions",nil];
[FBSession openActiveSessionWithPublishPermissions:permissionArray
defaultAudience:FBSessionDefaultAudienceEveryone
allowLoginUI:YES
completionHandler:^(FBSession *session,
FBSessionState state,
NSError *error) {
[self sessionStateChanged:session
state:state
error:error];
}];
}
}
But ..Not login..
error code see below:
Error = Error Domain=com.facebook.sdk Code=2 "The operation couldn’t be completed. (com.facebook.sdk error 2.){com.facebook.sdk:ErrorLoginFailedReason=com.facebook.sdk:SystemLoginCancelled, com.facebook.sdk:ErrorInnerErrorKey=Error Domain=com.apple.accounts Code=7 "The Facebook server could not fulfill this access request: The app must ask for a basic read permission like email at install time."
I'd spent a few months this issue..Please Help me
Your error message says:
The app must ask for a basic read permission like email at install time.
From the docs:
When someone connects with an app using Facebook login, the app can access their public profile and friend list, the pieces of information that are visible to everyone. To create this basic connection, apps must always request access to a person's basic profile information by asking for the basic_info permission.
Try adding basic_info to permissionArray array. If that doesn't work, try adding email as well.
I replaced :
[FBSession openActiveSessionWithPublishPermissions:#[#"publish_actions"] defaultAudience:FBSessionDefaultAudienceFriends allowLoginUI:YES completionHandler:stateHandler];
with
[FBSession openActiveSessionWithPublishPermissions:#[#"basic_info", #"publish_actions", #"email"] defaultAudience:FBSessionDefaultAudienceFriends allowLoginUI:YES completionHandler:stateHandler];
And now it's working
(permissions orders matter, basic_info have to be in first position)