WKWebView的重定向(objective_c)
背景
第三方支付回调时需要重定向到app的某个页面,比如支付完成后回到原生订单详情页,这个时间会有两种情况:
1、直接在web页面重定向到app的订单详情页,这个时候只需要实现 WKNavigationDelegate 中的一个核心方法webView:decidePolicyForNavigationAction:decisionHandler: 方法。
2、在支付中心跳转到第三方app然后支付完成后需要跳转回自己的app的订单详情页,这个时候可以采用Scheme方式或者是通用链接的方式解决
wkWebView重定向实现
实现这一目标,您需要让您的 WKWebView 所在的控制器遵循 WKNavigationDelegate 协议,并实现 webView:decidePolicyForNavigationAction:decisionHandler: 方法。
self.webView.navigationDelegate = self; // 设置代理
#pragma mark - WKNavigationDelegate
- (**void**)webView:(WKWebView *)webView
decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction
decisionHandler:(**void** (^)(WKNavigationActionPolicy))decisionHandler {
NSURL *url = navigationAction.request.URL;
NSString *scheme = url.scheme;
// 1. 检查 URL Scheme 是否是我们的自定义 Scheme
if ([scheme isEqualToString:@"coolpet"]) {
// 1.1. 阻止 WKWebView 加载这个 URL
decisionHandler(WKNavigationActionPolicyCancel);
// 1.2. 实现了 handleCoolPetURL: 方法
[self handleCoolPetURL:url];
// 1.3. 跳转后关闭当前的 WebView 页面
[self.navigationController popViewControllerAnimated:YES];
return;
}
// 2. 对于其他 HTTP/HTTPS 链接,允许正常加载
// 特别检查 navigationType 是否是新的主框架加载,例如用户点击了链接
// if (navigationAction.navigationType == WKNavigationTypeLinkActivated && ![scheme hasPrefix:@"http"]) {
// // 如果是点击了非 HTTP/HTTPS 的链接(但不是我们自定义的 Scheme),可以根据需要处理,
// // 比如打开 App Store 或其他应用。这里我们通常允许其他系统 Scheme
// // 允许继续,但更安全的做法是只允许 http(s)
// // decisionHandler(WKNavigationActionPolicyAllow);
// }
// 3. 默认允许其他所有导航行为(如页内跳转、HTTP/HTTPS 加载等)
decisionHandler(WKNavigationActionPolicyAllow);
}
// 通过URL跳转对应页面
- (void)handleCoolPetURL:(NSURL *)url {
NSString *host = url.host;
NSString *path = url.path; // 路径: /order/detail
NSURLComponents *components = [NSURLComponents componentsWithURL:url resolvingAgainstBaseURL:NO];
NSMutableDictionary *queryParams = [NSMutableDictionary dictionary];
for (NSURLQueryItem *item in components.queryItems) {
queryParams[item.name] = item.value;
}
// 根据路径判断是否是订单详情页
if ([host isEqualToString:kAPPUniversalTypeOrderDetailsHost] && [path isEqualToString:kAPPUniversalTypeOrderDetailsPath]) {
// 获取我们需要的订单号
NSString *tradeNo = [queryParams[@"tradeNo"] stringValue];
// 执行跳转
if (tradeNo.length > 0) {
dispatch_async(dispatch_get_main_queue(), ^{
/// 做跳转
});
}
}
}
Scheme方式
第三方支付平台完成支付后,是通过你App的 URL Scheme 来唤醒你的App并携带支付结果的。
- 配置 App URL Scheme
-
操作: 在 Xcode 项目的
Info.plist或项目设置的 Info 选项卡下的 URL Types 中添加你的 App 的 Scheme。- 例如,你可以设置一个 Scheme 叫
myscheme。
- 例如,你可以设置一个 Scheme 叫
- 处理 App Delegate 中的回调
App 被第三方支付应用唤醒后,系统会调用 AppDelegate 中的特定方法。你需要在这里接收并处理回调 URL。
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<UIApplicationOpenURLOptionsKey, **id**> *)options {
// 1. 检查是否是你的支付回调 Scheme
if ([url.scheme isEqualToString:@"myappscheme"]) {
[self handleCoolPetURL:url];
}
// 如果是其他URL(如通用链接),也在这里处理
// ...
return NO;
}
通用链接方式
当用户点击一个配置了通用链接的 HTTPS 链接时:
- 如果 App 已经安装,系统会直接调用
AppDelegate中的这个方法。 - 如果 App 未安装,该链接会直接在 Safari 中打开。
这个机制的主要优点是安全(基于 HTTPS)和用户体验更好(避免了 URL Scheme 引起的跳转确认和安全问题)。
🔗 通用链接(Universal Links)实现指南
步骤 1: 服务器端配置(Association File)
这是通用链接能够工作的基础。您需要在您的 Web 服务器上创建一个特殊的 JSON 文件,告诉 iOS 系统哪些路径应该由您的 App 处理。
1. 创建 apple-app-site-association 文件
-
文件名: 必须是
apple-app-site-association(注意,没有.json扩展名)。 -
内容格式(JSON):
{ "applinks": { "apps": [], "details": [ { "appID": "TeamID.BundleID", "paths": [ "/orders/*", // 匹配所有 /orders/ 下的路径 "/products/*", // 匹配所有 /products/ 下的路径 "NOT /account/login/*" // 排除某些路径 ] } ] } }-
TeamID: 您的 Apple Developer Team ID。 -
BundleID: 您的 App 的 Bundle Identifier。 -
paths: 定义您希望 App 能够处理的 URL 路径。
-
2. 部署文件
-
部署位置: 将此文件上传到您的域名根目录或
.well-known/目录下。- 例如:
https://yourdomain.com/apple-app-site-association - 或者:
https://yourdomain.com/.well-known/apple-app-site-association
- 例如:
-
内容类型: 确保服务器以正确的 MIME 类型提供此文件:
application/json或text/plain。 -
HTTPS: 您的整个网站必须使用 HTTPS。
步骤 2: App 端配置(Xcode & Objective-C)
1. 开启 Associated Domains Capability
在 Xcode 中为您的 App 开启 Associated Domains 功能。
-
路径: Xcode -> 项目设置 -> 目标 (Target) -> Signing & Capabilities 选项卡
-
操作: 点击
+ Capability,添加 Associated Domains。 -
添加域名: 在列表中添加您的域名,格式为:
applinks:yourdomain.com注意: 不带
https://或http://。
2. 在 AppDelegate 中接收回调
当用户点击一个通用链接并唤醒 App 时,系统会调用 AppDelegate 中的 continueUserActivity 方法。您需要在此方法中解析 URL 并进行页面跳转。
// AppDelegate.m
#import "OrderViewController.h" // 假设您的订单处理页面
// ...
- (BOOL)application:(UIApplication *)application
continueUserActivity:(NSUserActivity *)userActivity
restorationHandler:(void (^)(NSArray<id<UIUserActivityRestoring>> * _Nullable))restorationHandler {
// 1. 检查活动类型是否为 Universal Link
if ([userActivity.activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) {
// 2. 获取用户点击的 HTTPS URL
NSURL *webpageURL = userActivity.webpageURL;
if (webpageURL) {
NSLog(@"Received Universal Link: %@", webpageURL.absoluteString);
// 3. 将 URL 转发给路由处理方法
[self handleUniversalLinkURL:webpageURL];
return YES;
}
}
return NO;
}
// 通用链接路由处理方法
- (void)handleUniversalLinkURL:(NSURL *)url {
// 示例:解析路径并跳转到订单详情
if ([url.path hasPrefix:@"/orders/detail"]) {
// 解析查询参数,例如 order_id=12345
NSString *orderID = [self extractParameter:@"order_id" fromURL:url];
if (orderID.length > 0) {
dispatch_async(dispatch_get_main_queue(), ^{
// 执行跳转逻辑
UINavigationController *nav = (UINavigationController *)self.window.rootViewController;
OrderViewController *orderVC = [[OrderViewController alloc] init];
orderVC.orderID = orderID;
[nav pushViewController:orderVC animated:YES];
});
}
}
}
// 辅助方法 (需要您自行实现,或使用前文提到的 dictionaryWithQueryString: 方法)
- (NSString *)extractParameter:(NSString *)paramName fromURL:(NSURL *)url {
// ... 解析 url.query 字符串,提取指定参数 ...
return nil;
}