iOS感測器:App前後臺切換後,獲取敏感資訊使用touch ID進行校驗

非典型技術宅發表於2019-02-26

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

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,所以,請真心的不要忘記了。

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將很大程度上不受我們控制,這個數值極有可能會被釋放掉。那怎麼辦?

所以我們要把這個時間存放在其他地方。資料持久化的幾種方法還記得嗎?

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 {
//            不支援距離感測器
        }
複製程式碼

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

啦啦啦啦。下一個系列,多執行緒。嗯。


iOS感測器系列之一:加速感測器

iOS感測器系列之二:陀螺儀

iOS感測器系列之三:磁力計

iOS感測器系列之四:指紋感測器&距離感測器

相關文章