Xamarin 中的 PassKitPassKit in Xamarin.iOS

IOS 钱包应用允许用户在其设备上存储数字刀路。The iOS Wallet app allows users to store digital passes on their devices. 这些阶段由商家生成,并通过电子邮件、Url 或商家自己的 iOS 应用发送给客户。These passes are generated by merchants and sent to the customer via email, URLs, or through the merchant's own iOS app. 这些刀路可以表示各种因素,从电影入场券到会员卡再到 "刀路"。These passes can represent various things, from movie tickets to loyalty cards to boarding passes. PassKit 框架允许开发人员以编程方式与传递交互。The PassKit framework allows developers to interact with passes programmatically.

本文档介绍钱包并将 PassKit API 与 Xamarin 配合使用。This document introduces Wallet and using the PassKit API with Xamarin.iOS.

要求Requirements

本文档中讨论的 PassKit 功能需要 iOS 6 和 Xcode 4.5 以及 Xamarin 6.0。The PassKit features discussed in this document require iOS 6 and Xcode 4.5, along with Xamarin.iOS 6.0.

简介Introduction

PassKit 解决的关键问题是分发和管理条形码。The key problem that PassKit solves is the distribution and management of barcodes. 当前使用条码的方式的一些真实示例包括:Some real-world examples of how barcodes are currently used include:

  • 在线购买电影入场券–客户通常通过电子邮件发送表示其票证的条形码。Buying movie tickets online – Customers are typically emailed a barcode that represents their tickets. 将打印此条形码,并将其转到电影以进行输入。This barcode is printed and taken to the cinema to be scanned for entry.
  • 会员卡–客户在其钱包或钱包中携带大量不同的商店特定卡,以便在购买货物时进行显示和扫描。Loyalty cards – Customers carry a number of different store-specific cards in their wallet or purse, for display and scanning when they purchase goods.
  • 赠券–赠券通过电子邮件分发,作为可打印网页,通过 letterboxes,并作为报纸和杂志中的条形码。Coupons – Coupons are distributed via email, as printable web pages, through letterboxes and as barcodes in newspapers and magazines. 客户将他们带入商店,以便在退货中接收商品、服务或折扣。Customers bring them to a store for scanning, to receive goods, services or discounts in return.
  • 入职pass –类似于购买电影票据。Boarding passes – Similar to buying a movie ticket.

PassKit 提供了一种替代方案:PassKit offers an alternative for each of these scenarios:

  • 电影票据–购买后,客户添加了事件票证传递(通过电子邮件或网站链接)。Movie tickets – After purchase, the customer adds an event ticket pass (via email or a website link). 作为电影方法的时间,刀路会自动在锁屏界面上显示为提醒,在电影院上,可以轻松地检索刀路,并将其显示在钱包中进行扫描。As the time for the movie approaches, the pass will automatically appear on the lock-screen as a reminder, and on arrival at the cinema the pass is easily retrieved and displayed in Wallet for scanning.
  • 会员卡–而不是提供物理卡,商店可以通过电子邮件或在网站登录后发出的存储卡通过。Loyalty cards – Rather than (or in addition to) providing a physical card, stores can issue (via email or after a website login) a Store Card Pass. 该商店可以提供其他功能,例如通过推送通知更新 pass 上的帐户余额,使用地理位置服务当客户在商店位置附近时,传递可能会自动出现在锁屏界面上。The store can provide additional features such as updating the balance of the account on the pass via push notifications, and using geolocation services the pass could automatically appear on the lock-screen when the customer is near a store location.
  • 赠券–可以通过独特的特征轻松生成赠券,通过电子邮件或网站链接进行跟踪。Coupons – Coupon passes can easily be generated with unique characteristics to help with tracking, and distributed via email or website links. 当用户在特定位置附近时,或在给定的日期(例如到期日期接近时),已下载的优惠券会自动出现在锁屏界面上。Downloaded coupons can automatically appear on the lock-screen when the user is near a specific location, and/or on a given date (such as when the expiry date is approaching). 由于优惠券存储在用户的手机上,因此它们始终很方便,并且不会出现错放。Because the coupons are stored on the user’s phone, they are always handy and do not get misplaced. 赠券可能鼓励客户下载相关应用,因为应用商店链接可以合并到 Pass 中,从而增加与客户的参与。Coupons might encourage customers to download Companion Apps because App Store links can be incorporated into the Pass, increasing engagement with the customer.
  • 入职pass –在联机签入过程之后,客户将通过电子邮件或链接接收其参与 pass。Boarding passes – After an online check-in process, the customer would receive their boarding pass via email or a link. 传输提供商提供的伴随应用可能包括签入过程,还允许客户执行其他功能,例如选择其座位或食物。A Companion App provided by the transport provider could include the check-in process, and also allow the customer to perform additional functions like choosing their seat or meal. 当传输延迟或取消时,传输提供程序可以使用推送通知来更新传递。The transport provider can use push notifications to update the pass if transport is delayed or canceled. 随着入职时间的接近,传递将作为提醒出现在锁屏界面上,并提供对 Pass 的快速访问。As the boarding time approaches the pass will appear on the lock-screen as a reminder and to provide quick access to the Pass.

