iOS6新特征:PassKit编程指南

第一章 介绍

 1.1.关于Pass Kit

Pass(通行证)是用数字化表示的信息,这些信息可能需要被打印在小纸片或者塑料上。在现实世界中,pass让用户像使用登机牌、会员卡、优惠卷一样的方式在使用。pass库包含了用户的通行证。用户可以使用Passbook应用程序来浏览和管理他们的通行证。

 

【注:便于理解,下面红色部分是译者根据下图表达的意思,增加进去的】

从下图,我们可以看出Pass(通行证)使用的步骤:

1、 服务器端创建一个pass,客户端可以通过email,url或者普通的应用程序进行安装

2、 一段时间过后,服务器端可能由于某种原因(例如登机牌的登机时间改变),把pass更新了,这是服务器端通过苹果的APNS,把消息推送给客户端

3、 客户端收到推送消息后,请求服务器,以查询详细信息

4、 服务器端把pass改变的详细信息返回给客户端

最后,用户可以在POS终端上显示pass,以进行消费

 




1.1.1.概述

 

本文档包含了Pass Kit技术的关键概念,以及解释了使用Pass Kit的方法。

 

1.1.2.创建和发布Pass(通行证)

创建一个pass的方法:通过编写一个pass.json文件,并提供适当的图片和本地化数据。所有的这些文件都被放在一个被签名和压缩过的pass包里面。

pass的发布可以通过电子邮件,网站或者你的应用程序。

 

 

1.1.3.更新Pass(通行证)

通过使用推送通知和web service进行pass的更新。当用户的设备收到了一条关于pass已经更新了的推送通知,那么设备就可以通过查询你的服务器来获取已经更新了的pass的最新版本。

 

 

 

1.1.4.与用户的Pass(通行证)进行交互

 

你的应用程序可以通过使用Objective-C API 进行pass(通行证)的添加,更新和移除,来给用户提供更丰富的体验。

 

 

第二章 创建一个pass(通行证)

Pass(通行证)被创建为一个包,这个包包含如下几个部分:

n pass.json文件:定义了pass(通行证)。在创建pass时,该文件需要做的工作内容最多。

n 图片:例如你的logo和通行证icon。

n 本地化本地化工程(.lproj文件夹):包含了字符串文件和本地化图片。

n manifest.json文件:包含了在包中每个文件的哈希。

n 签名文件:包含了所有文件的签名哈希,用于创建pass的源。


2.1. 设置开发环境

 

为了创建和发布pass(通行证),你需要一个pass类型标示符和签名证书。

 

前往开发者门户网站,请求一个pass类型标示符和证书。这个证书用在“构建和发布一个pass(通行证)”,以进行pass签名,并用于在“更新一个pass(通行证)”,以通过推送通知进行pass更新。


2.2.Pass.json文件的顶层

 

pass.json文件是一个JSON字典,列表1-1显示了pass的一部分:

 

列表1-1 pass的顶层key

{

"passTypeIdentifier" :"pass.com.example.myExamplePass",

"formatVersion" : 1,

"organizationName" : "Example Company",

"serialNumber" : "123456",

"teamIdentifier" : "A1B2C3D4E5",

"description" : "What this pass is called",

...

}


pass type identifier、teamidentifier和organization name与开发者门户网站设置的内容相匹配。pass type identifier与bundle identifier或类名是相同的概念。它定义了pass的类或者类别。例如,5%或10%折扣的优惠券应该使用相同的pass type identifier,但是礼品卡应该使用与优惠券不同的pass type identifier。准确的来说,每一个pass type identifier是有你和你的程序决定的。在应用程序的Entitlements File内部可以指定允许与应用程序发生交互的pass类型;这样做了之后,在合适的时候也允许你在应用程序之间创建一定数量的分离。

