iOS 14 新增剪切闆權限
随着 iOS 14 的釋出,剪切闆的濫用也被大家所知曉。凡是 APP 讀取剪切闆内容,系統都會在頂部彈出提醒,而且這個提醒不能夠關閉。這樣,大家在使用 APP 的過程中就能夠看到哪些 APP 使用了剪切闆。
衆所周知,淘寶 APP 在各個社交平台傳播分享是通過淘密碼的,被分享者通過複制淘密碼打開淘寶 APP 可以定位到分享的商品,完成購買。而這個步驟就是通過讀取剪切闆内容實作的。那麼,在 iOS 14 上,隻要啟動淘寶,就會出現系統提示,淘寶讀取了剪切闆内容。
淘密碼适配 iOS 14
前段時間,淘寶釋出了新版本,并公布了新的淘密碼規則:數字+淘密碼+連結。
為什麼要這樣做呢?
因為随着 iOS 14 對剪切闆隐私的保護,新增加了兩個 API
detectPatternsForPatterns:completionHandler:
detectPatternsForPatterns:inItemSet:completionHandler:
來判斷剪切闆中的内容格式,使用這兩個 API 不會觸發系統剪切闆提示,但是也拿不到剪切闆的具體内容。
是以,淘密碼的适配使用了這兩個 API,來判斷剪切闆内容是否符合淘密碼規則。
但是這兩個 API 隻是暴露了三種規則:數字
UIPasteboardDetectionPatternNumber
、連結
UIPasteboardDetectionPatternProbableWebURL
、搜尋内容
UIPasteboardDetectionPatternProbableWebSearch
。是以淘寶也隻能使用這些規則。為了保證讀取淘密碼的精确度,便使用了數字+連結兩種方式比對,降低讀取剪切闆的錯誤率。不過,最終識别出了淘密碼,讀取内容還是需要使用其他 API,仍然會出現系統提醒。
可能很多人以為淘寶這個規則是通過正規表達式實作的。其實,蘋果這兩個 API 的判斷規則是不支援正規表達式的。而且它的比對規則具體沒法知曉,比如數字的判定,一段文字開頭是數字可以識别,中間有數字就識别不出來。又比如連結的比對,正常 http:// 的連結可以識别,其他 scheme 比如 taobao:// 也可以識别,甚至 😕 都可以識别。
如果根據目前了解的資訊來看的話,淘寶應該就是這樣實作的,但是不排除其他騷操作。不過,有一說一,淘寶對于淘密碼的适配真的是費了一番功夫的。
下面是我猜測實作的代碼,測試了不同種類的密碼,發現與淘寶效果一緻。
UIPasteboard *pasteboard = [UIPasteboard generalPasteboard];
void (^checkBlock)(NSSet *set, int type) = ^(NSSet *set, int type) {
BOOL isNumber = NO, isUrl = NO;
for (NSString *s in set) {
if ([s isEqualToString:UIPasteboardDetectionPatternNumber]) {
isNumber = YES;
}
if ([s isEqualToString:UIPasteboardDetectionPatternProbableWebURL]) {
isUrl = YES;
}
if ([s isEqualToString:UIPasteboardDetectionPatternProbableWebSearch]) {
// NSLog(@"todo --- hehe");
}
}
if (isNumber && isUrl) {
dispatch_async(dispatch_get_main_queue(), ^{
NSString *s = pasteboard.string;
NSLog(@"todo -- %@", s);
if (type == 1) {
self->_label1.text = @"1";
}
else if (type == 2) {
self->_label2.text = @"2";
}
});
}
};
[pasteboard detectPatternsForPatterns:[NSSet setWithObjects:UIPasteboardDetectionPatternNumber, UIPasteboardDetectionPatternProbableWebURL, UIPasteboardDetectionPatternProbableWebSearch, nil] completionHandler:^(NSSet<UIPasteboardDetectionPattern> * _Nullable set, NSError * _Nullable error) {
checkBlock(set, 1);
}];