PassKit 提供了一种简单而方便的方法,可在 iOS 设备上存储和显示条形码。At its core, PassKit provides a simple and convenient way to store and display barcodes on your iOS device. 随着额外的时间和位置锁定屏幕集成,推送通知和配套应用程序集成可为非常复杂的销售、票证和计费服务提供基础。With the additional time and location lock-screen integration, push notifications and Companion Application integrate it offers a foundation for very sophisticated sales, ticketing and billing services.

PassKit 生态系统PassKit Ecosystem

PassKit 不只是 CocoaTouch 内的 API,而是更大的应用、数据和服务生态系统的一部分,有助于安全共享和管理条形码及其他数据。PassKit is not just an API within CocoaTouch, rather it is part of a larger ecosystem of apps, data and services that facilitate the secure sharing and management of barcodes and other data. 此高级关系图显示了在创建和使用传递过程中可以涉及的不同实体:This high-level diagram shows the different entities that can be involved in creating and using passes:

生态系统的每个部分都有明确定义的角色:Each piece of the ecosystem has a clearly-defined role:

  • 钱包–存储并显示刀路的 Apple 内置 iOS 应用。Wallet – Apple’s built-in iOS app that stores and displays passes. 这是为在现实世界中使用而呈现的传递的唯一位置(即显示条形码,同时传递中的所有本地化数据)。This is the only place that passes are rendered for use in the real world (ie the barcode is displayed, along with all the localized data in the pass).
  • 伴生应用–由 pass 提供商构建的 iOS 6 应用程序,用于扩展其发出的传递的功能,例如,将值添加到存储卡、更改代表的座位或其他特定于业务的功能。Companion Apps – iOS 6 apps built by pass providers to extend the functionality of the passes they issue, such as adding value to a store card, changing the seat on a boarding pass or other business-specific function. 要使 pass 有用,传递应用不是必需的。Companion Apps are not required for a pass to be useful.
  • 你的服务器-一种安全服务器,可在其中生成传递并对其进行签名以进行分发。Your server – A secure server where passes can be generated and signed for distribution. 你的附属应用可能会连接到你的服务器,以生成新的 pass 或请求更新现有的阶段。Your Companion App may connect to your server to generate new passes or request updates to existing passes. 你可以选择实现钱包为更新通过而调用的 web 服务 API。You may optionally implement the web service API that Wallet would call to update passes.
  • Apns 服务器–您的服务器能够使用 APNS 在给定设备上通知经过更新的钱包。APNS Servers – Your server has the ability to notify Wallet of updates to a pass on a given device using APNS. 将通知推送到钱包,然后将联系你的服务器以获取更改的详细信息。Push a notification to Wallet which will then contact your server for details of the change. 伴生应用不需要为此功能实现 APNS (它们可以侦听 PKPassLibraryDidChangeNotification)。Companion apps do not need to implement APNS for this feature (they can listen to the PKPassLibraryDidChangeNotification ).
  • 管道应用–不直接操作 pass 的应用程序(例如,配套应用程序),但可以通过识别 pass 并允许将其添加到钱包来改善其实用工具。Conduit Apps – Applications that don’t directly manipulate passes (like companion apps do), but which can improve their utility by recognizing passes and allowing them to be added to Wallet. 邮件客户端、社交网络浏览器和其他数据聚合应用程序都可能会遇到要传递的附件或链接。Mail clients, social network browsers and other data aggregation apps may all encounter attachments or links to passes.

整个生态系统看起来很复杂,因此,值得注意的是,某些组件是可选的,并且可能会有更简单的 PassKit 实现。The entire ecosystem looks complex, so it’s worth noting that some components are optional and much simpler PassKit implementations are possible.

什么是传递?What is a Pass?

Pass 是表示票据、优惠券或卡片的数据集合。A pass is a collection of data representing a ticket, coupon or card. 它可能适用于个人的单一使用(因此包含航班号和座位分配等详细信息),或者它可能是可由任意数量的用户(例如折扣优惠券)共享的多个使用令牌。It may be intended for a single use by an individual (and therefore contain details such as a flight number and seat allocation) or it may a multiple use token that can be shared by any number of users (such as a discount coupon). 有关 "传递文件" 文档中提供了有关详细说明,请访问 Apple。A detailed description is available in Apple’s About Pass Files document.

类型Types

目前有五种受支持的类型,可通过刀路的布局和上边缘来区分钱包应用:Currently five supported types, which can be distinguished in the Wallet app by the layout and top edge of the pass:

  • 事件票证–小型半圆切口。Event Ticket – small semicircular cutout.
  • 入职 Pass –两侧的凹槽,可以指定传输特定的图标(例如Boarding Pass – notches in side, transport-specific icon can be specified (eg. 总线、训练、飞行)。bus, train, airplane).
  • 存储卡–向上舍入,如信用卡或借记卡。Store Card – rounded top, like a credit or debit card.
  • 优惠券– perforated 在顶部。Coupon – perforated along the top.
  • 一般–与存储卡相同,向上舍入。Generic – same as Store Card, rounded top.

此屏幕截图中显示了五个 pass 类型(按顺序:赠券、通用、商店卡、入职 pass 和事件票证):The five pass types are shown in this screenshot (in order: coupon, generic, store card, boarding pass and event ticket):

文件结构File Structure

