6.0 KiB
6.0 KiB
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
MSStickerBrowserViewControllerso 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
- Change main target product type:
com.apple.product-type.application.messages→com.apple.product-type.application - Add Sources build phase to main target (empty — synced groups handle inclusion)
- Add
Shared/PBXFileSystemSynchronizedRootGroup to both targets'fileSystemSynchronizedGroups - Add App Group entitlements via
CODE_SIGN_ENTITLEMENTSbuild setting on both targets
Data model changes
Models.swift (Shared/)
// 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
@mainSwiftUI app- Single
PackListViewas 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
PackDetailViewper 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
StickerBrowserViewControlleras 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
MSStickerfrom each URL - Standard
MSStickerBrowserViewControllerdata source
Entitlements
Both targets get identical entitlements:
<key>com.apple.security.application-groups</key>
<array>
<string>group.de.felixfoertsch.StickerCloner</string>
</array>
Commits
convert to standalone iOS app, add shared folder, app group entitlementsadd iOS app ui: pack list, pack detail with sticker filteringsimplify iMessage extension to read-only sticker browser
Verification
- Build in Xcode → no compile errors for both targets
- iOS app appears on home screen
- Paste pack URL → downloads (once backend 404 is fixed)
- Tap pack → see sticker grid, toggle individual stickers
- Open Messages → stickers appear in sticker picker
- Only enabled stickers shown
- Kill app, reopen → state persists via App Group