贴出源码回报社区!
下面贴出关键代码,稍后完善更多细节步骤。个人ios开发经验不多,如果有更好的集成方式,请大家分享!
前提:
1. 能在Xcode中成功运行官方给出的离线打包演示应用 HBuilder-Hello (iOS平台5+SDK集成)
2. 注册了facebook开发者平台,并完成了官方ios集成步骤,参考:[](https://developers.facebook.com/docs/facebook-login/ios?sdk=fbsdk&locale=zh_CN)
源代码目录结构
1. js层代码调用插件login.js
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.h
//
// 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的代码,这里仅贴出需要修改的函数的代码)
//
// 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文件