传递文件实际上是扩展名为pkpass的 ZIP 存档,其中包含一些特定的 JSON 文件(必需)、各种图像文件(可选)以及本地化字符串(也是可选的)。A pass file is actually a ZIP archive with a .pkpass extension, containing some specific JSON files (required), a variety of image files (optional) as well as localized strings (also optional).

  • pass. json –必需。pass.json – required. 包含通过的所有信息。Contains all the information for the pass.
  • .manifest –必需。manifest.json – required. 包含传递中每个文件的 SHA1 哈希(签名文件和此文件除外)。Contains SHA1 hashes for each file in the pass except the signature file and this file (manifest.json).
  • 签名–必需。signature – required. 通过使用 iOS 预配门户中生成的证书对 manifest.json 文件进行签名来创建。Created by signing the manifest.json file with the certificate generated in the iOS Provisioning Portal.
  • 徽标键–可选。logo.png – optional.
  • 背景 .png –可选。background.png – optional.
  • icon .png –可选。icon.png – optional.
  • 本地化的字符串文件–可选。Localizable strings files – optional.

传递文件的目录结构如下所示(这是 ZIP 存档的内容):Directory structure of a pass file is shown below (this is the contents of the ZIP archive):

pass jsonpass.json

JSON 格式是因为通常在服务器上创建传递,这意味着在服务器上,生成代码是不可知的。JSON is the format because passes are typically created on a server – it means that the generation code is platform-agnostic on the server. 每个阶段的三个关键信息是:The three key pieces of information in every pass are:

  • teamIdentifier –这会将生成的所有传递链接到应用商店帐户。teamIdentifier – This links all passes you generate to your App Store account. 此值在 iOS 预配门户中可见。This value is visible in the iOS Provisioning Portal.
  • passTypeIdentifier -在预配门户中注册,以将刀路组合在一起(如果生成多种类型)。passTypeIdentifier – Register in Provisioning Portal to group passes together (if you produce more than one type). 例如,咖啡店可能会创建商店卡 pass 类型,以允许其客户获得会员信用额度,还可以创建单独的优惠券 pass 类型来创建和分发折扣优惠券。For example, a coffee shop might create a store card pass type to allow their customers to earn loyalty credits, but also a separate coupon pass type to create and distribute discount coupons. 该咖啡店甚至可能包含实时音乐事件,并针对这些活动发出事件票证传递。That same coffee shop might even hold live music events and issue Event Ticket passes for those.
  • serialNumber –此 passTypeidentifier 中的唯一字符串。serialNumber – A unique string within this passTypeidentifier . 该值对于钱包是不透明的,但对于在与服务器通信时跟踪特定经历非常重要。The value is opaque to Wallet, but is important for tracking specific passes when communicating with your server.

每次传递中有大量其他 JSON 密钥,示例如下所示:There is a large number of other JSON keys in each Pass, an example of which is shown below:

{
   "passTypeIdentifier":"com.xamarin.passkitdoc.banana",  //Type Identifier (iOS Provisioning Portal)
   "formatVersion":1,                                     //Always 1 (for now)
   "organizationName":"Xamarin",                          //The name which appears on push notifications
   "serialNumber":"12345436XYZ",                          //A number for you to identify this pass
   "teamIdentifier":"XXXAAA1234",                         //Your Team ID
   "description":"Xamarin Demo",                          //
   "foregroundColor":"rgb(54,80,255)",                    //color of the data text (note the syntax)
   "backgroundColor":"rgb(209,255,247)",                  //color of the background
   "labelColor":"rgb(255,15,15)",                         //color of label text and icons
   "logoText":"Banana ",                                  //Text that appears next to logo on top
   "barcode":{                                            //Specification of the barcode (optional)
      "format":"PKBarcodeFormatQR",                       //Format can be QR, Text, Aztec, PDF417
      "message":"FREE-BANANA",                            //What to encode in barcode
      "messageEncoding":"iso-8859-1"                      //Encoding of the message
   },
   "relevantDate":"2012-09-15T15:15Z",                    //When to show pass on screen. ISO8601 formatted.
  /* The following fields are specific to which type of pass. The name of this object specifies the type, e.g., boardingPass below implies this is a boarding pass. Other options include storeCard, generic, coupon, and eventTicket */
   "boardingPass":{
/*headerFields, primaryFields, secondaryFields, and auxiliaryFields are arrays of field object. Each field has a key, label, and value*/
      "headerFields":[          //Header fields appear next to logoText
         {
            "key":"h1-label",   //Must be unique. Used by iOS apps to get the data.
            "label":"H1-label", //Label of the field
            "value":"H1"        //The actual data in the field
         },
         {
            "key":"h2-label",
            "label":"H2-label",
            "value":"H2"
         }
      ],
      "primaryFields":[       //Appearance differs based on pass type
         {
            "key":"p1-label",
            "label":"P1-label",
            "value":"P1"
         }
      ],
      "secondaryFields":[     //Typically appear below primaryFields
         {
            "key":"s1-label",
            "label":"S1-label",
            "value":"S1"
         }
      ],
      "auxiliaryFields":[    //Appear below secondary fields
         {
            "key":"a1-label",
            "label":"A1-label",
            "value":"A1"
         }
      ],
      "transitType":"PKTransitTypeAir"  //Only present in boradingPass type. Value can
                                        //Air, Bus, Boat, or Train. Impacts the picture
                                        //that shows in the middle of the pass.
   }
}

条形码Barcodes

仅支持2D 格式: PDF417、Aztec、QR。Only 2D formats are supported: PDF417, Aztec, QR. Apple 声称,一 unsuited 的条形码在 backlit 的电话屏幕上进行扫描。Apple claims that 1D barcodes are unsuited to scanning on a backlit phone screen.

