├── LICENSE └── README.md /LICENSE: -------------------------------------------------------------------------------- 1 | Creative Commons Corporation (“Creative Commons”) is not a law firm and does not provide legal services or legal advice. Distribution of Creative Commons public licenses does not create a lawyer-client or other relationship. Creative Commons makes its licenses and related information available on an “as-is” basis. Creative Commons gives no warranties regarding its licenses, any material licensed under their terms and conditions, or any related information. Creative Commons disclaims all liability for damages resulting from their use to the fullest extent possible. 2 | 3 | Using Creative Commons Public Licenses 4 | 5 | Creative Commons public licenses provide a standard set of terms and conditions that creators and other rights holders may use to share original works of authorship and other material subject to copyright and certain other rights specified in the public license below. The following considerations are for informational purposes only, are not exhaustive, and do not form part of our licenses. 6 | 7 | Considerations for licensors: Our public licenses are intended for use by those authorized to give the public permission to use material in ways otherwise restricted by copyright and certain other rights. Our licenses are irrevocable. Licensors should read and understand the terms and conditions of the license they choose before applying it. Licensors should also secure all rights necessary before applying our licenses so that the public can reuse the material as expected. Licensors should clearly mark any material not subject to the license. This includes other CC-licensed material, or material used under an exception or limitation to copyright. More considerations for licensors. 8 | 9 | Considerations for the public: By using one of our public licenses, a licensor grants the public permission to use the licensed material under specified terms and conditions. If the licensor’s permission is not necessary for any reason–for example, because of any applicable exception or limitation to copyright–then that use is not regulated by the license. Our licenses grant only permissions under copyright and certain other rights that a licensor has authority to grant. Use of the licensed material may still be restricted for other reasons, including because others have copyright or other rights in the material. A licensor may make special requests, such as asking that all changes be marked or described. Although not required by our licenses, you are encouraged to respect those requests where reasonable. More considerations for the public. 10 | 11 | Creative Commons Attribution 4.0 International Public License 12 | 13 | By exercising the Licensed Rights (defined below), You accept and agree to be bound by the terms and conditions of this Creative Commons Attribution 4.0 International Public License ("Public License"). To the extent this Public License may be interpreted as a contract, You are granted the Licensed Rights in consideration of Your acceptance of these terms and conditions, and the Licensor grants You such rights in consideration of benefits the Licensor receives from making the Licensed Material available under these terms and conditions. 14 | 15 | Section 1 – Definitions. 16 | 17 | Adapted Material means material subject to Copyright and Similar Rights that is derived from or based upon the Licensed Material and in which the Licensed Material is translated, altered, arranged, transformed, or otherwise modified in a manner requiring permission under the Copyright and Similar Rights held by the Licensor. For purposes of this Public License, where the Licensed Material is a musical work, performance, or sound recording, Adapted Material is always produced where the Licensed Material is synched in timed relation with a moving image. 18 | Adapter's License means the license You apply to Your Copyright and Similar Rights in Your contributions to Adapted Material in accordance with the terms and conditions of this Public License. 19 | Copyright and Similar Rights means copyright and/or similar rights closely related to copyright including, without limitation, performance, broadcast, sound recording, and Sui Generis Database Rights, without regard to how the rights are labeled or categorized. For purposes of this Public License, the rights specified in Section 2(b)(1)-(2) are not Copyright and Similar Rights. 20 | Effective Technological Measures means those measures that, in the absence of proper authority, may not be circumvented under laws fulfilling obligations under Article 11 of the WIPO Copyright Treaty adopted on December 20, 1996, and/or similar international agreements. 21 | Exceptions and Limitations means fair use, fair dealing, and/or any other exception or limitation to Copyright and Similar Rights that applies to Your use of the Licensed Material. 22 | Licensed Material means the artistic or literary work, database, or other material to which the Licensor applied this Public License. 23 | Licensed Rights means the rights granted to You subject to the terms and conditions of this Public License, which are limited to all Copyright and Similar Rights that apply to Your use of the Licensed Material and that the Licensor has authority to license. 24 | Licensor means the individual(s) or entity(ies) granting rights under this Public License. 25 | Share means to provide material to the public by any means or process that requires permission under the Licensed Rights, such as reproduction, public display, public performance, distribution, dissemination, communication, or importation, and to make material available to the public including in ways that members of the public may access the material from a place and at a time individually chosen by them. 26 | Sui Generis Database Rights means rights other than copyright resulting from Directive 96/9/EC of the European Parliament and of the Council of 11 March 1996 on the legal protection of databases, as amended and/or succeeded, as well as other essentially equivalent rights anywhere in the world. 27 | You means the individual or entity exercising the Licensed Rights under this Public License. Your has a corresponding meaning. 28 | 29 | Section 2 – Scope. 30 | 31 | License grant. 32 | Subject to the terms and conditions of this Public License, the Licensor hereby grants You a worldwide, royalty-free, non-sublicensable, non-exclusive, irrevocable license to exercise the Licensed Rights in the Licensed Material to: 33 | reproduce and Share the Licensed Material, in whole or in part; and 34 | produce, reproduce, and Share Adapted Material. 35 | Exceptions and Limitations. For the avoidance of doubt, where Exceptions and Limitations apply to Your use, this Public License does not apply, and You do not need to comply with its terms and conditions. 36 | Term. The term of this Public License is specified in Section 6(a). 37 | Media and formats; technical modifications allowed. The Licensor authorizes You to exercise the Licensed Rights in all media and formats whether now known or hereafter created, and to make technical modifications necessary to do so. The Licensor waives and/or agrees not to assert any right or authority to forbid You from making technical modifications necessary to exercise the Licensed Rights, including technical modifications necessary to circumvent Effective Technological Measures. For purposes of this Public License, simply making modifications authorized by this Section 2(a)(4) never produces Adapted Material. 38 | Downstream recipients. 39 | Offer from the Licensor – Licensed Material. Every recipient of the Licensed Material automatically receives an offer from the Licensor to exercise the Licensed Rights under the terms and conditions of this Public License. 40 | No downstream restrictions. You may not offer or impose any additional or different terms or conditions on, or apply any Effective Technological Measures to, the Licensed Material if doing so restricts exercise of the Licensed Rights by any recipient of the Licensed Material. 41 | No endorsement. Nothing in this Public License constitutes or may be construed as permission to assert or imply that You are, or that Your use of the Licensed Material is, connected with, or sponsored, endorsed, or granted official status by, the Licensor or others designated to receive attribution as provided in Section 3(a)(1)(A)(i). 42 | 43 | Other rights. 44 | Moral rights, such as the right of integrity, are not licensed under this Public License, nor are publicity, privacy, and/or other similar personality rights; however, to the extent possible, the Licensor waives and/or agrees not to assert any such rights held by the Licensor to the limited extent necessary to allow You to exercise the Licensed Rights, but not otherwise. 45 | Patent and trademark rights are not licensed under this Public License. 46 | To the extent possible, the Licensor waives any right to collect royalties from You for the exercise of the Licensed Rights, whether directly or through a collecting society under any voluntary or waivable statutory or compulsory licensing scheme. In all other cases the Licensor expressly reserves any right to collect such royalties. 47 | 48 | Section 3 – License Conditions. 49 | 50 | Your exercise of the Licensed Rights is expressly made subject to the following conditions. 51 | 52 | Attribution. 53 | 54 | If You Share the Licensed Material (including in modified form), You must: 55 | retain the following if it is supplied by the Licensor with the Licensed Material: 56 | identification of the creator(s) of the Licensed Material and any others designated to receive attribution, in any reasonable manner requested by the Licensor (including by pseudonym if designated); 57 | a copyright notice; 58 | a notice that refers to this Public License; 59 | a notice that refers to the disclaimer of warranties; 60 | a URI or hyperlink to the Licensed Material to the extent reasonably practicable; 61 | indicate if You modified the Licensed Material and retain an indication of any previous modifications; and 62 | indicate the Licensed Material is licensed under this Public License, and include the text of, or the URI or hyperlink to, this Public License. 63 | You may satisfy the conditions in Section 3(a)(1) in any reasonable manner based on the medium, means, and context in which You Share the Licensed Material. For example, it may be reasonable to satisfy the conditions by providing a URI or hyperlink to a resource that includes the required information. 64 | If requested by the Licensor, You must remove any of the information required by Section 3(a)(1)(A) to the extent reasonably practicable. 65 | If You Share Adapted Material You produce, the Adapter's License You apply must not prevent recipients of the Adapted Material from complying with this Public License. 66 | 67 | Section 4 – Sui Generis Database Rights. 68 | 69 | Where the Licensed Rights include Sui Generis Database Rights that apply to Your use of the Licensed Material: 70 | 71 | for the avoidance of doubt, Section 2(a)(1) grants You the right to extract, reuse, reproduce, and Share all or a substantial portion of the contents of the database; 72 | if You include all or a substantial portion of the database contents in a database in which You have Sui Generis Database Rights, then the database in which You have Sui Generis Database Rights (but not its individual contents) is Adapted Material; and 73 | You must comply with the conditions in Section 3(a) if You Share all or a substantial portion of the contents of the database. 74 | 75 | For the avoidance of doubt, this Section 4 supplements and does not replace Your obligations under this Public License where the Licensed Rights include other Copyright and Similar Rights. 76 | 77 | Section 5 – Disclaimer of Warranties and Limitation of Liability. 78 | 79 | Unless otherwise separately undertaken by the Licensor, to the extent possible, the Licensor offers the Licensed Material as-is and as-available, and makes no representations or warranties of any kind concerning the Licensed Material, whether express, implied, statutory, or other. This includes, without limitation, warranties of title, merchantability, fitness for a particular purpose, non-infringement, absence of latent or other defects, accuracy, or the presence or absence of errors, whether or not known or discoverable. Where disclaimers of warranties are not allowed in full or in part, this disclaimer may not apply to You. 80 | To the extent possible, in no event will the Licensor be liable to You on any legal theory (including, without limitation, negligence) or otherwise for any direct, special, indirect, incidental, consequential, punitive, exemplary, or other losses, costs, expenses, or damages arising out of this Public License or use of the Licensed Material, even if the Licensor has been advised of the possibility of such losses, costs, expenses, or damages. Where a limitation of liability is not allowed in full or in part, this limitation may not apply to You. 81 | 82 | The disclaimer of warranties and limitation of liability provided above shall be interpreted in a manner that, to the extent possible, most closely approximates an absolute disclaimer and waiver of all liability. 83 | 84 | Section 6 – Term and Termination. 85 | 86 | This Public License applies for the term of the Copyright and Similar Rights licensed here. However, if You fail to comply with this Public License, then Your rights under this Public License terminate automatically. 87 | 88 | Where Your right to use the Licensed Material has terminated under Section 6(a), it reinstates: 89 | automatically as of the date the violation is cured, provided it is cured within 30 days of Your discovery of the violation; or 90 | upon express reinstatement by the Licensor. 91 | For the avoidance of doubt, this Section 6(b) does not affect any right the Licensor may have to seek remedies for Your violations of this Public License. 92 | For the avoidance of doubt, the Licensor may also offer the Licensed Material under separate terms or conditions or stop distributing the Licensed Material at any time; however, doing so will not terminate this Public License. 93 | Sections 1, 5, 6, 7, and 8 survive termination of this Public License. 94 | 95 | Section 7 – Other Terms and Conditions. 96 | 97 | The Licensor shall not be bound by any additional or different terms or conditions communicated by You unless expressly agreed. 98 | Any arrangements, understandings, or agreements regarding the Licensed Material not stated herein are separate from and independent of the terms and conditions of this Public License. 99 | 100 | Section 8 – Interpretation. 101 | 102 | For the avoidance of doubt, this Public License does not, and shall not be interpreted to, reduce, limit, restrict, or impose conditions on any use of the Licensed Material that could lawfully be made without permission under this Public License. 103 | To the extent possible, if any provision of this Public License is deemed unenforceable, it shall be automatically reformed to the minimum extent necessary to make it enforceable. If the provision cannot be reformed, it shall be severed from this Public License without affecting the enforceability of the remaining terms and conditions. 104 | No term or condition of this Public License will be waived and no failure to comply consented to unless expressly agreed to by the Licensor. 105 | Nothing in this Public License constitutes or may be interpreted as a limitation upon, or waiver of, any privileges and immunities that apply to the Licensor or You, including from the legal processes of any jurisdiction or authority. 106 | 107 | Creative Commons is not a party to its public licenses. Notwithstanding, Creative Commons may elect to apply one of its public licenses to material it publishes and in those instances will be considered the “Licensor.” Except for the limited purpose of indicating that material is shared under a Creative Commons public license or as otherwise permitted by the Creative Commons policies published at creativecommons.org/policies, Creative Commons does not authorize the use of the trademark “Creative Commons” or any other trademark or logo of Creative Commons without its prior written consent including, without limitation, in connection with any unauthorized modifications to any of its public licenses or any other arrangements, understandings, or agreements concerning use of licensed material. For the avoidance of doubt, this paragraph does not form part of the public licenses. 108 | 109 | Creative Commons may be contacted at creativecommons.org. 110 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | iOS Good Practices 2 | ================== 3 | 4 | _Just like software, this document will rot unless we take care of it. We encourage everyone to help us on that – just open an issue or send a pull request!_ 5 | 6 | Interested in other mobile platforms? Our [Best Practices in Android Development][android-best-practices] and [Windows App Development Best Practices][windows-app-development-best-practices] documents have got you covered. 7 | 8 | [android-best-practices]: https://github.com/futurice/android-best-practices 9 | [windows-app-development-best-practices]: https://github.com/futurice/windows-app-development-best-practices 10 | 11 | ## Why? 12 | 13 | Getting on board with iOS can be intimidating. Neither Swift nor Objective-C are widely used elsewhere, the platform has its own names for almost everything, and it's a bumpy road for your code to actually make it onto a physical device. This living document is here to help you, whether you're taking your first steps in Cocoaland or you're curious about doing things "the right way". Everything below is just suggestions, so if you have a good reason to do something differently, by all means go for it! 14 | 15 | ## Contents 16 | 17 | If you are looking for something specific, you can jump right into the relevant section from here. 18 | 19 | 1. [Getting Started](#getting-started) 20 | 1. [Common Libraries](#common-libraries) 21 | 1. [Architecture](#architecture) 22 | 1. [Stores](#stores) 23 | 1. [Assets](#assets) 24 | 1. [Coding Style](#coding-style) 25 | 1. [Security](#security) 26 | 1. [Diagnostics](#diagnostics) 27 | 1. [Analytics](#analytics) 28 | 1. [Building](#building) 29 | 1. [Deployment](#deployment) 30 | 1. [In-App Purchases (IAP)](#in-app-purchases-iap) 31 | 1. [License](#license) 32 | 33 | ## Getting Started 34 | 35 | ### Human Interface Guidelines 36 | 37 | If you're coming from another platform, do take some time to familiarize yourself with Apple's [Human Interface Guidelines][ios-hig] for the platform. There is a strong emphasis on good design in the iOS world, and your app should be no exception. The guidelines also provide a handy overview of native UI elements, technologies such as 3D Touch or Wallet, and icon dimensions for designers. 38 | 39 | [ios-hig]: https://developer.apple.com/ios/human-interface-guidelines/ 40 | 41 | ### Xcode 42 | 43 | [Xcode][xcode] is the IDE of choice for most iOS developers, and the only one officially supported by Apple. There are some alternatives, of which [AppCode][appcode] is arguably the most famous, but unless you're already a seasoned iOS person, go with Xcode. Despite its shortcomings, it's actually quite usable nowadays! 44 | 45 | To install, simply download [Xcode on the Mac App Store][xcode-app-store]. It comes with the newest SDK and simulators, and you can install more stuff under _Preferences > Downloads_. 46 | 47 | [xcode]: https://developer.apple.com/xcode/ 48 | [appcode]: https://www.jetbrains.com/objc/ 49 | [xcode-app-store]: https://itunes.apple.com/us/app/xcode/id497799835 50 | 51 | ### Project Setup 52 | 53 | A common question when beginning an iOS project is whether to write all views in code or use Interface Builder with Storyboards or XIB files. Both are known to occasionally result in working software. However, there are a few considerations: 54 | 55 | #### Why code? 56 | * Storyboards are more prone to version conflicts due to their complex XML structure. This makes merging much harder than with code. 57 | * It's easier to structure and reuse views in code, thereby keeping your codebase [DRY][dry]. 58 | * All information is in one place. In Interface Builder you have to click through all the inspectors to find what you're looking for. 59 | * Storyboards introduce coupling between your code and UI, which can lead to crashes e.g. when an outlet or action is not set up correctly. These issues are not detected by the compiler. 60 | 61 | [dry]: https://en.wikipedia.org/wiki/Don%27t_repeat_yourself 62 | 63 | #### Why Storyboards? 64 | * For the less technically inclined, Storyboards can be a great way to contribute to the project directly, e.g. by tweaking colors or layout constraints. However, this requires a working project setup and some time to learn the basics. 65 | * Iteration is often faster since you can preview certain changes without building the project. 66 | * Custom fonts and UI elements are represented visually in Storyboards, giving you a much better idea of the final appearance while designing. 67 | * For [size classes][size-classes], Interface Builder gives you a live layout preview for the devices of your choice, including iPad split-screen multitasking. 68 | 69 | [size-classes]: http://futurice.com/blog/adaptive-views-in-ios-8 70 | 71 | #### Why not both? 72 | To get the best of both worlds, you can also take a hybrid approach: Start off by sketching the initial design with Storyboards, which are great for tinkering and quick changes. You can even invite designers to participate in this process. As the UI matures and reliability becomes more important, you then transition into a code-based setup that's easier to maintain and collaborate on. 73 | 74 | ### Ignores 75 | 76 | A good first step when putting a project under version control is to have a decent `.gitignore` file. That way, unwanted files (user settings, temporary files, etc.) will never even make it into your repository. Luckily, GitHub has us covered for both [Swift][swift-gitignore] and [Objective-C][objc-gitignore]. 77 | 78 | [swift-gitignore]: https://github.com/github/gitignore/blob/master/Swift.gitignore 79 | [objc-gitignore]: https://github.com/github/gitignore/blob/master/Objective-C.gitignore 80 | 81 | ### Dependency Management 82 | 83 | #### CocoaPods 84 | 85 | If you're planning on including external dependencies (e.g. third-party libraries) in your project, [CocoaPods][cocoapods] offers easy and fast integration. Install it like so: 86 | 87 | sudo gem install cocoapods 88 | 89 | To get started, move inside your iOS project folder and run 90 | 91 | pod init 92 | 93 | This creates a Podfile, which will hold all your dependencies in one place. After adding your dependencies to the Podfile, you run 94 | 95 | pod install 96 | 97 | to install the libraries and include them as part of a workspace which also holds your own project. For reasons stated [here][committing-pods-cocoapods] and [here][committing-pods], we recommend committing the installed dependencies to your own repo, instead of relying on having each developer run `pod install` after a fresh checkout. 98 | 99 | Note that from now on, you'll need to open the `.xcworkspace` file instead of `.xcproject`, or your code will not compile. The command 100 | 101 | pod update 102 | 103 | will update all pods to the newest versions permitted by the Podfile. You can use a wealth of [operators][cocoapods-pod-syntax] to specify your exact version requirements. 104 | 105 | [cocoapods]: https://cocoapods.org/ 106 | [cocoapods-pod-syntax]: http://guides.cocoapods.org/syntax/podfile.html#pod 107 | [committing-pods]: https://www.dzombak.com/blog/2014/03/including-pods-in-source-control.html 108 | [committing-pods-cocoapods]: https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 109 | 110 | #### Carthage 111 | 112 | [Carthage][carthage] takes the ["simple, not easy"][simple-made-easy] approach by building your dependencies into binary frameworks, without magically integrating them with your project in any way. This also greatly reduces build times, because your dependencies have already been compiled by the time you start building. 113 | 114 | There is no centralized repository of projects, which means any library that can be compiled into a framework supports Carthage out of the box. 115 | 116 | To get started, follow the [instructions][carthage-instructions] in Carthage's documentation. 117 | 118 | [carthage]: https://github.com/Carthage/Carthage 119 | [simple-made-easy]: http://www.infoq.com/presentations/Simple-Made-Easy 120 | [carthage-instructions]: https://github.com/Carthage/Carthage#installing-carthage 121 | 122 | ### Project Structure 123 | 124 | To keep all those hundreds of source files from ending up in the same directory, it's a good idea to set up some folder structure depending on your architecture. For instance, you can use the following: 125 | 126 | ├─ Models 127 | ├─ Views 128 | ├─ Controllers (or ViewModels, if your architecture is MVVM) 129 | ├─ Stores 130 | ├─ Helpers 131 | 132 | First, create them as groups (little yellow "folders") within the group with your project's name in Xcode's Project Navigator. Then, for each of the groups, link them to an actual directory in your project path by opening their File Inspector on the right, hitting the little gray folder icon, and creating a new subfolder with the name of the group in your project directory. 133 | 134 | #### Localization 135 | 136 | Keep all user strings in localization files right from the beginning. This is good not only for translations, but also for finding user-facing text quickly. You can add a launch argument to your build scheme to launch the app in a certain language, e.g. 137 | 138 | -AppleLanguages (Finnish) 139 | 140 | For more complex translations such as plural forms that depending on a number of items (e.g. "1 person" vs. "3 people"), you should use the [`.stringsdict` format][stringsdict-format] instead of a regular `localizable.strings` file. As soon as you've wrapped your head around the crazy syntax, you have a powerful tool that knows how to make plurals for "one", some", "few" and "many" items, as needed [e.g. in Russian or Arabic][language-plural-rules]. 141 | 142 | Find more information about localization in [these presentation slides][l10n-slides] from the February 2012 HelsinkiOS meetup. Most of the talk is still relevant. 143 | 144 | [stringsdict-format]: https://developer.apple.com/library/prerelease/ios/documentation/MacOSX/Conceptual/BPInternational/StringsdictFileFormat/StringsdictFileFormat.html 145 | [language-plural-rules]: http://www.unicode.org/cldr/charts/latest/supplemental/language_plural_rules.html 146 | [l10n-slides]: https://speakerdeck.com/hasseg/localization-practicum 147 | 148 | #### Constants 149 | 150 | Keep your constants' scope as small as possible. For instance, when you only need it inside a class, it should live in that class. Those constants that need to be truly app-wide should be kept in one place. In Swift, you can use enums defined in a `Constants.swift` file to group, store and access your app-wide constants in a clean way: 151 | 152 | ```swift 153 | 154 | enum Config { 155 | static let baseURL = NSURL(string: "http://www.example.org/")! 156 | static let splineReticulatorName = "foobar" 157 | } 158 | 159 | enum Color { 160 | static let primaryColor = UIColor(red: 0.22, green: 0.58, blue: 0.29, alpha: 1.0) 161 | static let secondaryColor = UIColor.lightGray 162 | 163 | // A visual way to define colours within code files is to use #colorLiteral 164 | // This syntax will present you with colour picker component right on the code line 165 | static let tertiaryColor = #colorLiteral(red: 0.22, green: 0.58, blue: 0.29, alpha: 1.0) 166 | } 167 | 168 | ``` 169 | 170 | When using Objective-C, keep app-wide constants in a `Constants.h` file that is included in the prefix header. 171 | 172 | Instead of preprocessor macro definitions (via `#define`), use actual constants: 173 | 174 | static CGFloat const XYZBrandingFontSizeSmall = 12.0f; 175 | static NSString * const XYZAwesomenessDeliveredNotificationName = @"foo"; 176 | 177 | Actual constants are type-safe, have more explicit scope (they’re not available in all imported/included files until undefined), cannot be redefined or undefined in later parts of the code, and are available in the debugger. 178 | 179 | 180 | ### Branching Model 181 | 182 | Especially when distributing an app to the public (e.g. through the App Store), it's a good idea to isolate releases to their own branch with proper tags. Also, feature work that involves a lot of commits should be done on its own branch. [`git-flow`][gitflow-github] is a tool that helps you follow these conventions. It is simply a convenience wrapper around Git's branching and tagging commands, but can help maintain a proper branching structure especially for teams. Do all development on feature branches (or on `develop` for smaller work), tag releases with the app version, and commit to master only via 183 | 184 | git flow release finish 185 | 186 | [gitflow-github]: https://github.com/nvie/gitflow 187 | 188 | 189 | ### Minimum iOS Version Requirement 190 | 191 | It’s useful to make an early decision on the minimum iOS version you want to support in your project: knowing which OS versions you need to develop and test against, and which system APIs you can rely on, helps you estimate your workload, and enables you to determine what’s possible and what’s not. 192 | 193 | Use these resources to gather the data necessary for making this choice: 194 | 195 | * Official “first-party” resources: 196 | * [Apple’s world-wide iOS version penetration statistics](https://developer.apple.com/support/app-store/): The primary public source for version penetration stats. Prefer more localized and domain-specific statistics, if available. 197 | * Third-party resources: 198 | * [iOS Support Matrix](http://iossupportmatrix.com): Useful for determining which specific device models are ruled out by a given minimum OS version requirement. 199 | * [DavidSmith: iOS Version Stats](https://david-smith.org/iosversionstats/): Version penetration stats for David Smith’s Audiobooks apps. 200 | * [Mixpanel Trends: iOS versions](https://mixpanel.com/trends/#report/ios_frag): Version penetration stats from Mixpanel. 201 | 202 | 203 | ## Common Libraries 204 | 205 | Generally speaking, make it a conscious decision to add an external dependency to your project. Sure, this one neat library solves your problem now, but maybe later gets stuck in maintenance limbo, with the next OS version that breaks everything being just around the corner. Another scenario is that a feature only achievable with external libraries suddenly becomes part of the official APIs. In a well-designed codebase, switching out the implementation is a small effort that pays off quickly. Always consider solving the problem using Apple's extensive (and mostly excellent) frameworks first! 206 | 207 | Therefore this section has been deliberately kept rather short. The libraries featured here tend to reduce boilerplate code (e.g. Auto Layout) or solve complex problems that require extensive testing, such as date calculations. As you become more proficient with iOS, be sure to dive into the source here and there, and acquaint yourself with their underlying Apple frameworks. You'll find that those alone can do a lot of the heavy lifting. 208 | 209 | ### AFNetworking/Alamofire 210 | 211 | The majority of iOS developers use one of these network libraries. While `NSURLSession` is surprisingly powerful by itself, [AFNetworking][afnetworking-github] and [Alamofire][alamofire-github] remain unbeaten when it comes to actually managing queues of requests, which is pretty much a requirement of any modern app. We recommend AFNetworking for Objective-C projects and Alamofire for Swift projects. While the two frameworks have subtle differences, they share the same ideology and are published by the same foundation. 212 | 213 | [afnetworking-github]: https://github.com/AFNetworking/AFNetworking 214 | [alamofire-github]: https://github.com/Alamofire/Alamofire 215 | 216 | ### DateTools 217 | As a general rule, [don't write your date calculations yourself][timezones-youtube]. Luckily, in [DateTools][datetools-github] you get an MIT-licensed, thoroughly tested library that covers pretty much all your calendar needs. 218 | 219 | [timezones-youtube]: https://www.youtube.com/watch?v=-5wpm-gesOY 220 | [datetools-github]: https://github.com/MatthewYork/DateTools 221 | 222 | ### Auto Layout Libraries 223 | If you prefer to write your views in code, chances are you've heard of either Apple's awkward syntaxes – the regular `NSLayoutConstraint` factory or the so-called [Visual Format Language][visual-format-language]. The former is extremely verbose and the latter based on strings, which effectively prevents compile-time checking. Fortunately, they've addressed the issue in iOS 9, allowing [a more concise specification of constraints][nslayoutanchor]. 224 | 225 | If you're stuck with an earlier iOS version, [Masonry/SnapKit][snapkit-github] remedies the problem by introducing its own [DSL][dsl-wikipedia] to make, update and replace constraints. [PureLayout][purelayout-github] solves the same problem using Cocoa API style. For Swift, there is also [Cartography][cartography-github], which builds on the language's powerful operator overloading features. For the more conservative, [FLKAutoLayout][flkautolayout-github] offers a clean, but rather non-magical wrapper around the native APIs. 226 | 227 | [visual-format-language]: https://developer.apple.com/library/ios/documentation/UserExperience/Conceptual/AutolayoutPG/VisualFormatLanguage.html 228 | [nslayoutanchor]: https://developer.apple.com/library/prerelease/ios/documentation/AppKit/Reference/NSLayoutAnchor_ClassReference/index.html 229 | [snapkit-github]: https://github.com/SnapKit/ 230 | [purelayout-github]: https://github.com/PureLayout/PureLayout 231 | [dsl-wikipedia]: https://en.wikipedia.org/wiki/Domain-specific_language 232 | [cartography-github]: https://github.com/robb/Cartography 233 | [flkautolayout-github]: https://github.com/floriankugler/FLKAutoLayout 234 | 235 | ## Architecture 236 | 237 | * [Model-View-Controller-Store (MVCS)][mvcs] 238 | * This is the default Apple architecture (MVC), extended by a Store layer that vends Model instances and handles the networking, caching etc. 239 | * Every Store exposes to the view controllers either `signals` or `void` methods with custom completion blocks. 240 | * [Model-View-ViewModel (MVVM)][mvvm] 241 | * Motivated by "massive view controllers": MVVM considers `UIViewController` subclasses part of the View and keeps them slim by maintaining all state in the ViewModel. 242 | * To learn more about it, check out Bob Spryn's [fantastic introduction][sprynthesis-mvvm]. 243 | * [View-Interactor-Presenter-Entity-Routing (VIPER)][viper] 244 | * Rather exotic architecture that might be worth looking into in larger projects, where even MVVM feels too cluttered and testability is a major concern. 245 | 246 | [mvcs]: http://programmers.stackexchange.com/questions/184396/mvcs-model-view-controller-store 247 | [mvvm]: https://www.objc.io/issues/13-architecture/mvvm/ 248 | [sprynthesis-mvvm]: http://www.sprynthesis.com/2014/12/06/reactivecocoa-mvvm-introduction/ 249 | [viper]: https://www.objc.io/issues/13-architecture/viper/ 250 | 251 | ### “Event” Patterns 252 | 253 | These are the idiomatic ways for components to notify others about things: 254 | 255 | * __Delegation:__ _(one-to-one)_ Apple uses this a lot (some would say, too much). Use when you want to communicate stuff back e.g. from a modal view. 256 | * __Callback blocks:__ _(one-to-one)_ Allow for a more loose coupling, while keeping related code sections close to each other. Also scales better than delegation when there are many senders. 257 | * __Notification Center:__ _(one-to-many)_ Possibly the most common way for objects to emit “events” to multiple observers. Very loose coupling — notifications can even be observed globally without reference to the dispatching object. 258 | * __Key-Value Observing (KVO):__ _(one-to-many)_ Does not require the observed object to explicitly “emit events” as long as it is _Key-Value Coding (KVC)_ compliant for the observed keys (properties). Usually not recommended due to its implicit nature and the cumbersome standard library API. 259 | * __Signals:__ _(one-to-many)_ The centerpiece of [ReactiveCocoa][reactivecocoa-github], they allow chaining and combining to your heart's content, thereby offering a way out of "callback hell". 260 | 261 | ### Models 262 | 263 | Keep your models immutable, and use them to translate the remote API's semantics and types to your app. For Objective-C projects, Github's [Mantle](https://github.com/Mantle/Mantle) is a good choice. In Swift, you can use structs instead of classes to ensure immutability, and use Swift's [Codable][codableLink] to do the JSON-to-model mapping. There are also few third party libraries available. [SwiftyJSON][swiftyjson] and [Argo][argo] are the popular among them. 264 | 265 | [swiftyjson]: https://github.com/SwiftyJSON/SwiftyJSON 266 | [argo]: https://github.com/thoughtbot/Argo 267 | [codableLink]: https://developer.apple.com/documentation/foundation/archives_and_serialization/encoding_and_decoding_custom_types 268 | 269 | ### Views 270 | 271 | With today's wealth of screen sizes in the Apple ecosystem and the advent of split-screen multitasking on iPad, the boundaries between devices and form factors become increasingly blurred. Much like today's websites are expected to adapt to different browser window sizes, your app should handle changes in available screen real estate in a graceful way. This can happen e.g. if the user rotates the device or swipes in a secondary iPad app next to your own. 272 | 273 | Instead of manipulating view frames directly, you should use [size classes][size-classes] and Auto Layout to declare constraints on your views. The system will then calculate the appropriate frames based on these rules, and re-evaluate them when the environment changes. 274 | 275 | Apple's [recommended approach][wwdc-autolayout-mysteries] for setting up your layout constraints is to create and activate them once during initialization. If you need to change your constraints dynamically, hold references to them and then deactivate/activate them as required. The main use case for `UIView`'s `updateConstraints` (or its `UIViewController` counterpart, `updateViewConstraints`) is when you want the system to perform batch updates for better performance. However, this comes at the cost of having to call `setNeedsUpdateConstraints` elsewhere in your code, increasing its complexity. 276 | 277 | If you override `updateConstraints` in a custom view, you should explicitly state that your view requires a constraint-based layout: 278 | 279 | Swift: 280 | ```swift 281 | override class var requiresConstraintBasedLayout: Bool { 282 | return true 283 | } 284 | ``` 285 | 286 | Objective-C: 287 | ```objective-c 288 | + (BOOL)requiresConstraintBasedLayout { 289 | return YES 290 | } 291 | ``` 292 | 293 | Otherwise, you may encounter strange bugs when the system doesn't call `updateConstraints()` as you would expect it to. [This blog post][edward-huynh-requiresconstraintbasedlayout] by Edward Huynh offers a more detailed explanation. 294 | 295 | [wwdc-autolayout-mysteries]: https://developer.apple.com/videos/wwdc/2015/?id=219 296 | [edward-huynh-requiresconstraintbasedlayout]: http://www.edwardhuynh.com/blog/2013/11/24/the-mystery-of-the-requiresconstraintbasedlayout/ 297 | 298 | ### Controllers 299 | 300 | Use dependency injection, i.e. pass any required objects in as parameters, instead of keeping all state around in singletons. The latter is okay only if the state _really_ is global. 301 | 302 | Swift: 303 | ```swift 304 | let fooViewController = FooViewController(withViewModel: fooViewModel) 305 | ``` 306 | 307 | Objective-C: 308 | ```objective-c 309 | FooViewController *fooViewController = [[FooViewController alloc] initWithViewModel:fooViewModel]; 310 | ``` 311 | 312 | Try to avoid bloating your view controllers with logic that can safely reside in other places. Soroush Khanlou has a [good writeup][khanlou-destroy-massive-vc] of how to achieve this, and architectures like [MVVM](#architecture) treat view controllers as views, thereby greatly reducing their complexity. 313 | 314 | [khanlou-destroy-massive-vc]: http://khanlou.com/2014/09/8-patterns-to-help-you-destroy-massive-view-controller/ 315 | 316 | ## Stores 317 | 318 | At the "ground level" of a mobile app is usually some kind of model storage, that keeps its data in places such as on disk, in a local database, or on a remote server. This layer is also useful to abstract away any activities related to the vending of model objects, such as caching. 319 | 320 | Whether it means kicking off a backend request or deserializing a large file from disk, fetching data is often asynchronous in nature. Your store's API should reflect this by offering some kind of deferral mechanism, as synchronously returning the data would cause the rest of your app to stall. 321 | 322 | If you're using [ReactiveCocoa][reactivecocoa-github], `SignalProducer` is a natural choice for the return type. For instance, fetching gigs for a given artist would yield the following signature: 323 | 324 | Swift + ReactiveSwift: 325 | ```swift 326 | func fetchGigs(for artist: Artist) -> SignalProducer<[Gig], Error> { 327 | // ... 328 | } 329 | ``` 330 | 331 | ObjectiveC + ReactiveObjC: 332 | ```objective-c 333 | - (RACSignal *> *)fetchGigsForArtist:(Artist *)artist { 334 | // ... 335 | } 336 | ``` 337 | 338 | Here, the returned `SignalProducer` is merely a "recipe" for getting a list of gigs. Only when started by the subscriber, e.g. a view model, will it perform the actual work of fetching the gigs. Unsubscribing before the data has arrived would then cancel the network request. 339 | 340 | If you don't want to use signals, futures or similar mechanisms to represent your future data, you can also use a regular callback block. Keep in mind that chaining or nesting such blocks, e.g. in the case where one network request depends on the outcome of another, can quickly become very unwieldy – a condition generally known as "callback hell". 341 | 342 | ## Assets 343 | 344 | [Asset catalogs][asset-catalogs] are the best way to manage all your project's visual assets. They can hold both universal and device-specific (iPhone 4-inch, iPhone Retina, iPad, etc.) assets and will automatically serve the correct ones for a given name. Teaching your designer(s) how to add and commit things there (Xcode has its own built-in Git client) can save a lot of time that would otherwise be spent copying stuff from emails or other channels to the codebase. It also allows them to instantly try out their changes and iterate if needed. 345 | 346 | [asset-catalogs]: http://help.apple.com/xcode/mac/8.0/#/dev10510b1f7 347 | 348 | ### Using Bitmap Images 349 | 350 | Asset catalogs expose only the names of image sets, abstracting away the actual file names within the set. This nicely prevents asset name conflicts, as files such as `button_large@2x.png` are now namespaced inside their image sets. Appending the modifiers `-568h`, `@2x`, `~iphone` and `~ipad` are not required per se, but having them in the file name when dragging the file to an image set will automatically place them in the right "slot", thereby preventing assignment mistakes that can be hard to hunt down. 351 | 352 | ### Using Vector Images 353 | 354 | You can include the original [vector graphics (PDFs)][vector-assets] produced by designers into the asset catalogs, and have Xcode automatically generate the bitmaps from that. This reduces the complexity of your project (the number of files to manage.) 355 | 356 | [vector-assets]: http://martiancraft.com/blog/2014/09/vector-images-xcode6/ 357 | 358 | ### Image optimisation 359 | 360 | Xcode automatically tries to optimise resources living in asset catalogs (yet another reason to use them). Developers can choose from lossless and lossy compression algorithms. App icons are an exception: Apps with large or unoptimised app icons are known to be rejected by Apple. For app icons and more advanced optimisation of PNG files we recommend using [pngcrush][pngcrush-website] or [ImageOptim][imageoptim-website], its GUI counterpart. 361 | 362 | [pngcrush-website]: http://pmt.sourceforge.net/pngcrush/ 363 | [imageoptim-website]:https://imageoptim.com/mac 364 | 365 | 366 | ## Coding Style 367 | 368 | ### Naming 369 | 370 | Apple pays great attention to keeping naming consistent. Adhering to their [coding guidelines for Objective-C][cocoa-coding-guidelines] and [API design guidelines for Swift][swift-api-design-guidelines] makes it much easier for new people to join the project. 371 | 372 | [cocoa-coding-guidelines]: https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/CodingGuidelines/CodingGuidelines.html 373 | [swift-api-design-guidelines]: https://swift.org/documentation/api-design-guidelines/ 374 | 375 | Here are some basic takeaways you can start using right away: 376 | 377 | A method beginning with a _verb_ indicates that it performs some side effects, but won't return anything: 378 | `- (void)loadView;` 379 | `- (void)startAnimating;` 380 | 381 | Any method starting with a _noun_, however, returns that object and should do so without side effects: 382 | `- (UINavigationItem *)navigationItem;` 383 | `+ (UILabel *)labelWithText:(NSString *)text;` 384 | 385 | It pays off to keep these two as separated as possible, i.e. not perform side effects when you transform data, and vice versa. That will keep your side effects contained to smaller sections of the code, which makes it more understandable and facilitates debugging. 386 | 387 | ### Structure 388 | 389 | `MARK:` comments (Swift) and [pragma marks][nshipster-pragma-marks] (Objective-C) are a great way to group your methods, especially in view controllers. Here is a Swift example for a common structure that works with almost any view controller: 390 | 391 | ```swift 392 | 393 | import SomeExternalFramework 394 | 395 | class FooViewController : UIViewController, FoobarDelegate { 396 | 397 | let foo: Foo 398 | 399 | private let fooStringConstant = "FooConstant" 400 | private let floatConstant = 1234.5 401 | 402 | // MARK: Lifecycle 403 | 404 | // Custom initializers go here 405 | 406 | // MARK: View Lifecycle 407 | 408 | override func viewDidLoad() { 409 | super.viewDidLoad() 410 | // ... 411 | } 412 | 413 | // MARK: Layout 414 | 415 | private func makeViewConstraints() { 416 | // ... 417 | } 418 | 419 | // MARK: User Interaction 420 | 421 | func foobarButtonTapped() { 422 | // ... 423 | } 424 | 425 | // MARK: FoobarDelegate 426 | 427 | func foobar(foobar: Foobar, didSomethingWithFoo foo: Foo) { 428 | // ... 429 | } 430 | 431 | // MARK: Additional Helpers 432 | 433 | private func displayNameForFoo(foo: Foo) { 434 | // ... 435 | } 436 | 437 | } 438 | ``` 439 | 440 | The most important point is to keep these consistent across your project's classes. 441 | 442 | [nshipster-pragma-marks]: http://nshipster.com/pragma/ 443 | 444 | ### External Style Guides 445 | 446 | Futurice does not have company-level guidelines for coding style. It can however be useful to peruse the style guides of other software companies, even if some bits can be quite company-specific or opinionated. 447 | 448 | * GitHub: [Swift](https://github.com/github/swift-style-guide) and [Objective-C](https://github.com/github/objective-c-style-guide) 449 | * Ray Wenderlich: [Swift](https://github.com/raywenderlich/swift-style-guide) and [Objective-C](https://github.com/raywenderlich/objective-c-style-guide) 450 | * Google: [Objective-C](https://google.github.io/styleguide/objcguide.xml) 451 | * The New York Times: [Objective-C](https://github.com/NYTimes/objective-c-style-guide) 452 | * Sam Soffes: [Objective-C](https://gist.github.com/soffes/812796) 453 | * Luke Redpath: [Objective-C](http://lukeredpath.co.uk/blog/2011/06/28/my-objective-c-style-guide/) 454 | 455 | ## Security 456 | 457 | Even in an age where we trust our portable devices with the most private data, app security remains an often-overlooked subject. Try to find a good trade-off given the nature of your data; following just a few simple rules can go a long way here. A good resource to get started is Apple's own [iOS Security Guide][apple-security-guide]. 458 | 459 | ### Data Storage 460 | 461 | If your app needs to store sensitive data, such as a username and password, an authentication token or some personal user details, you need to keep these in a location where they cannot be accessed from outside the app. Never use `NSUserDefaults`, other plist files on disk or Core Data for this, as they are not encrypted! In most such cases, the iOS Keychain is your friend. If you're uncomfortable working with the C APIs directly, you can use a wrapper library such as [SSKeychain][sskeychain] or [UICKeyChainStore][uickeychainstore]. 462 | 463 | When storing files and passwords, be sure to set the correct protection level, and choose it conservatively. If you need access while the device is locked (e.g. for background tasks), use the "accessible after first unlock" variety. In other cases, you should probably require that the device is unlocked to access the data. Only keep sensitive data around while you need it. 464 | 465 | ### Networking 466 | 467 | Keep any HTTP traffic to remote servers encrypted with TLS at all times. To avoid man-in-the-middle attacks that intercept your encrypted traffic, you can set up [certificate pinning][certificate-pinning]. Popular networking libraries such as [AFNetworking][afnetworking-github] and [Alamofire][alamofire-github] support this out of the box. 468 | 469 | ### Logging 470 | 471 | Take extra care to set up proper log levels before releasing your app. Production builds should never log passwords, API tokens and the like, as this can easily cause them to leak to the public. On the other hand, logging the basic control flow can help you pinpoint issues that your users are experiencing. 472 | 473 | ### User Interface 474 | 475 | When using `UITextField`s for password entry, remember to set their `secureTextEntry` property to `true` to avoid showing the password in cleartext. You should also disable auto-correction for the password field, and clear the field whenever appropriate, such as when your app enters the background. 476 | 477 | When this happens, it's also good practice to clear the Pasteboard to avoid passwords and other sensitive data from leaking. As iOS may take screenshots of your app for display in the app switcher, make sure to clear any sensitive data from the UI _before_ returning from `applicationDidEnterBackground`. 478 | 479 | [apple-security-guide]: https://www.apple.com/business/docs/iOS_Security_Guide.pdf 480 | [sskeychain]: https://github.com/soffes/sskeychain 481 | [uickeychainstore]: https://github.com/kishikawakatsumi/UICKeyChainStore 482 | [certificate-pinning]: https://possiblemobile.com/2013/03/ssl-pinning-for-increased-app-security/ 483 | [alamofire-github]: https://github.com/Alamofire/Alamofire 484 | 485 | ## Diagnostics 486 | 487 | ### Compiler warnings 488 | 489 | Enable as many compiler warnings as possible and treat those warnings as errors -- [it will be worth it in the long run][warnings-slides]. 490 | 491 | For Objective-C code, add these values to the _“Other Warning Flags”_ build setting: 492 | 493 | - `-Wall` _(enables lots of additional warnings)_ 494 | - `-Wextra` _(enables more additional warnings)_ 495 | 496 | and then enable the _“Treat warnings as errors”_ build setting. 497 | 498 | To treat warnings as errors for Swift code, add `-warnings-as-errors` to the _"Other Swift Flags"_ build setting. 499 | 500 | [warnings-slides]: https://speakerdeck.com/hasseg/the-compiler-is-your-friend 501 | 502 | ### Clang Static Analyzer 503 | 504 | The Clang compiler (which Xcode uses) has a _static analyzer_ that performs control and data flow analysis on your code and checks for lots of errors that the compiler cannot. 505 | 506 | You can manually run the analyzer from the _Product → Analyze_ menu item in Xcode. 507 | 508 | The analyzer can work in either “shallow” or “deep” mode. The latter is much slower but may find more issues due to cross-function control and data flow analysis. 509 | 510 | Recommendations: 511 | 512 | - Enable _all_ of the checks in the analyzer (by enabling all of the options in the “Static Analyzer” build setting sections). 513 | - Enable the _“Analyze during ‘Build’”_ build setting for your release build configuration to have the analyzer run automatically during release builds. (Seriously, do this — you’re not going to remember to run it manually.) 514 | - Set the _“Mode of Analysis for ‘Analyze’”_ build setting to _Shallow (faster)_. 515 | - Set the _“Mode of Analysis for ‘Build’”_ build setting to _Deep_. 516 | 517 | ### [Faux Pas](http://fauxpasapp.com/) 518 | 519 | Created by our very own [Ali Rantakari][ali-rantakari-twitter], Faux Pas is a fabulous static error detection tool. It analyzes your codebase and finds issues you had no idea even existed. There is no Swift support yet, but the tool also offers plenty of language-agnostic rules. Be sure to run it before shipping any iOS (or Mac) app! 520 | 521 | _(Note: all Futurice employees get a free license to this — just ask Ali.)_ 522 | 523 | [ali-rantakari-twitter]: https://twitter.com/AliRantakari 524 | 525 | ### Debugging 526 | 527 | When your app crashes, Xcode does not break into the debugger by default. To achieve this, add an exception breakpoint (click the "+" at the bottom of Xcode's Breakpoint Navigator) to halt execution whenever an exception is raised. In many cases, you will then see the line of code responsible for the exception. This catches any exception, even handled ones. If Xcode keeps breaking on benign exceptions in third party libraries e.g., you might be able to mitigate this by choosing _Edit Breakpoint_ and setting the _Exception_ drop-down to _Objective-C_. 528 | 529 | For view debugging, [Reveal][reveal] and [Spark Inspector][spark-inspector] are two powerful visual inspectors that can save you hours of time, especially if you're using Auto Layout and want to locate views that are collapsed or off-screen. Xcode also has integrated [view debugger][xcode-view-debugging] which is good enough and free to use. 530 | 531 | [reveal]: http://revealapp.com/ 532 | [spark-inspector]: http://sparkinspector.com 533 | [xcode-view-debugging]: https://developer.apple.com/library/prerelease/content/documentation/DeveloperTools/Conceptual/debugging_with_xcode/chapters/special_debugging_workflows.html 534 | 535 | ### Profiling 536 | 537 | Xcode comes with a profiling suite called Instruments. It contains a myriad of tools for profiling memory usage, CPU, network communications, graphics and much more. It's a complex beast, but one of its more straight-forward use cases is tracking down memory leaks with the Allocations instrument. Simply choose _Product_ > _Profile_ in Xcode, select the Allocations instrument, hit the Record button and filter the Allocation Summary on some useful string, like the prefix of your own app's class names. The count in the Persistent column then tells you how many instances of each object you have. Any class for which the instance count increases indiscriminately indicates a memory leak. 538 | 539 | Pay extra attention to how and where you create expensive classes. `NSDateFormatter`, for instance, is very expensive to create and doing so in rapid succession, e.g. inside a `tableView:cellForRowAtIndexPath:` method, can really slow down your app. Instead, keep a static instance of it around for each date format that you need. 540 | 541 | ## Analytics 542 | 543 | Including some analytics framework in your app is strongly recommended, as it allows you to gain insights on how people actually use it. Does feature X add value? Is button Y too hard to find? To answer these, you can send events, timings and other measurable information to a service that aggregates and visualizes them – for instance, [Google Tag Manager][google-tag-manager]. The latter is more versatile than Google Analytics in that it inserts a data layer between app and Analytics, so that the data logic can be modified through a web service without having to update the app. 544 | 545 | [google-tag-manager]: https://www.google.com/analytics/tag-manager/ 546 | 547 | A good practice is to create a slim helper class, e.g. `AnalyticsHelper`, that handles the translation from app-internal models and data formats (`FooModel`, `NSTimeInterval`, …) to the mostly string-based data layer: 548 | 549 | ```swift 550 | 551 | func pushAddItemEvent(with item: Item, editMode: EditMode) { 552 | let editModeString = name(for: editMode) 553 | 554 | pushToDataLayer([ 555 | "event": "addItem", 556 | "itemIdentifier": item.identifier, 557 | "editMode": editModeString 558 | ]) 559 | } 560 | 561 | ``` 562 | 563 | This has the additional advantage of allowing you to swap out the entire Analytics framework behind the scenes if needed, without the rest of the app noticing. 564 | 565 | ### Crash Logs 566 | 567 | First you should make your app send crash logs onto a server somewhere so that you can access them. You can implement this manually (using [PLCrashReporter][plcrashreporter] and your own backend) but it’s recommended that you use an existing service instead — for example one of the following: 568 | 569 | * [Fabric](https://get.fabric.io) 570 | * [HockeyApp](http://hockeyapp.net) 571 | * [Crittercism](https://www.crittercism.com) 572 | * [Splunk MINTexpress](https://mint.splunk.com) 573 | * [Instabug](https://instabug.com/) 574 | 575 | [plcrashreporter]: https://www.plcrashreporter.org 576 | 577 | Once you have this set up, ensure that you _save the Xcode archive (`.xcarchive`)_ of every build you release. The archive contains the built app binary and the debug symbols (`dSYM`) which you will need to symbolicate crash reports from that particular version of your app. 578 | 579 | ## Building 580 | 581 | This section contains an overview of this topic — please refer here for more comprehensive information: 582 | 583 | - [iOS Developer Library: Xcode Concepts][apple-xcode-concepts] 584 | - [Samantha Marshall: Managing Xcode][pewpew-managing-xcode] 585 | 586 | [apple-xcode-concepts]: https://developer.apple.com/library/ios/featuredarticles/XcodeConcepts/ 587 | [pewpew-managing-xcode]: http://pewpewthespells.com/blog/managing_xcode.html 588 | 589 | ### Build Configurations 590 | 591 | Even simple apps can be built in different ways. The most basic separation that Xcode gives you is that between _debug_ and _release_ builds. For the latter, there is a lot more optimization going on at compile time, at the expense of debugging possibilities. Apple suggests that you use the _debug_ build configuration for development, and create your App Store packages using the _release_ build configuration. This is codified in the default scheme (the dropdown next to the Play and Stop buttons in Xcode), which commands that _debug_ be used for Run and _release_ for Archive. 592 | 593 | However, this is a bit too simple for real-world applications. You might – no, [_should!_][futurice-environments] – have different environments for testing, staging and other activities related to your service. Each might have its own base URL, log level, bundle identifier (so you can install them side-by-side), provisioning profile and so on. Therefore a simple debug/release distinction won't cut it. You can add more build configurations on the "Info" tab of your project settings in Xcode. 594 | 595 | [futurice-environments]: http://futurice.com/blog/five-environments-you-cannot-develop-without 596 | 597 | #### `xcconfig` files for build settings 598 | 599 | Typically build settings are specified in the Xcode GUI, but you can also use _configuration settings files_ (“`.xcconfig` files”) for them. The benefits of using these are: 600 | 601 | - You can add comments to explain things. 602 | - You can `#include` other build settings files, which helps you avoid repeating yourself: 603 | - If you have some settings that apply to all build configurations, add a `Common.xcconfig` and `#include` it in all the other files. 604 | - If you e.g. want to have a “Debug” build configuration that enables compiler optimizations, you can just `#include "MyApp_Debug.xcconfig"` and override one of the settings. 605 | - Conflict resolution and merging becomes easier. 606 | 607 | Find more information about this topic in [these presentation slides][xcconfig-slides]. 608 | 609 | [xcconfig-slides]: https://speakerdeck.com/hasseg/xcode-configuration-files 610 | 611 | ### Targets 612 | 613 | A target resides conceptually below the project level, i.e. a project can have several targets that may override its project settings. Roughly, each target corresponds to "an app" within the context of your codebase. For instance, you could have country-specific apps (built from the same codebase) for different countries' App Stores. Each of these will need development/staging/release builds, so it's better to handle those through build configurations, not targets. It's not uncommon at all for an app to only have a single target. 614 | 615 | ### Schemes 616 | 617 | Schemes tell Xcode what should happen when you hit the Run, Test, Profile, Analyze or Archive action. Basically, they map each of these actions to a target and a build configuration. You can also pass launch arguments, such as the language the app should run in (handy for testing your localizations!) or set some diagnostic flags for debugging. 618 | 619 | A suggested naming convention for schemes is `MyApp () [Environment]`: 620 | 621 | MyApp (English) [Development] 622 | MyApp (German) [Development] 623 | MyApp [Testing] 624 | MyApp [Staging] 625 | MyApp [App Store] 626 | 627 | For most environments the language is not needed, as the app will probably be installed through other means than Xcode, e.g. TestFlight, and the launch argument thus be ignored anyway. In that case, the device language should be set manually to test localization. 628 | 629 | ## Deployment 630 | 631 | Deploying software on iOS devices isn't exactly straightforward. That being said, here are some central concepts that, once understood, will help you tremendously with it. 632 | 633 | ### Signing 634 | 635 | Whenever you want to run software on an actual device (as opposed to the simulator), you will need to sign your build with a __certificate__ issued by Apple. Each certificate is linked to a private/public keypair, the private half of which resides in your Mac's Keychain. There are two types of certificates: 636 | 637 | * __Development certificate:__ Every developer on a team has their own, and it is generated upon request. Xcode might do this for you, but it's better not to press the magic "Fix issue" button and understand what is actually going on. This certificate is needed to deploy development builds to devices. 638 | * __Distribution certificate:__ There can be several, but it's best to keep it to one per organization, and share its associated key through some internal channel. This certificate is needed to ship to the App Store, or your organization's internal "enterprise app store". 639 | 640 | ### Provisioning 641 | 642 | Besides certificates, there are also __provisioning profiles__, which are basically the missing link between devices and certificates. Again, there are two types to distinguish between development and distribution purposes: 643 | 644 | * __Development provisioning profile:__ It contains a list of all devices that are authorized to install and run the software. It is also linked to one or more development certificates, one for each developer that is allowed to use the profile. The profile can be tied to a specific app or use a wildcard App ID (*). The latter is [discouraged][jared-sinclair-signing-tips], because Xcode is notoriously bad at picking the correct files for signing unless guided in the right direction. Also, certain capabilities like Push Notifications or App Groups require an explicit App ID. 645 | 646 | * __Distribution provisioning profile:__ There are three different ways of distribution, each for a different use case. Each distribution profile is linked to a distribution certificate, and will be invalid when the certificate expires. 647 | * __Ad-Hoc:__ Just like development profiles, it contains a whitelist of devices the app can be installed to. This type of profile can be used for beta testing on 100 devices per year. For a smoother experience and up to 1000 distinct users, you can use Apple's newly acquired [TestFlight][testflight] service. Supertop offers a good [summary of its advantages and issues][testflight-discussion]. 648 | * __App Store:__ This profile has no list of allowed devices, as anyone can install it through Apple's official distribution channel. This profile is required for all App Store releases. 649 | * __Enterprise:__ Just like App Store, there is no device whitelist, and the app can be installed by anyone with access to the enterprise's internal "app store", which can be just a website with links. This profile is available only on Enterprise accounts. 650 | 651 | [jared-sinclair-signing-tips]: http://blog.jaredsinclair.com/post/116436789850/ 652 | [testflight]: https://developer.apple.com/testflight/ 653 | [testflight-discussion]: http://blog.supertop.co/post/108759935377/app-developer-friends-try-testflight 654 | 655 | To sync all certificates and profiles to your machine, go to Accounts in Xcode's Preferences, add your Apple ID if needed, and double-click your team name. There is a refresh button at the bottom, but sometimes you just need to restart Xcode to make everything show up. 656 | 657 | #### Debugging Provisioning 658 | 659 | Sometimes you need to debug a provisioning issue. For instance, Xcode may refuse to install the build to an attached device, because the latter is not on the (development or ad-hoc) profile's device list. In those cases, you can use Craig Hockenberry's excellent [Provisioning][provisioning] plugin by browsing to `~/Library/MobileDevice/Provisioning Profiles`, selecting a `.mobileprovision` file and hitting Space to launch Finder's Quick Look feature. It will show you a wealth of information such as devices, entitlements, certificates, and the App ID. 660 | 661 | When dealing with an existing app archive (`.ipa`), you can inspect its provisioning profile in a similar fashion: Simply rename the `*.ipa` to `*.zip`, unpack it and find the `.app` package within. From its Finder context menu, choose "Show Package Contents" to see a file called `embedded.mobileprovision` that you can examine with the above method. 662 | 663 | [provisioning]: https://github.com/chockenberry/Provisioning 664 | 665 | ### Uploading 666 | 667 | [App Store Connect][appstore-connect] is Apple's portal for managing your apps on the App Store. To upload a build, Xcode requires an Apple ID that is part of the developer account used for signing. Nowadays Apple has allowed for single Apple IDs to be part of multiple App Store Connect accounts (i.e. client organizations) in both Apple's developer portal as well as App Store Connect. 668 | 669 | After uploading the build, be patient as it can take up to an hour for it to show up under the Builds section of your app version. When it appears, you can link it to the app version and submit your app for review. 670 | 671 | [appstore-connect]: https://appstoreconnect.apple.com 672 | 673 | ## In-App Purchases (IAP) 674 | 675 | When validating in-app purchase receipts, remember to perform the following checks: 676 | 677 | - __Authenticity:__ That the receipt comes from Apple 678 | - __Integrity:__ That the receipt has not been tampered with 679 | - __App match:__ That the app bundle ID in the receipt matches your app’s bundle identifier 680 | - __Product match:__ That the product ID in the receipt matches your expected product identifier 681 | - __Freshness:__ That you haven’t seen the same receipt ID before. 682 | 683 | Whenever possible, design your IAP system to store the content for sale server-side, and provide it to the client only in exchange for a valid receipt that passes all of the above checks. This kind of a design thwarts common piracy mechanisms, and — since the validation is performed on the server — allows you to use Apple’s HTTP receipt validation service instead of interpreting the receipt `PKCS #7` / `ASN.1` format yourself. 684 | 685 | For more information on this topic, check out the [Futurice blog: Validating in-app purchases in your iOS app][futu-blog-iap]. 686 | 687 | [futu-blog-iap]: http://futurice.com/blog/validating-in-app-purchases-in-your-ios-app 688 | 689 | ## License 690 | 691 | [Futurice][futurice] • Creative Commons Attribution 4.0 International (CC BY 4.0) 692 | 693 | [futurice]: http://futurice.com/ 694 | 695 | 696 | ## More Ideas 697 | 698 | - Add list of suggested compiler warnings 699 | - Ask IT about automated Jenkins build machine 700 | - Add section on Testing 701 | - Add "proven don'ts" 702 | 703 | [reactivecocoa-github]: https://github.com/ReactiveCocoa/ReactiveCocoa 704 | --------------------------------------------------------------------------------