iOS应用内购买优化
之前写的一个iOS应用内购买模块的开发有很多地方需要考虑优化,其中最主要的是App意外结束和网络连接断开。应用内购买比较缓慢,再加上购买后需要请求Server更新数据库以及Server进行二次验证,整个过程时间比较长,对于可能发生的意外需要慎重考虑。
接下来以Web调用IAP模块进行虚拟币购买为例说明
处理概览
整个处理过程可分为下图几部分,其中主要关注的是:
- Client向App Store交易虚拟币购买
- Client请求Server更新余额
App意外结束
Client向App Store交易虚拟币购买
这个阶段主要通过Apple提供的StoreKit框架来处理,交易从addPayment
开始,至finishTransaction
结束。需要注意的是在结束之前,payment一直处于队列中,并且监听器的代理函数`updatedTransactions`会被反复调用。这样就方便我们处理这个阶段中App意外结束的情况了,只要:在
finishTransaction
之前处理IAP交易成功后的后续工作(更新Server余额等);- 待后续工作启动后才会将交易从队列中移除,这能保证后续工作总能启动,可也会带来App重启时重复启动后续工作的问题,这个可以用后面讨论的重传机制规避。
1
/// send request to Server ... [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
2. 在App启动之时就添加交易监听器(`SKPaymentTransactionObserver`)
- 我将添加交易监听器的处理移到了AppDelegate的`didFinishLaunchingWithOptions`函数中,使得App启动后就能处理先前意外结束留在队列中的交易。
- 这里推荐一篇好文[In-App Purchase Best Practices](https://developer.apple.com/library/ios/technotes/tn2387/_index.html),这*Best*可是Apple官方说的。[中文翻译在此](http://mobile.51cto.com/iphone-448610.htm)
- 为了保证App在重启时能处理意外结束的交易,我重构了代码:
之前的交易处理中,监听器IAPObserver
通过Block调用IAPViewController
来做IAP交易完成后的处理工作,它们之间是强依赖关系。
这样的问题是:重启时IAPViewController
还没有创建,此时监听到的未完成交易无法启动后续处理工作。
考虑到:
1. 后续处理工作主要是与Server的交互,可以独立成新的类;
2. 采用`NSNotification`的方式可以弱化耦合,也可以实现一对多的关联
于是我将代码改成下面的样子:
更新Server的工作封装成单例ServerManager
,和IAPViewController
一样,都接收来自IAPObserver
的消息,完成IAP交易成功后的工作。同时ServerManager
会发送通知给IAPViewController
,告知工作完成情况,由其来展示UI和通知调用者;当IAPViewController
没有创建时,并不会影响Client请求Server更新的操作。
- Client请求Server更新余额
这个阶段由Client向Server发更新余额请求。为了应对可能出现的App结束,可以设立重传机制,具体来说:- 先将请求数据本地化存储,再发起请求;
- 只有服务器正确返回且返回的是更新成功/更新重复/二次验证失败等情况时才移除本地存储的请求数据;
- 当App启动/发起虚拟币充值/网络连接恢复 时检查是否存在本地请求数据,如果存在则再次请求。
设立重传机制还可以应对网络连接断开出现的问题。
网络连接断开
网络连接断开处理首要是保证交易的一致性,即请求App Store购买虚拟币成功后也要成功更新Server虚拟币信息。Client分以下几个阶段考虑:
- 虚拟币充值发起前
- 不作特别处理
- 与App Store进行虚拟币购买
- 不作特别处理
- 发送更新虚拟币余额请求到Server前
- 本地存储相关信息,下次再请求,确定成功更新Server的余额后删除
- 收到Server的返回信息前
- 本地存储相关信息,下次再请求,确定成功更新Server的余额后删除
详细处理流程如下图:
至于Server与App Store进行二次验证时发生的网络断开,这里不讨论。
Demo
GitHub上的代码库,有处理不当的地方,欢迎提Issues。