条形码下显示的备用文本是可选的–某些商家希望能够手动读取/键入内容。Alternate text displayed below the barcode is optional – some merchants want to be able to read/type manually.

ISO-8859-1 编码是最常见的,可检查将读取你的阶段的扫描系统所使用的编码。ISO-8859-1 encoding is the most common, check which encoding is used by the scanning systems that will read your passes.

关联性(锁定屏幕)Relevancy (Lock Screen)

有两种类型的数据可导致在锁屏界面上显示传递:There are two types of data that can cause a pass to be displayed on the lock-screen:

位置Location

在一次中最多可以指定10个位置,例如客户经常访问的商店,或电影院或机场的位置。Up to 10 locations can be specified in a Pass, eg stores that a customer frequently visits, or the location of a cinema or airport. 客户可以通过配套应用设置这些位置,或者提供商可以通过使用情况数据(如果是使用客户的权限收集的)来确定这些位置。A customer could set these locations via a Companion App or the provider could determine them from usage data (if collected with the customer’s permission).

如果在锁屏界面上显示了 pass,则会计算出一个围栏,这样当用户离开该区域时,将会在锁定屏幕中隐藏刀路。When the pass is displayed on the lock-screen, a fence is calculated so that when the user leaves the area the pass is hidden from the lock-screen. Radius 绑定到传递样式,以防止滥用。The radius is tied to pass style to prevent abuse.

日期和时间Date and Time

在一个阶段中只能指定一个日期/时间。Only one date/time can be specified in a Pass. "日期和时间" 可用于触发用于 "刀路" 和 "活动" 票证的锁屏提醒。The date and time is useful for triggering lock-screen reminders for boarding passes and event tickets.

可以通过推送或 via PassKit API 进行更新,以便在使用多个票证(如对一只是地区或体育运动的季节票据)时可以更新日期/时间。Can be updated via push or via PassKit API, so that the date/time could be updated in the case of a multiple-use ticket (such as a season ticket to a theatre or sporting complex).

本地化Localization

将传入翻译成多种语言类似于本地化 iOS 应用程序–使用 .lproj 扩展创建语言特定的目录,并将本地化的元素放入其中。Translating a pass into multiple languages is similar to localizing an iOS application – create language specific directories with the .lproj extension and place the localized elements inside. 应将文本翻译输入到 pass.strings 文件中,而本地化的图像应与在 Pass 根中替换的图像的名称相同。Text translations should be entered into a pass.strings file, while localized images should have the same name as the image they replace in the Pass root.

安全Security

将使用你在 iOS 预配门户中生成的私有证书对传递进行签名。Passes are signed with a private certificate that you generate in the iOS Provisioning Portal. 对通过进行签名的步骤如下:The steps to sign the pass are:

  1. 计算传递目录中每个文件的 SHA1 哈希值(不包括 manifest.jsonsignature 文件,而这两个文件都不应存在于此阶段)。Calculate a SHA1 hash for each file in the pass directory (do not include the manifest.json or signature file, neither of which should exist at this stage anyway).
  2. 写入 manifest.json 作为每个文件名的 JSON 键/值列表及其哈希。Write manifest.json as a JSON key/value list of each filename with its hash.
  3. 使用证书对 manifest.json 文件进行签名,并将结果写入名为 signature 的文件。Use the certificate to sign the manifest.json file and write the result to a file called signature .
  4. 将所有内容压缩,并为生成的文件指定 .pkpass 的文件扩展名。ZIP the everything up and give the resulting file a .pkpass file extension.

因为你需要使用私钥对传递进行签名,所以仅应在你控制的安全服务器上执行此过程。Because your private key is required to sign the pass, this process should only be done on a secure server that you control. 不要分发密钥来尝试在应用程序中生成 pass。DO NOT distribute your keys to try and generate passes in an application.

配置和设置Configuration and Setup

本部分包含的说明可帮助设置预配详细信息并创建第一个阶段。This section contains instructions to help setup your provisioning details and create your first pass.

预配 PassKitProvisioning PassKit

为了使 pass 进入应用商店,必须将其链接到开发人员帐户。In order for a pass to enter the App Store, it must be linked to a developer account. 这需要两个步骤:This requires two steps:

  1. 必须使用唯一标识符(称为传递类型 ID)注册传递。The pass must be registered using a unique identifier, called the Pass Type ID.
  2. 必须生成有效的证书才能使用开发人员的数字签名对传递进行签名。A valid Certificate must be generated to sign the pass with the developer's digital signature.

若要创建 Pass 类型 ID,请执行以下操作。To create a Pass Type ID do the following.

创建传递类型 IDCreate a Pass Type ID

第一步是为要支持的每种不同_类型_的传递设置传递类型 ID。The first step is to set up a Pass Type ID for each different type of pass to be supported. Pass ID (或 Pass 类型标识符)创建传递的唯一标识符。The Pass ID (or Pass Type identifier) creates a unique identifier for the Pass. 我们将使用此 ID 将该传递与开发人员帐户结合使用证书。We will use this ID to link the pass with your developer account using a Certificate.

  1. IOS 设置门户的 "证书、标识符和配置文件" 部分中,导航到 "标识符",然后选择 "传递类型 id "。In the Certificates, Identifiers, and Profiles section of the iOS Provisioning Portal, navigate to Identifiers and select Pass Type IDs . 然后选择 " + " 按钮以创建新的 "传递类型":Then select the + button to create a new pass type:

  2. 提供传递的说明(名称)和标识符(唯一字符串)。Provide a Description (name) and Identifier (unique string) for the Pass. 请注意,所有传递类型 Id 必须以字符串开头 pass. 在本示例中,我们使用 pass.com.xamarin.coupon.bananaNote that all Pass Type IDs must begin with the string pass. In this example we use pass.com.xamarin.coupon.banana :

  3. 按 "注册" 按钮确认传递 ID。Confirm the Pass ID by pressing the Register button.

