就我個人所知创千,iOS中存在3種方式可以打開(喚醒)其它手機(jī)App(除開系統(tǒng)級應(yīng)用)跛锌,分別是:
- 第三方登錄、分享、支付煞肾、導(dǎo)航等
- Open in my app
- Deeplink
三種方式原理一樣,均是注冊自定義URL Schemes
猎物,并處理URL請求棍现。
第三方
- 使用第三方用戶登錄,如微信横缔,QQ铺遂,微博登錄,授權(quán)后返回用戶名和密碼
- 內(nèi)容分享茎刚,跳轉(zhuǎn)到分享App的對應(yīng)頁面襟锐,如分享給微信好友、分享給微信朋友圈膛锭、分享到微博
- 第三方支付粮坞,跳轉(zhuǎn)到第三方支付App,如支付寶支付初狰,微信支付
- 顯示位置莫杈、地圖導(dǎo)航,跳轉(zhuǎn)到地圖應(yīng)用奢入,如高德地圖筝闹,百度地圖等
- 應(yīng)用程序推廣,跳轉(zhuǎn)到iTunes并顯示指定App下載頁腥光,或使用Safari打開指定網(wǎng)頁
1~4 第三方平臺均提供了相應(yīng)SDK丁存,具體不再闡述,5實(shí)際只需要一個網(wǎng)址柴我,調(diào)用[[UIApplication sharedApplication] openURL:url]
方法即可解寝。
在iOS9中,如果使用
canOpenURL:
方法艘儒,該方法所涉及到的URL Schemes
必須在"Info.plist"中將它們列為白名單夫偶,否則不能使用。key叫做LSApplicationQueriesSchemes
觉增,鍵值內(nèi)容是對應(yīng)應(yīng)用程序的URL Schemes
兵拢。
Open in my app
iOS有個不常使用的功能,叫Open in my app逾礁,即在我的app中打開说铃,此功能允許文件在其他app中打開。
常見如郵件的附件嘹履,輕觸蘋果會默認(rèn)使用QLPreviewController
打開預(yù)覽界面腻扇,而長按這時會彈出共享列表菜單,此菜單會列出所有添加了該類型文件的應(yīng)用砾嫉,它允許此文件在添加了對應(yīng)Document Type
支持的應(yīng)用中打開幼苛,如下圖所示:
實(shí)現(xiàn)步驟
1. 修改Info.plist文件
1)在plist文件中添加URLTypes字段,指定程序的對外接口:
由于我之前已經(jīng)集成了社會化分享焕刮,這一步就直接跳過舶沿。
2)添加一個Documents Type字段,該字段指定與程序關(guān)聯(lián)的文件類型配并,詳情參考System-Declared Uniform Type Identifiers
CFBundleTypeExtensions
指定文件類型括荡,例如doc
溉旋,xls
一汽,ppt
,txt
等低滩。
CFBundleTypeIconFiles
指定用UIActionSheet
向用戶提示打開應(yīng)用時顯示的圖標(biāo)。
DocumentTypeName
可以自定岩喷,對應(yīng)文件類型名恕沫。
Document Content Type UTIs
指定官方指定的文件類型,UTIs 即 Uniform Type Identifiers纱意。
-
PDF文件
.pdf的配置 -
Doc文件
.doc(s)的配置 -
Excel文件
.xls(x)的配置 -
PPT文件
.ppt的配置
注意:預(yù)支持PPT需要 額外配置增加
public.data
字段婶溯,被這個坑了好久!
-
TXT(或RTF)
.txt的配置
2. 在 AppDelegate.m 中添加外部調(diào)用代碼
#import <QuickLook/QuickLook.h>
@interface AppDelegate () <QLPreviewControllerDataSource>
@property (nonatomic,strong) NSURL *openUrl;
@end
注意:iOS 9之后請使用這個方法:
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString*, id> *)options;
-(BOOL)application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
if (url && [url isFileURL]) {
self.openUrl=url;
QLPreviewController* previewController = [[QLPreviewController alloc] init];
[self.window.rootViewController presentViewController:previewController animated:YES completion:nil];
dispatch_async(dispatch_get_global_queue(0, 0), ^{
NSData *data = [NSData dataWithContentsOfURL:url];
dispatch_async(dispatch_get_main_queue(), ^{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsPath=[[paths objectAtIndex:0] stringByAppendingPathComponent:@"downloadFile"];
BOOL dirHas;
if (![[NSFileManager defaultManager] fileExistsAtPath:dirPath isDirectory:&dirHas] ) {
[[NSFileManager defaultManager] createDirectoryAtPath:dirPath withIntermediateDirectories:NO attributes:nil error:nil];
}
NSString *filePath = [documentsPath stringByAppendingPathComponent:[[url relativePath] lastPathComponent]];//Add the file name
[data writeToFile:filePath atomically:YES];
//寫入成功后再讀取刷新數(shù)據(jù),避免跳轉(zhuǎn)界面時等待太久
previewController.dataSource = self;
[previewController reloadData];
});
});
return YES;
}
return NO;
}
//使用QLPreviewController預(yù)覽
- (NSInteger) numberOfPreviewItemsInPreviewController: (QLPreviewController *) controller {
return 1;
}
- (id <QLPreviewItem>)previewController: (QLPreviewController*)controller previewItemAtIndex:(NSInteger)index {
// NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
// NSString *docDir = [[paths objectAtIndex:0] stringByAppendingPathComponent:@"Inbox"];
NSString *filePath=[getDocumentPath() stringByAppendingPathComponent:[[self.openUrl relativePath] lastPathComponent]];
if ([[filePath pathExtension] isEqualToString:@"txt"]) {
//處理txt格式內(nèi)容顯示有亂碼的情況
NSData *fileData = [NSData dataWithContentsOfFile:filePath];
//判斷是UNICODE編碼
NSString *isUNICODE = [[NSString alloc] initWithData:fileData encoding:NSUTF8StringEncoding];
//還是ANSI編碼(-2147483623偷霉,-2147482591迄委,-2147482062,-2147481296)encoding 任選一個就可以了
NSString *isANSI = [[NSString alloc] initWithData:fileDataencoding:-2147483623];
if (isUNICODE) {
NSString *retStr = [[NSString alloc]initWithCString:[isUNICODE UTF8String] encoding:NSUTF8StringEncoding];
NSData *data = [retStr dataUsingEncoding:NSUTF16StringEncoding];
[data writeToFile:filePath atomically:YES];
} else if (isANSI) {
NSData *data = [isANSI dataUsingEncoding:NSUTF16StringEncoding];
[data writeToFile:filePath atomically:YES];
}
}
NSURL *fileUrl = [NSURL fileURLWithPath:filePath];
return fileUrl;
}
至于為什么要下載?
我經(jīng)過測試發(fā)現(xiàn)文件都是存在沙盒Documents/Inbox/
目錄下的类少,獲取到的文件路徑例如:file:///private/var/mobile/Containers/Data/Application/A9D2A924-A1F8-4915-B31D-57127ED987BE/Documents/Inbox/工作日報_0113.xlsx
叙身,然而卻怎么也打不開,嘗試拼接取文件路徑也不成功硫狞,于是重新下載了一份放到我指定的目錄下信轿,也可以直接使用copyItemAtPath: toPath: error:
方法拷貝文件到指定的路徑下晃痴。
更多支持文件類型(如MP3,AIFF,WAV, etc.)
stack overflow給出了各個類型所需要添加的plist字段:
http://stackoverflow.com/questions/9266079/why-is-my-ios-app-not-showing-up-in-other-apps-open-in-dialog
Deeplink(深度鏈接)
Deeplink,簡單來說就是給定一個鏈接财忽,即可跳轉(zhuǎn)到已安裝應(yīng)用中的某一個頁面的技術(shù)倘核。
通過Deep Link,App可以通過搜索引擎進(jìn)行導(dǎo)流即彪〗舫可以通過 SEO 增加訪問量從而提高 app 下載量和開啟率,可以實(shí)現(xiàn)Web與App間切換保留上下文信息隶校。
最初聽到Deeplink是在魔窗的宣講會漏益,那時一臉懵,回來仔細(xì)了解了下惠况,相比推送跳轉(zhuǎn)的方式(現(xiàn)在很多用戶都是直接關(guān)閉推送的)遭庶,通過分享邀請和短信喚醒,對運(yùn)營拉新稠屠、拉活峦睡、留存、轉(zhuǎn)化权埠,提供了更多幫助榨了。
技術(shù)要求:
- APP要想被其他APP直接打開,自身得支持攘蔽,讓自己具備被人打開的能力龙屉。(URL Schemes)
- APP要想打開其他的APP,自身也得支持满俗。(判斷設(shè)備是否安裝转捕、各種跳轉(zhuǎn)的處理)
iOS 9以上的用戶,可以通過
Universal Links
通用鏈接無縫的重定向到一個App應(yīng)用唆垃,而不需要通過Safari打開跳轉(zhuǎn)五芝。
如果用戶沒有安裝這個app,則會在Safari中打開這個鏈接指向的網(wǎng)頁辕万。
喚醒方法為:- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray * _Nullable))restorationHandler;
栗子??(關(guān)于Universal Links的具體配置建議查看官方文檔 蘋果有個網(wǎng)址可以測試apple-app-site-association是否有效測試地址)
- (BOOL)application:(UIApplication *)application continueUserActivity:(NSUserActivity *)userActivity restorationHandler:(void (^)(NSArray *restorableObjects))restorationHandler{
if ([userActivity.activityType isEqualToString:NSUserActivityTypeBrowsingWeb]) {
NSURL *webUrl = userActivity.webpageURL;
if ([webUrl.host isEqualToString:@"com.example.ios"]) {
//打開對應(yīng)頁面
// NSString *paramStr = [[webUrl query] substringFromIndex:[[webUrl query] rangeOfString:@"?"].location+1];
NSDictionary *paramDic = [[webUrl absoluteString] getURLParameters];
NSLog(@"paramDic: %@",paramDic);
//跳轉(zhuǎn)本地ViewController
} else {
//不能識別枢步,safari打開
[[UIApplication sharedApplication] openURL:webUrl];
}
}
return YES;
}
// NSString+UrlEncoding.h
/**
* 截取URL中的參數(shù)
*
* @return NSDictionary parameters
*/
- (NSDictionary *)getURLParameters {
// 查找參數(shù)
NSRange range = [self rangeOfString:@"?"];
if (range.location == NSNotFound) {
return nil;
}
NSMutableDictionary *params = [NSMutableDictionary dictionary];
// 截取參數(shù)
NSString *parametersString = [self substringFromIndex:range.location + 1];
// 判斷參數(shù)是單個參數(shù)還是多個參數(shù)
if ([parametersString containsString:@"&"]) {
// 多個參數(shù),分割參數(shù)
NSArray *urlComponents = [parametersString componentsSeparatedByString:@"&"];
for (NSString *keyValuePair in urlComponents) {
// 生成Key/Value
NSArray *pairComponents = [keyValuePair componentsSeparatedByString:@"="];
NSString *key = [pairComponents.firstObject stringByRemovingPercentEncoding];
NSString *value = [pairComponents.lastObject stringByRemovingPercentEncoding];
// Key不能為nil
if (key == nil || value == nil) {
continue;
}
id existValue = [params valueForKey:key];
if (existValue != nil) {
// 已存在的值渐尿,生成數(shù)組
if ([existValue isKindOfClass:[NSArray class]]) {
// 已存在的值生成數(shù)組
NSMutableArray *items = [NSMutableArray arrayWithArray:existValue];
[items addObject:value];
[params setValue:items forKey:key];
} else {
// 非數(shù)組
[params setValue:@[existValue, value] forKey:key];
}
} else {
// 設(shè)置值
[params setValue:value forKey:key];
}
}
} else {
// 單個參數(shù)
// 生成Key/Value
NSArray *pairComponents = [parametersString componentsSeparatedByString:@"="];
// 只有一個參數(shù)醉途,沒有值
if (pairComponents.count == 1) {
return nil;
}
// 分隔值
NSString *key = [pairComponents.firstObject stringByRemovingPercentEncoding];
NSString *value = [pairComponents.lastObject stringByRemovingPercentEncoding];
// Key不能為nil
if (key == nil || value == nil) {
return nil;
}
// 設(shè)置值
[params setValue:value forKey:key];
}
return [NSDictionary dictionaryWithDictionary:params];
}
測試方法:在短信或備忘錄中輸入相應(yīng)域名,若能跳轉(zhuǎn)到相應(yīng)App即植入成功砖茸。直接在Safari中輸入鏈接是無效的隘擎,必須從一處跳入才可以(比如上一級網(wǎng)頁)。
Deferred Deeplink(延遲深度鏈接)
Deeplink只針對手機(jī)中已經(jīng)安裝過App的用戶才有用凉夯。而升級版本的Deferred Deeplink卻可以解決這個問題:
Deferred Deeplink 可以先判斷用戶是否已經(jīng)安裝了App應(yīng)用嵌屎,如果沒有則先引導(dǎo)至App應(yīng)用商店中下載App推正, 在用戶安裝App后跳轉(zhuǎn)到指定App頁面 Deeplink 中。
Deferred Deeplink的應(yīng)用場景:
- 追蹤廣告效果
- 追蹤用戶推薦/邀請鏈接
- 在 app 內(nèi)保持網(wǎng)頁瀏覽的上下文宝惰,如登錄信息植榕,購物車等
App分享邀請好友,好友通過鏈接(只有通過此鏈接跳轉(zhuǎn)到App Store下載App才算有效)安裝App之后雙方獲得獎勵尼夺,省去了過去注冊輸入邀請碼這一步尊残。