iOS 教學, iOS進階

Facebook login 連結作法

我們在開發一個 App 時,會員系統以及社群平台的連結是常見的需求。
在社群的文章發佈方面,iOS 己在 iOS 5/6 引入了 Social Framework,省去了開發者不少時間。
然而僅止於此。如果需要進一步取得 facebook 的帳號 email,還是必需要透過 Facebook SDK 才行。

本文主要參考2016年3月份的 Facebook 線上教學。由於 Facebook 會不定時修改 API, 若照著本文裡的作法發現問題時,有可能是 Facebook 官方已作變動,請務必仔細參考最新官方線上教學

1. 下載與佈署最新的 FacebookSDK

下載最新的 Facebook SDK

2. 到 Facebook Developer 網站註冊一個 App

這裡對於初次開發與 Facebook 連結的 iOS App developer 新手來說容易混淆,在這裡多作一些說明:

Facebook 上的 App 與 iOS App 不同,是在 Facebook 上的應用程式。
iOS App 要使用 Facebook 帳號登入,需要先在 Facebook 上註冊一個 App, 然後在 Facebook Login 過程中, iOS App 透過這個 Facebook App 來進行登入作業,其中包括了取得使用者的權限同意,例如是否同意該 App 取用我的朋友列表等等。
當然,為了要達成這個目的,iOS App 與 Facebook App 都需要作相關的設定。

先填寫 Facebook App 的名字

FB_APP_CREATE

選擇平台:iOS

FB_APP_select_platform

2.1 iOS App 方需要作的設定

2.1.1 加Facebook SDK Framework

將~/Documents/FacebookSDK下的 FBSDKCoreKit.Framework, FBSDKLoginKit.Framework加入 Projects 裡

2.1.2 修改 .plist 檔

在 projects 的 .plist 檔裡,新增 Facebook App 的資訊。在創建 Facebook App 的過程中,可以看到下面的xml設定

<key>CFBundleURLTypes</key>
<array>
  <dict>
  <key>CFBundleURLSchemes</key>
  <array>
    <string>fb.......</string>
  </array>
  </dict>
</array>
<key>FacebookAppID</key>
<string>.......</string>
<key>FacebookDisplayName</key>
<string>iOS Login Example</string>

將.plist 檔以 source code 方式開啟(點滑鼠右鍵選 source code) 將之複製至 .plist 檔裡即可

plist_open_as

2.1.3 修改 .plist 檔 (iOS9)

在 iOS9 以上,需要增加下面的 xml 至 .plist 檔裡

<key>NSAppTransportSecurity</key>
<dict>
    <key>NSExceptionDomains</key>
    <dict>
        <key>facebook.com</key>
        <dict>
            <key>NSIncludesSubdomains</key>
            <true/>
            <key>NSThirdPartyExceptionRequiresForwardSecrecy</key>
            <false/>
        </dict>
        <key>fbcdn.net</key>
        <dict>
            <key>NSIncludesSubdomains</key>
            <true/>
            <key>NSThirdPartyExceptionRequiresForwardSecrecy</key>
            <false/>
        </dict>
        <key>akamaihd.net</key>
        <dict>
            <key>NSIncludesSubdomains</key>
            <true/>
            <key>NSThirdPartyExceptionRequiresForwardSecrecy</key>
            <false/>
        </dict>
    </dict>
</dict>
<key>LSApplicationQueriesSchemes</key>
<array>
  <string>fbapi</string>
  <string>fb-messenger-api</string>
  <string>fbauth2</string>
  <string>fbshareextension</string>
</array>

2.2 Facebook App 方需要作的設定

2.2.1 填入 iOS App Bundle ID

需要將 iOS App 的 Bundle ID 填入 Facebook App 的 Setting 裡

FB_APP_bundle_id

2.2.2 允許 Facebook App 讓每個人使用

需要在 App Review(App審查)裡,將 Your app is currently live and available to the public. 打開

make_public若不打開的話,就會只有 Facebook App 的開發者自己可以使用。還有在 Roles 設定裡有設定的使用者可以使用。

3. xcode projects 使用步驟

3.1 AppDelegate.m 修改

3.1.1 import

#import <FBSDKCoreKit/FBSDKCoreKit.h>
#import <FBSDKLoginKit/FBSDKLoginKit.h>

