iOS傳感器:App前后臺切換后,獲取敏感信息使用touch ID進行校驗

今天咱們主要是說指紋識別傳感器,在文章的最后也會順帶說一下距離傳感器。

Touch ID是蘋果公司的一種指紋識別技術。Touch ID不存儲用戶的任何指紋圖像,只保存代表指紋的數字字符。iPhone 的處理器采用了新的高級安全架構,其中有一塊名為Secure Enclave的區域用以專門保護密碼和指紋數據。只有Secure Enclave可以訪問指紋數據,而且它還把這些數據同處理器和系統隔開,因而這些永遠不會被存儲在蘋果的服務器上,也不會被同步到iCloud或其他地方。除了Touch ID之外,它們不會被匹配到其他指紋庫中。

也就是說,每個Touch ID組件只與一個處理器匹配。對于重視安全性的用戶來說,這個發現當然是個好消息。不過這讓iPhone的維修更為復雜,假如你的Touch ID不小心壞了,或者拆屏幕的時候不小心碰斷了Touch ID的某根線纜,或許你就再也無法在你的手機上使用指紋識別功能了。

以下視頻截圖來自重案組第四季第四集,看上有點玄乎呀。

重案組S4.png
重案組S4.png
重案組S4.png

今天咱們要實現的一個案例需求就是:

  1. 使用touch ID進行指紋識別
  2. 指紋識別錯誤之后,可以使用apple ID的密碼進行驗證
  3. APP進入到后臺,10秒之內切回到前臺,不做二次驗證。
  4. APP進入到后臺,超過10秒切回到前臺,再次進行指紋驗證。

1. 指紋識別傳感器的用法介紹

上面聽完介紹,感覺好像屌屌的有沒有?很高深,可是iOS封裝的已經非常完善了。我們只需要簡單的幾個步驟就可以利用好手機最下面這個圓圓的指紋傳感器了。

蘋果在iOS8.0以后開放的TouchID接口,是包含在LocalAuthentication這個框架里面。我們需要引入頭文件。

今天本文都是以Swfit為案例,OC的同學可以進行參考。思路一模一樣,語法也幾乎一模一樣。

插一個私信里面的問題,挺具有代表性的。

宅胖你為什么可以又可以寫Swift又可以寫OC?Swift難嗎?
1,我感覺現在會寫Swift的同學基本上都是會寫OC的。
2,Swift用了之后,當真會覺得OC麻煩很多,各種層面的麻煩。
3,我所寫的這些所有的例子里面其實真正用到Swift特性的很少,絕大部分情況下都只是簡單翻譯了一下OC。
4,Swift難嗎?你看到了,基本語法幾乎和OC一模一樣。只不過OC很多都是NS開頭,Swift把它去掉了。
別害怕,快上車??纯磁判邪?,使用Swift的開發者數量正在穩定的上升。

好,回到今天的主題。使用指紋傳感器,一樣需要典型的幾步:

  1. 導入頭文件LocalAuthentication
  2. 判斷版本號,必須在8.0以上
  3. 創建LAContext對象,開始驗證

好了,就結束了。就這么簡單,下面我們就幾個重點部分分享一下代碼。

然后,敲黑板?。?!真正應用開發中中,幾乎沒人只是驗證一下touch ID,就不干別的了。驗證識別指紋,肯定是為了下一步的業務流程做服務。

既然是這樣,驗證的結果肯定直接影響到下一步的業務流程,同時也極大的影響了界面的展示。必然會影響到好幾個控制器或者好幾個View,極有可能是一對多的關系

一對多,聽上去好耳熟。是不是要暗示點什么?對了。通知,通知,通知,通知。嗯。這個不是這篇文章的重點。別忘記了通知。

因為會影響到好幾個控制器或者好幾個View,所以,請真心的不要忘記了咱們前面分享過的四大對象之UIApplication對象、 iOS四大對象之AppDelegate及UIApplicationMain函數/程序啟動過程。 一定要好好看看呀。

