原文地址:http://download.csdn.net/download/fjp1230123/9138833html
跟随iOS开发技术发展的潮流,我将持续维护本文档的中文版,若是你喜欢这个译本,也请给个 Star 鼓励下~~react
本文档的英文原版在这里,感谢Futurice团队卓越的工做,为咱们提供这么优质的文档。android
知识是人类进步的阶梯ios
翻译,喵 ~~git
就像一个软件项目同样,这份文档若是咱们不持续维护就会逐渐失效,咱们鼓励你们参与到这个项目中来---仅需提交一个 issue 或发送一份 pull requestgithub
对其余移动平台感兴趣?个人Andriod开发最佳实践以及Windows App开发最佳实践可能会帮到你哦。web
iOS开发要上手比较困难,由于不管是 Objective-C 仍是 Swift 在别处都没有普遍被应用,iOS 这个平台彷佛对一切都有一套不一样的叫法。当你尝试在真机上跑程序时不免会磕磕碰碰。这份持续更新的文档就是你的救星!不管你是Cocoa王国的新手,或是老练到只想知道"最佳作法"是什么,这份文档都值得一读。固然,内容仅供参考,你有理由采起不一样的作法只要你愿意!objective-c
若是你想阅读指定的小节,能够经过目录直接跳转数据库
Xcode是绝大多数 iOS 开发者选择的 IDE,也是 Apple 惟一一个官方支持的 IDE. 也有一些其余的选择,最著名的可能就是 AppCode了。但除非你已经对 iOS 游刃有余,不然仍是用 Xcode 吧,尽管 Xcode 有一些缺点,但它如今还算是至关实用的!express
要安装 Xcode ,只需在 Mac 的 AppStore 上下载便可。它自带最新版的 SDK 和 iOS 模拟器,其余版本能够在 Preferences > Downloads处安装。
开始一个新的 iOS 项目时,一个常见的问题是:界面用代码写仍是用 Storyboard、xib来画?现有的 App 中两种方式都占有至关的市场。就此咱们须要考虑如下几点:
用代码写界面有啥好处?
用 Storyboard 画界面有啥好处?
为何不一样时使用二者?
为告终合二者之间的优势,你也能够采用一种混合的方法:使用 Storyboard 绘制最初的设计,对于时不时要作出改变修修补补来讲很是合适,你甚至能够邀请设计师参与到这个过程当中来。当 UI 的设计更为成熟和可靠时,你再过分到代码层进行配置,这有利于相互合做及代码的维护。
要为一个项目添加版本控制,最好第一步就添加一个恰当的.gitignore
文件。这样一来不须要的文件(如用户配置、临时文件等)就不会进入 repository(版本仓库)了。可喜的是,Github 已经帮咱们准备了 Objective-C版 和 Swift版。
若是你准备在工程中引入外部依赖(例如第三方库),Cocoapods提供了快速而便捷的集成方法。安装方法以下:
sudo gem install cocoapods
首先进入你的工程目录,而后运行
pod init
这样会建立一个 Podfile 文件,在这里集中管理全部的依赖。添加你所须要的依赖而后运行
pod install
来安装这些库,并把它们和你的工程一块儿放进一个 workspace
里。在 commit 的时候,推荐把依赖库在你的 repo 里安装好以后再 commit,最好不要让每一个开发者 checkout
后还要本身跑一下 pod install
。
注意:今后之后要用.workspace
打开工程,不要再用.xcproject
打开,不然代码编译不经过。
下面这条指令:
pod update
会把全部的 pod 都更新到 Podfile 容许的最新版本。你能够经过一系列的 语法来准确指定你对版本的要求。
把这些数以百计的源文件都保存在同一目录下,不根据工程结构来构建一个目录结构是没法想象的。你可使用下面的结构:
|- Models |- Views |- Controllers |- Stores |- Helpers
首先,在 Xcode 的 Project Navigator (左边栏)里,把这些目录创建为group (小小的黄色"文件夹"),建在工程的同名 group 下。而后,把每个 group 与工程路径下实际的文件夹连接起来,方法是:
一开始就应该把全部的文案放在本地化文件里,这不只有利于翻译,也能让你更快地找到面向用户的文本。你能够在 build scheme 里添加一个 launch 参数,指定在某种语言下启动 App,例如:
-AppleLanguages (Finnish)
对于更复杂的翻译,好比与名词的数量有关的复数形式(如 "1 person" 对应 "3 people"),你应该使用 .stringsdict
格式来替换普通的 localizable.strings
文件。只要你能习惯这种奇葩的语法,你就拥有了一个强大的工具,你能够根据须要(如俄语或阿拉伯语的规则)把名词变为"一个"、"一些"、"少数"和"许多"等复数形式。
更多关于本地化的信息,请参考2012年2月的Helsink iOS会议的幻灯片。其中大部分演讲至少到2014年10月为止仍然不过期!
建立被 prefix header 引入的一个 Constants.h
文件
不要用宏定义( #define
),用实际的常量定义
static CGFloat const XYZBrandingFontSizeSmall = 12.0f; static NSString * const XYZAwesomenessDeliveredNotificationName = @"foo";
常量类型安全并有更明确的做用域(在全部没有引入的文件中不能使用),不能被重定义,而且能够在调试器中使用。
App发布的时候把 Release 代码从原有的分支上隔离出来,而且加上适当的tag,是很好的作法,对于向公众分发(好比经过Appstore)的 app 这一点尤为重要。同时,涉及大量 commit 的 feature 应该在独立的分支上完成。 git-flow
是一个帮助你遵照这些规则的工具。它只是在 git 的分支和 tag 命令上简单加了一层包装,就能够帮助维护一套适当的分支结构,对于团队协做尤其有用。全部的开发都应该在 feature 对应的分支上完成(小改动在 develop 分支上完成),给 release 打上 app 版本的 tag,而后 commit 到 master 分支时只能用下面这条命令:
git flow release fininsh <version>
通常来讲,在工程里添加外部依赖要谨慎。固然,眼下某个第三方库能漂亮地解决你的问题,但或许不久以后就陷入了维护的泥淖,最后随着下一版 OS 的发布全线崩溃。另外一种状况是,原先只能经过引用外部库来实现的 feature,忽然官方 API 也支持了。在设计良好的项目里,把第三方库替换为官方的实现花不了多少功夫,但在未来会大有裨益。永远要优先考虑用苹果官方的框架(也是最好的框架)来解决问题!
所以,这一章有意写得比较简短。下面介绍的第三方库主要用来减小模板代码(例如 Auto Layout)或者用来解决复杂的、须要大量测试的问题,例如计算日期。随着你对 iOS 愈来愈精通,务必要四处看看它们的源码,熟悉它们所使用的底层框架。你会发现作好这些就能减轻许多重担了。
99.95% 的 iOS 开发者使用这个库,当 NSURLSession 本身自己也很是完善的时候, AFNetworking 仍然能凭借不少 App 需求的队列请求管理能力立于不败之地。
总的来讲,不要本身计算日期。DateTools 是一个通过完全测试的开源库,你能够放心使用它来作这种事情。
若是你更喜欢用代码写界面,你会用过 Apple 难用的 NSLayoutConstraint
的工厂方法或者 Visual Format Language
。前者很啰嗦,后者基于字符串不利于编译检查。
masonry 经过他本身的 DSL 来建立、更新和替换约束,利用语言丰富的操做符重载特性较优雅地实现了 AL。Swift 中一个相似的库是 Cartography。若是更加保守的话, FLKAutoLayout 是一个好的选择,它为原生API添加了一层简洁而不奇异的包装。
RACSignal
,或是返回值为 void
,参数中带有自定义的 completion block
的方法。如下是组建之间互发通知的一些常见手段:
events
给多个观察者的方法。耦合性很是松 - 没有任何对当前派发对象的引用的状况下,通知也可以在全局范围内被观察到。要确保你的 Model 是不可变的,他们用来把远程 API 的语义和类型转换为 App 适用的语义和类型。对Objective-C来讲 Github的Mantle是个不错的选择。在 Swift 中,你可使用 structs 而非 classes 来确保其不可变性,并使用一个相似 SwiftyJSON或者 Argo的解析库来作 JSON - Model 之间的转换。
今天 Apple 生态系统中丰富的屏幕尺寸及分屏多任务 iPad 的问世,使得设备和它的构成形式之间的界限愈来愈模糊。就像今天的网站要预先适配不一样的窗口尺寸同样,你的 App 也应该以一种优雅的方式来处理各类屏幕的尺寸变化。用户旋转设备或者在你的 App 旁边滑动第二个 iPad App 时(分屏多任务),这种需求简直是必须的。
你应该使用size classes和 AutoLayout 来申明你的视图约束,而不是直接操做视图的 frame。基于这些约束规则,系统将为视图 计算合适的 frame 并在环境改变时(切换设备或者分屏展现等)从新计算他们。
Apple 在设置布局约束的推荐方法中推荐在初始化方法中建立并激活你的布局约束.若是你须要动态地改变某些约束,hold 住他们的引用并在必要的时候关闭或激活他们。这主要用于在你想要系统执行批量更新以获取更好性能的时候, 执行 UIView
的 updateConstraints
(或者它对应的 UIViewController
的 updateViewContraints
)。但这样作的代价是你须要调用 setNeedsUpdateConstraints
方法, 这会增长代码的复杂性。
若是你在自定义的视图中重写 updateConstraints
,你应该明确指出你的视图支持基于约束的布局:
Swift:
override class func requiresConstraintBasedLayout() -> Bool { return true; }
Objective-C:
+ (BOOL)requiresConstraintBasedLayout { return YES; }
否则,系统可能不会如期调用 -updateConstraints
,而致使奇怪的 bug 。这一点上 Edward Huynh 提供的这个博客有更详细的解释。
要使用依赖注入,也就是说,应该把 controller 须要的数据用参数传进来,而非把全部的状态都保持在单例中。后者仅当这些状态的确是全局状态的状况下才适用。
Swift:
let fooViewController = FooViewController(viewModel: fooViewModel)
Objective-C
FooViewController *fooViewController = [[FooViewController alloc] initWithViewModel:fooViewModel];
尽可能避免在 view controller 中引入大量的本能够安全地放在其余地方实现的业务逻辑,这会让 view Controller 变得十分臃肿。Soroush Khanlou 有一篇 很好的博客 介绍了如何实现这种机制,而相似 MVVM 这样的程序架构将 view controller 当 views 对待,所以大大地减小了 view controller 的复杂度。
//GigStore.h typedef void (^FetchGigsBlock)(NSArray *gigs, NSError *error); - (void)fetchGigsForArtist:(Artist *)artist completion:(FetchGigsBlock)completion; //GigStore.m [GigStore sharedStore] fetchGigsForArtist:artist completion:^(NSArray *gigs, NSError *error) { if(!error) { //Do something with gigs } else { // :( } };
这样虽可行,但若是要发起几个链式请求,很容易致使回调深渊。
若是你身陷回调深渊,能够看看 ReactiveCocoa(RAC).这是一个多功能、多用途的库,它能够改变整个 App 的写法。但你也能够仅在适合用它的时候,零散地用一下。
Teehan+lax以及NSHipster很好地介绍了 RAC 概念(以及整个 FRP 的概念)。
//GigStore.h - (RACSignal *)gigsForArtist:(Artist *)artist; //GigsViewController.m [[[GigStore sharedStore] gigsForArtist:artist] subscribeNext:^(NSArray *gigs) { // Do something with gigs } error:^(NSError *error) { // :( }];
在这里咱们能够把 gig(演出) 信号与其余信号结合,所以能够在展现 gig 以前作一些修改、过滤等处理。
做为一个能够"在地面上移动"的移动应用,一般有某种存储模型把数据保存在某个地方,如硬盘上、本地数据库中或者远程的服务器上。在把模型对象的任意活动抽象出来的方面,Store 层也很是有用。
抓取数据一般是异步进行的,但它是意味着关闭后台请求仍是从硬盘反序列化一个大文件呢?你的 Store 层的 API 必须经过提供某种延期机制反映出这种状况,就像同步返回数据将引发线程阻塞那样。
若是你使用 ReactiveCocoa, 一般会选择 SignalProducer
做为返回类型。举个栗子,获取某个艺术家的演出信息将会产生下面这样 Signature:
Swift + RAC 3:
func fetchGigsForArtist(artist: Artist) -> SignalProducer<[Gig], NSError> { //... }
Objective-C + RAC 2:
- (RACSignal *)fetchGigsForArtist:(Artist *)artist { //... }
这里,返回的 SignalProducer
仅仅是获取演出列表的一个"配方"。仅当被订阅者(如:一个 viewModel )启动时才会执行获取演出列表的实际的动做,在数据返回前取消订阅将会取消该网络请求。
若是你不想使用信号、"期货"或相似的机制来表明你将来的数据,你也可使用常规的 block 回调。但要记住,block 块嵌套地进行链式调用,如在某个网络请求依赖于另外一个的结果的状况下,就会迅速变得很是笨重 --- 这种状况一般被称为“回调深渊"。
Asset catalogs是管理你全部项目可视化资源的最好方式,他们能够同时管理通用的以及设备相关的(iPhoen4-inch,iPhone Retina,iPad 等)资源,而且会经过他们的名字自动分组。告诉你的设计师如何添加它们,(Xcode有内建的 Git 客户端)能够节省不少时间,不然你会不少时间从邮件或者其余渠道把它们复制到代码库中。同时,这样也可让设计师即刻看到本身的改动,能够根据需求进行迭代。
Asset catalog 只会暴露出一套图片的名字,省略了每张图片实际的文件名。这样相似 button_large@2x.png
这类文件的命名空间仅限于 asset 内部,很好地避免了 asset 的命名冲突。然而,命名 asset 时遵循一些原则可让生活更轻松:
IconCheckmarkHighlighted.png // Universal, non-Retina IconCheckmarkHighlighted@2x.png // Universal, Retina IconCheckmarkHighlighted~iPhone.png // iPhone, non-Retina IconCheckmarkHighlighted@2x~iPhone.png // iPone, Retina IconCheckmarkHighlighted-568@2x~iPhone.png // iPhone, Retina, 4-inch IconCheckmarkHighlighted~iPad.png // iPad, non-Retina IconCheckmarkhighlighted@2x~iPad.png // iPad, Retina
其中的 -568h
、@2x
、~iPhone
以及~iPad
这些标示符本省并非必需的,但若是在文件名里加上它们,把文件拖动到 asset 时就能自动落到正确的"格子"上,所以能避免难以察觉的错误拖放。
你能够把设计师设计的原始图矢量图(PDFs)放进 Asset catalog,让 Xcode 来自动生成位图。这样能减小工程的复杂度(减小文件的个数)。
Apple 很是注意在 API 中保持命名一致性,即使是很是冗长的命名也如此。作 cocoa 开发时要遵循 Apple的命名规范, 这样能让加入项目的新人轻松许多。
如下是几条看了就能用上的基本规则:
以动词开头的方法,表示它执行的操做会形成一些影响( 译者注:有时候是函数反作用 ),可是不返回任何值。
- (void)loadView;
或者 - (void)startAnimating;
如下注释来自"维基魔杖"
在计算机科学中,函数反作用指当调用函数时,除了返回函数值以外,还对主调用函数产生附加的影响。例如修改全局变量(函数外的变量)或修改参数。
函数反作用会给程序设计带来没必要要的麻烦,给程序带来十分难以查找的错误,而且下降程序的可读性。严格的函数式语言要求函数必须无反作用。
任何以名字开头的方法,应该返回一个对象而且不能形成额外的影响 (即不带函数反作用)。
- (UINavigationItem *)navigationItem;
+ - (UILabel *)labelWithText:(NSString *)text;
尽量地区分这两种方法有不少好处。好比当您转换数据的时候就不该该形成额外的影响 ( 译者注:即函数反作用。数据转换的时候即上面那些使用名字开头的方法,其实是一种数据转换的方法),反过来也同样(没有函数反作用的函数应该返回某个对象,具体可参考严格意义上的函数式语言的要求)。这样的话可让具备函数反作用的代码保持在一个小的比较集中的区域内,能够帮助理解代码并有利于 Debug.(相似咱们的初始化全局变量的方法或者那些设置控制属性的方法等)
Pragma marks是给方法分组很好的方法,特别是在 ViewController 中。下面是 swift/Objective-C 语言的一个在 viewController 中常见的结构:
Swift MARK 风格:
import someExternalFramework class FooViewController : UIViewController, FoobarDelegate { let foo: Foo private let fooStringConstant = "FooConstant" private let floatConstant = 1234.5 //MARK: LifeCycle //Custom initializers go here //MARK: View LifeCycle override func viewDidLoad() { super.viewDidLoad() // ... } //MARK: Layout private func makeViewConstaints() { // ... } //MARK: User Interaction func foobarButtonTapped () { // ... } //MARK:FoobarDelegate func foobar(foobar: Foobar didSomethingWithFoo foo: Foo) { // ... } //MARK: Additional Helpers private func displayNameForFoo(foo: Foo) { // ... } }
Objective-C MARK风格:
#import "someModel.h" #import "someView.h" #import "someController.h" #import "someStore.h" #import "someHelper.h" #import <someExternalLibrary/someExternalLibraryHeader.h> static NSString * const XYZFooStringConstant = @"FoobarConstant"; static CGFloat const XYZFooFloatConstant = 1234.5; @interface XYZFooViewController () <XYZBarDelegate> @property (nonatomic, readonly, copy) Foo *foo; @property (nonatomic, strong) UILabel *label; //译者加 @end @implementation XYZFooViewController #pragma mark - LifeCycle - (instancetype)initWithFoo:(Foo *)foo; - (void)dealloc; #pragma mark - View LifeCycle - (void)viewDidLoad; - (void)viewWillAppear:(BOOL)animated; #pragma mark - Layout - (void)makeViewConstraints; #pragma mark - Public Interface - (void)startFooing; - (void)stopFooing; #pragma mark - User Interface - (void)foobarButtonTapped; #pragma mark - XYZFoobarDelegate - (void)foobar:(Foobar *)foobar didSomethingWithFoo:(Foo *)foo; #pragma mark - Internal Helpers - (NSString *)displayNameForFoo:(Foo *)foo; #pragma mark - Setter / Getter (译者加) - (UILabel *)label; @end
最重要的是让这些分块标记在工程里全部的类里保持一致!
Futurice(做者所在的公司)并无公司范围的编码风格指南。不过,仔细研究一下其余开发社区的 Objective-C 风格指南会很是有用,尽管有些部分可能只对特定公司有效或比较主观。
即便在这样一个时代,咱们信任咱们的便携设备,让其携带本身最私有的数据,但 app 的安全性仍然是一个常常被忽视的主题。尝试对数据安全性的设定找到一个良好的权衡,如下有一些简单的经久耐用的法则。另外,Apple 的 iOS安全指南是一个很好的入门教程。
若是你的 app 须要存储敏感数据,好比用户名、密码、认证 "Token" 或者一些我的的用户信息,你须要将它们保存在本地且不容许从 App 外部进行读取。毫不能用 NSUserDefaults
或别的存放在闪存的 plist 文件,也不能用 CoreData 来作,由于他们没有加密!绝大多数相似的状况,iOS KeyChain
是你的救星。若是不习惯直接使用 C 的 APIs,你可使用像 SSKeyChain或者 UICKeyChainStore 这样的一些封装。
在保存文件和密码时,确保正确而谨慎地选择恰当的安全等级。若是在设备锁定时(好比后台任务)你还须要访问文件,使用 "accessible after first unlock" 选项便可。其余的状况下,你应该要求设备在解锁以后才能访问数据。仅在须要使用敏感数据时才读取。
确保任什么时候候与服务端的 HTTP 通讯都是 TLS 加密的。为避免中间人攻击窃听你的加密数据,你能够设置证书约束(certificate pinning),像 AFNetworking或Alamofire这种流行的网络库都支持这样通讯。
发布你的 app 以前,应特别当心地设置好合适的日志级别。构建的产品(ipa文件)毫不能(日志)记录登陆密码、API的Tokens等相似的敏感信息,由于这很容易致使将他们泄露给公众。另外一方面,记录基本的控制流程能够帮你定位用户所遇到的问题。
当使用 UITextField
作密码输入时,记住设置它们的 secureTextEntry
属性为 true
,以避免明文显示密码。同时也应该关闭其"输入自动校订"的功能,并在任何合适的时刻清空密码,好比当 app 退到后台时。
当 app 退到后台时,清空剪切板能够避免密码或其余敏感数据被泄露。因为 iOS 可能须要你 app 的屏幕截图,以显示在 app 切换器中,因此在 applicationDidEnterBackground
方法返回前,应该确保 UI 上显示的全部敏感数据被清空。
建议把编译警告都打开,而且像对待 error 同样对待 warning。这份幻灯片论证了这一点。幻灯片里同时还讲到了如何在特定文件或特定代码段中忽略特定的 warning。
一句话,在 build setting 的 "Other Warning Flags"中至少要加入如下两个值:
-Wall
(开启很是多的额外的 warning)-Wextra
(开启许多额外的 warning)同时打开 build setting 里的 "Treat warnings as errors"
Clang 编译器 (也就是 Xcode 适用的编译器) 有一个静态分析器(static analyer),用来执行代码控制流和数据流的分析,能够发现许多编译器检查不出来的问题。
你能够在 Xcode 的 Product ---> Analyze里手动运行分析器。
分析器能够运行"shallow"和"deep"两种模式。后者要慢不少,可是有跨方法的控制流分析以及数据流分析,所以能发现更多问题。
建议:
由咱们员工Ali Rantakari创做的 Faux Pas 是一个出色的静态 Error 检测工具。它能分析你的代码库,找出你全然不知的错误。在发布任何iOS (或 Mac)App以前务必要运行它一次!
当 App 崩溃时,默认状况下 Xcode 不会进入 Debugger。要想进入 Debugger,须要添加一个 Exception Breakpoint (点击 Xcode 的 Debug Navigator 底部的"+"号),遇到 Exception 时就会暂停执行。在大部分状况下,你都能看到致使 Exception 的那行代码。 这种方法会捕捉到任何 Exception,包括已经作了处理的 Exception。若是Xcode常常停在良性的 Exception 上(好比第三方库),选择 Edit Breakpoint而后在 Exception 下拉菜单中选择 Objective-C能够减小这种状况的出现。
在 View 的 Debug 方面, Reveal和Spark Inspector是两个强大的可视化检查器,能够节约你大量的时间,尤为是用 AutoLayout 时想知道消失的视图去哪儿了的状况。 Xcode 也免费提供了一个相似的东西,不过只支持 iOS8 + ,而且还不够完善。
Xcode 自带一套评测工具 "Instruments"。它包含了众多的评测工具:评测内存使用、CPU、网络链接、图像等等。它自己是个庞然大物,但一个比较简单直接的用途是用 Allocations Instrument 来检测内存泄露。只需在 Xcode 中选择 Product ---> Profile,选择 Allocations instrument,点击 Record按钮,而后从 Allocation Summary中过滤一些有用的字符串,好比 app 中你本身写的类的类名前缀。在 Persistant一栏中的计数显示了每一个对象有多少个实例。若是某个类的实例个数一直胡乱增加,说明有内存泄露。
众所周知的是 Instruments 有一个 Automation 工具能够把 UI 交互录制为 JavaScript 文件并重放。UI Auto Monkey是一个脚本,它能够借助 Automation 在你的 App 上随机点击、清扫、旋转,这对压力测试/浸泡测试颇有帮助。
要格外注意的是,你在哪里以何种方式建立了巨耗资源的类。举个栗子,NSDateFormatter
建立起来很是耗资源,当快速而连续这么作时,好比在 tableView:cellForRowAtIndePath:
方法中,会真正减慢 App 的响应速度。你应该建立一个它的 static 实例,并在须要格式化日期时直接使用该实例。
强烈推荐在你的 App 中添加一个统计分析的框架,它能帮助你看到用户其实是怎么用你的 App 的。X 功能有价值吗?按钮 Y 太难找到了吗?要回答这些问题,能够把点击事件、计时以及其余可测的信息发送到一个能收集并可视化这些信息的服务上,好比 Google Tag Manager。Google Tag Manager 比 Google Analytics 更灵活一些,它在 App 和 Analytics 之间插了一个数据层,所以不须更新 app 就能够经过 web service 更改数据逻辑。
一种好的作法是加一个轻量的辅助类,好比 XYZAnalyticsHelper
,用来把 App 内部的 model 和数据格式(XYZModel, NSTimeInterval等)翻译成以字符串为主的数据层。
Swift :
fun pushAddItemEventWithItem(item: Item, editMode: EditMode) { let editModeString = nameForEditMode(editMode) pushToDataLayer([ "event" : "addItem", "itemIdentifier" : item.identifier, "editMode" : editModeString ]) }
Objective-C :
- (void)pushAddItemEventWithItem:(XYZItem *)item editMode:(XYZEditMode)editMode { NSString *editModeString = [self nameForEditMode:editMode]; [self pushToDataLayer:@{ @"event" : @"addItem", @"itemIdentifier" : item.identifier, @"editMode" : editModeString }]; }
这样作有一个额外的好处:在有必要时,能够清除掉整个统计分析框架,而 App 其他的部分不受任何影响。
首先应该让 App 把崩溃日志发送到某个服务器上,这样你才能看获得。可使用 PLCrashReporter结合本身的后台实现这个功能,但推荐使用已有的第三方服务,好比下面这些:
设置好这些以后,每次发布都要确保保存了 Xcode archive(.xcarchive).Archive 里包含编译出的二进制文件以及 Debug symbol( dSYM ),你须要这些数据来解析这个版本 App 的崩溃报告。
即便最简单的 App 也有不一样的构建方式。 Xcode 提供的最基本的区别是Debug和Release模式。后者的编译时优化要强不少,代价是损失了 Debug 的可能性。苹果建议你开发时使用 Debug模式,提交到 AppStore 的包用 Release模式编译。默认的模式(在 Xcode 里的运行/中止按钮旁边的下拉菜单能够更改)就是这么设置的,Run 用 Debug, Archive 用 Release。
不过对于真实的应用,这样仍是过于简单。你能够--- 不!是应该 --- 有几套不一样的环境,分别用于测试、更新和其余与服务相关的操做。每套环境均可以有本身的 base URL、log 级别、bundle identifier (这样就能够同时安装)、provision profile 等。所以,简单的 Debug/Release 不能知足需求。你能够在 Xcode 工程设置的 "Info" 一栏里添加更多的编译配置。
编译配置通常是在 Xcode 的界面里设置的,不过你也可使用配置文件(".xcconfig 文件")来设置。这样作的好处是:
#include
其余编译文件,帮助避免重复:
Common.xcconfig
文件,而后把它 #include
到其余文件里;#include "MyApp_Debug.xcconfig"
,而后覆盖相应的设置更多关于本话题的信息,能够参考这些幻灯片。
Targets 的概念比 project 低一个级别,即一个 project 能够有多个 targets,这些 targets 的设置 能够覆盖它的 project 的设置。粗略地说,每个 target 对应着代码库上下文中的一个 app。举个栗子,你可能针对不一样国家的 Appstore 有不一样的 App (都是从同一个代码库编译出来的)。每个 App 都须要 开发/staging(阶段性成果)/发布 的编译配置,所以用编译配置(build configurations)会比 target 更好一些。一个 App 对应只有一个 target 很是常见。
Schemes 告诉 Xcode 在 Run、Test、Profile、Analyze 和 Archive 时分别应该干什么。基本上,以上每一个操做的 Scheme 对应一个 target 和 一套编译配置。你也能够传递启动参数,好比 App 运行的语言(对于测试本地化很方便)或者设置一些 Debug 用的诊断标记。
Scheme 推荐的命名方式是 MyApp(<language>) [Environment]
:
MyApp (English) [Development] MyApp (German) [Development] MyApp [Testing] MyApp [Staging] MyApp [AppStore]
大部分环境下,语言是不须要标明的,由于 App 有可能经过 Xcode 以外的途径安装,好比 TestFlight,这样启动参数就会被忽略,这种状况下,只能手动设置设备语言来测试本地化。
将 app 安装到 iOS 设备上并不简单。那么咱们在这里会介绍几个核心的概念,理解了这些概念会对你部署 app 有很大帮助。
只要你想把应用跑在真机上,你就须要在编译时用一个 Apple 颁发的 证书来签名。每个证书对应一对公钥/私钥,私钥保存在你Mac的钥匙串中。证书有两种:
除了证书以外,还有 Provisioning profiles(配置文件),它是关联证书与设备的一环。一样有两类,分别用于开发和发布:
*
结尾,好比 "net.senink.*")。要把全部的证书和 profile 同步到你的设备上,在 Xcode 的 Preference 中的 Accounts里添加你的 Apple ID,而后双击团队(team)名称。底部有一个刷新按钮,但有时须要重启 Xcode 才能正常刷新。
有时你须要 Debug 一个 provisioning 问题。好比,Xcode 可能拒绝把包安装到设备上,由于设备不在(development 或 ad-hoc 的) profile 的设备列表上。这种状况下,你可使用 CraigHockenberry 优秀的 Provisioning插件定位到~/Library/MobileDevice/Provisioning Profiles
中,选择.mobileprovision
文件而后按空格键启动 Finder 的快速搜索功能,它会展现出很是丰富的信息,包括:设备、受权、证书和 App ID 等。
iTunes Connect是苹果 AppStore 上的 App 管理平台。上传一个包,Xcode 须要一个开发者帐户的 Apple ID 来签名。若是你有多个开发者帐户,想要分别上传他们的 App,可能遇到一些麻烦,由于不知道为何 一个特定的 Apple ID只能与一个 iTunes Connect 帐户相关联。替代的方法是,为每一个 iTunes Connect 帐户都建立一个新的 Apple ID,而后使用 Application Loader 代替 Xcode 来上传包。这样就把打包签名与上传 .ipa
文件的过程解耦了。
上传包以后,保持耐心,可能一个小时后这个版本的 App 才会出如今 Builds 一栏,当它出现后,你能够把它与 App 的版本信息关联起来,而后提交审核。
验证 App 内购的收据时,请记得进行如下检查:
设计你的 IAP 系统时,尽可能把售卖的内容存储再 server 端,而后仅当收到有效的、经过以上全部检查的收据后才把内容提供给 client 端。这样的设计防止了常规的盗版机制,而且--- 既然验证是在 server 端进行的 --- 你能够利用 Apple 的 HTTP 收据验证服务,而不是本身解析收据的 PKCS #7 / ASN.1
格式文件。
关于这个问题,更多的信息请参考Futurice blog:Validating in-app purchases in your iOS app
Futurice 署名 - 相同方式共享 4.0 国际许可协议(CC BY 4.0)
KevinHM,喜欢就 Follow 吧,更多精彩将分享给您!
文档的翻译也参考了iOS-good-practices-in-Chinese!