| 1 | # Rastrillo Native |
| 2 | |
| 3 | Optional native components and app starting points in the Rastrillo family. |
| 4 | The first package supports iOS 17+ and macOS 14+. Android and Go Mobile |
| 5 | integration are documented extension points, not tested shipped adapters. |
| 6 | |
| 7 | ## Use the package |
| 8 | |
| 9 | Add `https://amadan.net/rastrillo/native` to your Swift package dependencies, |
| 10 | pinned to a reviewed commit, then depend on its `RastrilloNative` product. |
| 11 | Read [SKILL.md](SKILL.md) before integrating it. |
| 12 | |
| 13 | ```swift |
| 14 | import RastrilloNative |
| 15 | |
| 16 | @MainActor |
| 17 | final class Inbox { |
| 18 | private let refresh = CoalescedRunner() |
| 19 | |
| 20 | func reload() async { |
| 21 | await refresh.run { |
| 22 | // Fetch and publish this inbox's current state here. |
| 23 | } |
| 24 | } |
| 25 | } |
| 26 | ``` |
| 27 | |
| 28 | Use a runner for one operation and account. Calls during a refresh queue one |
| 29 | trailing refresh; every caller waits until shared work completes. Calls |
| 30 | during the trailing refresh can queue another pass. The first caller's |
| 31 | closure supplies every pass, so do not mix different operations in one runner. |
| 32 | Handle errors within that closure. Cancelling a caller does not cancel the |
| 33 | shared refresh. Calling the same runner from its own pass deadlocks. |
| 34 | |
| 35 | ## Start a companion app |
| 36 | |
| 37 | `examples/companion` is a small SwiftUI app with iOS and macOS targets. It |
| 38 | connects to a Rastrillo server's public `/api/version` endpoint. It has no |
| 39 | account linking, credentials, offline data or push enrolment. |
| 40 | |
| 41 | Copy that directory into your app. Replace the local package path in |
| 42 | `project.yml` with the repository URL and a reviewed revision, change the |
| 43 | bundle identifiers, and add your app's screens. Run `xcodegen generate` in |
| 44 | the copied directory and open `Companion.xcodeproj`. Sign with your own team |
| 45 | for physical devices or distribution. |
| 46 | |
| 47 | Prefer fully native UI so navigation, menus, contextual actions, links and |
| 48 | accessibility meet the platform's expectations. For a complex app, keep a |
| 49 | native shell around selected webview screens. A shared app manifest can |
| 50 | describe destinations and commands for separate web and native renderers; |
| 51 | that is a possible architecture, not a compiler supplied by this kit. See |
| 52 | [docs/app-architecture.md](docs/app-architecture.md). Bind shared Go logic only |
| 53 | when it removes duplication: see [docs/go-mobile.md](docs/go-mobile.md). |
| 54 | |
| 55 | ## Validate |
| 56 | |
| 57 | `make ci` runs the Swift tests and compiles both companion targets. It |
| 58 | requires macOS, Xcode and XcodeGen. Builds are unsigned; this gate does not |
| 59 | claim physical-device, signing or store-distribution validation. |
| 60 | |
| 61 | ## Provenance |
| 62 | |
| 63 | `CoalescedRunner` comes from Eleven/Ocho's `ios/LChatCore` at commit |
| 64 | `000b882c`, also copied into Keymail's `apple/KeymailCore`. The extraction |
| 65 | preserves its implementation and tests the failure it prevented: concurrent |
| 66 | refresh bursts and callers resuming before their new data has been fetched. |
| 67 | Consumer branches replace the copies with public type aliases so existing |
| 68 | call sites keep their API. See the adoption status on amadan before treating |
| 69 | a consumer branch as landed. |
| 70 | |
| 71 | MPL-2.0; see [LICENSE](LICENSE). The extracted source retains that licence. |
| 72 | |