- All user-facing text should be localized with String(localized:) unless passed directly to a SwiftUI view like Text().
- When interpolating values like numbers and dates, use FormatStyle, like "Expires in (days, format: .number) days."
- Use curly-quotes in user-facing strings.
- Format all changes with swift format, and verify all changes with swiftlint.
- Adhere to Swiftlint's
type_contents_ordersetting:
type_contents_order:
order:
[
[type_alias, associated_type],
[case],
[type_property],
[instance_property],
[ib_inspectable],
[ib_outlet],
[initializer],
[type_method],
[view_life_cycle_method],
[ib_action, ib_segue_action],
[other_method],
[subscript],
[deinitializer],
[subtype],
]- When defining groups of related variables or constants, use the compound
letorvarsyntax. - When using
guard letorif letto assert non-nil, shadow the variable name (useif let foo, notif let bar = foo, notif let foo = foo).
- Capitalize all acronyms unless they conflict with a type name (e.g. "convertToKIAS`).
- Consecutive capitalized acronyms should be separated by an underscore (e.g.,
IAS_KPH). Do not use an underscore except to separate two acronyms (e.g., not forIASKts). - Do not abbreviate words unless it definitely enhances readability (e.g., do not use "seg" for "segment", but "TAS" for "trueAirspeed" is sometimes OK). Long variable names are acceptable if it enhances understanding.
- Complex functions should be orchestrators that call out to smaller functions.
- Avoid magic numbers; define private static constants.
- For larger types, use extensions to group related computed vars, functions, etc. into "functionality clusters".
- Use Swift 6 concurrency wherever appropriate: move related into TaskGroups; use actors when access synchronization is appropriate, etc.
- Avoid using
nonisolated(unsafe)and@unchecked Sendableexcept in situations where it is unavoidable (e.g., working with pre-concurrency libraries that cannot be imported with@preconcurrency import).
- When working with dimensional values, use Measurement for front-end display and manipulation. For low-level calculations, primitives are OK.
- Suffix any dimensional primitives or functions with the abbreviated units (e.g.,
timeMinordistanceNM).
The two performance models answer to different standards, and code that serves both must not average them into one behaviour.
- Tabular must adhere to the AFM tables. It interpolates between tabulated points and nothing
more: where the tables give no value, it reports N/A rather than inventing one. Clamping an input
to the nearest tabulated point is inventing a value — it presents an answer the AFM never gave.
It may hold an input at the edge of the tables on two conditions: that the substitution is
conservative, which on the distance tables means the low edge only, since distance grows with
weight, altitude and temperature alike; and that the figure is returned marked, as the clamped
payload of an offscale
Value, so that every readout carrying it says the AFM never covered the conditions asked for. An unmarked substitution is still inventing a value. - Regression may extrapolate and interpolate wherever that is sensible, since that is what the fitted equations are for. Where extrapolation becomes nonsensical it must clamp or report N/A, whichever suits the context.
A figure the model cannot stand behind is reported as N/A. Silently substituting the nearest answer it does have is the one thing neither model may do.
- This app uses icons sparingly. Do not use icons for every label; only when it enhances readability or as a shorthand for a text label. Icons or images without labels should have their accessibility label set.
- This app uses colors sparingly. Use bright colors only when it clearly enhances readability. Use basic shades for information hierarchy.
- Prefer the default padding and spacing values unless more or less padding/spacing is required to set up a proper visual flow.
- When working with complex views, prefer making subviews over vars or functions that return views. Non-trivial subviews should be in their own file.
- Prefer using a
Labelview over anHStackwith anImageandText. - All views should have
#Previewblocks covering major view modes. UsePreviewHelperto inject data into this previews.
- Major functionality should have unit tests. Major user flows should have UI tests.
- Unit tests are written using Swift Testing. Use
#expectfor assertions and#requireto verify non-null. - Do not write trivial or tautological unit tests that verify simple and obviously correct logic.
- Create a protocol that inherits Error for each general category of errors.
- Errors should implement
LocalizedError.errorDescriptionshould be a general description of the error category, and typically is the same for all error cases (e.g., "Couldn’t download file.").failureReasonshould contain specific error details and interpolate occurrence-specific information (e.g., "Received HTTP error %lld when trying to download.").recoverySuggestionshould only be provided if the error is user-actionable. - Use
fatalErrororpreconditionFailurefor errors that should never happen.
- Use xcbeautify to reduce the context load of build and test runs.
- Use xclogparser and xcresultparser to efficiently parse Xcode output.