You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/android/best_practices.md
+94-1Lines changed: 94 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -93,6 +93,95 @@ Tie your coroutines to an Android lifecycle (e.g., `viewModelScope` or `lifecycl
93
93
94
94
For more details on race conditions, see [Race Condition](https://en.wikipedia.org/wiki/Race_condition#In_software).
95
95
96
+
## Use strong types instead of strings for logic
97
+
98
+
Use strings for storing and displaying text, not for controlling logic or behavior in your code. Relying on strings for logic-such as passing a string to determine a destination or behavior-can introduce errors like typos and make it harder to track or refactor your code. Instead, use strong types, such as a `sealed` class or, if necessary, an `enum`, to represent these concepts. Reserve strings for raw values from third-party sources or for UI display. If you must use strings, define them as `const val` (following our [codestyle](/docs/android/codestyle#avoid-magic-numbers-and-strings)) or wrap them in a strong type, such as an [inline value class](https://kotlinlang.org/docs/inline-classes.html).
99
+
100
+
:::note[Example]
101
+
102
+
#### ❌ Avoid this pattern
103
+
104
+
```kotlin
105
+
funnewInstance(destination:String): Intent {
106
+
// Logic based on string value
107
+
returnIntent().apply {
108
+
putExtra("destination", destination)
109
+
}
110
+
}
111
+
```
112
+
113
+
#### ✅ Prefer this approach
114
+
115
+
```kotlin
116
+
privateconstvalDESTINATION_KEY="destination"
117
+
118
+
@Parcelize
119
+
sealedinterfaceDestination : Parcelable {
120
+
data objectGeneral : Destination
121
+
data objectNotifications : Destination
122
+
data objectPrivacy : Destination
123
+
}
124
+
125
+
funnewInstance(destination:Destination): Intent {
126
+
returnIntent().apply {
127
+
putExtra(DESTINATION_KEY, destination)
128
+
}
129
+
}
130
+
131
+
funonIntent(intent:Intent) {
132
+
val destination =IntentCompat.getParcelableExtra(intent, DESTINATION_KEY, Destination::class.java)
Using strong types for destinations helps prevent errors, improves code navigation, and makes refactoring more reliable. When you use `sealed` classes with `when`, the compiler can catch missing cases, and your IDE can quickly locate all usages of a specific destination, making updates and maintenance easier.
145
+
146
+
### Why sealed classes are better than enums
147
+
148
+
Sealed classes provide more flexibility and safety than enums. With sealed classes, you can define subclasses with their own properties, allowing you to pass additional data as needed for each type. This makes your APIs more expressive and adaptable.
149
+
150
+
For example, if the `Notifications` destination needs a `title` parameter, define it like this:
151
+
152
+
```kotlin
153
+
154
+
privateconstvalDESTINATION_KEY="destination"
155
+
156
+
@Parcelize
157
+
sealedinterfaceDestination : Parcelable {
158
+
data objectGeneral : Destination
159
+
data classNotifications(valtitle:String) : Destination
160
+
data objectPrivacy : Destination
161
+
}
162
+
163
+
funonIntent(intent:Intent) {
164
+
val destination =IntentCompat.getParcelableExtra(intent, DESTINATION_KEY, Destination::class.java)
165
+
when (destination) {
166
+
Destination.General->// Handle General
167
+
isDestination.Notifications-> {
168
+
val title = destination.title
169
+
// Handle Notifications with title
170
+
}
171
+
Destination.Privacy->// Handle Privacy
172
+
null->// Handle missing destination
173
+
}
174
+
}
175
+
```
176
+
177
+
:::note
178
+
When you use `when` with a sealed class, avoid adding an `else` branch. This ensures that if you add a new case, the compiler will require you to handle it, making your code safer and easier to maintain.
179
+
:::
180
+
181
+
By using sealed classes, you can safely add new destination types with their own required fields, and the compiler will enforce handling all cases. This approach makes your code more robust, maintainable, and less error-prone than using enums or strings for logic control.
182
+
183
+
Read more about sealed modifier on the [Kotlin documentation](https://kotlinlang.org/docs/sealed-classes.html).
184
+
96
185
## Code organization
97
186
98
187
### Keep your classes small
@@ -173,7 +262,11 @@ The further you progress in development, the more difficult it becomes to debug
173
262
174
263
### Leverage Kotlin compiler
175
264
176
-
The Kotlin compiler can help you catch issues early. For example, using the `when` operator with sealed classes/interfaces ensures that all cases are handled.
265
+
The Kotlin compiler can help you catch issues early. For example, using the `when` operator with `sealed` classes/interfaces ensures that all cases are handled.
266
+
267
+
:::note
268
+
Favor [composition over inheritance](https://en.wikipedia.org/wiki/Composition_over_inheritance) when designing your classes. Composition leads to more flexible, maintainable, and testable code by allowing you to build complex behavior from simpler, reusable components, rather than relying on rigid class hierarchies.
0 commit comments