3.1.2 增加 method

- (BOOL)application:(UIApplication *)application
            openURL:(NSURL *)url
  sourceApplication:(NSString *)sourceApplication
         annotation:(id)annotation {
    return [[FBSDKApplicationDelegate sharedInstance] application:application
                                                          openURL:url
                                                sourceApplication:sourceApplication
                                                       annotation:annotation];
}

3.2 FBSDKLoginButton 設置

Storyboard 裡增加一個 button, 並將 class 改為 FBSDKLoginButton

FBLoginButton

3.2.1 AppDelegate.didFinishLaunchingWithOptions 修改

為了要讓 Storyboard 裡的 FBSDKLoginButton 正常運作,必需要在AppDelegate.m 裡的 didFinishLaunchingWithOptions 增加一行

[FBSDKLoginButton class];

這是個蠻 trick 的作法。官方的範例並沒有使用 storyboard 而是用程式碼 new button,不過如此一來不方便使用 auto layout 。
而若使用 storyboard 將 UIButton 的 class 設為 FBSDKLoginButton 的話,在執行時會發生 Unknown class FBSDKLoginButton in Interface Builder file 的錯誤。
其簡單的解法,就是在AppDelegate.m 裡的 didFinishLaunchingWithOptions先行使用 FBSDKLoginButton 讓它載入。

3.3 import frameworks

在使用到的 .m 檔裡 import 必要的 frameworks

#import <FBSDKCoreKit/FBSDKCoreKit.h>
#import <FBSDKLoginKit/FBSDKLoginKit.h>

3.4 Permission 設置

增加 button 的 IBOutlet,需要由 Storyboard 拉過來進行連結。若這個技巧尚不會的新手,請參考官方文件:Create Outlet

@property (weak, nonatomic) IBOutlet FBSDKLoginButton *fbLoginButton;

在 viewDidLoad 裡設定

self.fbLoginButton.readPermissions = @[@"public_profile", @"email", @"user_friends"];

3.5 login delegate

在 viewDidLoad 裡將 FBSDKLoginButton 的 delegate 設為自己

    self.fbLoginButton.delegate=self;

3.6 處理登入結果

- (void)loginButton:	(FBSDKLoginButton *)loginButton
didCompleteWithResult:	(FBSDKLoginManagerLoginResult *)result
              error:	(NSError *)error
{
    if (result.isCancelled==YES) {
        NSLog(@"User login canceled.");
    }else{
        NSLog(@"grantedPermissions=%@",result.grantedPermissions);
        FBSDKAccessToken *token=result.token;
        NSLog(@"userID=%@",token.userID);
        NSLog(@"tokenString=%@",token.tokenString);
        
        if ([result.grantedPermissions containsObject:@"email"]) {
            if ([FBSDKAccessToken currentAccessToken]) {
                NSDictionary *param = @{@"fields" : @"email,id,name,picture.width(100).height(100)"};
                [[[FBSDKGraphRequest alloc] initWithGraphPath:@"me" parameters:param] startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) {
                    if (!error && result!=nil) {
                        if([result isKindOfClass:[NSDictionary class]])
                        {
                            NSLog(@"email=%@",result[@"email"]);
                            [_emailLabel setText:result[@"email"]];
                            NSLog(@"name=%@",result[@"name"]);
                            [_nameLabel setText:result[@"name"]];

                            NSString *imageStringOfLoginUser = [[[result valueForKey:@"picture"] valueForKey:@"data"] valueForKey:@"url"];
                            
                            NSData *imageData = [NSData dataWithContentsOfURL:[NSURL URLWithString:imageStringOfLoginUser]];
                            self.imageView.image = [UIImage imageWithData:imageData];
                        }
                    }
                }];
            }
        } else {
            NSLog(@"Not granted");
        }
    }
    if(error != nil)
        NSLog(@"error=%@",error);
}
- (void)loginButtonDidLogOut:(FBSDKLoginButton *)loginButton
{
    
}

在登入之後立即可以取得的是 userID 與 access token,不過一般來說,更常使用的是 email。
要取得 email ,必需要用這個 token 進一步的透過 FBSDKGraphRequest 來向 Facebook 詢問才能取得。

4. 範例程式

請參考  範例程式