serial number 是唯一标示pass的一个字符串。可以是单调递增的整数,或者是通过很方便的方法赋予的一串序号,但是,你也可以使用任意方法,来赋予它你认为有意义的内容。在PassKit 框架中,serial number被认为是不透明的数据。如果两个pass具有相同的pass typeidentifier和serial number,这这两个pass会被认为是同一个pass------在pass中其它任意数据都可以通过更新pass的方式进行改变。

iOSaccessibility把description用来描述pass。Description值是由PKPass的方法localizedDescription返回的。



2.3.设置Pass(通行证)的风格

不同类别的pass显示不同的视觉风格,因此你需要提供不同的域(field)。这与你自己定义的pass type identifier是不同的,你只能从固定的列表中选择pass风格,这是由API决定的。你指定使用哪种风格的pass,这种风格的key在Pass Kit包格式参考的“Style-Specific Dictionary Keys”中有列出。key的值是一个字典,它包含的域指定了pass的风格。除了登机牌需要一个transitTpyekey外,这些pass的风格的字典内容大多数都是相同的。例如,列表1-2显示的登机牌pass,是从San Francisco至London的航班。

 

列表1-2 登机牌pass的格式域

 

{

"passTypeIdentifier" :"pass.com.example.myExamplePass",

"formatVersion" : 1,

"serialNumber" : "123456",

"description" : "What this pass is called",

"boardingPass" : {

"primaryFields" : [

{

"key" : "origin",

"label" : "SanFrancisco",

"value" : "SFO"

},

{

"key" :"destination",

"label" : "London",

                   "value" : "LHR"

}

],

"secondaryFields" : [

{

"key" :"board-gate",

"changeMessage" : "Gatechanged to %@.",

"label" : "Gate",

"value" : "F12"

},

{

"key" :"board-time",

"changeMessage" :"Boarding time changed to %@.",

"dateStyle" :"PKDateStyleFull",

"timeStyle" :"PKTimeStyleFull",

"label" : "Boards",

"value" : "2012-04-01T0700-8"

},

],

"auxilaryFields" : [

{

"key" : "seat",

"label" : "Seat",

"value" : "7A"

},

{

"key" :"passenger-name",

"label" :"Passenger",

"value" : "JohnAppleseed"

}

],

"backFields" : [

{

"key" :"freq-flier-num",

"label" : "Frequentflier number",

"value" :"1234-5678"

},

]

"transitType" : "PKTransitTypeAir"

}

}



2.4.声明Pass(通行证)的域

 

pass的域包含的信息用于以特定的方式显示给用户。pass里的大多数信息都是域的一部分。一些域的例子:一个事件的名称和场所,某航班登机牌的出发时间,以及礼品卡的当前余额。一些域总是需要用到,一些则需要指出pass风格,而有些则是可选的。

 

每一个域都有一个唯一的key。Objective-CAPI提供localizedValueForFieldKey:方法来访问pass数据。

 

域包含一个value和一个label(在UI上显示),一个唯一key,以及域如何显示的相关信息。这些信息存储在域字典中。列表1-3显示了一个具有3个域的入场券:乐队名称,地点和音乐会时间。列表中的event-name域具有最简的域:只有key和value。列表中的start-time域给出了入场的开始时间和日期,这个域同时还给出了域信息如何显示,以及如果域的值有改变了,那么消息应该如何显示给用户。

当域的值改变了,通过使用changeMessage key来提供message信息。如果不提供change message,那么值会静默更新。

列表1-3 入场券示例

 

{

"passTypeIdentifier" :"pass.com.example.myExamplePass",

"formatVersion" : 1,

"serialNumber" :"123456",

"description" : "Whatthis pass is called",

...

"eventTicket" : {

"primaryFields" : {

"key":"event-name",

"value" : "The HecticGlow in concert",

},

{

"key":"venue-name",

"label" : "Venue",

"value" : "Joe\u2019sCoffee Shop",

},

{

"changeMessage" :"Concert time changed to %@.",

"dateStyle" :"PKDateStyleMedium",

"timeStyle" :"PKTimeStyleMedium",

"isRelative" : true,

"label" : "Startsin",

"key" :"start-time",

"value" :"2012-12-31T20:03Z"

}

}

}

 