2. Touch ID指紋識別的代碼實現

  • 第一步:導入頭文件;
  • 第二步:判斷系統是否高于iOS 8.0 。下面會單獨有一章來介紹四種方法,花樣判斷。啦啦啦啦啦。
  • 第三步:創建LAContext。這個就是LocalAuthentication暴露出來,讓開發者使用的類。
  • 第四步:檢查Touch ID是否可用。
    不是判斷了系統就好了嘛?當然不是啊。還有很多種情況下,Touch ID是不好用的。模擬器不可以使用,被替換了Touch ID,老手機木有這個硬件啦,等等。
  • 第五步:進行識別。
    只要識別,就有成功和不成功對不?所以我們還要根據結果進行下一步操作。
    成功:
    要回到主線程刷新UI,進行成功后的業務流程。
    不成功:
    根據返回的錯誤碼,分析錯誤的原因。

因為多線程咱們說好了是下一個系列要分享的內容,所以這次關于線程的地方我就用偽代碼替代了。

image.png
image.png
let laContext = LAContext()

//localizedFallbackTitle:驗證TouchID時彈出Alert的輸入密碼按鈕的標題
//ocalizedCancelTitle可以設置驗證TouchID時彈出Alert的取消按鈕的標題(iOS10才有)
laContext.localizedFallbackTitle = "手氣不好,輸入密碼吧"
laContext.localizedCancelTitle = "點錯了,取消取消"

var requestError: NSError? = nil
//        檢查Touch ID是否可用
if laContext.canEvaluatePolicy(.deviceOwnerAuthentication, error: &requestError) {
    print("Touch ID可以使用,開始驗證")
    
    laContext.evaluatePolicy(.deviceOwnerAuthentication, localizedReason: "需要驗證您的指紋來確認您的身份信息", reply: { (success, error) in
        
        if success {
            print("Successful,驗證成功")
            //回主線程刷新UI
            OperationQueue.main.addOperation {
                self.successToInterface()
            }
            
        } else {
            print("Sorry,error = \(String(describing: error))")
            if let error1 = (error as NSError?) {
                switch error1.code {
                case LAError.userCancel.rawValue:
                    print("User Cancel")
                case LAError.userFallback.rawValue:
                    print("Wrong touch ID")
                case LAError.systemCancel.rawValue:
                    print("System Cancel")
                default:
                    break;
                }
            }
            self.successView?.removeFromSuperview()
        }
    })
} else {
    print("模擬器上不能使用,或者其他原因導致touchID不可使用");
}

3. 判斷系統版本號的幾種方法

3.1 系統預留的快速通道 ,推薦使用

if #available(iOS 8.0, *) {
    //系統版本高于8.0
} else {
    //系統版本低于8.0
}

3.2 通過UIDevice獲取版本號,不推薦

//        獲取當前字符串類型的版本號信息,最不推薦的一種方法
        let sysVersionString = UIDevice.current.systemVersion

3.3 通過ProcessInfo,判斷是否高于指定的版本號

//        獲取當前系統版本號。majorVersion:主版本號;minorVersion:次版本號;patchVersion:最后一位小版本號
       let systemVersion =  OperatingSystemVersion(majorVersion: 8, minorVersion: 0, patchVersion: 0)
        
        if ProcessInfo.processInfo.isOperatingSystemAtLeast(systemVersion) {
            //系統版本高于8.0
        } else {
            //系統版本低于8.0
        }

3.4 通過系統給定的Double類型版本號進行判斷

//        通過系統給定的Double類型版本號進行判斷
        if NSFoundationVersionNumber >= NSFoundationVersionNumber_iOS_8_0 {
            //系統版本高于8.0
        } else {
            //系統版本低于8.0
        }

4. App從后臺到前臺,從前臺到后臺的動作

指紋驗證是已經做完了。但是,咱們需求里面是不是還有兩條沒實現?

APP進入到后臺,10秒之內切回到前臺,不做二次驗證。
APP進入到后臺,超過10秒切回到前臺,再次進行指紋驗證

接下來我們就要在AppDelegate.swift做文章了。

UIApplicationDelegate有很多方法,我們只說一些跟這次相關的方法。

4.1 App被加載到內存后首次并且唯一次調用的方法

@available(iOS 3.0, *)
optional public func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey : Any]? = nil) -> Bool

程序被加載到內存,完成啟動,application對象會自動調用delegate的上面這個方法,證明程序已經啟動完成。這個方法是首先會被application回調的方法,且這個方法在整個程序的生命周期中只會被調用一次。

如果是手動創建根控制器就要在這里寫點神馬了,但是這次咱們就是使用最原始的加載,所以這里什么也不用寫。

4.2 App已經進入到后臺會被調用的方法

