iOS11中使用CoreNFC

在2017的WWDC,苹果终于发布了众多开发者期待已久的系统级框架 CoreNFC。可能你对于NFC是什么并不是很了解,简而言之,NFC(近场通信)就是当两台硬件设备相距4cm以内时可以实现互相通信。NFC在商业上的应用是把NFC芯片集成到各类卡片中,极大的加强安全性。目前 CoreNFC 只支持一种格式:NFC Data Exchange Format,简称NDEF(常被用于平板电脑和智能手机中)。

提示:接下来的教程你需要Xcode9 beta,同时你也需要一部iPhone7或者iPhone 7 Plus运行iOS 11系统来验证本教程中的一些新特性。Xcode9 beta可以同时支持Swift 3.2Swift 4.0,本教程的代码都将使用Swift 4.0来编写。只是在项目中导入CoreNFC是无法构建或者编译一个App的,必须在物理设备上才可以运行。同时你还需要一个已经付费的开发者账号才可以。

什么是CoreNFC

通过NFC,你可以阅读到包括NFC数据交换格式(NDEF)数据中的1到5类的近场通讯标签。读取标签,你的App创建了一个NFC数据交换格式reader session并且提供了一个 delegate 。正在运行的reader session 轮询 NFC 标签,并在 delegate 方法中找到包含 NDEF 消息的标签时传递消息,并将该消息传递给 delegatedelegate可以读取消息并处理异常。

NFC谈不上“新”技术。苹果终于向开发者开放了这个 API,以便我们可以利用 NFC 识别信息。

举一个实际的例子
你经营一家商店,希望客户进入后,使用有NFC功能的手机扫描一件商品,就直接完成了所有的流程。没有任何麻烦,没有等待时间。作为一名App开发人员,在不使用NFC的情况下,还可以选择条形码二维码,但依然比NFC麻烦很多。

