貼出源碼回報社區!javascript
下面貼出關鍵代碼,稍後完善更多細節步驟。我的ios開發經驗很少,若是有更好的集成方式,請你們分享!java
前提:
1. 能在Xcode中成功運行官方給出的離線打包演示應用 HBuilder-Hello (iOS平臺5+SDK集成)
2. 註冊了facebook開發者平臺,並完成了官方ios集成步驟,參考:[](https://developers.facebook.com/docs/facebook-login/ios?sdk=fbsdk&locale=zh_CN)ios
源代碼目錄結構
json
1. js層代碼調用插件login.jsapp
loginWithFacebook: function() { Common.showLoading(); setTimeout(function(){ if(App.timeout) { Common.hideLoading(); mui.toast(Common.messages.LOGIN_TIMEOUT,{ duration:'short', type:'div' }); } }, 30000); if(Common.debug) { setTimeout(function(){ App.facebookAuthSuccessCallBack('{"picture":{"data":{"height":50,"is_silhouette":false,"url":"https:\/\/platform-lookaside.fbsbx.com\/platform\/profilepic\/?asid=1799672767573170&height=50&width=50&ext=1529946538&hash=AeT-c8Gg8kljhvAC","width":50}},"name":"Nick Name","id":"1791872767573170","email":"last.first@gmail.com","permissions":{"data":[{"permission":"user_birthday","status":"granted"},{"permission":"user_hometown","status":"granted"},{"permission":"user_location","status":"granted"},{"permission":"user_posts","status":"granted"},{"permission":"user_gender","status":"granted"},{"permission":"user_link","status":"granted"},{"permission":"user_age_range","status":"granted"},{"permission":"email","status":"granted"},{"permission":"public_profile","status":"granted"}]},"age_range":{"min":21},"link":"https:\/\/www.facebook.com\/app_scoped_user_id\/YXNpZADpBWEZAmSmJjc01BLXJNTGt4UDd0RlZAicXpYVG5HbTV3YUl2T29uNNNNMWpGWndhVEVieTI4S18zcDVuUjczUHRmUHgwYm8xUkwtU1lHWENuRW9vUnE2clFtalVLaWs4SldxT05iWUxZAUlZA2bjRHemhE\/","gender":"male","birthday":"01\/01\/1990","location":{"id":"106324044973002","name":"Shanghai, China"}}'); //App.facebookAuthCancelCallBack(); //App.facebookAuthErrorCallBack("auth fail!"); }, 2000); }else { //plus.facebookplug.logOut(); plus.facebookplug.logIn(App.facebookAuthSuccessCallBack, App.facebookAuthErrorCallBack); } }, // 這個回調函數會在 cn.shaketowin.app.SDK_WebApp.java 原生類中調用, 參考類中的excuteJSCode()方法 facebookAuthSuccessCallBack: function(data) { App.timeout = false; if(data != null) { //alert("===facebookAuthSuccessCallBack(), data: " + JSON.stringify(data)); // data example: {"picture":{"data":{"height":50,"is_silhouette":false,"url":"https:\/\/platform-lookaside.fbsbx.com\/platform\/profilepic\/?asid=1791872767573170&height=50&width=50&ext=1529946538&hash=AeT-c8Gg8kljhvAC","width":50}},"name":"Nick Name","id":"1987872767573170","email":"last.first@gmail.com","permissions":{"data":[{"permission":"user_birthday","status":"granted"},{"permission":"user_hometown","status":"granted"},{"permission":"user_location","status":"granted"},{"permission":"user_posts","status":"granted"},{"permission":"user_gender","status":"granted"},{"permission":"user_link","status":"granted"},{"permission":"user_age_range","status":"granted"},{"permission":"email","status":"granted"},{"permission":"public_profile","status":"granted"}]},"age_range":{"min":21},"link":"https:\/\/www.facebook.com\/app_scoped_user_id\/YXNpZADpBWEZAmSmJjc01BLXJNTGt4UD09RlZAicXpYVG5HbTV3YUl2T29uVTNNMWpGWndhVEVieTI4S18zcDVuUjczUHRmUHgwYm8xUkwtU1lHWENuRW9vUnE2clFtalVLaWs4SldxT05iWUxZAUlZA2bjRHemhE\/","gender":"male","birthday":"01\/01\/1990","location":{"id":"116324086073002","name":"Shanghai, China"}} try{ var facebookUser = JSON.parse(data); facebookUser.unionid = facebookUser.id; mui.toast(Common.messages.LOGIN_SUCCESS,{ duration:'short', type:'div' }); App.checkUserIsExist(facebookUser, "facebook"); }catch(err) { Common.hideLoading(); mui.toast(Common.messages.LOGIN_FAIL,{ duration:'long', type:'div' }); Common.log("====login.js, facebookAuthSuccessCallBack(), decode facebook user JSON string fail!"); } }else { Common.hideLoading(); App.facebookAuthErrorCallBack(); } }, // 這個回調函數會在 cn.shaketowin.app.SDK_WebApp.java 原生類中調用, 參考類中的excuteJSCode()方法 facebookAuthCancelCallBack: function() { App.timeout = false; Common.hideLoading(); mui.toast(Common.messages.LOGIN_CANCEL,{ duration:'long', type:'div' }); }, // 這個回調函數會在 cn.shaketowin.app.SDK_WebApp.java 原生類中調用, 參考類中的excuteJSCode()方法 facebookAuthErrorCallBack: function(error) { App.timeout = false; Common.hideLoading(); mui.toast(Common.messages.LOGIN_FAIL,{ duration:'long', type:'div' }); },
2. js層代碼插件facebookplug.js異步
document.addEventListener( "plusready", function() { var facebookplug = { logIn : function (successCallback, errorCallback ) { var success = typeof successCallback !== 'function' ? null : function(args) { successCallback(args); }, fail = typeof errorCallback !== 'function' ? null : function(code) { errorCallback(code); }; callbackID = window.plus.bridge.callbackId(success, fail); return window.plus.bridge.exec('facebookloginplugin', "LogIn", [callbackID]); }, logOut : function () { window.plus.bridge.exec('facebookloginplugin', "LogOut", []); }, }; window.plus.facebookplug = facebookplug; }, true );
3. 原生層FacebookLoginPlugin.hide
// // FacebookLoginPlugin.h // // #include "PGPlugin.h" #include "PGMethod.h" #import <Foundation/Foundation.h> @interface FacebookLoginPlugin : PGPlugin - (void)LogIn:(PGMethod*)command; - (void)LogOut:(PGMethod*)command; @end
4. 原生層FacebookLoginPlugin.m函數
// // FacebookLoginPlugin.m // #import "FacebookLoginPlugin.h" #import "PDRCoreAppFrame.h" #import "H5WEEngineExport.h" #import "PDRToolSystemEx.h" // 擴展插件中須要引入須要的系統庫 #import <LocalAuthentication/LocalAuthentication.h> #import <FBSDKCoreKit/FBSDKCoreKit.h> #import <FBSDKLoginKit/FBSDKLoginKit.h> @implementation FacebookLoginPlugin #pragma mark 這個方法在使用WebApp方式集成時觸發,WebView集成方式不觸發 /* * WebApp啓動時觸發 * 須要在PandoraApi.bundle/feature.plist/註冊插件裏添加autostart值爲true,global項的值設置爲true */ - (void) onAppStarted:(NSDictionary*)options{ NSLog(@"5+ WebApp啓動時觸發"); // 能夠在這個方法裏向Core註冊擴展插件的JS } // 監聽基座事件事件 // 應用退出時觸發 - (void) onAppTerminate{ // NSLog(@"APPDelegate applicationWillTerminate 事件觸發時觸發"); } // 應用進入後臺時觸發 - (void) onAppEnterBackground{ // NSLog(@"APPDelegate applicationDidEnterBackground 事件觸發時觸發"); } // 應用進入前天時觸發 - (void) onAppEnterForeground{ // NSLog(@"APPDelegate applicationWillEnterForeground 事件觸發時觸發"); } #pragma mark 如下爲插件方法,由JS觸發, WebView集成和WebApp集成均可以觸發 // 登陸 - (void)LogIn:(PGMethod*)commands { if ( commands ) { // CallBackid 異步方法的回調id,H5+ 會根據回調ID通知JS層運行結果成功或者失敗 NSString* cbId = [commands.arguments objectAtIndex:0]; if ([FBSDKAccessToken currentAccessToken]) { //NSLog(@"==========LogIn(), already have token"); [self GetUserInfo:cbId]; }else{ /* * Facebook會對i應用訪問的字段進行控制(https://developers.facebook.com/apps/193845774573048/review-status/) * 應用審覈說明:https://developers.facebook.com/docs/apps/review/#app-review * 默認不須要審覈的有:@"public_profile", @"email" * 以下權限系統提交應用審覈才能夠訪問: @"public_profile", @"email", @"user_age_range", @"user_birthday", @"user_gender", @"user_hometown", @"user_link", @"user_location" * 若是須要訪問相對應的字段,將字段放入的代碼 [login logInWithReadPermissions:@[@"public_profile", @"email"] 參數便可 */ FBSDKLoginManager *login = [[FBSDKLoginManager alloc] init]; login.loginBehavior = FBSDKLoginBehaviorWeb; [login logInWithReadPermissions:@[@"public_profile", @"email"] handler:^(FBSDKLoginManagerLoginResult *result, NSError *error) { if (error) { [self CallWebAppJSFun: cbId status: @"ERROR" message: error.localizedDescription]; } else if (result.isCancelled) { [self CallWebAppJSFun: cbId status: @"ERROR" message: @"cancel"]; } else { // If you ask for multiple permissions at once, you // should check if specific permissions missing //NSLog(@"========Permission 2: %@",result.grantedPermissions); [self GetUserInfo:cbId]; } }]; } } } // 退出登陸 - (void)LogOut:(PGMethod *)command { FBSDKLoginManager *loginManager = [[FBSDKLoginManager alloc] init]; [loginManager logOut]; // or //[FBSDKAccessToken setCurrentAccessToken:nil]; } // 獲取用戶信息 - (void)GetUserInfo:(NSString *) cbId { /* * Facebook會對i應用訪問的字段進行控制(https://developers.facebook.com/apps/193845774573048/review-status/) * 應用審覈說明:https://developers.facebook.com/docs/apps/review/#app-review * 對應默認不須要審覈的字段@"public_profile", @"email"的是@"id,name,first_name,last_name,email,picture.type(large)「 * 對應提交應用審覈才能夠訪問字段有 @"cover,picture.type(large),id,name,first_name,last_name,gender,birthday,email,location,hometown,about,photos,age_range,link" * 若是須要訪問相對應的字段,將字段放入的代碼 parameters:[NSDictionary dictionaryWithObject:@"cover,..." forKey:@"fields"] 參數便可 */ [[[FBSDKGraphRequest alloc] initWithGraphPath:@"me" parameters:[NSDictionary dictionaryWithObject:@"id,name,first_name,last_name,email,picture" forKey:@"fields"]] startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error) { if (!error) { //NSLog(@"==========GetUserInfo(), username: %@", [result valueForKey:@"name"]); // NSString *userID = [[FBSDKAccessToken currentAccessToken] userID]; // NSString *userName = [result valueForKey:@"name"]; // NSString *email = [result valueForKey:@"email"]; // NSString *userImageURL = [NSString stringWithFormat:@"https://graph.facebook.com/%@/picture?type=large", [[FBSDKAccessToken currentAccessToken] userID]]; NSData *jsonData = [NSJSONSerialization dataWithJSONObject:result options:NSJSONWritingPrettyPrinted error:nil]; NSString *userInfo = [[NSString alloc] initWithData:jsonData encoding:NSUTF8StringEncoding]; //NSLog(@"==========GetUserInfo(), userInfo: %@", userInfo); [self CallWebAppJSFun: cbId status: @"OK" message: userInfo]; } else{ [self CallWebAppJSFun: cbId status: @"ERROR" message: error.localizedDescription]; } }]; } // 回調通知JS層 - (void)CallWebAppJSFun:(NSString *) cbId status:(NSString *) st message:(NSString *) msg { // 運行Native代碼結果和預期相同,調用回調通知JS層運行成功並返回結果 // PDRCommandStatusOK 表示觸發JS層成功回調方法 // PDRCommandStatusError 表示觸發JS層錯誤回調方法 // 若是方法須要持續觸發頁面回調,能夠經過修改 PDRPluginResult 對象的keepCallback 屬性值來表示當前是否可重複回調, true 表示能夠重複回調 false 表示不可重複回調 默認值爲false PDRPluginResult * pResult = nil; if([st isEqualToString: @"OK"]) { pResult = [PDRPluginResult resultWithStatus:PDRCommandStatusOK messageAsString: msg]; }else { // 若是Native代碼運行結果和預期不一樣,須要經過回調通知JS層出現錯誤,並返回錯誤提示 pResult = [PDRPluginResult resultWithStatus:PDRCommandStatusError messageAsString:msg]; } // 通知JS層Native層運行結果 [self toCallback:cbId withReslut:[pResult toJSONString]]; } // 調用指紋解鎖 - (void)AuthenticateUser:(PGMethod*)command { if (nil == command) { return; } BOOL isSupport = false; NSString* pcbid = [command.arguments objectAtIndex:0]; NSError* error = nil; NSString* LocalReason = @"HBuilder指紋驗證"; // Touch ID 是IOS 8 之後支持的功能 if ([PTDeviceOSInfo systemVersion] >= PTSystemVersion8Series) { LAContext* context = [[LAContext alloc] init]; if ([context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&error]) { isSupport = true; [context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics localizedReason:LocalReason reply:^(BOOL success, NSError * _Nullable error) { PDRPluginResult * pResult = nil; if (success) { pResult = [PDRPluginResult resultWithStatus: PDRCommandStatusOK messageAsDictionary:@{@"state":@(0), @"message":@"成功"}]; } else{ NSDictionary* pStringError = nil; switch (error.code) { case LAErrorSystemCancel: { pStringError = @{@"state":@(-1), @"message":@"系統取消受權(例如其餘APP切入)"}; break; } case LAErrorUserCancel: { pStringError = @{@"state":@(-2), @"message":@"用戶取消Touch ID受權"}; break; } case LAErrorUserFallback: { pStringError = @{@"state":@(-3), @"message":@"用戶選擇輸入密碼"}; break; } case LAErrorTouchIDNotAvailable:{ pStringError = @{@"state":@(-4), @"message":@"設備Touch ID不可用"}; break; } case LAErrorTouchIDLockout:{ pStringError = @{@"state":@(-5), @"message":@"Touch ID被鎖"}; break; } case LAErrorAppCancel:{ pStringError = @{@"state":@(-6), @"message":@"軟件被掛起取消受權"}; break; } default: { pStringError = @{@"state":@(-7), @"message":@"其餘錯誤"}; break; } } pResult = [PDRPluginResult resultWithStatus:PDRCommandStatusError messageAsDictionary:pStringError]; } [self toCallback:pcbid withReslut:[pResult toJSONString]]; }]; } else{ NSDictionary* pStringError = nil; switch (error.code) { case LAErrorTouchIDNotEnrolled: { pStringError = @{@"state":@(-11), @"message":@"設備Touch ID不可用"}; break; } case LAErrorPasscodeNotSet: { pStringError = @{@"state":@(-12), @"message":@"用戶未錄入Touch ID"}; break; } default: break; } } } if (!isSupport) { PDRPluginResult* pResult = [PDRPluginResult resultWithStatus:PDRCommandStatusError messageAsString:@"Device Not Support"]; [self toCallback:pcbid withReslut:[pResult toJSONString]]; } } @end
5. 原生層AppDelegate.m(在原有函數上新增一行FB的代碼,這裏僅貼出須要修改的函數的代碼)post
// // AppDelegate.m // #import "AppDelegate.h" #import "PDRCore.h" #import "PDRCommonString.h" #import "ViewController.h" #import "PDRCoreApp.h" #import "DCADManager.h" #import "PDRCoreAppManager.h" #import <FBSDKCoreKit/FBSDKCoreKit.h> // 示例默認帶開屏廣告,若是不須要廣告,可註釋下面一行 #define dcSplashAd @interface AppDelegate () <DCADManagerDelgate, PDRCoreDelegate> @property (strong, nonatomic) ViewController *h5ViewContoller; @end @implementation AppDelegate @synthesize window = _window; #pragma mark - #pragma mark app lifecycle /* * @Summary:程序啓動時收到push消息 */ - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { BOOL ret = [PDRCore initEngineWihtOptions:launchOptions withRunMode:PDRCoreRunModeNormal withDelegate:self]; DCADManager *adManager = [DCADManager adManager]; UIViewController* adViewController = [adManager getADViewController]; adManager.delegate = self; UIWindow *window = [[UIWindow alloc] initWithFrame:[UIScreen mainScreen].bounds]; self.window = window; ViewController *viewController = [[ViewController alloc] init]; self.h5ViewContoller = viewController; UINavigationController *navigationController = [[UINavigationController alloc] initWithRootViewController:viewController]; self.rootViewController = navigationController; navigationController.navigationBarHidden = YES; if ( adViewController ) { [navigationController pushViewController:adViewController animated:NO]; } else { [self startMainApp]; self.h5ViewContoller.showLoadingView = YES; } self.window.rootViewController = navigationController; [self.window makeKeyAndVisible]; [[FBSDKApplicationDelegate sharedInstance] application:application didFinishLaunchingWithOptions:launchOptions]; return ret; } #pragma mark - #pragma mark URL - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options{ //NSLog(@"==========here(), url: %@", url); [self application:application handleOpenURL:url]; [[FBSDKApplicationDelegate sharedInstance] application:application openURL:url sourceApplication:options[UIApplicationOpenURLOptionsSourceApplicationKey] annotation:options[UIApplicationOpenURLOptionsAnnotationKey] ]; return YES; }
6. Supporting Files/Info.plist文件ui