文档章节

html5+ & mui框架facebook登录插件(ios版)

coton_chen
 coton_chen
发布于 2018/10/08 08:51
字数 2021
阅读 242
收藏 1

贴出源码回报社区!

下面贴出关键代码,稍后完善更多细节步骤。个人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文件

 

© 著作权归作者所有

coton_chen
粉丝 23
博文 25
码字总数 16358
作品 0
静安
架构师
私信 提问
Facebook iOS 新版开发手记:两倍速度的背后

Facebook上周发布了新版iOS应用,号称速度提升两倍。Facebook工程师Jonathan Dan在Facebook官方页面中撰文,介绍了新版iOS应用、Facebook iOS应用的发展历程以及开发思路。《创事记》特选取此...

oschina
2012/08/30
2.8K
13
法国开源软件公司 Nexedi 起诉苹果扼杀 HTML5

据法国媒体Challenges报道,10月6日法国开源软件公司Nexedi向法国巴黎商业法庭提起诉讼,状告苹果公司有违公平原则,在iOS平台上对HTML5缺乏必要支持(例如强制第三方浏览器使用Safari浏览器...

凝小紫
2016/10/08
5.3K
36
克服 iOS HTML5 音频的局限

简介 过去几年,开发人员一直都在制造完善的交互体验,努力使其可以在浏览器中正确运行。这样的站点通常需要使用浏览器插件 (Flash)。随着智能手机和平板电脑的推出,交互体验看似与新的小部...

tommyfok
2014/02/22
7.9K
0
10大优秀的移动Web应用程序开发框架推荐

在最近几年里,移动互联网高速发展、市场潜力巨大。继计算机、互联网之后,移动互联网正掀起第三次信息技术革命的浪潮,新技术、新应用不断涌现。今天这篇文章向大家推荐10大优秀的移动Web开...

老盖
2011/09/20
4.3K
4
10大优秀的移动Web应用程序开发框架

今天这篇文章向大家推荐10大优秀的移动Web开发框架,帮助开发者更加高效的开发移动Web应用。 1.Sencha Touch Framework Sencha Touch是世界上第一个基于HTML5的移动Web开发框架,支持最新的H...

晨曦之光
2012/03/01
1K
1

没有更多内容

加载失败,请刷新页面

加载更多

for循环

九九乘法表 示例:for(int i = 1; i <= 9; i++){ for (int j = 1; j <= i; j++) { // 每次开始i循环,j都会重新定义为j=1,然后开始循环计算 System.out.print(j +......

Shutting
3分钟前
1
0
小王子1

一定要帅! 韩国设计师品牌 insgram全世界得网红 韩国潮男穿搭 HM 找到穿衣服最好看的人,跟他比,比他好看。 在兴趣前,不要表现目的性,压力 关系是不热就冷的! 不喜欢压力,不喜欢负责任...

阿锋zxf
22分钟前
5
0
时间戳

1 loadTimeString(ts) { var d = new Date(); if (String(ts).length == 10) { d = new Date(ts * 1000); ......

东方巨人
24分钟前
3
0
Redis Cluster

Redis Cluster 集群 redis集群有以下几种方式 普通一主多从 普通一主多从+哨兵 cluster分片模式 一主多从 搭建方式网上很多,就不多描述了。 这种集群方式,一般master用作写,slave用做读,...

lazy~
24分钟前
4
0
 介绍一款优秀的通用管理权限快速开发框架

这是一套以权限管理为主的轻量化快速开发框架,配置有流程、专业表单、权限、app、企业微信等基础功能模块,在开发通用软件的效率上很有优势。 软件平台常用研发需求分析 《那些年我们一起做...

我想造火箭
40分钟前
12
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部