生成证书Generate A Certificate

若要为此 Pass 类型 ID 创建新证书,请执行以下操作:To create a new Certificate for this Pass Type ID, do the following:

  1. 从列表中选择新创建的 "传递 ID",然后单击 "编辑":Select the newly created Pass ID from the list, and click Edit :

    然后选择 "创建证书 ... "Then, select Create Certificate… ::

  2. 按照步骤创建证书签名请求(CSR)。Follow the steps to create a Certificate Signing Request (CSR).

  3. 按开发人员门户中的 "继续" 按钮,并上传 CSR 以生成证书。Press the Continue button on the developer portal and upload the CSR to generate your certificate.

  4. 下载证书,并双击它以将其安装在密钥链中。Download the certificate and double-click on it to install it in your keychain.

现在,我们已创建了此 Pass 类型 ID 的证书,接下来的部分将介绍如何手动生成 pass。Now that we have created a certificate for this Pass Type ID, the next section describes how to build a pass manually.

有关为钱包预配的详细信息,请参阅使用功能指南。For more information on Provisioning for Wallet, refer to the Working with Capabilities guide.

手动创建 PassCreate a Pass Manually

现在,我们已创建了 Pass 类型,接下来可以手动在模拟器或设备上创建一个 pass 测试。Now that we’ve created the Pass Type we can manually craft a pass to test on the simulator or a device. 创建传递的步骤如下:The steps to create a pass are:

  • 创建目录以包含传递文件。Create a directory to contain the pass files.
  • 创建包含所有必需数据的 pass json 文件。Create a pass.json file that contains all the required data.
  • 在文件夹中包括图像(如果需要)。Include images in folder (if required).
  • 计算文件夹中每个文件的 SHA1 哈希,并将其写入到清单 json。Calculate SHA1 hashes for every file in the folder, and write to manifest.json.
  • 用下载的证书 p12 文件对 manifest 进行签名。Sign manifest.json with the downloaded certificate .p12 file.
  • 压缩目录的内容,并将其重命名为 pkpass 扩展名。ZIP the directory’s contents and rename with .pkpass extension.

本文的示例代码中有一些源文件可用于生成通过。There are some source files in the sample code for this article that can be used to generate a pass. 使用 CreateAPassManually 目录的 CouponBanana.raw 目录中的文件。Use the files in the CouponBanana.raw directory of the CreateAPassManually directory. 存在下列文件:The following files are present:

打开 pass. json 并编辑 JSON。Open pass.json and edit the JSON. 必须至少更新 passTypeIdentifier,并 teamIdentifer 以与 Apple 开发人员帐户匹配。You must at least update the passTypeIdentifier and teamIdentifer to match your Apple Developer account.

"passTypeIdentifier" : "pass.com.xamarin.coupon.banana",
"teamIdentifier" : "?????????",

然后,必须计算每个文件的哈希值并创建 manifest.json 文件。You must then calculate the hashes for each file and create the manifest.json file. 完成后,它将如下所示:It will look something like this when you’re done:

{
  "icon@2x.png" : "30806547dcc6ee084a90210e2dc042d5d7d92a41",
  "icon.png" : "87e9ffb203beb2cce5de76113f8e9503aeab6ecc",
  "pass.json" : "c83cd1441c17ecc6c5911bae530d54500f57d9eb",
  "logo.png" : "b3cd8a488b0674ef4e7d941d5edbb4b5b0e6823f",
  "logo@2x.png" : "3ccd214765507f9eab7244acc54cc4ac733baf87"
}

接下来,必须使用为此传递类型 ID 生成的证书(p12 文件)为此文件生成签名。Next a signature must be generated for this file using the certificate (.p12 file) that was generated for this Pass Type ID.

在 Mac 上签名Signing On a Mac

Apple 下载站点下载钱包 Seed 支持材料Download the Wallet Seed Support Materials from the Apple Downloads site. 使用 signpass 工具将文件夹转换为通过(这也将计算 SHA1 哈希,并将输出压缩为 pkpass 文件)。Use the signpass tool to turn your folder into a pass (this will also calculate the SHA1 hashes and ZIP the output into a .pkpass file).

正在测试Testing

如果要检查这些工具的输出(通过将文件名设置为 .zip 并打开它),则会看到以下文件(请注意,添加了 manifest.jsonsignature 文件):If you were to examine the output of these tools (by setting the filename to .zip and then opening it), you would see the following files (note the addition of the manifest.json and signature files):

签名后,压缩并重命名该文件(例如,Once you have signed, ZIPped and renamed the file (eg. 若要 BananaCoupon.pkpass),可以将其拖到模拟器中进行测试,或通过电子邮件将其发送给你自己以在真实设备上检索。to BananaCoupon.pkpass) you can drag it into the simulator to test, or email it to yourself to retrieve on a real device. 应该会看到一个屏幕来添加刀路,如下所示:You should see a screen to Add the pass, like this:

