Written the README.md file for the Xcode project.
This commit is contained in:
@@ -14,6 +14,7 @@
|
|||||||
/* Begin PBXFileReference section */
|
/* Begin PBXFileReference section */
|
||||||
02870A0D2FF7EB610079EA3A /* Attendi.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Attendi.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
02870A0D2FF7EB610079EA3A /* Attendi.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Attendi.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||||
02870A262FF7F2990079EA3A /* README.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = README.pdf; sourceTree = "<group>"; };
|
02870A262FF7F2990079EA3A /* README.pdf */ = {isa = PBXFileReference; lastKnownFileType = image.pdf; path = README.pdf; sourceTree = "<group>"; };
|
||||||
|
02870A322FF831CD0079EA3A /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = "<group>"; };
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */
|
||||||
@@ -58,6 +59,7 @@
|
|||||||
028709F12FF7E8E40079EA3A = {
|
028709F12FF7E8E40079EA3A = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
|
02870A322FF831CD0079EA3A /* README.md */,
|
||||||
02870A202FF7ECD00079EA3A /* Packages */,
|
02870A202FF7ECD00079EA3A /* Packages */,
|
||||||
02870A082FF7EB360079EA3A /* Apps */,
|
02870A082FF7EB360079EA3A /* Apps */,
|
||||||
02870A232FF7F07F0079EA3A /* Frameworks */,
|
02870A232FF7F07F0079EA3A /* Frameworks */,
|
||||||
|
|||||||
@@ -0,0 +1,96 @@
|
|||||||
|
# Attendi Sample App
|
||||||
|
|
||||||
|
A sample app that implements an audio recording flow in SwiftUI: start, pause, and resume a recording while a timer
|
||||||
|
tracks the elapsed time, then send the recording for (simulated) processing.
|
||||||
|
|
||||||
|
## Context
|
||||||
|
|
||||||
|
This sample app was created for a technical interview for a senior Mobile (iOS + Android) engineer role at
|
||||||
|
[Attendi](https://attendi.nl), a Dutch health-tech company that makes voice-driven reporting software for
|
||||||
|
healthcare professionals.
|
||||||
|
|
||||||
|
## Requirements
|
||||||
|
|
||||||
|
| Requirement | Version |
|
||||||
|
| --- | --- |
|
||||||
|
| Xcode | 26 or later |
|
||||||
|
| Swift | 6.3 |
|
||||||
|
| Platforms | iOS/macOS/visionOS 26 |
|
||||||
|
|
||||||
|
## Project structure
|
||||||
|
|
||||||
|
The project separates the app shell from the feature code, which lives in a local Swift package:
|
||||||
|
|
||||||
|
```
|
||||||
|
Attendi/
|
||||||
|
├── Attendi.xcodeproj
|
||||||
|
├── Apps/
|
||||||
|
│ └── Attendi/ # The app target (thin shell)
|
||||||
|
│ ├── AttendiApp.swift # The @main entry point
|
||||||
|
│ ├── ContentView.swift # The root view, hosting the Recording feature
|
||||||
|
│ └── Assets.xcassets
|
||||||
|
└── Packages/
|
||||||
|
└── Features/ # Local Swift package with the feature code
|
||||||
|
├── Package.swift
|
||||||
|
├── Sources/
|
||||||
|
│ └── Recording/ # The Recording feature target
|
||||||
|
│ ├── RecordingView.swift
|
||||||
|
│ ├── RecordingViewModel.swift
|
||||||
|
│ ├── RecordingButtonStyle.swift
|
||||||
|
│ └── Images.xcassets # Record, pause, and send icons
|
||||||
|
└── Tests/
|
||||||
|
├── Recording/
|
||||||
|
│ └── RecordingViewModelTests.swift
|
||||||
|
└── Features.xctestplan
|
||||||
|
```
|
||||||
|
|
||||||
|
The app target only depends on the `Features` package and renders its public `RecordingView` view; all recording logic
|
||||||
|
and UI live in the package's `Recording` target.
|
||||||
|
|
||||||
|
## The Recording feature
|
||||||
|
|
||||||
|
### State machine
|
||||||
|
|
||||||
|
The flow is modeled as a state machine in `RecordingView.Model`, an `@Observable`, `@MainActor` view model:
|
||||||
|
|
||||||
|
| State | Meaning |
|
||||||
|
| --- | --- |
|
||||||
|
| `notRecording` | Idle; no recording in progress. |
|
||||||
|
| `recording` | A recording is in progress and the timer is ticking. |
|
||||||
|
| `paused` | The recording is paused; it can be resumed or sent. |
|
||||||
|
| `processing` | The sent recording is being processed. |
|
||||||
|
|
||||||
|
The main button starts, pauses, and resumes a recording. While paused, a send button appears; pressing it moves the
|
||||||
|
flow into processing (currently simulated with a delay) before returning to idle.
|
||||||
|
|
||||||
|
### Timer
|
||||||
|
|
||||||
|
While recording, an async task increments the elapsed seconds once per second — pausing stops it, resuming continues
|
||||||
|
it, and a new recording resets it. The view formats the count as `mm:ss` and animates digit changes with a numeric
|
||||||
|
text content transition.
|
||||||
|
|
||||||
|
### Views
|
||||||
|
|
||||||
|
- **`RecordingView`** — the public entry point of the feature. It renders the timer label and the control buttons for
|
||||||
|
the model's current state, and forwards state changes back to the model.
|
||||||
|
- **`RecordingButtonStyle`** — a custom `ButtonStyle` used by the controls: the label sits on a padded, circular red
|
||||||
|
background, shrinks while pressed, and dims while disabled. The label and its padding scale with Dynamic Type
|
||||||
|
via `@ScaledMetric`, and an `invertStyle` flag controls whether the label's color scheme is inverted for contrast.
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
The view model is covered by Swift Testing suites in `RecordingViewModelTests.swift`, grouped with nested `@Suite`
|
||||||
|
types (initial state, button presses, computed properties, timer, and processing). The state-dependent behaviors are
|
||||||
|
exercised with parameterized tests across all four states.
|
||||||
|
|
||||||
|
Run the tests with the `Features` scheme in Xcode (`⌘U`), or from the command line:
|
||||||
|
|
||||||
|
```sh
|
||||||
|
xcodebuild test -scheme Features -destination 'platform=iOS'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Tooling
|
||||||
|
|
||||||
|
- **`.swift-format`** — configuration for Apple's `swift-format`, used by Xcode's built-in formatter (4-space
|
||||||
|
indentation, 200-column lines).
|
||||||
|
- **`README.pdf`** — the original assignment requirements.
|
||||||
Reference in New Issue
Block a user