iOS传感器:App前后台切换后,获取敏感信息使用touch ID进行校验

今天咱们要实现的一个案例需求就是:

  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的开发者数量正在稳定的上升。

好,回到今天的主题。使用指纹传感器,一样需要典型的几步:

导入头文件LocalAuthentication 判断版本号,必须在8.0以上 创建LAContext对象,开始验证

好了,就结束了。就这么简单,下面我们就几个重点部分分享一下代码。 然后,敲黑板!!!真正应用开发中中,几乎没人只是验证一下touch ID,就不干别的了。验证识别指纹,肯定是为了下一步的业务流程做服务。 既然是这样,验证的结果肯定直接影响到下一步的业务流程,同时也极大的影响了界面的展示。必然会影响到好几个控制器或者好几个View,极有可能是一对多的关系。 一对多,听上去好耳熟。是不是要暗示点什么?对了。通知,通知,通知,通知。嗯。这个不是这篇文章的重点。别忘记了通知。 因为会影响到好几个控制器或者好几个View,所以,请真心的不要忘记了。

2. Touch ID指纹识别的代码实现

第一步:导入头文件; 第二步:判断系统是否高于iOS 8.0 。下面会单独有一章来介绍四种方法,花样判断。啦啦啦啦啦。 第三步:创建LAContext。这个就是LocalAuthentication暴露出来,让开发者使用的类。 第四步:检查Touch ID是否可用。 不是判断了系统就好了嘛?当然不是啊。还有很多种情况下,Touch ID是不好用的。模拟器不可以使用,被替换了Touch ID,老手机木有这个硬件啦,等等。 第五步:进行识别。 只要识别,就有成功和不成功对不?所以我们还要根据结果进行下一步操作。 成功: 要回到主线程刷新UI,进行成功后的业务流程。 不成功: 根据返回的错误码,分析错误的原因。

因为多线程咱们说好了是下一个系列要分享的内容,所以这次关于线程的地方我就用伪代码替代了。

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 {
//            不支持距离传感器
        }

Last updated