Skip to content

Commit b4d37ee

Browse files
authored
[Docs] Create firebase-api-guidelines.md (#14325)
1 parent 0d885d2 commit b4d37ee

File tree

1 file changed

+199
-0
lines changed

1 file changed

+199
-0
lines changed

docs/firebase-api-guidelines.md

Lines changed: 199 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
# Firebase API Guidelines
2+
3+
This document builds upon the design guidelines provided by [Swift's official
4+
API design guidelines][1] and the [Google Swift Style Guide][2]. It is essential
5+
to be familiar with these resources before proceeding with the API proposal
6+
process.
7+
8+
If Objective-C is required, additionally refer to the [Google Objective-C Style
9+
Guide][3].
10+
11+
## Guidance {:#guidance}
12+
13+
### New APIs should be Swift-only {:#new-apis}
14+
15+
Swift is the preferred language for Apple development, so new Firebase APIs
16+
should be fully optimized for Swift. When designing new APIs, consider whether
17+
they can be implemented in Swift only. If an Objective-C API is required, it
18+
should be carefully considered and justified.
19+
20+
Note that Swift and Objective-C are interoperable, and a module's public
21+
Objective-C API will be exposed to Swift via a generated Swift interface. In
22+
some cases where the public Objective-C API surface is added to, the
23+
corresponding generated Swift interface should be manually refined to be more
24+
idiomatic. Apple provides a [guide][4] for improving such Objective-C API
25+
declarations for Swift.
26+
27+
### Include Swift code samples {:#include-swift}
28+
29+
It is important for new APIs to be as easy to use in Swift as possible. When
30+
creating a proposal, prioritize Swift code samples to demonstrate the new API's
31+
usage.
32+
33+
### Async API should be written in async/await form {:#async-api}
34+
35+
Swift has built-in [support][5] for writing asynchronous code. If a
36+
callback-based API is required, it should be carefully considered and justified.
37+
Callbacks are still useful for capturing work to be done later, like for
38+
example, an event handler like SwiftUI's [`onSubmit`][6] view modifier.
39+
40+
```swift
41+
// ✔ Preferred async/await form.
42+
public func fetchData() async throws -> Data { ... }
43+
44+
// x Pre Swift Structured Concurrency. No longer preferred.
45+
public func fetchData(completion: (Data, any Error) -> Void) { ... }
46+
```
47+
48+
### New APIs should be Sendable {:#new-apis}
49+
50+
A [Sendable][7] type is one that is thread-safe and can be shared safely across
51+
multiple concurrency contexts. The requirements for conforming to the Sendable
52+
protocol vary depending on the type (class, actor, enum, struct, etc.). Swift 6
53+
introduces strict concurrency checking and enforces Sendable types in
54+
asynchronous code. If applicable, new APIs should be Sendable and designed to be
55+
used in an async context (e.g. `Task`).
56+
57+
### API Availability {:#api-availability}
58+
59+
By design, an API may not be available on a given Apple platform. Swift supports
60+
platform-specific compilation and runtime checks.
61+
62+
Code can be conditionally compiled for only iOS by wrapping it with `#if
63+
os(iOS)and #endif`. For more info, refer to [NSHipster's guide][8].
64+
65+
#### Platform Versioning Specification
66+
67+
For code that builds on all platforms but should only be available on select
68+
platforms, use the `@available` declaration attribute discussed in [NSHipster's
69+
guide][9].
70+
71+
Firebase's main distribution channel, Swift Package Manager, only supports a
72+
single minimum supported version for all products. This minimum supported
73+
version will correspond to Crashlytics and Analytics, which historically share a
74+
minimum supported version that is lower than the rest of Firebase. When adding a
75+
new API to the rest of Firebase, refer to the minimum supported versions in the
76+
product's CocoaPods podspec, and declare it on the API's signature via the
77+
[`@available`][9] declaration attribute.
78+
79+
```swift
80+
@available(iOS 13, tvOS 13, macOS 10.15, macCatalyst 13, watchOS 7, *)
81+
func myNewAPI() { ... }
82+
```
83+
84+
### Constants {:#constants}
85+
86+
In Objective-C, constants were usually defined at the global level. In Swift,
87+
constants can instead be grouped under a case-less enum. The benefit of a
88+
case-less enum over a struct or class is that a case-less enum cannot be
89+
initialized. Additionally, Swift constants do not contain a "k" prefix.
90+
91+
```swift
92+
public enum NetworkConstants {
93+
public static let httpPostMethod = "POST"
94+
public static let httpGetMethod = "GET"
95+
}
96+
```
97+
98+
### Minimizing optionals {:#minimizing-optionals}
99+
100+
Unlike Objective-C, Swift handles nullability in a type safe way using
101+
[optionals][10]. While useful, avoid overusing them as they can complicate
102+
the callsite.
103+
104+
### Structs over enums for backwards compatibility {:#structs-enums}
105+
106+
Adding a case to a Swift enum is a breaking change for clients switching
107+
over the enum's cases. Enums should be chosen when they are considered
108+
frozen or very unlikely to be changed (as major version releases occur
109+
about once a year).
110+
111+
An alternative to using enums is to use structs. The following PRs use
112+
structs to model cases similar to how an enum does. One downside to this
113+
approach is that clients switching over a struct do not get a compile
114+
time check ensuring each case is handled.
115+
116+
1. [firebase-ios-sdk/13728][11]
117+
1. [firebase-ios-sdk/13976][12]
118+
119+
### Avoid Any, AnyObject, and NS-prefixed types {:#avoid-any,}
120+
121+
In Swift, `Any` represents an instance of a reference or value type,
122+
while `AnyObject` represents reference types exclusively (making it
123+
similar to `id` in Objective-C). NS-prefixed types like `NSString` or
124+
`NSNumber` are reference types bridged from Objective-C. None of these
125+
should be used in the public Swift API. Using `Any` and `AnyObject` can
126+
make APIs harder to work with due to their lack of type safety and
127+
ambiguousness. NS-prefixed types on the other hand should be swapped
128+
\out for their corresponding Swift value type like `String` for
129+
`NSString`.
130+
131+
For example, if a public API's signature uses a dictionary whose values
132+
should be either a `String` or `Int`, consider modeling this with an
133+
enum or struct rather than `Any`.
134+
135+
```swift
136+
public struct CustomValue {
137+
public static func string(_ string: String) -> Self { ... }
138+
public static func integer(_ integer: Int) -> Self { ... }
139+
}
140+
141+
func setValues(_ values: [String: CustomValue]) async throws { ... }
142+
```
143+
144+
### Documentation {:#documentation}
145+
146+
New APIs should have corresponding documentation using [Swift-flavored
147+
Markdown][13]. Xcode can generate a documentation block's structure when the
148+
cursor is in the method signature and ⌥ ⌘ / is pressed.
149+
150+
### Naming Conventions {:#naming-conventions}
151+
152+
[Swift's official API design guidelines][1] provide an overview to creating
153+
idiomatic Swift API.
154+
155+
#### Optimize for clarity and expressiveness
156+
157+
Choose names that leverage clarity over conciseness. For example, choose
158+
`fetchMetadata()` over `fetch()`.
159+
160+
Avoid using _get_ as a prefix. For example, in Apple's WeatherKit, Apple
161+
[uses][14] `weather(for location: CLLocation)` instead of `getWeather(for
162+
location: CLLocation)`.
163+
164+
#### Consistency with existing Firebase APIs
165+
166+
Consider the precedent set by the existing API that is adjacent to the new API
167+
being added. New APIs should be consistent with the existing Firebase API
168+
surface, and diverge only when justified.
169+
170+
### Errors {:#errors}
171+
172+
If the new API can fail, mark the API as `throws` and create (or add onto) an
173+
existing public [error][15] for the API to throw. Swift provides an `Error`
174+
protocol that can be used to create descriptive errors that can be easily
175+
handled by clients.
176+
177+
### Don't define typedefs {:#don't-define}
178+
179+
_This guideline only applies when changing public Objective-C API_.
180+
181+
Don't define typedefs as they hide the underlying type in the corresponding
182+
generated Swift API. Instead, use the full type, regardless of how long the
183+
Objective-C API signature becomes.
184+
185+
[1]: https://www.swift.org/documentation/api-design-guidelines/
186+
[2]: https://google.github.io/swift/
187+
[3]: https://google.github.io/styleguide/objcguide.html
188+
[4]: https://developer.apple.com/documentation/swift/improving-objective-c-api-declarations-for-swift
189+
[5]: https://docs.swift.org/swift-book/documentation/the-swift-programming-language/concurrency/
190+
[6]: https://developer.apple.com/documentation/swiftui/view/onsubmit(of:_:)
191+
[7]: https://developer.apple.com/documentation/swift/sendable
192+
[8]: https://nshipster.com/swift-system-version-checking/
193+
[9]: https://nshipster.com/available/
194+
[10]: https://developer.apple.com/documentation/swift/optional
195+
[11]: https://github.com/firebase/firebase-ios-sdk/pull/13728
196+
[12]: https://github.com/firebase/firebase-ios-sdk/pull/13976
197+
[13]: https://nshipster.com/swift-documentation/
198+
[14]: https://developer.apple.com/documentation/weatherkit/weatherservice/weather(for:)
199+
[15]: https://docs.swift.org/swift-book/documentation/the-swift-programming-language/errorhandling/

0 commit comments

Comments
 (0)