iOS應(yīng)用間跳轉(zhuǎn):從Open in my app聊到Deeplink

就我個人所知创千,iOS中存在3種方式可以打開(喚醒)其它手機(jī)App(除開系統(tǒng)級應(yīng)用)跛锌,分別是:

  • 第三方登錄、分享、支付煞肾、導(dǎo)航等
  • Open in my app
  • Deeplink

三種方式原理一樣,均是注冊自定義URL Schemes猎物,并處理URL請求棍现。

URL schemes.jpeg

第三方


  1. 使用第三方用戶登錄,如微信横缔,QQ铺遂,微博登錄,授權(quán)后返回用戶名和密碼
  2. 內(nèi)容分享茎刚,跳轉(zhuǎn)到分享App的對應(yīng)頁面襟锐,如分享給微信好友、分享給微信朋友圈膛锭、分享到微博
  3. 第三方支付粮坞,跳轉(zhuǎn)到第三方支付App,如支付寶支付初狰,微信支付
  4. 顯示位置莫杈、地圖導(dǎo)航,跳轉(zhuǎn)到地圖應(yīng)用奢入,如高德地圖筝闹,百度地圖等
  5. 應(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)用中打開幼苛,如下圖所示:

Open in my app

實(shí)現(xiàn)步驟

1. 修改Info.plist文件

1)在plist文件中添加URLTypes字段,指定程序的對外接口:

Info.plist

由于我之前已經(jīng)集成了社會化分享焕刮,這一步就直接跳過舶沿。

2)添加一個Documents Type字段,該字段指定與程序關(guān)聯(lián)的文件類型配并,詳情參考System-Declared Uniform Type Identifiers

CFBundleTypeExtensions指定文件類型括荡,例如pdfdoc溉旋,xls一汽,ppttxt等低滩。
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之后雙方獲得獎勵尼夺,省去了過去注冊輸入邀請碼這一步尊残。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市淤堵,隨后出現(xiàn)的幾起案子寝衫,更是在濱河造成了極大的恐慌,老刑警劉巖拐邪,帶你破解...
    沈念sama閱讀 218,755評論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件慰毅,死亡現(xiàn)場離奇詭異,居然都是意外死亡扎阶,警方通過查閱死者的電腦和手機(jī)汹胃,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來东臀,“玉大人着饥,你說我怎么就攤上這事《韪常” “怎么了宰掉?”我有些...
    開封第一講書人閱讀 165,138評論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長赁濒。 經(jīng)常有香客問我轨奄,道長,這世上最難降的妖魔是什么拒炎? 我笑而不...
    開封第一講書人閱讀 58,791評論 1 295
  • 正文 為了忘掉前任挪拟,我火速辦了婚禮,結(jié)果婚禮上枝冀,老公的妹妹穿的比我還像新娘。我一直安慰自己耘子,他們只是感情好果漾,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評論 6 392
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著谷誓,像睡著了一般绒障。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上捍歪,一...
    開封第一講書人閱讀 51,631評論 1 305
  • 那天户辱,我揣著相機(jī)與錄音鸵钝,去河邊找鬼。 笑死庐镐,一個胖子當(dāng)著我的面吹牛恩商,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播必逆,決...
    沈念sama閱讀 40,362評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼怠堪,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了名眉?” 一聲冷哼從身側(cè)響起粟矿,我...
    開封第一講書人閱讀 39,264評論 0 276
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎损拢,沒想到半個月后陌粹,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡福压,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評論 3 336
  • 正文 我和宋清朗相戀三年掏秩,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片隧膏。...
    茶點(diǎn)故事閱讀 40,040評論 1 350
  • 序言:一個原本活蹦亂跳的男人離奇死亡哗讥,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出胞枕,到底是詐尸還是另有隱情杆煞,我是刑警寧澤,帶...
    沈念sama閱讀 35,742評論 5 346
  • 正文 年R本政府宣布腐泻,位于F島的核電站决乎,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏派桩。R本人自食惡果不足惜构诚,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評論 3 330
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望铆惑。 院中可真熱鬧范嘱,春花似錦、人聲如沸员魏。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽撕阎。三九已至受裹,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背棉饶。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評論 1 270
  • 我被黑心中介騙來泰國打工厦章, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人照藻。 一個月前我還...
    沈念sama閱讀 48,247評論 3 371
  • 正文 我出身青樓袜啃,卻偏偏與公主長得像,于是被迫代替她去往敵國和親岩梳。 傳聞我的和親對象是個殘疾皇子囊骤,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評論 2 355

推薦閱讀更多精彩內(nèi)容