通常,该过程会在服务器上自动执行,但对于仅创建不需要后端服务器支持的优惠券的小型企业,手动传递创建可能是一个选项。Normally that process would be automated on a server, however manual pass creation might be an option for small businesses that are only creating coupons that do not require the support of a back-end server.

WalletWallet

钱包是 PassKit 生态系统的核心部分。Wallet is the central piece of the PassKit ecosystem. 此屏幕截图显示了空钱包,并说明了传递列表和各个刀路的外观:This screenshot shows the empty Wallet, and how the pass list and individual passes look:

钱包的功能包括:Features of Wallet include:

  • 只会将传递的位置与其条形码一起用于扫描。It is the only place that passes are rendered with their barcode for scanning.
  • 用户可以更改更新的设置。User can change the settings for updates. 如果启用,推送通知可以触发对传递中数据的更新。If enabled, push notifications can trigger updates to the data in the Pass.
  • 用户可以启用或禁用锁定屏幕集成。User can enable or disable lock-screen integration. 如果启用此项,则可根据嵌入在传递中的相关时间和位置数据,自动在其锁定屏幕上显示。If enabled, this allows the pass to automatically appear on their lock screen, based on relevant time and location data embedded in the pass.
  • 如果在传递 JSON 中提供了 web 服务器 URL,则 pass 的反向支持请求刷新。The reverse side of the pass supports pull-to-refresh, if a web-server-URL is supplied in the pass JSON.
  • 如果在传递 JSON 中提供应用的 ID,则可以打开(或下载)相关应用。Companion Apps can be opened (or downloaded) if the app’s ID is supplied in the pass JSON.
  • 可以删除传递(使用漂亮的清除动画)。Passes can be deleted (with a cute shredding animation).

将刀路添加到钱包Adding Passes into Wallet

可以通过以下方式将 pass 添加到钱包:Passes can be added to Wallet in the following ways:

  • 管道应用程序-这些操作不会直接操作传递,它们只是加载传递文件并向用户显示将其添加到钱包的选项。Conduit Apps – These do not manipulate passes directly, they simply load pass files and present the user with the option of adding them to Wallet.

  • 附属应用-这些应用程序由提供商编写,用于分发刀路,并提供其他功能来浏览或编辑这些应用。Companion Apps – These are written by providers to distribute passes and offer additional functionality to browse or edit them. Xamarin iOS 应用程序可以完全访问 PassKit API,以创建和操作 pass。Xamarin.iOS applications have complete access to the PassKit API to create and manipulate passes. 然后,可以使用 PKAddPassesViewController将刀路添加到钱包。Passes can then be added to Wallet using the PKAddPassesViewController. 本文档的相关应用程序部分详细介绍了此过程。This process is described in more detail in the Companion Applications section of this document.

管道应用程序Conduit Applications

管道应用程序是可代表用户接收传入的中间应用,应对其进行编程以识别其内容类型,并提供要添加到钱包中的功能。Conduit applications are intermediate apps that might receive passes on behalf of a user, and should be programmed to recognize their content-type and provide functionality to add to the Wallet. 管道应用的示例包括:Examples of conduit apps include:

  • Mail –将附件识别为 Pass。Mail – Recognizes attachment as a Pass.
  • Safari –在单击 "传递 URL" 链接时识别 "传递内容类型"。Safari – Recognizes the pass Content-Type when a pass URL link is clicked.
  • 其他自定义应用–接收附件或打开链接的任何应用(社交媒体客户端、邮件读取器等)。Other custom apps – Any app that receive attachments or open links (social media clients, mail readers, etc).

此屏幕截图显示了 iOS 6 中的邮件如何识别电子邮件附件,并将其添加到钱包。This screenshot shows how Mail in iOS 6 recognizes a pass attachment and (when touched) offers to Add it to Wallet.

如果要构建的应用可能是用于传递的管道,可以通过以下方式识别它们:If you are building an app that could be a conduit for passes, they can be recognized by:

  • 文件扩展名-. pkpassFile extension - .pkpass
  • MIME 类型-application/vnd.apple.mpegurl/pkpassMIME Type - application/vnd.apple.pkpass
  • UTI – pkpassUTI – com.apple.pkpass

管道应用程序的基本操作是检索传递文件并调用 PassKit 的 PKAddPassesViewController,为用户授予向其钱包添加刀路的选项。The basic operation of a conduit application is to retrieve the pass file and call PassKit’s PKAddPassesViewController to give the user the option to add the pass to their Wallet. 此视图控制器的实现将在下一节中介绍的应用程序The implementation of this view controller is covered in the next section on Companion Applications.

不需要针对特定的传递类型 ID 设置管道应用程序,这与伴随应用程序的方式相同。Conduit Applications do not need to be provisioned for a specific pass Type ID in the same way that Companion Applications do.

助理应用程序Companion Applications

伴生应用程序提供了用于处理 Pass 的附加功能,包括创建 Pass、更新与 Pass 关联的信息,以及管理与应用程序关联的传递。A companion application provides additional functionality for working with passes, including creating a Pass, updating information associated with a Pass and otherwise managing passes associated with the application.

助理应用程序不应尝试复制钱包的功能。Companion applications should not attempt to duplicate the features of Wallet. 它们不用于显示扫描的通过。They are not intended to display passes for scanning.