@available(iOS 4.0, *)
optional public func applicationDidEnterBackground(_ application: UIApplication)

在調用這個方法之前,還會被調用那個叫做WillResignActive,我們這次不會用到。那個方法是告訴我們,程序將要失去焦點,也就是失去控制。

緊接著,就會調用這個DidEnterBackground方法。在這個方法里面,我們需要記錄一下當前時間。好到時候判斷是不是超過了10秒鐘。

可是這個地方我們并不能直接賦值到App里面的某個屬性里面,進入后臺后,App將很大程度上不受我們控制,這個數值極有可能會被釋放掉。那怎么辦?

所以我們要把這個時間存放在其他地方。數據持久化的幾種方法還記得嗎?不記得啦?傳送門:《iOS使用沙盒進行數據持久化》

func applicationDidEnterBackground(_ application: UIApplication) {
    enterBackgroundDate = Date()
    UserDefaults.standard.set(enterBackgroundDate, forKey: "enterBackgroundDate")
    print("進入后臺,時間:\(String(describing: enterBackgroundDate))")
    
}

我們在控制臺打印一下,方便調試和看到結果。

4.3 App進入到前臺會被調用的方法

@available(iOS 4.0, *)
optional public func applicationWillEnterForeground(_ application: UIApplication)

無論通過什么途徑進入到前臺,都會調用這個方法。什么叫做無論什么途徑? 當然啦,我們回到App有各種情況啊,例如點桌面的應用圖標進來了,雙擊Home鍵從后臺切換回來的。

在這個里面咱們要干幾件事情:

  1. 把剛才持久化存儲的進入后臺的時間取出來
  2. 獲取當前時間
  3. 比較兩個時間是不是相差超過10秒鐘,選擇執行相應的操作。
    比10秒鐘長:重新進行指紋驗證
    短語10秒:直接進入

這里需要注意,不管是什么結果,可能都會存在需要修改若干控制器和View。所以建議如果是這種一對多的情況下,最好使用通知,告訴大家判斷的結果。另外,刷新UI請回到UI線程中。

func applicationWillEnterForeground(_ application: UIApplication) { 
    print("即將進入前臺")
    let backgroundTime = UserDefaults.standard.value(forKey: "enterBackgroundDate")
    let currentDate = Date()
    
    print("enterBackgroundDate: \(String(describing: backgroundTime)), currentDate : \(currentDate)")
    
    let timeInterval = (backgroundTime as! Date).addingTimeInterval(10)
    
    let result = timeInterval.compare(currentDate)
    if result == .orderedAscending {
        homeVC.checkTouchID()
    } else {
        print("進入后臺不足10秒,不需要驗證")
    }
    
}

5. 距離傳感器

我們在打電話的時候,當屏幕靠近自己的大臉( ̄ε(# ̄)☆╰╮( ̄▽ ̄///) ,屏幕就會關閉了。當遠離障礙物的時候,屏幕就又亮了。這其實就用到了距離傳感器。

要想實現距離傳感器很簡單,很簡單就能讓App支持檢測是否有物體靠近了屏幕。但是并不是所有的 iOS 設備都支持,所以使用前和其他傳感器一樣,我們依然需要判斷一下設備是否支持。

        //判斷當前設備是否支持距離傳感器
        if UIDevice.current.isProximityMonitoringEnabled {
//            設備支持距離傳感器
            NotificationCenter.default.addObserver(self, selector: #selector(xxxxx), name: NSNotification.Name.UIDeviceProximityStateDidChange, object: nil)

//xxxxx 就是當靠近物體的時候需要執行的方法            
        } else {
//            不支持距離傳感器
        }

今天的分享就到這里啦。代碼實在太少了,就不上傳了。好不好?


俺又回去看了看這個系列第一天的時候立下的宏愿,哈哈,要寫八篇。加速傳感器、陀螺儀、磁力計、指紋識別傳感器、距離傳感器、好玩的綜合例子,這六個都搞完了。

最后就剩下藍牙了。目前還沒有想好定位是啥,要不要分享BLE的iBeacon在室內定位上的應用。所以計劃就改了,宅胖就是這么善變隨機應變的人。

啦啦啦啦。下一個系列,多線程。嗯。


iOS傳感器系列之一:加速傳感器
iOS傳感器系列之二:陀螺儀
iOS傳感器系列之三:磁力計
iOS傳感器系列之四:指紋傳感器&距離傳感器

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容