在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
开源软件名称:futurice/ios-good-practices开源软件地址:https://github.com/futurice/ios-good-practices开源编程语言:开源软件介绍:iOS Good PracticesJust 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! Interested in other mobile platforms? Our Best Practices in Android Development and Windows App Development Best Practices documents have got you covered. Why?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! ContentsIf you are looking for something specific, you can jump right into the relevant section from here.
Getting StartedHuman Interface GuidelinesIf you're coming from another platform, do take some time to familiarize yourself with Apple's Human Interface Guidelines 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. XcodeXcode is the IDE of choice for most iOS developers, and the only one officially supported by Apple. There are some alternatives, of which 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! To install, simply download Xcode on the Mac App Store. It comes with the newest SDK and simulators, and you can install more stuff under Preferences > Downloads. Project SetupA 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: Why code?
Why Storyboards?
Why not both?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. IgnoresA good first step when putting a project under version control is to have a decent Dependency ManagementCocoaPodsIf you're planning on including external dependencies (e.g. third-party libraries) in your project, CocoaPods offers easy and fast integration. Install it like so:
To get started, move inside your iOS project folder and run
This creates a Podfile, which will hold all your dependencies in one place. After adding your dependencies to the Podfile, you run
to install the libraries and include them as part of a workspace which also holds your own project. For reasons stated here and here, we recommend committing the installed dependencies to your own repo, instead of relying on having each developer run Note that from now on, you'll need to open the
will update all pods to the newest versions permitted by the Podfile. You can use a wealth of operators to specify your exact version requirements. CarthageCarthage takes the "simple, not 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. There is no centralized repository of projects, which means any library that can be compiled into a framework supports Carthage out of the box. To get started, follow the instructions in Carthage's documentation. Project StructureTo 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:
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. LocalizationKeep 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.
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 Find more information about localization in these presentation slides from the February 2012 HelsinkiOS meetup. Most of the talk is still relevant. ConstantsKeep 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 enum Config {
static let baseURL = NSURL(string: "http://www.example.org/")!
static let splineReticulatorName = "foobar"
}
enum Color {
static let primaryColor = UIColor(red: 0.22, green: 0.58, blue: 0.29, alpha: 1.0)
static let secondaryColor = UIColor.lightGray
// A visual way to define colours within code files is to use #colorLiteral
// This syntax will present you with colour picker component right on the code line
static let tertiaryColor = #colorLiteral(red: 0.22, green: 0.58, blue: 0.29, alpha: 1.0)
}
When using Objective-C, keep app-wide constants in a Instead of preprocessor macro definitions (via
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. Branching ModelEspecially 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.
Minimum iOS Version RequirementIt’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. Use these resources to gather the data necessary for making this choice:
Common LibrariesGenerally 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! 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. AFNetworking/AlamofireThe majority of iOS developers use one of these network libraries. While DateToolsAs a general rule, don't write your date calculations yourself. Luckily, in DateTools you get an MIT-licensed, thoroughly tested library that covers pretty much all your calendar needs. Auto Layout LibrariesIf you prefer to write your views in code, chances are you've heard of either Apple's awkward syntaxes – the regular If you're stuck with an earlier iOS version, Masonry/SnapKit remedies the problem by introducing its own DSL to make, update and replace constraints. PureLayout solves the same problem using Cocoa API style. For Swift, there is also Cartography, which builds on the language's powerful operator overloading features. For the more conservative, FLKAutoLayout offers a clean, but rather non-magical wrapper around the native APIs. Architecture
“Event” PatternsThese are the idiomatic ways for components to notify others about things:
ModelsKeep 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 is a good choice. In Swift, you can use structs instead of classes to ensure immutability, and use Swift's Codable to do the JSON-to-model mapping. There are also few third party libraries available. SwiftyJSON and Argo are the popular among them. ViewsWith 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. Instead of manipulating view frames directly, you should use 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. Apple's recommended approach 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 If you override Swift: override class var requiresConstraintBasedLayout: Bool {
return true
} Objective-C: + (BOOL)requiresConstraintBasedLayout {
return YES
} Otherwise, you may encounter strange bugs when the system doesn't call ControllersUse 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. Swift: let fooViewController = FooViewController(withViewModel: fooViewModel) Objective-C: FooViewController *fooViewController = [[FooViewController alloc] initWithViewModel:fooViewModel]; Try to avoid bloating your view controllers with logic that can safely reside in other places. Soroush Khanlou has a good writeup of how to achieve this, and architectures like MVVM treat view controllers as views, thereby greatly reducing their complexity. StoresAt 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. 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. If you're using ReactiveCocoa, Swift + ReactiveSwift: func fetchGigs(for artist: Artist) -> SignalProducer<[Gig], Error> {
// ...
} ObjectiveC + ReactiveObjC: - (RACSignal<NSArray<Gig *> *> *)fetchGigsForArtist:(Artist *)artist {
// ...
} Here, the returned 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". AssetsAsset 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. Using Bitmap ImagesAsset 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 Using Vector ImagesYou can include the original vector graphics (PDFs) 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.) Image optimisationXcode 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 or ImageOptim, its GUI counterpart. Coding StyleNamingApple pays great attention to keeping naming consistent. Adhering to their coding guidelines for Objective-C and API design guidelines for Swift makes it much easier for new people to join the project. Here are some basic takeaways you can start using right away: A method beginning with a verb indicates that it performs some side effects, but won't return anything:
Any method starting with a noun, however, returns that object and should do so without side effects:
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. Structure
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 makeViewConstraints() {
// ...
}
// MARK: User Interaction
func foobarButtonTapped() {
// ...
}
// MARK: FoobarDelegate
func foobar(foobar: Foobar, didSomethingWithFoo foo: Foo) {
// ...
}
// MARK: Additional Helpers
private func displayNameForFoo(foo: Foo) {
// ...
}
} The most important point is to keep these consistent across your project's classes. External Style GuidesFuturice 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.
SecurityEven 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. Data StorageIf 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 |
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论