很多公司为了盈利,往往会在应用或网站中开放一些广告位以实现盈利的目的,广告链接内容往往是不可控,例如有些网页顶部会有个提示条“下载APP”,这样的问题在微信中他们也会拦截掉,那我们如何让用户点击“下载APP”按钮无法跳转App Store中去下载呢?
一、网页如何实现跳转App Store
跳转App Store协议:
(https)|(itms-apps)://itunes.apple.com/app/id{appID}
js应用:
下面方法最终也会转为itms-appss://协议
window.location.href = "https://itunes.apple.com/us/app/appName/id{appID}"?mt=8";
或者直接使用应用跳转协议
window.location.href = "itms-apps://itunes.apple.com/app/id{appID}";
iOS 实现跳转:
itms-apps://协议是不能直接跳转的,需要借助openURL:
方法才能跳转到应用商店,我们在下面方法可以实现跳转App Store、拨打电话、调用第三方应用等功能
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
NSURLRequest *request = navigationAction.request;
NSString *scheme = [request.URL scheme];
if (![scheme isEqualToString:@"https"] && ![scheme isEqualToString:@"http"]) {
decisionHandler(WKNavigationActionPolicyCancel);
[[UIApplication sharedApplication] openURL:request.URL options:@{UIApplicationOpenURLOptionUniversalLinksOnly:@NO} completionHandler:^(BOOL success) {
}];
return;
}
decisionHandler(WKNavigationActionPolicyAllow);
}
二、如何拦截
都知道它是怎么跳转的,拦截感觉很简单了
方法一
不实现- (void)webView:decidePolicyForNavigationAction:decisionHandler:
代理方法
方法二
- (void)webView:(WKWebView *)webView decidePolicyForNavigationAction:(WKNavigationAction *)navigationAction decisionHandler:(void (^)(WKNavigationActionPolicy))decisionHandler {
NSURLRequest *request = navigationAction.request;
NSString *scheme = [request.URL scheme];
if (![scheme isEqualToString:@"https"] && ![scheme isEqualToString:@"http"]) {
decisionHandler(WKNavigationActionPolicyCancel);
if ([scheme hasPrefix:@"itms-apps://"] || [scheme hasPrefix:@"itms-appss://"]) return;
[[UIApplication sharedApplication] openURL:request.URL options:@{UIApplicationOpenURLOptionUniversalLinksOnly:@NO} completionHandler:^(BOOL success) {
}];
return;
}
decisionHandler(WKNavigationActionPolicyAllow);
}
上面这种做法会拦截所有的跳转App Store请求,但是我们想要某些可以跳转,这个时候我们需要一个白名单,如果appId在跳转白名单内就可以跳转。
方法三
有的时候我们能会有多个WebView来展示网页,在每个网页处理起来就会有很多重复的代码。另外一种方法就是使用MethodSizzling来实现拦截
#import "UIApplication+OpenUrlBlock.h"
@implementation UIApplication (OpenUrlBlock)
+ (void)load {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
SEL originalSelector = @selector(openURL:options:completionHandler:);
SEL swizzledSelector = @selector(swiz_openURL:options:completionHandler:);
[self swizzlingInClass:[self class] originalSelector:originalSelector swizzledSelector:swizzledSelector];
SEL originalSelector2 = @selector(openURL:);
SEL swizzledSelector2 = @selector(swiz_openURL:);
[self swizzlingInClass:[self class] originalSelector:originalSelector2 swizzledSelector:swizzledSelector2];
});
}
+ (void)swizzlingInClass:(Class)cls originalSelector:(SEL)originalSelector swizzledSelector:(SEL)swizzledSelector {
Class class = cls;
Method originalMethod = class_getInstanceMethod(class, originalSelector);
Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector);
BOOL didAddMethod =
class_addMethod(class,
originalSelector,
method_getImplementation(swizzledMethod),
method_getTypeEncoding(swizzledMethod));
if (didAddMethod) {
class_replaceMethod(class,
swizzledSelector,
method_getImplementation(originalMethod),
method_getTypeEncoding(originalMethod));
} else {
method_exchangeImplementations(originalMethod, swizzledMethod);
}
}
- (void)swiz_openURL:(NSURL*)url options:(NSDictionary<UIApplicationOpenExternalURLOptionsKey, id> *)options completionHandler:(void (^)(BOOL success))completion {
NSLog(@"调用openURL:options:completionHandler方法打开:%@", url.absoluteString);
NSURL *filtrateUrl = [self filtrateDowloadWithUrl:url];
[self swiz_openURL:filtrateUrl options:options completionHandler:completion];
}
- (BOOL)swiz_openURL:(NSURL*)url {
NSLog(@"调用openURL方法打开:%@", url.absoluteString);
NSURL *filtrateUrl = [self filtrateDowloadWithUrl:url];
return [self swiz_openURL:filtrateUrl];
}
// 过滤非白名单应用,不让去跳转下载
- (NSURL *)filtrateDowloadWithUrl:(NSURL *)url {
NSURL *filtrateUrl = url;
if ([url.absoluteString hasPrefix:@"itms-apps://"] || [url.absoluteString hasPrefix:@"itms-appss://"]) {
if (![self isAppDowloadWithUrl:url]) {
filtrateUrl = [NSURL URLWithString:@""];
}
}
return filtrateUrl;
}
// 拦截openUrl判断应用是否可以下载
- (BOOL)isAppDowloadWithUrl:(NSURL *)url {
NSArray *whiteList = [self appDowloadWhiteList];
__block BOOL isAppDowload = NO;
[whiteList enumerateObjectsUsingBlock:^(NSDictionary *obj, NSUInteger idx, BOOL *stop) {
NSString *appId = obj[@"appId"];
if ([url.absoluteString containsString:appId]) {
isAppDowload = YES;
*stop = YES;
return;
}
}];
return isAppDowload;
}
- (NSArray *)appDowloadWhiteList {
NSString *filePath = [[NSBundle mainBundle] pathForResource:@"AppDowload" ofType:@"plist"];
NSArray *ary = [NSArray arrayWithContentsOfFile:filePath];
return ary;
}
@end
应用白名单plist文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array>
<dict>
<key>appName</key>
<string>应用名称</string>
<key>appId</key>
<string>应用id</string>
</dict>
</array>
</plist>
总结
- 如果提供给外部网页加载WebView很少,还是建议使用第二种在代理方法内拦截
- 使用MethodSizzling虽然在WebView中不用写任何代码,但是同样存在出现问题难排查情况
网友评论