feat(ios-ql): add iOS QLThumbnailProvider extension#941
Conversation
Adds PkmdsQuickLookThumbnail — a C# QLThumbnailProvider app extension that renders trainer cards (saves) and species sprites (entities / wonder cards) as iOS Quick Look thumbnails in Files.app grid/column view. Architecture mirrors the macOS Swift thumbnail provider, but written entirely in C# (no NativeAOT dylib) with PKHeX.Core running in-process: - ThumbnailProvider.cs — QLThumbnailProvider subclass; dispatches PKHeX.Core work on a background queue, then draws into the QLThumbnailReply CGContext via UIGraphics.PushContext/PopContext. - SaveCard.cs — UIKit trainer card drawing (UIBezierPath, UIFont, NSString attributed drawing); ports macOS drawTrainerCard with y-down coordinate system; includes per-character gradient text for RB/GS ambiguous groups. - SpriteDrawer.cs — loads bundled sprites (Resources/sprites/), computes tight opaque bounding box via CGBitmapContext pixel scan, and draws centred into the QL CGContext with a vertical flip transform. - build-extension.sh — updated to build both extensions, embed both .appex bundles, copy a/ai/bi sprites into the thumbnail appex, and sign both. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…ension Three binding-level differences from the macOS extension required fixes: - QLThumbnailReply: no public constructors in net10.0-ios binding. Fall back to UIGraphicsImageRenderer → PNG → replyWithImageFileURL: via P/Invoke into libobjc. - CGBitmapContext.Create(IntPtr.Zero,...) matched the adaptive (callback-based) overload; use new CGBitmapContext(IntPtr.Zero,...) constructor directly. - CGRect.MidX/MidY: not properties in the iOS binding; compute inline as X+Width/2 etc. - NSString.DrawString: use NSAttributedString.DrawString(CGRect) instead. - NSDictionary attrs: use UIStringAttributes.Dictionary as the NSAttributedString ctor arg. - DispatchQueue: use Task.Run instead (avoids CoreFoundation using). - request.FileURL → request.FileUrl: correct C# property name in iOS binding. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…card UTType UIGraphicsRendererContext.CGContext calls UIApplication.EnsureUIThread() which aborts when the drawing block runs on a Task.Run background thread. Wrap the UIGraphicsImageRenderer.CreateImage call in InvokeOnMainThread so the extension's main run loop services it. Also add com.bondcodes.pkmds.wonder-card to project.yml UTExportedTypeDeclarations (wc4/wc5full/wc6/wc6full/wc7/wc7full/wc8/wc8full/wc9/pcd/pgt/pgf) so Files.app recognises mystery gift files and dispatches them to both QL extensions. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Tested in the iOS 17 Pro simulator and found two bugs, both now fixed in 425eb4c. Bug 1 — thumbnail extension crashes (SIGABRT) on every file Crash log: Fix: wrap the Bug 2 — wonder card preview and thumbnail do nothing
Fix: add the UTType to |
There was a problem hiding this comment.
Pull request overview
This PR adds an iOS Quick Look thumbnail extension that renders PKMDS-supported files as Files.app thumbnails, complementing the existing iOS Quick Look preview extension and shared preview/sprite infrastructure.
Changes:
- Adds
PkmdsQuickLookThumbnailwith a C#QLThumbnailProvider, sprite drawing, and trainer-card rendering for saves. - Updates the iOS build script to build, embed, sprite-bundle, and sign both preview and thumbnail extensions.
- Adds/updates iOS UTI declarations for wonder cards and supported document types.
Reviewed changes
Copilot reviewed 8 out of 8 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
tools/ios-quicklook-poc/xcode/project.yml |
Adds the wonder-card UTI and document type to the iOS host project generation config. |
tools/ios-quicklook-poc/xcode/PkmdsHost/Info.plist |
Updates declared file extensions and wonder-card UTI metadata for the host app. |
tools/ios-quicklook-poc/PkmdsQuickLookThumbnail/PkmdsQuickLookThumbnail.csproj |
Defines the new .NET iOS app-extension project. |
tools/ios-quicklook-poc/PkmdsQuickLookThumbnail/Info.plist |
Declares the thumbnail extension point and supported PKMDS UTIs. |
tools/ios-quicklook-poc/PkmdsQuickLookThumbnail/ThumbnailProvider.cs |
Implements thumbnail generation, save parsing, sprite selection, rendering, and QLThumbnailReply creation. |
tools/ios-quicklook-poc/PkmdsQuickLookThumbnail/SpriteDrawer.cs |
Loads bundled sprites, finds opaque bounds, and draws centered sprite thumbnails. |
tools/ios-quicklook-poc/PkmdsQuickLookThumbnail/SaveCard.cs |
Draws iOS trainer-card thumbnails for save files. |
tools/ios-quicklook-poc/build-extension.sh |
Builds and embeds both Quick Look extensions and copies sprite assets into the thumbnail extension. |
…ions pkm-file: add sk2, ck3, xk3, bk4, rk4 (Gen 2/3 alternate entity formats), pa9 save-file: add gci (GameCube), dsv, srm, dat (Virtual Console), bin (BDSP/LGP) wonder-card: add wc3, wr7, wb7, wb7full, wb8, wa8, wa9 (Gen 3 and Bank/Workshop gifts) All these extensions are already handled by preview-shared; the UTType declarations just weren't exposing them to Files.app for QL dispatch. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…spatch Adding dat and bin to save-file caused iOS to reject QL dispatch for all extensions in that UTType (including sav) and appears to also invalidate wonder-card dispatch in the same registration pass — same "prohibited extension" behaviour as macOS. Mirror the macOS split: save-file: sav, gci, dsv, srm (safe, unambiguous) save-file-restricted: dat, bin, fla (common extensions, separated) Both UTTypes carry public.composite-content so Files.app dispatches them. Add save-file-restricted to QLSupportedContentTypes in both extension plists. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…plist re-read Microsoft.iOS.Sdk caches the merged Info.plist as obj/.../AppManifest.plist. Deleting bin/ alone is insufficient; plist-only changes (no .cs edits) are silently skipped on incremental builds. Delete AppManifest.plist from obj/ before each dotnet build call so QLSupportedContentTypes is always up to date. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…upportedContentTypes iOS ThumbnailsAgent blacklists an entire QLThumbnailExtension when any of its registered UTTypes contain a prohibited filename extension (dat, bin, fla). Adding save-file-restricted to the thumbnail extension's plist blocked dispatch for ALL types — including pkm-file, save-file, and wonder-card. Remove it. QL preview (com.apple.quicklook.preview) is dispatched by a different path that does not apply this prohibition, so save-file-restricted remains in the preview extension's plist for .dat/.bin/.fla preview support. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Both macOS and iOS host apps still listed only .pk* and .sav files. Update to reflect all supported types: entity files (pk1–pk9, pa*, pb*, sk2, ck3, xk3, bk4, rk4), save files (sav, gci, dsv, srm, dat, bin, fla), and Wonder Cards (wc3–wc9, wa*, wb*, wr7, pcd, pgt, pgf). Also note thumbnail support in both hosts. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
.bin (BDSP, Let's Go Pikachu/Eevee save format) was declared in the iOS and macOS UTType tag specs but missing from the shared C# source of truth. Align PreviewFileTypes.SaveFiles with the plist declarations. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Summary
PkmdsQuickLookThumbnail— a C#QLThumbnailProviderthat renders trainer cards (saves) and species sprites (entities / wonder cards) as iOS Quick Look thumbnails in Files.app..pk*,.pa*,.pb*,.sk2,.ck3,.xk3,.bk4,.rk4,.sav,.gci,.dsv,.srm,.wc3–.wc9and variants,.pcd,.pgt,.pgf,.wr7,.wb7,.wa8.save-filefromsave-file-restricted(.dat/.bin/.fla) to avoid iOS UTType poisoning.build-extension.shupdated to build, embed, sprite-bundle, sign both extensions, and invalidateobj/.../AppManifest.plistso Info.plist-only changes are always picked up.Bugs found and fixed during testing
UIKit main-thread crash —
UIGraphicsImageRenderer.CreateImage(and allUIBezierPath/NSAttributedStringdrawing) callsUIApplication.EnsureUIThread()and SIGABRTs on background threads. Fixed by wrapping all UIKit rendering inInvokeOnMainThread(...).Wonder card dispatch missing —
com.bondcodes.pkmds.wonder-cardUTType was absent fromproject.yml; xcodegen regeneratesInfo.plistfromproject.ymlon every build, silently dropping any hand-edited UTType. Fixed by adding the full wonder-card entry (identifier, conformances, all extensions) toproject.yml.Prohibited extension poisons UTType — Adding
.dat/.bintosave-filecaused iOS to block QL dispatch for ALL extensions in that UTType. Same fix as the macOS POC: split intosave-file(safe extensions) +save-file-restricted(prohibited extensions).ThumbnailsAgentblacklists entire extension for prohibited extensions — Includingsave-file-restrictedin the thumbnail extension'sQLSupportedContentTypescaused ThumbnailsAgent to log "trying to register for a prohibited filename extension: dat, bin, fla" and reject all dispatch for the extension — includingwonder-cardandsave-file. Fix: removesave-file-restrictedfrom the thumbnail extension only. The preview extension's dispatch path does not apply this prohibition, so.dat/.bin/.flafiles still get QL preview.Incremental build skips Info.plist-only changes —
Microsoft.iOS.Sdkcaches the merged plist inobj/.../AppManifest.plist. Deletingbin/output is insufficient;build-extension.shnow also deletesAppManifest.plistfromobj/before eachdotnet build.Result
.pk*,.pa*,.pb*, etc..sav,.gci,.dsv,.srm.dat,.bin,.fla.wc3–.wc9,.pcd,.pgt,.pgfTest plan
./build-extension.sh(simulator) — both extensions build without error.pk*/.sav/.gcifiles → thumbnail shows species sprite / trainer card.wc3,.wc6files → thumbnail shows species/item placeholder sprite.dat,.binfiles → no thumbnail (expected), QL preview worksCloses #935
🤖 Generated with Claude Code