2.5.格式化Pass(通行证)的域

 

带有一个数字或者日期的域能够指定自身格式化的方式。

 

为了指定一个数字的格式串,这里提供一个numberStyle key的值即可。“Pass Kit包格式参考”中的“Number Style Keys”列出了所支持的number 风格。十进制数代表了money,这里提供了currencyCode key的ISO货币代码。

为了指定日期和时间的格式串,提供dateStyle和timeStyle两个key的值即可。参考“Pass Kit包格式参考”里面的“Date Style Keys”。为了显示为相关日期,需要把isRelative key设置为true。

 

为了在某个域的值中插入一个换行,可以使用\n。

 


2.6.添加相关信息

 

为了在lock screen中,能够访问你的pass(通行证),需要提供时间和位置。这属于相关信息,使用relevantDate和locations两个key即可。这样在lockscreen进行集成,用户会更加方便和快捷的找到他们需要的pass。列表1-4显示了pass示例中的相关信息部分内容。

 

1-4 pass的相关信息

 

{

"passTypeIdentifier" :"pass.com.example.myExamplePass",

"formatVersion" : 1,

"serialNumber" :"123456",

"description" : "Whatthis pass is called",

...

"locations" : [

{"latitude" : 37.296,"longitude" : -122.038 },

{"latitude" : 37.302,"longitude" : -122.103}

{"altitude" : 67.0,"latitude" : 37.331, "longitude" : -122.029 },

],

"relevantDate" :"2010-02-05T09:00-08"

}

 

Location是一个location数组的字典,每一个location包含了经度、维度和可选的海拔高度,这里描述的位置表示用户可以在该处做一些与pass相关的动作。一个pass最多可以指定10个location。例如,一张store 卡可以存储与用户最近的store位置信息,或者存储用户访问频率最高的store位置信息。就像pass中的其它数据一样,相关信息也是可以被更新的,具体请参考“更新pass(通行证)”。例如,如果某个航班的时间改变了,你可以推送一个携带最新时间的更新,或者用户在你的网站上更新了他的个人信息,你可以更新位置,以匹配用户当前所在城市。

 

 


2.7.添加一个条形码(Barcode)

 

pass可以包含一个条形码,对于在快速又准确的给各种各样的应用程序输入信息时,这是非常方便的方法——例如,在结账时或上火车前。为了添加条形码,需要制定条形码类型和它的内容。pass可以有几种格式来显示条形码:QR,PDF417,Aztec以及纯文本。列表1-5显示了一个关于条形码所包含内容的。你必须指定message的编码。通常使用ISO 8859-1编码(也叫Latin-1),条形码扫描器和英语程序通常都支持这种编码,当然,如果你的设备支持其它的编码,你也可以使用其它的编码。

 

注意:激光扫描仪从iOS设备屏幕中读取条形码,通常识别率都不好。建议使用光条形码扫描仪来代替。所有支持的条形码格式都是二维的。

 

列表1-5 条形码信息

 

{

"passTypeIdentifier" :"pass.com.example.myExamplePass",

"formatVersion" : 1,

"serialNumber" :"123456",

"description" : "Whatthis pass is called",

...

"barcode" : {

"format" : PKBarcodeFormatQR,

"message" : "Helloworld!",

"messageEncoding" :"iso-8859-1"

}

}

 



2.8.本地化Pass(通行证)

 

为了本地化一个pass,需要为每个区域提供一个字符串文件和本地化图片,相关内容定义在Bundle Programming Guide的“Localized Resources in Bundles”里面,以及Internationalization Programming Topics的“Localizing String Resources”里面。

 

例如,下面给出的pass片段

 

{

"formatVersion" : 1,

"organizationName" :"Example Company",

...

}

 

作为人为的一个例子,这里把字符串文件本地化为倒序的英文,如下:

/* Organizationname */

"ExampleCompany" = "ynapmoC elpmaxE";

 

