wip: iOS refactor — iMessage extension, sticker browser, app icon assets
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
146
PLAN.md
Normal file
146
PLAN.md
Normal file
@@ -0,0 +1,146 @@
|
||||
# Plan: standalone iOS app + invisible iMessage sticker extension
|
||||
|
||||
## Goal
|
||||
|
||||
- **iOS app** on the home screen: paste Telegram URL, download packs, preview stickers, toggle individual stickers on/off
|
||||
- **iMessage extension** is invisible — no custom UI. It serves stickers via `MSStickerBrowserViewController` so they appear in the system sticker picker (emoji keyboard → stickers tab on iOS 17+)
|
||||
- Data flows from the iOS app → shared App Group container → extension reads it
|
||||
|
||||
## Why the 404 happens
|
||||
|
||||
The backend always calls `manager.getPack()` (Telegram Bot API) before checking its cache. If the bot token is invalid or Telegram is unreachable, it returns `None` → 404, even when sticker files exist on disk. This is a **backend bug** — fix separately. The iOS URL construction is correct.
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────┐ ┌──────────────────────────┐
|
||||
│ iOS App (home screen) │ │ iMessage Extension │
|
||||
│ │ │ (no custom UI) │
|
||||
│ - Paste URL │ │ │
|
||||
│ - Download packs │ ──► │ MSStickerBrowserVC │
|
||||
│ - Preview stickers │ │ reads shared container │
|
||||
│ - Toggle on/off │ │ → stickers appear in │
|
||||
│ - Delete packs │ │ system sticker picker │
|
||||
└─────────────────────────┘ └──────────────────────────┘
|
||||
│ │
|
||||
└──────── App Group ───────────────┘
|
||||
UserDefaults(suiteName:)
|
||||
shared Caches/stickers/
|
||||
```
|
||||
|
||||
## File layout after changes
|
||||
|
||||
```
|
||||
StickerCloner/
|
||||
├── Shared/ ← NEW synced folder, both targets
|
||||
│ ├── Models.swift moved + updated
|
||||
│ └── StickerStore.swift moved + rewritten (no Messages import)
|
||||
│
|
||||
├── StickerCloner/ ← iOS app target (auto-synced)
|
||||
│ ├── Assets.xcassets/ existing
|
||||
│ ├── StickerClonerApp.swift NEW — SwiftUI @main entry
|
||||
│ ├── PackListView.swift NEW — list of packs + URL input
|
||||
│ ├── PackDetailView.swift NEW — sticker grid with toggles
|
||||
│ └── StickerClonerAPI.swift moved from extension (only app needs it)
|
||||
│
|
||||
├── StickerCloner MessagesExtension/ ← extension target (auto-synced)
|
||||
│ ├── Assets.xcassets/ existing
|
||||
│ ├── Base.lproj/MainInterface.storyboard existing
|
||||
│ ├── Info.plist existing
|
||||
│ ├── MessagesViewController.swift rewritten (minimal)
|
||||
│ └── StickerBrowserViewController.swift rewritten (reads shared data)
|
||||
│
|
||||
└── StickerCloner.xcodeproj/
|
||||
```
|
||||
|
||||
## Project file (pbxproj) changes
|
||||
|
||||
1. **Change main target product type**: `com.apple.product-type.application.messages` → `com.apple.product-type.application`
|
||||
2. **Add Sources build phase** to main target (empty — synced groups handle inclusion)
|
||||
3. **Add `Shared/` PBXFileSystemSynchronizedRootGroup** to both targets' `fileSystemSynchronizedGroups`
|
||||
4. **Add App Group entitlements** via `CODE_SIGN_ENTITLEMENTS` build setting on both targets
|
||||
|
||||
## Data model changes
|
||||
|
||||
### Models.swift (Shared/)
|
||||
|
||||
```swift
|
||||
// API types (unchanged)
|
||||
struct StickerSetResponse: Codable { ... }
|
||||
struct StickerResponse: Codable { ... }
|
||||
|
||||
// Local persistence — now tracks enabled stickers
|
||||
struct SavedPack: Codable {
|
||||
let name: String
|
||||
let title: String
|
||||
let stickerIds: [String] // all sticker IDs in order
|
||||
var enabledStickerIds: Set<String> // subset the user wants
|
||||
}
|
||||
```
|
||||
|
||||
### StickerStore.swift (Shared/)
|
||||
|
||||
- Uses `UserDefaults(suiteName: "group.de.felixfoertsch.StickerCloner")` instead of `.standard`
|
||||
- Uses `FileManager.containerURL(forSecurityApplicationGroupIdentifier:)` instead of caches directory
|
||||
- `stickerFileURLs(for pack:)` returns `[URL]` (not MSSticker — no Messages dependency)
|
||||
- Returns only enabled stickers
|
||||
|
||||
### StickerClonerAPI.swift (iOS app only)
|
||||
|
||||
Stays in the app target — the extension never calls the API. Unchanged except move location.
|
||||
|
||||
## iOS App views
|
||||
|
||||
### StickerClonerApp.swift
|
||||
- `@main` SwiftUI app
|
||||
- Single `PackListView` as root in NavigationStack
|
||||
|
||||
### PackListView.swift
|
||||
- Text field + "Add" button to paste Telegram URL or pack name
|
||||
- List of saved packs with swipe-to-delete
|
||||
- Navigation link to `PackDetailView` per pack
|
||||
- Loading/error states during download
|
||||
|
||||
### PackDetailView.swift
|
||||
- Grid of sticker thumbnails (loaded from local PNG files)
|
||||
- Tap to toggle enabled/disabled (dimmed + checkmark overlay)
|
||||
- "Select All" / "Deselect All" toolbar buttons
|
||||
- Pack title in navigation bar
|
||||
|
||||
## Extension changes
|
||||
|
||||
### MessagesViewController.swift
|
||||
- Hosts `StickerBrowserViewController` as child, fills entire view
|
||||
- No expanded mode handling, no SwiftUI views
|
||||
- Reloads stickers on `willBecomeActive`
|
||||
|
||||
### StickerBrowserViewController.swift
|
||||
- Loads enabled sticker file URLs from `StickerStore`
|
||||
- Creates `MSSticker` from each URL
|
||||
- Standard `MSStickerBrowserViewController` data source
|
||||
|
||||
## Entitlements
|
||||
|
||||
Both targets get identical entitlements:
|
||||
```xml
|
||||
<key>com.apple.security.application-groups</key>
|
||||
<array>
|
||||
<string>group.de.felixfoertsch.StickerCloner</string>
|
||||
</array>
|
||||
```
|
||||
|
||||
## Commits
|
||||
|
||||
1. `convert to standalone iOS app, add shared folder, app group entitlements`
|
||||
2. `add iOS app ui: pack list, pack detail with sticker filtering`
|
||||
3. `simplify iMessage extension to read-only sticker browser`
|
||||
|
||||
## Verification
|
||||
|
||||
1. Build in Xcode → no compile errors for both targets
|
||||
2. iOS app appears on home screen
|
||||
3. Paste pack URL → downloads (once backend 404 is fixed)
|
||||
4. Tap pack → see sticker grid, toggle individual stickers
|
||||
5. Open Messages → stickers appear in sticker picker
|
||||
6. Only enabled stickers shown
|
||||
7. Kill app, reopen → state persists via App Group
|
||||
Reference in New Issue
Block a user