Skip to content

feat(ios-ql): add iOS QLThumbnailProvider extension#941

Merged
codemonkey85 merged 9 commits into
mainfrom
dev
May 27, 2026
Merged

feat(ios-ql): add iOS QLThumbnailProvider extension#941
codemonkey85 merged 9 commits into
mainfrom
dev

Conversation

@codemonkey85
Copy link
Copy Markdown
Owner

@codemonkey85 codemonkey85 commented May 27, 2026

Summary

  • Adds PkmdsQuickLookThumbnail — a C# QLThumbnailProvider that renders trainer cards (saves) and species sprites (entities / wonder cards) as iOS Quick Look thumbnails in Files.app.
  • Expands UTType declarations for all supported file types: .pk*, .pa*, .pb*, .sk2, .ck3, .xk3, .bk4, .rk4, .sav, .gci, .dsv, .srm, .wc3.wc9 and variants, .pcd, .pgt, .pgf, .wr7, .wb7, .wa8.
  • Splits save-file from save-file-restricted (.dat/.bin/.fla) to avoid iOS UTType poisoning.
  • build-extension.sh updated to build, embed, sprite-bundle, sign both extensions, and invalidate obj/.../AppManifest.plist so Info.plist-only changes are always picked up.

Bugs found and fixed during testing

  1. UIKit main-thread crashUIGraphicsImageRenderer.CreateImage (and all UIBezierPath/NSAttributedString drawing) calls UIApplication.EnsureUIThread() and SIGABRTs on background threads. Fixed by wrapping all UIKit rendering in InvokeOnMainThread(...).

  2. Wonder card dispatch missingcom.bondcodes.pkmds.wonder-card UTType was absent from project.yml; xcodegen regenerates Info.plist from project.yml on every build, silently dropping any hand-edited UTType. Fixed by adding the full wonder-card entry (identifier, conformances, all extensions) to project.yml.

  3. Prohibited extension poisons UTType — Adding .dat/.bin to save-file caused iOS to block QL dispatch for ALL extensions in that UTType. Same fix as the macOS POC: split into save-file (safe extensions) + save-file-restricted (prohibited extensions).

  4. ThumbnailsAgent blacklists entire extension for prohibited extensions — Including save-file-restricted in the thumbnail extension's QLSupportedContentTypes caused ThumbnailsAgent to log "trying to register for a prohibited filename extension: dat, bin, fla" and reject all dispatch for the extension — including wonder-card and save-file. Fix: remove save-file-restricted from the thumbnail extension only. The preview extension's dispatch path does not apply this prohibition, so .dat/.bin/.fla files still get QL preview.

  5. Incremental build skips Info.plist-only changesMicrosoft.iOS.Sdk caches the merged plist in obj/.../AppManifest.plist. Deleting bin/ output is insufficient; build-extension.sh now also deletes AppManifest.plist from obj/ before each dotnet build.

Result

File type Thumbnail QL Preview
.pk*, .pa*, .pb*, etc. ✅ species sprite ✅ HTML
.sav, .gci, .dsv, .srm ✅ trainer card ✅ HTML
.dat, .bin, .fla ❌ (iOS prohibits) ✅ HTML
.wc3.wc9, .pcd, .pgt, .pgf ✅ species/item sprite ✅ HTML

Test plan

  • ./build-extension.sh (simulator) — both extensions build without error
  • .pk* / .sav / .gci files → thumbnail shows species sprite / trainer card
  • .wc3, .wc6 files → thumbnail shows species/item placeholder sprite
  • Long-press any file → Quick Look → HTML preview works
  • .dat, .bin files → no thumbnail (expected), QL preview works

Closes #935

🤖 Generated with Claude Code

codemonkey85 and others added 3 commits May 27, 2026 01:12
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>
@codemonkey85
Copy link
Copy Markdown
Owner Author

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: UIGraphicsRendererContext.get_CGContext → UIApplication.EnsureUIThread() — the .NET iOS binding enforces that UIKit drawing must happen on the main thread. The renderer's CreateImage block was running inside Task.Run() on a thread-pool thread.

Fix: wrap the UIGraphicsImageRenderer.CreateImage(...) call in InvokeOnMainThread(...) so the extension's main run loop services it. PKHeX parsing and file I/O still happen on the background thread; only the UIKit drawing portion is marshalled to the main thread.

Bug 2 — wonder card preview and thumbnail do nothing

project.yml was missing com.bondcodes.pkmds.wonder-card in UTExportedTypeDeclarations. xcodegen overwrites Info.plist from project.yml on every build, so any manual addition to Info.plist doesn't survive. Without the UTType exported, iOS doesn't recognise .wc*/.pcd/.pgt/.pgf files as com.bondcodes.pkmds.wonder-card and never dispatches them to the extensions.

Fix: add the UTType to project.yml (with extensions wc4, wc5full, wc6, wc6full, wc7, wc7full, wc8, wc8full, wc9, pcd, pgt, pgf) and a matching CFBundleDocumentTypes entry. Also regenerated Info.plist via xcodegen.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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 PkmdsQuickLookThumbnail with 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.

Comment thread tools/ios-quicklook-poc/xcode/project.yml
Comment thread tools/ios-quicklook-poc/xcode/PkmdsHost/Info.plist
Comment thread tools/ios-quicklook-poc/xcode/PkmdsHost/Info.plist
Comment thread tools/ios-quicklook-poc/xcode/PkmdsHost/Info.plist
codemonkey85 and others added 6 commits May 27, 2026 08:58
…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>
@codemonkey85 codemonkey85 merged commit c5cb729 into main May 27, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Thumbnail providers for PKHeX files (Windows + macOS + iOS)

2 participants