之前写的一个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重启时重复启动后续工作的问题,这个可以用后面讨论的重传机制规避。
/// send request to Server ... [[SKPaymentQueue defaultQueue] finishTransaction:transaction];
- 在App启动之时就添加交易监听器(
SKPaymentTransactionObserver
)- 我将添加交易监听器的处理移到了AppDelegate的
didFinishLaunchingWithOptions
函数中,使得App启动后就能处理先前意外结束留在队列中的交易。 - 这里推荐一篇好文In-App Purchase Best Practices,这Best可是Apple官方说的。中文翻译在此
- 为了保证App在重启时能处理意外结束的交易,我重构了代码:
- 我将添加交易监听器的处理移到了AppDelegate的
- 在
之前的交易处理中,监听器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。