说来说去NFC到底是什么?苹果虽然放开了NFC的使用权限,但苹果公司严格限制了我们的访问。这意味着CoreNFC只支持前面提到的NDEF格式。如果您打算使用CoreNFC替换您的`RFID卡,恐怕还有待时间。

准备工作

我们将用一个 demo 来展示如何使用 CoreNFC 。我们的应用程序将读取存储在 NDEF格式 卡上的信息。

为此,我使用 Arduino UnoAdafruit PN532 Shield 配对,将消息编程到 N​​DEF 格式的样品卡上。如果您没有这些工具,或者根本不想将时间和金钱投入到这样的硬件中,请尝试找到一张带有消息的预格式化卡。在本教程中,我将不会将 NFC 格式化或将消息嵌入到 NDEF 卡中。

我们开始吧

要创建我们的项目,请打开 Xcode 9 并创建一个新的 single-view application 。然后命名您的项目,并确保选择 Swift 作为您的语言。

设计消息视图

为达到目的,我们需要制作用户界面(UI),供用户进行交互。我们先创建一个导航控制器。点击 Main.storyboard 并选择View-Controller。然后,转到status-Bar,然后单击Editor>embed in>Navigation Controller。这会在View-Controller的顶部创建一个导航栏。您可以选择一个合适的标题。我起的标题为 Message in a Bottle

接下来,拖动UIButton并将其放在 View-Controller 的底部。将按钮的文本更改为扫描,并根据需要进行初始化。添加扫描按钮后,利用UILabel添加背景标题。

现在我们的应用应该是这样的:

设置扫描和消息

现在我们已经做好了基础设置,接下来我们将为按钮标签通过拖线设置属性,并为按钮设置点击行为。

代码如下:

import UIKit

class ViewController: UIViewController {

    @IBOutlet weak var messageLabel: UILabel!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    @IBAction func scanPressed(_ sender: Any) {
        // this is our newly created IBAction
    }

}

设置应用程序权限和隐私

接下来,在我们真正开始研究我们的 NFC 实现之前,我们需要设置我们的应用权限。

注意:您必须非常仔细地关注此部分,否则您的应用配置将无法正常工作。此外,您将需要付费的开发者帐户。


App ID

首先,请转到 developer.apple.com 登录您的帐户,一旦您进入 account 面板,请转到 Certificates , Identifiers & Profiles 标签页。在Identifiers下,单击App IDs。然后点击 (+)注册一个新的App IDApp ID说明应简单(例如NFC)。
填入 Explicit App IDBundle ID 。 必须与您在 Xcode 项目中使用的Bundle ID 完全一致,就是com.YOURDOMAIN.Message-in-a-Bottle。一旦你把你的Bundle ID放入,向下滚动并检测服务列表。点击下一步,确保您的确认页面与我的相似:

Provisioning Profiles

完成设置后,我们需要为此应用程序创建一个新的配置文件。转到 Provisioning Profiles 选项卡,然后单击 all。然后,单击(+)创建一个新的配置文件。选择iOS Development,继续选择 App ID的名称(我的是NFC),继续选择您使用的证书,添加想要测试此应用程序的任何手机。命名新建的配置文件,我们就生成了一个新的有效的配置文件

此处解释一下什么是 配置文件配置文件 要验证在所选设备上运行的特定的应用。这样,您可以确认在设备上运行的应用程序可追溯出处并确保安全。
所以我们也需要为我们的 App 选择特定的 配置文件 。为此,请返回 Xcode > build setting > 禁用 Automatically manage signing 。对于 DebugRelease,选择下拉菜单并选择 Download profile。找到对应的相关配置文件,前期准备工作就完成了!

App Entitlements

然而,Xcode 团队尚未启用 CoreNFC 的自动授权。现在,下载此预构建的授权文件,并将文件位置放在 Project> build setting > Code Signing Entitlement 的文本框中。

我们预计苹果将在不久的将来更新此功能,但现在这个步骤不可避免。所以如果发布了一个新的测试版本,可以回到这个教程重新测试。

App Privacy

打开 Info.plist 并右键单击以添加一行。在 Key 列中,打开下拉菜单并选择 Privacy - NFC Scan Usage Description。在 value 中设置自定义的提示信息。我们对 plist 文件的更新允许我们的 App 获取访问 NFC 的必要权限。

实现CoreNFC

接下来让我们来看看有趣的部分!我们将继续向 ViewController.swift 添加几行代码。但在我们写代码之前,需要提一下一个让我调试了几个小时的问题。

目前 CoreNFC 框架尚未编译进 iOS 模拟器。这意味着如果你尝试import CoreNFC 你会得到一个错误,说没有 CoreNFC 模块。简单的修复方法就是主动选择您的 iPhone 或 通用设备。


import UIKit
import CoreNFC 

class ViewController: UIViewController, NFCNDEFReaderSessionDelegate { 

    @IBOutlet weak var messageLabel: UILabel!
    var nfcSession: NFCNDEFReaderSession?

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view, typically from a nib.
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }

    @IBAction func scanPressed(_ sender: Any) {

    }

    func readerSession(_ session: NFCNDEFReaderSession, didInvalidateWithError error: Error) {
        print("The session was invalidated: \(error.localizedDescription)")
    }

    func readerSession(_ session: NFCNDEFReaderSession, didDetectNDEFs messages: [NFCNDEFMessage]) {
        // Parse the card's information
    }

我们只需要再添加两行代码就可以启动 NFC reader 。更新我们之前创建的 scanPressed 方法来调用NFCNDEFReaderSession初始化器。

@IBAction func scanPressed(_ sender: Any) {

    nfcSession = NFCNDEFReaderSession.init(delegate: self, queue: nil, invalidateAfterFirstRead: true)
    nfcSession?.begin()
}

如果您的程序在运行时出现 Session is invalidated unexpectedly 错误,请返回并再次检查 设置权限和隐私 部分。

解析消息记录

首先,我们来看看 func readerSession(_ session: NFCNDEFReaderSession, didDetectNDEFs messages: [NFCNDEFMessage])

我们可以通过打印messages 的第一个元素来认识它。

( 
    // Payload one (There's only one payload in this card)
    "TNF=1, /* Type Name Format */
    Payload Type=<55>,
    Payload ID=<>,
    Payload=<0048656c 6c6f21>" /* What we're really interested in */
)
`messages` 是一个 存储`NFCNDEFMessages` 数据格式数组,在 `NFC` 会话无效之前,我们执行的每次扫描都有一个数组,而在我们扫描后,会话会自动失效。我们只需要关注数组中的一个对象。

messages[0] 是一个 NFCNDEFMessage,它包含一个 NFCNDEFPayload
messages[0].records 是一个 NFCNDEFPayload 数组,因为 NDEF卡 可以包含多个 payLoad

单一 NFCNDEFPayload 包含4项信息:

  1. identifier
  2. type
  3. typeNameFormat
  4. payload是一个 Data 类型的对象
func readerSession(_ session: NFCNDEFReaderSession, didDetectNDEFs messages: [NFCNDEFMessage]) {
    var result = ""
    for payload in messages[0].records {
        result += String.init(data: payload.payload.advanced(by: 3), encoding: .utf8)! // 1
    }

    DispatchQueue.main.async {
        self.messageLabel.text = result
    }

此处有几个问题。为什么要前进3?DispatchQueue的内容是什么?

为什么 NFC 标签总是以 enHelloenMessage 开头。在对 NDEF 规范和法规进行了一些研究后发现:

所有语言代码必须根据 RFC 3066 完成。语言代码不能省略。语言代码长度被编码在状态字节的六个最低有效位中。因此,通过使用值 0x3F 屏蔽状态字节很容易找到。

第3行将 payloaddata 类型转换为可读的 string 字符串。”

关于这个问题 DispatchQueue,在readerSessionmessageLabel 是不可访问的。所以我们要返回主线程,给 messageLabel 赋值,这也是线程间通信的一个最简单的方法。

这是最终的样式:

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容