本部分的其余部分介绍如何生成与 PassKit 交互的基本辅助应用。This remainder of this section describes how to build a basic Companion App that interacts with PassKit.

预配Provisioning

因为钱包是一种存储技术,所以需要单独预配应用程序,并且不能使用团队预配配置文件或通配符应用 ID。Because Wallet is a store technology, the application needs to be provisioned separately and cannot use Team Provisioning Profile or Wildcard App ID. 请参阅使用功能指南,为钱包应用程序创建唯一的应用程序 ID 和预配配置文件。Refer to the Working with Capabilities guide to create a unique App ID and Provisioning Profile for the Wallet application.

下方Entitlements

Info.plist文件应包含在所有最新的 Xamarin iOS 项目中。The Entitlements.plist file should be included in all recent Xamarin.iOS project. 若要添加新的 info.plist 文件,请按照使用权利指南中的步骤进行操作。To add a new Entitlements.plist file, follow the steps in the Working with Entitlements guide.

若要设置权利,请执行以下操作:To set entitlements do the following:

双击 "Solution Pad 中的info.plist文件以打开 info.plist 编辑器:Double-click on the Entitlements.plist file in the Solution Pad to open the Entitlements.plist editor:

在 "钱包" 部分下,选择 "启用钱包" 选项Under the Wallet section, select the Enable Wallet option

默认选项是应用允许所有传递类型。The default option is for your app to allow all pass types. 但是,可以限制你的应用程序,并且只允许一部分团队传递类型。However, it is possible to restrict your app and only allow a subset of team pass types. 若要启用此选择,请选择 "允许团队传递类型子集",然后输入你希望允许的子集的 "传递类型" 标识符。To enable this select the Allow subset of team pass types and enter the pass type identifier of the subset that you wish to allow.

调试Debugging

如果在部署应用程序时遇到问题,请检查是否正在使用正确的预配配置文件,以及是否在iPhone 捆绑签名选项中选择了 "Entitlements.plist 作为自定义权利文件。If you have problems deploying your application, check that you are using the correct Provisioning Profile and that the Entitlements.plist is selected as the Custom entitlements file in the iPhone Bundle Signing options.

如果在部署时遇到此错误:If you experience this error when deploying:

Installation failed: Your code signing/provisioning profiles are not correctly configured (error: 0xe8008016)

pass-type-identifiers 的权利数组不正确(或与预配配置文件不匹配)。then the pass-type-identifiers entitlements array is incorrect (or does not match the Provisioning Profile). 验证 Pass 类型 Id 和你的团队 ID 是否正确。Verify the Pass Type IDs and your Team ID are correct.

Classes

以下 PassKit 类可用于应用程序访问:The following PassKit classes are available for apps to access passes:

  • PKPass –传递的实例。PKPass – An instance of a Pass.
  • PKPassLibrary –提供用于访问设备上的传递的 API。PKPassLibrary – Provides the API to access the passes on the device.
  • PKAddPassesViewController -用于显示用户在其钱包内保存的通过。PKAddPassesViewController – Used to display a pass for the user to save in their Wallet.
  • PKAddPassesViewControllerDelegate – Xamarin iOS 开发人员PKAddPassesViewControllerDelegate – Xamarin.iOS developers

示例Example

请参阅本文中的示例代码中的 PassLibrary 项目。Refer to the PassLibrary project in the sample code for this article. 它演示了在钱包伴应用程序中需要的以下常见函数:It demonstrates the following common functions that would be required in a Wallet Companion Application:

检查钱包是否可用Check that Wallet is Available

钱包在 iPad 上不可用,因此应用程序应在尝试访问 PassKit 功能之前进行检查。Wallet is not available on the iPad, so applications should check before attempting to access PassKit features.

if (PKPassLibrary.IsAvailable) {
    // create an instance and do stuff...
}

创建传递库实例Creating a Pass Library Instance

PassKit 库不是单一实例,应用程序应创建并存储和实例,以访问 PassKit API。The PassKit library is not a singleton, applications should create and store and instance to access the PassKit API.

if (PKPassLibrary.IsAvailable) {
    library = new PKPassLibrary ();
    // do stuff...
}

获取通过列表Get a List of Passes

应用程序可以从库中请求传递列表。Applications can request a list of passes from the library. 此列表由 PassKit 自动筛选,因此,你只能看到已使用你的团队 ID 创建并在你的权利中列出的传递。This list is automatically filtered by PassKit, so that you can only see passes that have been created with your Team ID and which are listed in your Entitlements.

var passes = library.GetPasses ();  // returns PKPass[]

请注意,模拟器不会筛选返回的传递列表,因此应始终在实际设备上测试此方法。Note that the simulator does not filter the list of passes returned, so this method should always be tested on real devices. 此列表可以显示在 UITableView 中。This list can be displayed in a UITableView. 添加两个优惠券后,示例应用如下所示:The sample app looks like this after two coupons have been added:

显示刀路Displaying Passes

一组有限的信息可用于呈现辅助应用内的传递。A limited set of information is available for rendering of passes within companion apps.

如示例代码所示,从此标准属性集中进行选择以显示刀路列表。Choose from this set of standard properties to display lists of passes, as the example code does.

string passInfo =
                "Desc:" + pass.LocalizedDescription
                + "\nOrg:" + pass.OrganizationName
                + "\nID:" + pass.PassTypeIdentifier
                + "\nDate:" + pass.RelevantDate
                + "\nWSUrl:" + pass.WebServiceUrl
                + "\n#" + pass.SerialNumber
                + "\nPassUrl:" + pass.PassUrl;

