最近在iOS上做一些事情,稍微记录下
1. 调用邮件客户端,或打开网页
都是调用[[UIApplication sharedApplication] openURL,传的参数不同而已。
注意前者在模拟器上是看不到的。貌似是因为模拟器上没有邮件客户端。
代码见 https://github.com/piaopolar/iOS_Samples/commit/7116ecc0de1d1dae5f8b00995b3f2b218be084d7
2. 启动程序时候进行版本更新检查
代码见 https://github.com/piaopolar/iOS_Samples/blob/master/iOS_Samples/AppDelegate.m
onCheckVersion
a. 其中用到了多线程,可以看到ObjC的多线程用起来比较舒服。
dispatch_async(dispatch_get_global_queue(0, 0), ^{
dispatch_async(dispatch_get_main_queue(), ^{
});
});
b. [infoDic objectForKey:@"appid"] 这个不是标准plist旧有的,是我们在plist中额外添加的值,不同语种版本应该有不同的Target,也有不同的appid,要和app Store上id对上。
c. 注意NSString的那段内存,在什么时候需要alloc,什么时候只要stringWithFormat就可以了,其实类比 char* 什么时候用new,什么时候直接= 就差不多明白了。
更多的情况,可以看这个讨论 http://www.cocoachina.com/bbs/simple/?t59782.html 个人感觉知道原理和复杂情况,可以对review和排错有帮助,但自己写还是写得简单点为好。
clickedButtonAtIndex
UIAlertView 按钮点击时候的回调。buttonIndex 从0开始
参考: http://blog.csdn.net/nong1209/article/details/7847346。
稍微要注意的是,这个功能本身是可以在模拟器上测试的,可是如果你装了iTunes,形如http://itunes.apple.com/lookup?id=459012102的网页可能会受到影响,给iTunes截获过去。
而形如 https://itunes.apple.com/us/app/conquer-online/id459012102?mt=8&uo=4 的trackViewUrl字段可能会由于苹果的用户限制,不让你打开。
这两个功能我已经都写成demo了,git://github.com/piaopolar/iOS_Samples.git 取下来就好,或直接在github上看。
3. 苹果新规,udid,Retina屏支持
Starting May 1, the App Store will no longer accept new apps or app updates that access UDIDs. (https://developer.apple.com/news/?id=3212013a)
Starting May 1, new apps and app updates submitted to the App Store must be built for iOS devices with Retina display and iPhone apps must also support the 4-inch display on iPhone 5.
五月一日起,新应用或者应用更新,若是其中使用了udid,则AppStore版本,苹果大爷审核就不让过了。并且你得支持Retina这种高分辨率的屏幕。
a. 关于udid
其实就是 UIDevice 的uniqueIdentifier 成员,这个不然用。虽然苹果建议说,你得用ASIdentifierManager的advertisingIdentifier。但更多的人会使用Mac地址的hash。
参考: 对IOS设备中UDID的一些思考 http://blog.csdn.net/xiaoguan2008/article/details/7457655
目前采用这种方案,https://github.com/gekitz/UIDevice-with-UniqueIdentifier-for-iOS-5/blob/master/Classes/UIDevice%2BIdentifierAddition.m
其实看运营或者广告商那边的需要来选择替代方案,但不管怎么样不应该再用uniqueIdentifier,否则今年5.1以后审核会遇到麻烦。
b. Retina屏,其实是苹果的一种高清屏,主要是分辨率比较高。
代码基本上不用做什么修改。主要是plist里面加载图片加几张。通过XCode查看的时候一个萝卜一个坑,补齐就好。
4. 编码。NSString
https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSString_Class/Reference/NSString.html
NSString构造的时候,本身就附带了编码信息(这点和C++的char*或std::string一样,仅仅只有字节数据信息,你可以先不论三七二十一给存好了,在用的时候在当成任意编码来理解,即使不能转换到这个编码页上。在多语种的时候,为了保持程序的正确,我们必须总是在心中惦记着,这段字符串数据是在哪个编码下才是有意义的)
正是因此,我们在构造的时候,就必须让NSString有正确的编码信息。
并非没有不显式指定编码来构造NSString的方法,如+ (id)stringWithCString:(const char *)cString,然而正如苹果所说,这样编码取决于系统:
stringWithCString:—note, though, that these are deprecated. The default C-string encoding is determined from system information and can’t be changed programmatically for an individual process.
与其这样不明不白的,不如用这个
(id)stringWithCString:(const char *)cString encoding:(NSStringEncoding)enc
另外CFStringEncoding和NSEncode是不同的,例如读取GB2312文件应该这样:
NSStringEncoding enc = CFStringConvertEncodingToNSStringEncoding(kCFStringEncodingGB_18030_2000);
NSString *textFile = [NSString stringWithContentsOfFile:filePath encoding:enc error:nil];
参考:iphone里读取gb2312(中文)编码文件或者二进制流。http://www.cnblogs.com/likwo/archive/2011/06/26/2090914.html
5. 本地通知 Local Notification
代码见 https://github.com/piaopolar/iOS_Samples/commit/376f8bd93aa0f5b5b7b0d4cdfb13531b0a21fb6f,我也放到上文说的demo里了
添加推送通知这样就好 [[UIApplication sharedApplication] scheduleLocalNotification:localNotif];
如果希望程序在前台的时候也做一些操作,可在 didReceiveLocalNotification 的回调做处理。
6. 推送通知 Push Notification
这个要求要有开发者主帐号来弄,就先不写demo了
参考 ios本地通知和远程通知 http://wangjun.easymorse.com/?p=1482 写得很详细了
7. 应用内购买 In App Purchase
http://developer.apple.com/library/mac/#documentation/NetworkingInternet/Conceptual/StoreKitGuide/Introduction/Introduction.html
我们这边采取的是服务器验证的模型 http://developer.apple.com/library/mac/#documentation/NetworkingInternet/Conceptual/StoreKitGuide/APIOverview/OverviewoftheStoreKitAPI.html#//apple_ref/doc/uid/TP40008267-CH100-SW14
下文的函数都可以在苹果官方文档找到 http://developer.apple.com/library/mac/#documentation/NetworkingInternet/Conceptual/StoreKitGuide/AddingaStoretoYourApplication/AddingaStoretoYourApplication.html#//apple_ref/doc/uid/TP40008267-CH101-SW2
a. 步骤1/2中 product identifiers 未必一定是从Developer Server取下来,如果你卖的东西相对固定,也可以直接放在客户端配置,省去这一步交互(当然这样灵活性会下降)
但得事先检查用户设置是否允许购买 [SKPaymentQueue canMakePayments]
b. 步骤3/4:
- (void) requestProductData
- (void) productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response
这里可能会遇到各种麻烦比如App Store返回的商品信息都是非法的(即didReceiveResponse的 [response.products count] 是0,[response.invalidProductIdentifiers count]是你查询的数目)
苹果大爷不给error code,请根据此列表排查 http://troybrant.net/blog/2010/01/invalid-product-ids/
如果测试时,使用越狱后的iOS 6的设备,使用非苹果发布时重签名过的ipa测试时,需要卸载App Sync才能正常测试。比如你发现iap.ini不对得改,连上91,把文件一覆盖,就得重新卸载App Sync。
但正常用户,无论越狱与否,iOS版本是5还是6,由于是从App Store下载,这个版本的签名是苹果重签过的,所以没问题。
c. 步骤4若正确执行完,即得到了可以正常购买的商品信息response.products。
步骤5只是程序UI自己的事情
步骤6开始是购买流程,发起购买即[[SKPaymentQueue defaultQueue] addPayment:payment];
这里的SKPayment也可以这样构造 SKPayment *payment = [SKPayment paymentWithProductIdentifier:kInAppPurchaseProUpgradeProductId];
成功发起购买后,苹果会让用户输入付钱用的帐号密码,这些不需要我们干涉。
有时候会发起失败,比如若是你上次正常购买流程没走完(未调用 [[SKPaymentQueue defaultQueue] finishTransaction: transaction])
d. 如果AppStore认为你可以买,则会顺利执行8,然后返回给你9。即会响应
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions 的回调。
注意,这个回调需要你实现注册一个观察者 [[SKPaymentQueue defaultQueue] addTransactionObserver:observer];
苹果的官方建议是在程序已启动就添加这个观察者,以免遗漏相应一些回调处理(Your application should add the observer when your application launches. The App Store remembers queued transactions even if your application exited before completing all transactions. Adding an observer during initialization ensures that all previously queued transactions are seen by your application.) 你闭上眼睛不看,事情还是会发生
这里需要做一些斟酌,通常启动后还没开始连上我们自己的服务器(帐号密码都没输呢),此时可能不方便把这个隶属于苹果帐号的交易,给关联到我们自己游戏服务器的角色上。
如果你没法安全的把这个信息延迟处理好,与其看了然后把状态搞乱了,还不如先说我不看(晚点添加观察者),因为苹果那边保存着交易队列,一个没有结束的交易,只要你不乱动,下一次还有机会看。
类似苹果和你说(updatedTransactions),那个人给钱了,你发货吧,你没听到(没有addTransactionObserver),苹果就说,我去,下次再找他说吧(SKPaymentQueue 总是存着的)。好过苹果和你说,那个人给钱了,你发货吧,你这边说知道了,我们看下一单吧(在updatedTransactions中做了[[SKPaymentQueue defaultQueue] finishTransaction: transaction]之类,然后说,欸?刚刚那个货是要给谁的(角色id还不知道呢)?
e. 步骤9后,根据苹果给的状态transaction.transactionState,做相应的处理。
非消耗品标识,SKPaymentTransactionStateRestored,这个不是交易流程,在游戏逻辑处理就好
失败 SKPaymentTransactionStateFailed ,就这个交易直接可以结束了。
购买成功 SKPaymentTransactionStatePurchased ,则把苹果给的凭证发到开发者服务器,准备验证和发货。
f. 步骤10开始是开发者服务器要做的事情
首先他要记录好苹果的凭证信息(步骤11)
然后向苹果验证这个凭证(步骤12)
如果苹果说凭证是有效的,把东西发给相应玩家(步骤13.14) 注意一个成功凭证只能发货一次。
服务器上,应该要记录好log,方便上线之后的异常问题排查。曾经遇到过部分玩家的支付问题,就是在一堆log文件中分析出问题所在的。
g. 无论验证是成功还是失败,都应该发给客户端结果,以便客户端能正常结束这个交易。 [[SKPaymentQueue defaultQueue] finishTransaction: transaction]; 否则会影响下一次的交易发起。
参考资料:http://blog.csdn.net/langresser/article/details/6783242 其他疑难问题都有解释了,其中有一句提到交易队列是在本地硬盘的,我感觉应该是在苹果那边的:) 里面也有其他的链接,就不附带了。
8. 杂项
a. 编译相关
提示 Prefix.pch has been modified since the precompiled header was built,先Clean了重编译
提示 ARC forbids ... 一般是引入的他人的库之类。
修改方法见 http://stackoverflow.com/questions/6646052/how-can-i-disable-arc-for-a-single-file-in-a-project
更多说明见 http://www.onevcat.com/2012/06/arc-hand-by-hand/
b. 签名相关
查看一个app的签名 终端下 codesign -dvvv x.app
注意一个应用,发布用来上传的签名和最后通过苹果审核后,给玩家下载的签名是不同的,后者经过苹果的重签名,见
参考 http://afp548.com/2012/06/05/re-signining-ios-apps/
查看一个签名的细节,可以直接文本打开 .app文件夹下的embedded.mobileprovision文件,即可看到诸如此类的签名标识
<key>UUID</key>
<string>BANANANA-1234-5678-ABCD-BANANANANANA</string>
又如签名过期时间
<key>ExpirationDate</key>
<date>2014-03-15T03:57:15Z</date>
事实上,除了用XCode,也可以直接使用命令行的codesign做重签名,重签名的批处理resign.sh,注意换号符要用unix的
IPA=$1
PROVISION="/Users/nd/Documents/resign/BANANANA-1234-5678-ABCD-BANANANANANA.mobileprovision"
CERTIFICATE="Banana Wireless Websoft Technology Limited" # must be in keychain
CENT="/Users/nd/Documents/resign/resign.xcent"
export CODESIGN_ALLOCATE="/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/codesign_allocate"
# unzip the ipa
rm -rf Payload
unzip -q "$IPA"
# remove the signature
#rm -rf Payload/*.app/_CodeSignature Payload/*.app/CodeResources
# replace the provision
cp "$PROVISION" Payload/*.app/embedded.mobileprovision
# sign with the new certificate
/usr/bin/codesign -f -s "$CERTIFICATE" --resource-rules Payload/*.app/ResourceRules.plist --entitlements "$CENT" Payload/*.app
# zip it back up
zip -qr resigned.ipa Payload
codesign -dvvv Payload/*.app
执行resign x.ipa即可。头几行要设置相关证书文件。