除了字符串文件,本地化至倒序的英语还需要在pass里面查询相关的图片,并提供一个需要用到的所有内容的倒序英文版。


第三章 构建和发布一个Pass(通行证)

在准备发布pass之前,需要对pass做如下事情:

l 创建manifest文件

l 通过加密签名manifest

l 压缩pass

3.1.制作Manifest

 

manifest是一个JSON字典,命名为manifest.json。key是pass包中的文件路径。每个key的值是那个文件内容的SHA-1哈希。例如,图2-1显示了一个pass包的目录结构,而列表2-1显示了相应的manifest:

 

图2-1 一个pass的目录结构

 


 

列表2-1 manifest文件

 

{

"background.png" :"844a6063e4192f4f4f34b2cf36996b6b06a6f355",

"background@2x.png" :"56c66001a5edb87c2b58180daa3e443dcac887e4",

"pass.json" :"a4f8506e362888755ddf744365cc3cf615e4e6b1",

"es.lproj/pass.strings" :"b698506e362888755ddf744365cc3cf615e4e6b1",

"icon.png" :"105d0f906f633c378d738477fef0d51e0ccec2d2",

"icon@2x.png" :"f5c3db953176da14d6d1c3c27de12e14119173da",

"logo.png" :"78a778accde869cea3364bb828074d7a8f0067ce",

"logo@2x.png" :"af77501cac762637bdb4545b3b758ae4b4632422",

"zh.lproj/pass.strings" :"a4f8506e362888755ddf744365cc3cf615e4e6b1",

"zh.lproj/background.png" :"2888755ddfa4f8506e36744365cc3cf615e4e6b1",

"zh.lproj/background.png@2x": "f8506e362a4888755ddf744365cc3cf615e4e6b1"

}

 

3.2.制作签名

 

签名是一个独立的PKCS#7签名清单。获取和安装证书,请看“设置开发环境”。列表2-2演示了使用openssl命令的过程,在开发过程中,你可能会需要使用到。

 

列表2-2 通过终端命令来创建签名

openssl pkcs12-in "My PassKit Cert.p12" -clcerts -nokeys -out certificate.pem

openssl pkcs12-in "My PassKit Cert.p12" -nocerts -out key.pem

# You can keepcertificate.pem and key.pem to use again later,

# or you candelete them after signing.

openssl smime-binary -sign \

-signer certificate.pem -inkey key.pem\

-in manifest.json -out signature \

-outform DER

 

创建签名是建立在由你提供的pass之上。这也提供了一个保护,即拒绝一个攻击者发布一个pass,这个pass看起来是来自你这里,由于攻击者没有使用你的pass进行签名,所以可以为你提供一个保护的机制。

 

 

 

3.3.压缩Pass

 

使用ZIP来把pass包里面的内容压缩为以.pkpass为后缀的单个文件,在发布的时候使用这个文件。例如,在终端执行下面的命令:

 

