---
skill: Mobile
description: "Frontend for mobile: native iOS (Swift, SwiftUI) and Android (Kotlin, Compose), from app lifecycle to store release."
owner: Developer D
guides: 2
exported: 2026-10-19
---

# Mobile

Frontend for mobile: native iOS (Swift, SwiftUI) and Android (Kotlin, Compose), from app lifecycle to store release.

## Contents

- [iOS Fundamentals](#ios-fundamentals) — Concept guide
- [Android Fundamentals](#android-fundamentals) — Concept guide

---

<a id="ios-fundamentals"></a>

# iOS Fundamentals

_Concept guide · written by Developer D · updated 2026-09-29_

> Swift, the app lifecycle, and how SwiftUI decides what to draw.

## Swift in one page

- **Optionals** are the type system telling you a value may be absent. Unwrap
  deliberately; `!` is a promise you will be held to at runtime.
- **Structs are values, classes are references.** Prefer structs. Reach for a
  class when you need identity or shared mutable state.
- **Protocols** describe capability, not hierarchy.

## App lifecycle

An app is not simply "open" or "closed":

| State | What it means |
| --- | --- |
| Not running | Never launched, or terminated |
| Inactive | Foreground but not receiving events |
| Active | Foreground and interactive |
| Background | Running briefly with limited time |
| Suspended | In memory, not executing — can be killed silently |

Anything you have not saved before suspension can disappear.

## SwiftUI is declarative

You describe what the UI should look like for a given state; the framework works
out the changes. You do not mutate views.

```swift
struct LevelBadge: View {
    let level: Int

    var body: some View {
        Text("L\(level)")
            .font(.caption.weight(.semibold))
            .padding(.horizontal, 8)
            .padding(.vertical, 2)
            .background(Capsule().fill(.quaternary))
    }
}
```

## State ownership

- `@State` — this view owns it.
- `@Binding` — someone else owns it, this view may change it.
- `@Observable` / `@StateObject` — a model object with a longer life.

Hoist state to the lowest common ancestor that needs it, and no higher.

---

<a id="android-fundamentals"></a>

# Android Fundamentals

_Concept guide · written by Developer D · updated 2026-09-29_

> Kotlin, the lifecycle that actually bites, and how Compose recomposes.

## Kotlin essentials

- **Null safety** is in the type system. `String?` and `String` are different
  types, and the compiler will hold you to it.
- **Data classes** give you `equals`, `hashCode`, `copy` and destructuring.
- **Sealed classes** model a closed set of states — perfect for UI state.

```kotlin
sealed interface SkillUiState {
    data object Loading : SkillUiState
    data class Ready(val skills: List<Skill>) : SkillUiState
    data class Failed(val message: String) : SkillUiState
}
```

## The lifecycle that actually bites

Rotation, dark mode, language change and low memory can all destroy and recreate
your Activity. Anything held only in the Activity is gone.

- Survives recreation: `ViewModel`.
- Survives process death: `SavedStateHandle`, or persisted storage.
- Survives nothing: a property on the Activity.

## Compose and recomposition

Compose re-runs composable functions when the state they read changes. This means:

- A composable must be **side-effect free** — it can run many times.
- Read state as late as possible, so less gets recomposed.
- Hoist state: a composable that takes a value and a callback is testable and
  reusable; one that owns its own state is neither.

```kotlin
@Composable
fun LevelChip(level: Int, modifier: Modifier = Modifier) {
    Text(
        text = "L$level",
        style = MaterialTheme.typography.labelSmall,
        modifier = modifier
            .background(MaterialTheme.colorScheme.surfaceVariant, CircleShape)
            .padding(horizontal = 8.dp, vertical = 2.dp),
    )
}
```

## Coroutines

- Structured concurrency: work is scoped, and cancelling the scope cancels the
  work. Use `viewModelScope`.
- `Flow` is a stream. `StateFlow` always has a current value — that is what a
  screen wants.
- Never block the main thread. Use `withContext(Dispatchers.IO)` for disk and
  network.