此字符串在示例中显示为警报:This string is shown as an alert in the sample:

您还可以使用 LocalizedValueForFieldKey() 方法从您已设计的中的字段检索数据(因为您将知道应该存在哪些字段)。You can also use the LocalizedValueForFieldKey() method to retrieve data from fields in the passes you have designed (since you will know what fields should be present). 示例代码不显示此代码。The example code does not show this.

从文件加载传递Loading a Pass from a File

由于只能向钱包添加用户的权限,因此必须提供视图控制器来决定。Because a pass can only be added to Wallet with the user’s permission, a view controller must be presented to let them decide. 此代码用在示例中的 "添加" 按钮,可加载嵌入在应用中的预建传递(应将其替换为已签名的传递):This code is used in the Add button in the example, to load a pre-built pass that is embedded in the app (you should replace this with one that you have signed):

NSData nsdata;
using ( FileStream oStream = File.Open (newFilePath, FileMode.Open ) ) {
        nsdata = NSData.FromStream ( oStream );
}
var err = new NSError(new NSString("42"), -42);
var newPass = new PKPass(nsdata,out err);
var pkapvc = new PKAddPassesViewController(newPass);
NavigationController.PresentModalViewController (pkapvc, true);

通过 "添加" 和 "取消" 选项提供传递:The pass is presented with Add and Cancel options:

替换现有传递Replace an Existing Pass

替换现有的传递不需要用户的权限,但如果尚未存在此传递,则会失败。Replacing an existing pass does not require the user’s permission, however it will fail if the pass does not already exist.

if (library.Contains (newPass)) {
     library.Replace (newPass);
}

编辑 PassEditing a Pass

PKPass 不是可变的,因此不能在代码中更新传递对象。PKPass isn’t mutable, so you cannot update pass objects in your code. 若要更改 pass 中的数据,应用程序必须有权访问 web 服务器,该服务器可以保留传递的记录并生成一个新的传递文件,其中包含可供应用程序下载的更新值。To alter the data in a pass an application must have access to a web server that can keep a record of passes and generate a new pass file with updated values which the application can download.

必须在服务器上完成传递文件创建,因为必须使用必须保密的证书来对传递进行签名。Pass file creation must be done on a server because passes must be signed with a certificate that must be kept private and secure.

生成更新的传递文件后,使用 Replace 方法覆盖设备上的旧数据。Once an updated pass file has been generated, use the Replace method to overwrite the old data on the device.

显示扫描的通过Display a Pass for scanning

如前所述,只有钱包可以显示扫描的通过。As previously noted, only Wallet can display a pass for scanning. 可以使用 OpenUrl 方法来显示 Pass,如下所示:A Pass can be displayed using the OpenUrl method as shown:

UIApplication.SharedApplication.OpenUrl (p.PassUrl);

接收更改通知Receiving Notifications Of Changes

应用程序可以使用 PKPassLibraryDidChangeNotification侦听对传递库所做的更改。Applications can listen for changes being made to the Pass Library using the PKPassLibraryDidChangeNotification. 更改可能是由于触发了后台更新的通知引起的,因此最好在应用程序中侦听。Changes could be caused by notifications triggering updates in the background, so it is good practice to listen for them in your app.

noteCenter = NSNotificationCenter.DefaultCenter.AddObserver (PKPassLibrary.DidChangeNotification, (not) => {
    BeginInvokeOnMainThread (() => {
        new UIAlertView("Pass Library Changed", "Notification Received", null, "OK", null).Show();
        // refresh the list
        var passlist = library.GetPasses ();
        table.Source = new TableSource (passlist, library);
        table.ReloadData ();
    });
}, library);  // IMPORTANT: must pass the library in

注册通知时传递库实例很重要,因为 PKPassLibrary 不是单一实例。It is important to pass a library instance when registering for the notification because PKPassLibrary is not a singleton.

服务器处理Server Processing

有关构建支持 PassKit 的服务器应用程序的详细讨论超出了本文的介绍范围。A detailed discussion of building a server application to support PassKit is beyond the scope of this introductory article.

请参阅dotnet-passbook开源C#服务器端代码。See dotnet-passbook open source C# server-side code.

推送通知Push Notifications

有关使用推送通知更新阶段的详细讨论不在本文介绍的讨论范围之内。A detailed discussion of using push notifications to update passes is beyond the scope of this introductory article.

需要进行更新时,你将需要实现 Apple 定义的类似于 REST 的 API,以响应来自钱包的 web 请求。You would be required to implement the REST-like API defined by Apple to respond to web requests from Wallet when updates are required.

有关详细信息,请参阅 Apple 的更新 Pass指南。See Apple's Updating a Pass guide for more information.

总结Summary

本文介绍了 PassKit,其中的一些原因非常有用,并描述了必须为完全 PassKit 解决方案实现的不同部分。This article introduced PassKit, outlined some of the reasons why it is useful and described the different parts that must be implemented for a full PassKit solution. 本文介绍了配置 Apple 开发人员帐户以创建 pass、手动进行传递的过程,以及如何从 Xamarin iOS 应用程序访问 PassKit Api 所需的步骤。It described the steps required to configure your Apple Developer account to create passes, the process to make a pass manually and also how to access the PassKit APIs from a Xamarin.iOS application.