Preface
Swift is a language whose goal is to be easy to write and read, perform well, and let the compiler do a lot of the work for you. These guidelines attempt to assist in achieving that goal.
If you have not already, read The Swift Programming Language (also available as a book!). That introduces concepts and patterns in a way that’s necessary before reading this guide.
If that book doesn’t appeal to you, to can also look at Apple’s Swift Language Guide, which is in a more traditional documentation format, including lexical structure.
Overall, embrace Swift. Swift-y patterns in .swift
files are preferred over patterns used in other languages. Following this simple rule helps the compiler create better assembly, avoids bugs, and makes the code more maintainable.
If you disagree with any of these or have something new to add, you are encouraged to create a pull request or issue using the links at the bottom.
Compatibility
These guidelines are relevant and accurate as of Swift 5.1
Deferral
Primarily, if anything is not explicitly defined here, defer to the below list (prefer items higher on the list). If you still can’t find anything in this or those guides, then what you are trying to do is freeform; do it however you prefer.
With very few exceptions, if Xcode’s build-in code completion/formatting does it some way, that is the way we want it done. If the compiler issues a warning about it, address that warning. Don’t fight the IDE.
- The Swift.org API Design Guidelines
- HIGHLY ENCOURAGED! WWDC 2016 Session 403: Official | Unofficial YouTube mirror
- Google’s Swift Style Guide
- The style already present in the file you’re editing
Naming
Names should be in American English.
See also:
- Swift.org’s naming guidelines: Swift.org - API Design Guidelines § Naming
If this is difficult for you to understand at the call-site, then you may have chosen the wrong words. If all else fails, simply use Quick Help (by ⌥-clicking the symbol or by selecting it and using the Quick Help inspector) and the resulting documentation will describe its declaration for you.
Capitalization
This one is simple. Types (class
es, protocol
s, typealias
es, struct
s, enum
s, etc.) are always CapitalizedCamelCase. Instances (let
s, var
s, case
s, etc.) and functions (func
s, dynamic var
s, non-anonymous closures, etc.) are always uncapitalizedCamelCase.
When an acronym or initialism like HTTP or JSON is a part of a name, treat it like an English word rather than an abbreviation, even if it’s not pronounceable like a word.
Good | Bad |
---|---|
|
|
Do not use Systems Hungarian notation!
Knowing the type, implementation, etc. in something’s name is not important. If, however, you find that such information is necessary, you may append it as full words to the end of a name, like “
nutritionString
”, “widthInt
”, or “base64ContentWideCString
”
Do not use Apps Hungarian notation!
Knowing the purpose of something in its name is very important. However, you should prepend it as full words (not short letter abbreviations), like “
contentLocalCoordinates
”, “unsanitizedUserInput
”, or “rowMaximumCount
”
Grammar
Names should be descriptive and self-documenting (however, that does not mean you should forego documentation). The name of a function, for instance, should describe everything that function does. If that name seems too unwieldy, it’s probably because that function does too much. In that case, split it up into multiple functions, each also with descriptive names.
If your instance, function, or type name is only a few characters long, it probably needs a longer name.
Here are some good rules from Swift.org’s design guidelines:
RULE-OF-THUMB Clarity at the point of use is your most important goal. Entities such as methods and properties are declared only once but used repeatedly. Design APIs to make those uses clear and concise. When evaluating a design, reading a declaration is seldom sufficient; always examine a use case to make sure it looks clear in context.
RULE-OF-THUMB Clarity is more important than brevity. Although Swift code can be compact, it is a non-goal to enable the smallest possible code with the fewest characters. Brevity in Swift code, where it occurs, is a side-effect of the strong type system and features that naturally reduce boilerplate.
RULE-OF-THUMB Write a documentation comment for every declaration. Insights gained by writing documentation can have a profound impact on your design, so don’t put it off.
GOLDEN RULE If you are having trouble describing your API’s functionality in simple terms, you may have designed the wrong API.
It’s important to note that this page uses placeholder names like foo, bar, baz, etc. to name things when the name is unimportant to the example. It’s strongly discouraged to use such obscure andor short names in production code!
Functions
- Getters are named the same as fields; that is to say they are simply the noun or noun phrase which describes the thing they get. If properties need to be passed in, they are named parameters, not part of the actual function name.
- Boolean (or other very restricted sets of values) getters are named as assertions about something. They might be prefixed with is/does/etc. or be a present-tense verb or verb phrase.
- Setters take the same syntax to getters, but with the word “
set
” prefixing them. Note that it’s preferred to use a var rather than a func to get & set a single value. The incoming parameter which holds the new value should not have a label (use_
). - Non-Setter Mutators are verbs or phrases describing the mutation they perform
- Copiers (functions which otherwise act like mutators, but instead of mutating return a new object with the changed data) are present- or past-participle verbs or verb phrases (ending in “ing”, “ed”, etc.) describing the mutation they perform
Good | Bad |
---|---|
|
|
Types
Types should not be prefixed; this was a Cocoa pattern used until Swift as a primitive form of namespacing, but since Swift is modular, no prefix is needed.
- Classes and structs have varied purposes, so they are named in a way that reflects their purpose, using nouns or noun phrases.
- Protocols define a contract, so there are two approaches:
- Protocols which define what something is are named exactly like the classes which might implement them; nouns or noun phrases.
- Protocols which define what something can do are named using present-participle verbs or verb phrases (end in
ing
,albe
,ible
, etc.).
- Enums are named as the singular noun which describes what their cases represent.
- Typealiases are named just like the type for which they are an alias.
- Associated types are named just like typealiases.
Good | Bad |
---|---|
|
|
Instances
Anything that holds an instance/value (stored fields, enum
s’ case
s, etc.) are named as nouns. An exception can be made for Booleans, which are named as asserting verbs or verb phrases like their function counterparts.
Note that fancy var
s may be named like functions.
All instances have the same naming scheme:
uncapitalizedCamelCase
.Do not use
ALL_CAPS_NAMES
,m
prefixes, etc.
_underscorePrefixes
should only be used if absolutely necessary.
Good | Bad |
---|---|
|
|
Plurality
Only collection types (and instances of those types) should be plural (for instance, OptionSet
s and Array
s). Everything else should be singular.
Good | Bad |
---|---|
|
|
Punctuation
Semicolons
Don’t use semicolons. If you think you need to use semicolons, try to find a different way to do it.
Sole Exception
There is actually one case where semicolons are acceptable in the code: When a scope opens and you are logging both the entrance and exit of that scope. In this case, you may place both these log lines and the defer block on the same line of code.
Good | Acceptable |
---|---|
|
|
Braces
Along with Apple’s guidelines, curly braces ({
and }
) open on the same line and close on a new line. Curly braces that are the first non-whitespace character on a line must be indented to the same level as the code around its block, not inside it.
The main difference between our guidelines and Apple’s is the else
statement; we place them on the line following their if
’s closing brace, whereas Apple places them on the same line.
Braces are required, by all guidelines and by the compiler, for all if
, else
, guard
, switch
, while
, and for
statements.
Good | Bad |
---|---|
|
|
|
|
Parentheses
RULE-OF-THUMB When in doubt, more parentheses is better.
if
, else
, guard
, switch
, while
, and for
statements should not use parentheses unless necessary for compiling, or if their logic is so complex that parentheses are required for clear human andor compiler understanding. If this logic is very complex, consider moving it into its own function.
Always strive for clarity; when using multiple operators (mathematical, Boolean, or otherwise), use parentheses to explicitly group each pair. This ensures it does what you think it does, and future readers know what you intended it to do.
Good | Bad |
---|---|
|
|
Whitespace
Indentation
Before lines, use 4 spaces, not tabs (set this in your preferences). Allow the IDE to guide your indentation practices; if it automatically indents something, don’t change that, but adopt it.
Always indent inside curly-brace-blocks, with the sole exception of switch
case
s, which appear at the same indentation level as the switch
statement, itself.
When there are multiple parts of an if
, else
, guard
, or while
statement, chop them down and align as the IDE prefers. This makes it clear that you have one clear intent for each line.
Good | Bad |
---|---|
|
|
|
|
Chopping-Down Functions
When a function has a long declaration with many parameters, it may be chopped-down at both the declaration and the call site. That is to say, hard-wrapped so that they align nicely. If you prefer to keep it all on one line, that is also OK.
Don’t chop-down Objective-C style (so that colons align).
Good | Bad |
---|---|
|
|
Inline Padding
The quick summary of the below table is:
- Colons are
X: Y
orX : Y
- Put a space before opening curly braces
- Put a space around the assignment “
=
” - Don’t put spaces around parentheses and angle brackets unless some other rule dictates that you should
- Keep parentheses as close to the tokens they group as possible
Place | Number of spaces | Good | Bad |
---|---|---|---|
Around typing colon |
Depending on preference |
|
|
Generics’ angle brackets |
Keep them flush with the type they modify and the types within them. Rules dictating more spacing override this (do place a space after closing angle bracket and opening curly brace). |
|
|
After type declaration Between last declaration character and opening curly brace |
|
|
|
Around assignment “ Around operators |
Consecutive assignments are allowed to have more spacing for pretty alignment. Note that Swift will not allow things like |
|
|
Functions |
|
|
|
|
First write or imagine it without parentheses. Then, place parentheses as necessary to group elements. |
|
|
Blank Lines
When in doubt, more blank lines are okay. It’s better for readability when things are spaced out rather than crammed together, and storage/bandwidth/compile time is not a concern.
The quick summary of the below table is:
- 0 or 1 blank line between fields
- 2 blank lines between functions and other such blocks
- 3 blank lines between classes and other types
Place | Number of spaces | Good | Bad | ||||
---|---|---|---|---|---|---|---|
Before opening comment (copyright, file documentation, etc.) |
|
|
|
||||
Between opening comment and group of imports |
|
||||||
After group of imports |
Depending on following number of variables/constants outside scope |
||||||
Around group of variables/constants outside scope |
Depending on number of variables/constants outside scope |
||||||
Around types (classes, enums, etc.) |
|
||||||
At beginning of type After opening brace and before actual code |
0 or 1 Depending on preference |
||||||
Around functions and other scoped blocks |
|
||||||
At beginning of functions and other scoped blocks After opening brace and before actual code |
Depending on preference |
||||||
At the end of functions and other scoped blocks |
|
||||||
Around variables |
Depending on complexity of declaration |
||||||
Around local blocks ( |
|
||||||
Before |
|
||||||
Before |
|
||||||
After |
|
Line breaks
Place | Number of spaces | Good | Bad |
---|---|---|---|
|
|
|
|
Scoping
Also called “visibility” or “access level”, it’s important to use the proper scope to ensure your code is used properly, both by others and by Future You.
Prefer more-private scopes first, and only make something more public if necessary.
In case it’s unclear, here are the visibility scopes as of Swift 5:
private
: Only the current context can see this- This is our preferred default scope
- Top-level in file: Only visible in this file; synonymous with
fileprivate
- In
class
,struct
,enum
,extension
, etc.: Only visible in this type, and only in this file. When used in a nested type, it’s only visible in the nested one, not its parent.
fileprivate
: Only (and all) things in this file can see this- Top-level in file: Avoid this; synonymous with
private
- In
class
,struct
,enum
,extension
, etc.: This is okay; use this for utilities only needed in this file
- Top-level in file: Avoid this; synonymous with
internal
: Only (and all) things in this Swift module can see this- You may use this liberally or judicously, to your preference
public
: Everything can see this- This is okay, but only if necessary.
open
: By itself, this is public, but subclassable/overridable.- If subclassing/overriding is the goal, great. Otherwise, avoid this, or at least pair it with another scope to shield it from the public.
- unspecified
- Assume surrounding context’s visibility. This is OK.
- If top-level (no surrounding context), synonymous with
internal
. Avoid this.
Objective-C Interop
Sometimes it is necessary to write a Swift class or function that is accessible by Objective-C code. For this, we have a pattern that allows us to keep everything important in Swift Land, only using a thin wrapper to expose our code to Objective-C Land. This has the added benefit of keeping all code that ties us to Objective-C contained, so as we move away from it, the only changes that must be made to Swift files are deleting these thin wrappers.
Good | Bad |
---|---|
|
|
EXCEPTION Assuming their counterparts are well-documented and easy to find, it is OK to not document these thin
__objc__
wrappers.
Cocoa-, Xcode-, and Swift-Specific Features
Selecting a kind of type
Prefer using Structs, Protocols, and Extensions more than classes and subclasses; Swift will behave more predictably and optimize better this way. It also allows for some patterns which are impossible with subclassing.
Documentation Comments
GOLDEN RULE If you are having trouble describing your API’s functionality in simple terms, you may have designed the wrong API.
Documentation comments are highly encouraged. We do not have any preferences of its style, so long as the doc comment is provided. You can view them in the Quick Help sidebar on the right of Xcode when the typing cursor is on the name, or by ⌥-clicking the name.
For reference, here are a couple examples of documentation comments:
Sparse Doc CommentAcceptable | GoodDetailed Doc Comment |
---|---|
|
|
Note that documentation comments are not to explain what something does in a way its name cannot, but instead to elaborate on what its name already says, including intended uses, parameter limits, possible return values, related code, external documentation, etc. Do not use documentation comments as an excuse to poorly name anything.
Also, be sure you know what something actually does before documenting it.
RULE-OF-THUMB Less documentation is better than incorrect documentation.
Good | Bad |
---|---|
|
ACTUAL EXAMPLE FROM CODEBASE 😱 |
See Also
Here’s a couple starting guides on how to write awesome documentation:
Numbers
By default, use Int
for integers and CGFloat
for fractions (or Length
if you have access to SwiftUI), as they automatically scale to the current processor architecture. If a specific task requires a specific number type like UInt8
or NSDecimalNumber
, then that’s OK, but for common operations, use those aforementioned types.
Optionals
In Swift, “optional” means “able to be nil
”. The great thing is that, in Swift, anything can be optional, even primitives like Int
s! This is a very powerful feature, and such power comes with a lot of responsibility.
Do Not Use Exclamation Points*†
”!
” means “I know this might be nil
, but I’m smart enough to know it isn’t nil
right now. It’s okay to crash if I’m wrong.” The problem here is that there is still a chance for crashing*, whereas there’s almost always another approach which does not provide a chance of crashing.
The following are preferred ways to unwrap an optional:
- The
?.
,?(
, and?[
operators. These somewhat mirror Objective-C’s behavior, meaning “If it’snil
, stop evaluating and returnnil
if anything expects a value. Either way, continue as normal.” - If the question-mark syntax does not accomplish the goal, or would be overly verbose for this context,
if let
(orif var
) is the next best option. This creates a new sub-context in which the existence of the value is guaranteed, but not before nor after. - If one or more values are required for a context (usually a function), or if the if let nesting is getting too deep,
guard let
(orguard var
) is preferred. This is much like an invertedif let
, in that it creates a sub-context in which the nonexistence of the value is guaranteed, the difference being that after that context, its existence is guaranteed. * These should go at the top of a context which requires the value, rather than farther down. - If you have a backup value handy, use
??
to use it as an alternative in case the ideal value doesn’t exist.
Good | Bad |
---|---|
|
|
* There is one exception where exclamation points are acceptable: On nib-initialized fields which are guaranteed to be available after load. If you are using SwiftUI, this exception does not apply.
† Obviously, using them to mean “Boolean invert” / “not” is okay (
!condition
).
Throwing functions
Another way to write a function that may or may not return a value is to declare it with the throws
keyword. This is perfectly acceptable, and in fact encouraged when it would lead to a more clear API. However, never return an optional value from a throwing function. Instead, where you would return nil
, throw a new, custom, descriptive error that tells why you couldn’t return a value. This is because, once the error has been handled, the API caller shouldn’t have to then deal with a nil
value, as from their perspective all errors that would cause that have been handled. Additionally, that approach wouldn’t play well with the try?
keyword.
Good | Bad |
---|---|
|
|
if
and guard
if
should never be used in place of guard
, and vice-versa. As its name implies, guard
should be used to guard against a bad state; if something is required, it acts as a guard that everything is OK before proceeding.
When entering a guard
’s else
block constitutes a bad state, use assertionFailure
(or at least log a message) when a guard
’s else
block was entered.
Do not use guard
or if
in place of each other! guard
is meant to be used as a guard against a bad state, not as an unless
-style statement.
extension
Extensions are the bread and butter of Swift and Cocoa. Always prefer creating an extension function over a utility function. Always prefer creating an extension over subclassing. It’s OK to make private extension
s if something is only needed in the current context and nowhere else.
GOLDEN RULE Always prefer extensions over subclassing.
Good | Bad |
---|---|
|
|
|
|
|
|
|
|
This is more natural. “I’m looking for a common URL; I will look in the URL class.” |
This is less natural. “I’m looking for a common URL; I will look in one of possibly several utility classes.” Equally bad is placing the constant in global scope, since it’s still not obvious how to find it, and it overpopulates autocomplete. |
// MARK:
Separate logical sections of code with // MARK:
and // MARK: -
messages. Use // MARK: -
more sparingly than // MARK:
.
When implementing a protocol (or subclassing) outside an extension
, make sure you place a // MARK:
message before the variables and functions you’ve implemented, making sure the mark’s message is the name of the protocol.
See also: Blank Lines
Good | Acceptable |
---|---|
|
|
Fancy var
s: get
, set
, willSet
, and didSet
In case you don’t know or need a refresher, here are the basics:
Swift
var
s are very powerful, much moreso than allowing a value to be re-set. There are three ways to use avar
:
- As a normal (unobserved*) instance, whose value can be set and gotten. The scope of these operations can be set individually, like
public private(set)
.- As an observed* instance whose changes are monitored just before andor just after the value is changed
- As a dynamic† value, whose setter and getter are specified by the developer. In this mode, it can also be read-only.
These are mutually exclusive; an unobserved instance cannot* report when it changes, an observed instance cannot have a dynamic† getter and setter, and a dynamic† value does not have a backing stored value.
It’s also important to know that a
var
in astruct
inherits the mutability and observation status of the instance in which it’s held. That is to say,let parentStruct
’s fields won’t be changeable, regardless of whether they’relet
s orvar
s. However,var parentStruct
’s fields will be changeable if (and only if) they arevar
s, but not if they’relet
s. Even better, ifparentStruct
has awillSet
or adidSet
, it will be notified if any of its fields changes.Do note that this behavior does not exist in
class
instances, even if those instances are inside astruct
(the best immutability those will get is not being able to change the reference; its own fields’ mutability won’t change). This is one reason why you should avoid usingclass
es as much as possible.
With the basics over, here’s the guidelines:
- Normal, unobserved*
var
s should be used only for values that need to change rapidly, but whose changes aren’t important. - Observed*
var
s should be used for important values, which should propagate other changes or send notifications when they change. - Dynamic†
var
s should be used in place of functions which only get or set a single value
Good |
---|
|
* Here, “unobserved” refers to lacking the built-in
willSet
andordidSet
blocks; these can still be observed using key-value approaches. Conversely, “observed” refers to the presence of these blocks and does not imply KVO.
† Here, “dynamic” refers to returning a generated, abstracted, translated, or conditional value (like a function). It does not refer to using the
dynamic
keyword for KVO.
Function Overloading and Default Parameters
In Objective-C, it was necessary to create multiple methods when you wanted one parameter to be optional. With Swift’s default parameters, this is no longer necessary.
Of course, overloading is okay if it is necessary for the current task.
Good | Bad |
---|---|
|
|
|
|
Function parameter labels
Sometimes it’s a good idea to label function parameters, and sometimes it’s not.
- Parameter labels may be omitted when the parameter reflects the main purpose of the function. For instance, the string message of a log function, the new value of a setter, or the two items in a two-item comparison function. This overrides the following guidelines.
- Boolean parameters must always have labels
- Unlike Objective-C, the first parameter can have a label separate from the method name. When possible, prefer this.
- When the value being passed in has a specific purpose other than just providing required data, try to give it a separate, more-English label than its variable name (not simple ones like
for
orwith
) - Do not provide a label for parameters whose name is in the function name
- When calling an external API which does not label its parameters, but leaves their purpose confusing, add “label” block-comments
RULE-OF-THUMB When in doubt, use a label.
Good | Bad |
---|---|
|
|
Mutability
Swift was designed around less-mutating, more-functional approaches first. In general, use approaches that create changed immutable copies, rather than ones that mutate an existing value. This may sound inefficient, but remember that the Swift compiler and runtime expect this to be the default approach, and will optimize for it.
If you run tests and find that using a non-mutating approach is too slow, then it’s okay to delicately choose which parts of your approach to convert to mutating. Use a scalpel, not a mallet when deciding what needs to be mutated. If you’re using Swift’s functional collection functions like map
and filter
, first try preceding these with .lazy
to turn it into a LazySequence
, whose performance is improved by waiting until the last moment to perform the operations. This can be even faster than using mutation.
This is much more expressive, safer, and often even faster than mutating.
Good | Bad |
---|---|
In my tests, this takes an average of ~82% as long as the mutating approach. |
In my tests, this takes an average of ~122% as long as the functional approach. |
Control Flow
One of the beautiful things Swift does is decrease the amount of control flow that’s needed by turning common patterns into production-ready functions in the standard library, called higher-order functions. When at all possible, prefer these functions and paradigms over traditional control flow.
Good | Bad |
---|---|
|
|
|
|
|
|
|
|
|
|
|
|
Enum Case Associated Values
See Also:
Associated values are a very powerful Swift paradigm, but there are many ways to use them. In our codebase, we prefer this approach:
- Always label the associated values
- When reading the values in a switch case, always give the reader variable the same name as the label
- When pattern-matching in a switch case, always use the label
- When pattern-matching in a switch case and ignoring the associated values, always write the label followed by an underscore, rather than just an underscore or omitting the associated values entirely
Good | Bad |
---|---|
|
|
NSCache
NSCache
is very useful when applied correctly. It cannot be used for all caching needs.
This is because NSCache
can and will evict things even if everything is under the limits.
In case you’re unfamiliar with NSCache
, it is a Cocoa class which allows you to give limits on both total number of items in that cache, as well as total cost (with each item optionally given a cost when it is cached). As previously mentioned, NSCache
only loosely respects these guidelines. For performance reasons, it might evict items even if no limits have been reached. Similarly, it might not yet have evicted items well after its limits have been passed.
Never use NSCache
to cache values which cannot be recreated (for instance, items with UUIDs)!
If your requirements are okay with this looseness, then NSCache
might be the right tool for the job. Otherwise, you should probably write your own cache which is specialized to fit those requirements.
General Patterns
Singleton
Singletons are often unnecessary, but if you find one necessary, name the single instance shared
(if there can only ever be one) or default
(if a custom instance can be created), and place it in the class’ static scope.
Good | Bad |
---|---|
|
|
Lazy initialization
Thankfully, Swift makes this easy with its lazy keyword. The only matter of style is how to handle resetting the lazy value.
Good | Bad |
---|---|
|
|
|
|
Resettable Lazy Fields
For resettable lazy fields, declare a private backing field as optional and a more-exposed field getter that will set it if it’s nil
. For this, you can use Swift Lazy Patterns’ ResettableLazy
:
Good | Bad |
---|---|
|
|
Assertions
Assertions can be a powerful way to help yourself debug; if a very bad state occurs, instead of it being quietly logged, the app halts immediately and won’t continue. Remember assertions are ignored in production builds so they won’t lead to end-user crashes.
When no logic is necessary and an assertion should always be thrown, use assertionFailure("message")
instead of assert(false, "message")
.
Good | Bad |
---|---|
|
|
Factory Initializers
A common Objective-C approach to fancy initializers is to create a static function named like arrayWithObject:
or stringByAppendingString:
. Swift no longer needs these bodges. Instead, create another initializer (perhaps in an extension
), whose first parameter is named like the old factory methods. These are often also annotated as convenience
initializers.
Good | Bad |
---|---|
|
|
Global Constants
See also:
There will always be a need for global constants. However, it’s better to keep them out of global scope. Prefer placing these constants in struct
s, extension
s, etc.
Good | Bad |
---|---|
|
|
Compile-Time Warnings and Errors
Treat warnings as errors. Very seldom is it okay to allow code with a warning to be committed. It’s up to you to make sure code that you commit is warning- and error-free.
The same goes for tests; if a test fails, treat that like a compiler error. It’s up to you to ensure every build passes its tests.
Runtime Warnings and Errors
It’s often necessary to indicate that a function could not complete its duties. The preferred Objective-C way was to use an NSError**
. In Swift, that is to throw
an Error
. Embrace this, and use it to bring more information and compile-time peace-of-mind.
Good | Bad |
---|---|
Example usage:
|
Example usage:
|
Runtime Errors in Callbacks
This is all well and good, but when receiving a callback from an asynchronous function, you might also want to know if any error occurred in there. Swift’s standard library provides the Result
type for this:
Good | Bad |
---|---|
Example usage:
|
Example usage:
|
Getting Results from Functions
There are two ways you should get results from a function: by them it directly, or by using a callback. Do not use “return parameters” (via inout
). Note that inout
, itself, has valid uses in our style, for instance when the point of the function is to mutate some other object, but it should not be used to “return” a value.
If more than 1 value must be returned, use a tuple. If a great many values must be returned (like, 4 or more), create a specialized struct.
To indicate an error, instead of using an inout Error
, use the throws
keyword.
Good | Bad |
---|---|
Remember,
… but always consider using a mutating func in an extension instead:
|
|