首先,用户在购买产品时,应用程序从应用商店获取该产品的信息,把商店 界面提供给用户,然后让用户选择产品,如下图中第二阶段:
一、获取产品 ID
在应用程序中出售的每个产品都有唯一的产品 ID。 应用程序使用 ID 到应用商店获取产品信息,比如价格,并在用户购买产品时,提交支付请求。 应用程序既可以从应用程序的文件中读取该产品 ID,也可以从应用服务器中获取产品 ID。表2-1 描述了这两种方法的区别:
如果应用程序是固定的产品列表,比如内购来移除广告或开启功能,就 把列表整合到应用程序文件夹中。 如果产品 ID 不需要应用程序更新就可以做改变,比如支持额外关卡或角色的游戏,产品 ID 可以从应用的服务器获取产品 ID 列表。
在获取 iTunes Connect 中,现在没有运行机制可以为特殊的应用程序获得所有的产品列表。开发者需要负责管理应用程序 ID 列表并提供该信息给应用程序。如果开发者需要管理大量的产品 ID,建议使用 iTunes Connect 中用bulk XML 上传下载功能。
1、在应用程序 Bundle 中镶入产品 IDs
在应用束 Bundle 中包含特性列表文件,它包括了一个产品 ID 数组。如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN"
"http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<array>
<string>com.example.level1</string>
<string>com.example.level2</string>
<string>com.example.rocket_car</string>
</array>
</plist>
要想从 plist 文件中获取产品 ID ,在应用 Bundle 中找到该文件并读取它,代码如下。
NSURL *url = [[NSBundle mainBundle] URLForResource:@"product_ids"
withExtension:@"plist"];
NSArray *productIdentifiers = [NSArray arrayWithContentsOfURL:url];
2、从服务器获取产品 IDs
上传 JSON 文件到应用服务器,文件里带有产品 ID,内容如下:
[
"com.example.level1",
"com.example.level2",
"com.example.rocket_car"
]
要想从服务器获取产品 ID,如列表2-1所示从应用服务器获得并读取该 JSON 文件。 考虑 JSON 文件的版本化,这样应用程序的未来版本就可以随时改变它的结构,而不需要打破应用程序的老版本。比如,你可以把使用老结构的产品命名为_v1.json, 把使用新结构的文件命名为_v2.json. 如果你的 JSON 文件比示例中的简单数组更复杂时,版本化方法更加有用。
Listing 2-1 从应用服务器获得并读取 JSON 文件
- (void)fetchProductIdentifiersFromURL:(NSURL *)url delegate:(id)delegate
{
dispatch_queue_t global_queue =
dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0);
dispatch_async(global_queue, ^{
NSError *err;
NSData *jsonData = [NSData dataWithContentsOfURL:url
options:NULL
error:&err];
if (!jsonData) { /* Handle the error */ }
NSArray *productIdentifiers = [NSJSONSerialization
JSONObjectWithData:jsonData options:NULL error:&err];
if (!productIdentifiers) { /* Handle the error */ }
dispatch_queue_t main_queue = dispatch_get_main_queue();
dispatch_async(main_queue, ^{
[delegate displayProducts:productIdentifiers]; // Custom method
});
});
}
更多关于使用 NSURLConnection 下载文件的信息,请看 “Using NSURLConnection” in URL Loading System Programming Guide.
为了确保应用程序保持响应,使用后台线程来下载 JSON 文件,并提取产品 ID 列表。 要想最小化数据转换,使用标准的HTTP缓存机制,比如 Last-Modified 和 If-Modified-Since 头。
二、在苹果商店中取回产品信息
为了确保用户只看到可买的产品,在显示应用的商店界面 之前,要在应用商店查询产品列表真实性。
在查询应用商店中,使用产品请求对象。首先,创建SKProductsRequest 对象,并用产品 ID 列表初始化它。 产品请求取回有效产品的信息,以及有效产品 ID 列表,然后调用它代理处理结果。 委托必须实现 SKProductsRequestDelegate 协议来处理从应用商店返回的响应。列表2-2简单代码实现。
// 自定义方法
- (void)validateProductIdentifiers:(NSArray *)productIdentifiers
{
SKProductsRequest *productsRequest = [[SKProductsRequest alloc]
initWithProductIdentifiers:[NSSet setWithArray:productIdentifiers]];
productsRequest.delegate = self;
[productsRequest start];
}
// SKProductsRequestDelegate 代理方法
- (void)productsRequest:(SKProductsRequest *)request
didReceiveResponse:(SKProductsResponse *)response
{
self.products = response.products;
for (NSString *invalidIdentifier in response.invalidProductIdentifiers) {
// Handle any invalid product identifiers.
}
[self displayStoreUI]; // 自定义展示界面方法
}
当用户购买产品时,需要给该产品对象提供支付请求,因此保留对象数组对产品的引用,该产品对象数组会被返回给委托。 如果应用出售的产品列表改变,或许想要创建自定义类来把引用和其它信息一起封装到产品对象中---比如, 图片或从服务器获取的描述文本。 支付请求在“Requesting Payment.” 中讨论。
返回的无效产品 ID 通常表明应用产品 ID 列表中发生了一个 error, 尽管它或许意味着该产品在 iTunes Connect 中没有被正确配置。良好日志和界面可以帮开发者更简单地解决该类型问题。 在产品构建中,应用需要优雅地失败,这意味着显示剩余的应用界面以及忽略无效产品。 在开发构建中,显示 error 来引起对该问题的关注。在产品和开发构建中,都使用 NSLog 来编写消息(message)来解决,这样就有无效识别码的记录。 如果应用从服务器获取列表,还要定义日志机制来让应用程序把无效识别码发送回服务器。
三、呈现应用商店界面
因为应用程序商店设计对内购销售有很重要的影响,因此界面设计值得投入时间和精力。 在应用内设计苹果商店界面。 商店 Kit 不会为苹果商店提供界面。 只有充分了解应用程序和据根程序去设计商店界面,才能以最佳方式展示商店产品。
当设计和实现应用商店的界面时,请考虑以下指南:
1.只在用户可以支付时显示商店。 调用 SKPaymentQueue 类 的canMakePayments 方法来确认用户是否可以支付。 如果用户不能支付(比如,父母限制), 可以显示 界面 表明一个商店不可用,或者完全忽略商店部分。
2.在应用程序中自然地呈现商店产品。 在应用 UI 找到最好的位置来显示应用商店 UI。 在用户可以使用它们的时候呈现产品---比如,当用户尝试使用高级功能时让用户解锁该功能。 特别关注用户第一次使用你的应用时的用户体验。
3.组织产品,这样探索就变得既简单又愉快。 如果应用只有少量的商店产品,你可以在屏幕上显示所有产品;否则就把产品分组或分类让用户可以容易找到。 带有大数量产品的应用,比如,漫画书或者有很多报道的杂志,制作可以让用户简单地发现它们想要购买的新产品会特别有用。 给产品不同的名字和视觉效果,明确地区分它们--如有需要,包括明确地比较。
4、向用户交流商店产品的价值。 用户想要准确地知道他们即将要买什么。 综合应用商店的信息,比如产品价格和描述,和服务器或应用束中的附加数据,比如,产品的图片或 demos。 让用户在购买之前以限制的方式体现产品。 举个例子,有一款游戏,让用户选择购买新赛车(race cars)可以让用户用新车试跑一圈。 又或者在绘图应用中,让用户购买额外的笔刷可以让用户用新笔刷在小便签上绘图,让它查看笔刷之间的区别。 这类的设计给用户提供体验产品的机会并说服它们购买。
5、使用应用商店返回的语言环境(locale)和货币清楚地显示价格。 确保能简单找到产品的价格并能简单地识别。 不要尝试在你的 UI 中把价格转换为一个不同的货币,即使用户的语言环境和价格的语言环境不一样。 想想看,一个在美国的用户因为它的单位和日期格式更喜欢英国的语言环境。 你的应用根据英国的语言环境显示它的 UI,但它任然需要用应用商店指定的语言环境显示产品信息。 为了尝试匹配余下界面的英国语言环境,把价格转换为英国英镑是不正确的。 用户在美国有一个应用商店账号,它用美元支付,因此会给你的应用提供一个以美元为单位的价格。还有,你得应用将以美元为单元显示价格。列表2-3 显示了如何通过使用产品的语言环境信息来正确设置价格单位。
表2-3 得到正确设置价格单位
NSNumberFormatter *numberFormatter = [[NSNumberFormatter alloc] init];
[numberFormatter setFormatterBehavior:NSNumberFormatterBehavior10_4];
[numberFormatter setNumberStyle:NSNumberFormatterCurrencyStyle];
[numberFormatter setLocale:product.priceLocale];
NSString *formattedPrice = [numberFormatter stringFromNumber:product.price];
当用户选择好要购买的产品后,应用程序会连接到应用商店请求为该产品提供支付。
四、建议测试步骤
测试代码的每个部分来认证你已经正确地实现了该功能。
1、用测试账号登入应用商店
在 iTunes Connect 里创建一个测试账号,请看 iTunes Connect Developer Guide. 的“Creating Test User Accounts” 。在一个 iOS 开发设备中,在设置中退出应用商店。然后从 Xcode 构建和运行的应用。在一个 OS X 开发设备中,退出 Mac 应用商店。 然后在 Xcode 中构建应用并运行它。使用应用程序来完成内置购买。当提示登陆应用商店时,使用测试账号。 注意提示框中出现的[Environment: Sandbox]文字提示目前是连接到测试环境。如果[Environment: Sandbox] 没有出现,则表示使用的是产品环境。 确保应用运行在一个开发签署(development-signed)构建。签署的产品构建使用产品环境。
重要提示:不要用测试用户账号登陆产品环境,否则测试用户账号将无效不能再使用。
2、测试获取产品认证码列表
如果产品 ID 列表被整合在应用程序中,在它们加载后,在代码里设置一个断点,并认证包含了预期产品 ID 列表的 NSArray 对象。
如果产品 ID 是从服务器中获取的,使用网络浏览器如 Safari 或命令行工具如 curl 来手工获取 JSON 文件,并认证从服务器返回的数据,该数据包含了预期的产品 ID 列表。 同时还要认证得服务器正确地实现了标准 HTTP 缓存机制。
3、测试处理无效产品识别码
在产品 ID 列表中故意包含进一个无效识别码。(确保在测试成功后删除它),在产品构建中,验证应用程序显示了其余的商店UI,用户可以购买其他的产品。 在开发构建中,去验证应用程序中引起关注的问题。检查控制台日志并验证可以正确地识别无效产品识别码。
4、测试一个产品请求
使用测试好的产品 ID 列表,创建并递交SKProductsRequest 实例。 在代码中设置一个断点,检查有效和无效的产品识别码。 如果有无效产品识别码,检阅 iTunes Connect 中的产品并更正 JSON 文件或特性列表。