PopupBridge iOS
PopupBridge 是一个 iOS 库,该库允许 WKWebViews 在 SFSafariViewController 浏览器中打开弹出窗口并向父页面的 WKWebView 发送数据。
PopupBridge 还提供 Android 版本,请见 Android 版本。
更多关于 PopupBridge 的信息,请参阅常见问题解答。欲了解如何将 PopupBridge 与 PayPal 结合使用,请参阅在 WebView 中使用 PayPal。
要求
- iOS 9.0+
安装
CocoaPods
要使用 CocoaPods 集成,请将以下行添加到您的 Podfile 中
pod 'PopupBridge'
Carthage
要使用Carthage集成,请将github "braintree/popup-bridge-ios"
添加到您的Cartfile
中,然后将框架添加到您的项目中。
Swift Package Manager
要使用Swift Package Manager集成,请选择文件 > Swift包 > 添加包依赖,并将https://github.com/braintree/popup-bridge-ios
作为仓库URL输入。勾选PopupBridge
的复选框。
如果查看您的应用程序目标,您将看到PopupBridge
已自动作为框架链接到您的应用程序(参见“通用”>“框架、库和嵌入的内容”)。
示例应用程序
要运行示例应用程序,请克隆仓库,打开PopupBridge.xcworkspace
,并运行Demo
应用程序目标。
快速入门
-
为您的应用程序注册一个URL类型
- 在Xcode中,点击项目导航器中的项目,然后导航到“应用程序目标”>“信息”>“URL类型”
- 点击“[+]" 添加一个新URL类型
- 在“URL方案”下,输入一个独特的URL方案,例如
com.my-app.popupbridge
-
在您的应用程序代理的
didFinishLaunchingWithOptions
方法中,设置返回URL方案。#import "POPPopupBridge.h" - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [POPPopupBridge setReturnURLScheme:@"com.my-app.popupbridge"]; return YES; }
- 检查返回URL,然后从您的应用程序代理或场景代理调用
PopupBridge:openURL
。
如果您正在使用
UISceneDelegate
(自iOS 13引入),请在scene:openURLContexts
代理方法内调用PopupBridge:openURL
。传递正确的UIOpenURLContext
上的URL。- (void)scene:(UIScene *)scene openURLContexts:(NSSet<UIOpenURLContext *> *)URLContexts { for (UIOpenURLContext *urlContext in URLContexts) { NSURL *url = [urlContext URL]; if ([url.scheme localizedCaseInsensitiveCompare:@"com.my-app.popupbridge"] == NSOrderedSame) { [POPPopupBridge openURL:urlContext.URL]; } } }
如果您没有使用
UISceneDelegate
,请在应用程序代理的application:openURL:
代理方法内调用PopupBridge:openURL
。- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options { if ([url.scheme localizedCaseInsensitiveCompare:@"com.my-app.popupbridge"] == NSOrderedSame) { return [POPPopupBridge openURL:url]; } return NO; }
- 检查返回URL,然后从您的应用程序代理或场景代理调用
-
将PopupBridge与WKWebView集成
#import "POPPopupBridge.h" @interface MyViewController () <POPPopupBridgeDelegate> @property (nonatomic, strong) WKWebView *webView; @property (nonatomic, strong) POPPopupBridge *popupBridge; @end @implementation MyViewController - (void)viewDidLoad { [super viewDidLoad]; self.webView = [[WKWebView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)]; [self.view addSubview:self.webView]; self.popupBridge = [[POPPopupBridge alloc] initWithWebView:self.webView delegate:self]; // replace https://:3099/ with the webpage you want to open in the webview [self.webView loadRequest:[NSURLRequest requestWithURL:[NSURL URLWithString:@"https://:3099/"]]]; } - (void)popupBridge:(POPPopupBridge *)bridge requestsPresentationOfViewController:(UIViewController *)viewController { [self presentViewController:viewController animated:YES completion:nil]; } - (void)popupBridge:(POPPopupBridge *)bridge requestsDismissalOfViewController:(UIViewController *)viewController { [viewController dismissViewControllerAnimated:YES completion:nil]; }
-
通过编写一些JavaScript,从网页中使用PopupBridge
var url = 'https://:3099/popup'; // or whatever the page is that you want to open in a popup if (window.popupBridge) { // Open the popup in a browser, and give it the deep link back to the app popupBridge.open(url + '?popupBridgeReturnUrlPrefix=' + popupBridge.getReturnUrlPrefix()); // Optional: define a callback to process results of interaction with the popup popupBridge.onComplete = function (err, payload) { if (err) { console.error('PopupBridge onComplete Error:', err); } else if (!err && !payload) { console.log('User closed popup.'); } else { alert('Your favorite color is ' + payload.queryItems.color); } }; } else { var popup = window.open(url); window.addEventListener('message', function (event) { var color = JSON.parse(event.data).color; if (color) { popup.close(); alert('Your favorite color is ' + color); } }); }
-
在弹窗内部返回到应用程序
<h1>What is your favorite color?</h1> <a href="#red" data-color="red">Red</a> <a href="#green" data-color="green">Green</a> <a href="#blue" data-color="blue">Blue</a> <script src="jquery.js"></script> <script> $('a').on('click', function (event) { var color = $(this).data('color'); if (location.search.indexOf('popupBridgeReturnUrlPrefix') !== -1) { var prefix = location.search.split('popupBridgeReturnUrlPrefix=')[1]; // Open the deep link back to the app, and send some data location.href = prefix + '?color=' + color; } else { window.opener.postMessage(JSON.stringify({ color: color }), '*'); } }); </script>
常见问题
为什么使用PopupBridge?
WKWebView可以通过其WKUIDelegate
打开弹出窗口,可以将其实现为在新WKWebView中显示弹出窗口。
然而,WKWebView不显示地址栏或HTTPS锁图标。如果弹出窗口接收敏感用户信息(例如,登录凭证),则用户必须隐式信任网页没有将其重定向到可能窃取他们信息的恶意欺骗页面。PopupBridge通过使用SFSafariViewController来解决这个问题。
使用PopupBridge的哪些用例?
- 需要打开弹出窗口的具有WebView的应用
- 当需要从弹出窗口发送数据回WKWebView时
- 当弹出窗口需要显示HTTPS锁图标来增加用户信任时
- 使用OAuth的应用
它是如何工作的?
- PopupBridge通过向页面注入一个用户脚本(通过
https://developer.apple.com/reference/webkit/wkuidelegate/1537448-adduserscript
)来附加到WKWebView上- 这向网页提供了一个JavaScript接口(通过
window.popupBridge
),以便网页可以与iOS代码交互
- 这向网页提供了一个JavaScript接口(通过
- 网页检测页面是否可以访问
window.popupBridge
;如果可以,则使用popupBridge.open
来打开弹出URLpopupBridge.open
创建一个SFSafariViewController来打开弹出URL,并且将其代理控制器显示出来- 网页还可以使用
popupBridge.onComplete
作为回调
- 弹出网页使用一个深度链接URL来关闭弹出
-
深度链接URL应与Xcode中的一个深度链接URL类型匹配
-
应用代理处理深度链接URL并将其转发给PopupBridge
-
避免硬编码深度链接的一种方法是将其添加为弹出URL的查询参数
popupBridge.open(url + '?popupBridgeReturnUrlPrefix=' + popupBridge.getReturnUrlPrefix());
- 可选地,您可以将路径组件和查询参数添加到深度链接URL中,以返回数据到父页面,这些数据包含在
popupBridge.onComplete
的有效载荷中
- 可选地,您可以将路径组件和查询参数添加到深度链接URL中,以返回数据到父页面,这些数据包含在
-
- 如果用户在 SFSafariViewController 上点击 完成 按钮,
popupBridge.onComplete
将被调用,错误和有效载荷为null
,并且代理拒绝关闭视图控制器
谁开发了 PopupBridge?
我们是 Braintree 开发者体验团队的工程师,在 Braintree 工作。
Braintree 为什么要开发 PopupBridge?
简答:为了在移动应用使用 WebView 实现结账流程时接受 PayPal 作为支付选项。
PayPal 身份验证发生在弹出窗口中。但是,这会给使用 WebView 在其应用中实现付款的 Braintree 商户带来问题:他们不能接受 PayPal,因为 WebView 无法打开弹出窗口并将 PayPal 付款授权数据返回给父结账页面。
PopupBridge 通过允许 braintree-web
或 PayPal 的 Checkout.js 从安全的迷你浏览器打开 PayPal 弹出窗口来解决这个问题。
在 WebView 中使用 PayPal
基于 WebView 的结账流程可以使用 PopupBridge 和 Braintree JS SDK 或 PayPal 的 Checkout.js 接受 PayPal。对于身份验证流程,PayPal 需要一个弹出窗口——这可以通过 PopupBridge 模拟。
设置
- 创建一个基于 Web 的结账页面以接受 Checkout.js 或 Braintree JS SDK 的 PayPal。
- 创建一个原生移动应用,在其中打开
WKWebView
(参见快速入门说明中的步骤 1-3) - 集成 PopupBridge 库
- 收集设备数据
- 为了帮助检测欺诈行为,在进行PayPal交易之前收集设备数据。这与使用我们原生iOS SDK收集设备数据类似,但有一些区别。
- 而不是导入整个数据收集器,您只需将PayPalDataCollector添加到您的应用程序中:
pod 'Braintree/PayPalDataCollector'
- 根据您是进行一次性付款还是托管付款,在您的原生应用程序中实现方法。请参阅PayPal + PopupBridge的iOS代码片段
- 而不是导入整个数据收集器,您只需将PayPalDataCollector添加到您的应用程序中:
- 为了帮助检测欺诈行为,在进行PayPal交易之前收集设备数据。这与使用我们原生iOS SDK收集设备数据类似,但有一些区别。
- 盈利!
使用PopupBridge将消息传递到WebView
尽管PopupBridge的主要目的是处理弹出窗口,但它可以在更普遍的使用场景中使用,将URL从应用程序发送到WebView中的JavaScript上下文。这些URL可以包含任意数据。
-
如快速入门中所述,为您应用程序注册URL类型。
-
在您的应用程序代理中,使用URL方案设置PopupBridge
#import "POPPopupBridge.h" - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { [POPPopupBridge setReturnURLScheme:@"com.my-app.popupbridge"]; return YES; }
-
将处理程序添加到
onComplete
回调if (window.popupBridge) { popupBridge.onComplete = function (err, payload) { if (err) { console.error('PopupBridge onComplete Error:', err); return; } console.log("Payload path:", payload.path); console.log("Payload query items:", payload.queryItems); console.log("Payload fragment:", payload.hash); }; }
-
创建一个以您的应用程序URL方案开头,路径为
popupbridgev1
的URL,例如com.my-app.popupbridge://popupbridgev1
。您可以将任何附加数据作为URL路径、查询项和片段的形式添加。 -
使用该URL调用PopupBridge的
openUrl:options:
方法。当onComplete
处理程序将接收URL作为有效载荷时。例如,如果URL是com.my-app.popupbridge://popupbridgev1/hi/there?foo=bar#baz=qux
console.log("Payload path:", payload.path); // "/hi/there" console.log("Payload query items:", payload.queryItems); // {foo: "bar"} console.log("Payload fragment:", payload.hash); // "baz=qux"
作者
Braintree, [email protected]
许可证
PopupBridge is available under the MIT license. See the LICENSE file for more info.