zip -rexample.pkpass -x '*.DS_Store' .../path/to/pass_package/*

 

3.4.向用户发布Pass(通行证)

 

这里有三种方法来向用户发布通行证:

l 以mail附件的形式发送给用户,用户使用手机中的Mail把pass添加到库中。

l 把pass部署在web server上,并给用户提供一个URL——例如,在网站上或email上放一个链接——使用手机中的Safari浏览器把pass添加到库中。

l 写一个与pass库交互的应用程序,章节“与用户的pass(通行证)进行交互”有描述。

 

如果是以mail附件的形式或者把pass部署在web上,需要什么MIME type为application/vnd.apple.pkpass,这样Mail或Safari才会将其当做pass来处理。



第四章 更新Pass(通行证)

更新pass是可选的,但是它可以为用户更新有意义的内容。通过更新,可以改变已经添加到用户pass库里面的某个pass的内容。例如,某个航班延误了,可以发送了一个更新通知,可以提醒用户最新的起飞时间。除了pass type identifier和serial number以外,pass中的所有内容都可以通过推送更新来改变。

为了更新pass,你需要能够发送push消息,以及设置web server(实现REST-style web service 协议)。

为了表明某个pass可以接收更新,需要在pass中添加webServiceURL和authenticationToken两个key,如列表3-1所示。authentication Token是用户设备和服务器之间的一个共享secret。它表明一个pass的更新请求是来自具有某个pass的用户,而不是第三方。关于共享secret和其他加密主题,请参考Security Overview

 

列表3-1 具有authenticationToken和webServiceURL的pass

 

{

"passTypeIdentifier" :"pass.com.example.myExamplePass",

"formatVersion" : 1,

"serialNumber" :"123456",

"description" : "Whatthis pass is called",

...

"authenticationToken" :"e45ffb6c383742ccb992f5e42d45adf",

"webServiceURL" :"https://example.com/pass-updates/"

}

 

这里有5点需要服务器做出响应。URL中的一部分是由协议定义的,告诉你的服务请求的是什么,而有一些则定义在pass中。图3-1演示了与服务器的典型交互;注意,在更新pass时,这里不仅仅是一个往返。

 

图3-1 客户端和服务器之间的交互

 


 

 

4.1.开始更新

 

当用户把一个pass添加到pass库中,并使pass可以更新,那么会有一个请求发送到你的服务。

 

deviceidentifier由设备决定,用于用户设备和服务之间的共享secret。它是一个设备的唯一标示,并用于授权一个请求。例如,所有pass的请求中,在创建请求时没有提供授权token。授权token由每个pass指定——device identifier可以满足请求的有效。

 

4.2.更新Pass(通行证)

 

为了更新pass,服务器首先需要发送一个空的push通知至设备,并并把pass type ID作为push主题。当用户设备收到push通知,它会请求已经改变了的serialnumber列表,然后请求pass的最新版本。

 

4.3.停止更新

 

当用户删除了某个pass或者取消了pass的push通知,会有一个请求发送到服务器。当服务器收到这个请求,服务器会停止发送pass相关的更新至设备上。

第五章 与用户的Pass(通行证)进行交互

Pass Kit框架提供的Objective-C API 用于与用户的pass库交互。有两类主要的应用程序使用Pass Kit框架:把pass添加到pass库的应用程序,如Mail和Sfari,以及集成了pass库的应用程序,它可以与pass库中的内容进行交互,以提供更丰富的体验。例如,FidoNet mail客户端只需要把pass添加到库中。相反,一个咖啡厅可以提供一个应用程序,让你预定一个座位,然后可以添加pass至pass库中,在结账的时候就可以使用。

PKPassLibrary类代表了pass库,而PKPass类代表了单个pass。框架同样提供了一个view controller,即PKAddPassesViewController类,用来显示某个pass并工用户将pass添加至pass库中。这些类以model-level形式供你访问pass库。你的应用程序负责展现数据。

 

 

5.1.添加Pass(通行证)至库中

 

添加pass至库中需要如下步骤:

1、  使用PKPassLibrary的类方法isPassLibraryAvailable来检测pass库是否可用。

2、  为pass创建一个PKPass实例对象,并用相关的pass数据进行初始化。

3、  使用PKPassLibrary的类方法containsPass:来检测pass是否在库中。你的应用程序可以使用该方法来检测pass,可以不用完全的从库中读取pass。

4、  如果库中没有某个pass,可使用PKAddPassesViewController类的实例让用户将其添加至库中。

 

5.2.改变Pass(通行证)

 

查找某个pass,使用PKPassLibrary类的passWithPassTypeIdentifier:serialNumber:方法。

 

要想替换某个pass,使用PKPassLibrary类的replacePassWithPass:方法。替换的时候pass的pass type identifier和serial number必须要匹配。

 从pass库中移除某个pass,使用PKPassLibrary类的removePass:方法。

 

阅读更多
想对作者说点什么?

博主推荐

换一批

没有更多推荐了,返回首页