<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: yamayu-dev</title>
    <description>The latest articles on DEV Community by yamayu-dev (@yamayu-dev).</description>
    <link>https://dev.to/yamayu-dev</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4012826%2F30671a5e-7ec6-494c-94e0-d3770e674e4e.webp</url>
      <title>DEV Community: yamayu-dev</title>
      <link>https://dev.to/yamayu-dev</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/yamayu-dev"/>
    <language>en</language>
    <item>
      <title>Shipping a libmpv-based video player on the Mac App Store</title>
      <dc:creator>yamayu-dev</dc:creator>
      <pubDate>Thu, 02 Jul 2026 22:53:25 +0000</pubDate>
      <link>https://dev.to/yamayu-dev/shipping-a-libmpv-based-video-player-on-the-mac-app-store-1f2l</link>
      <guid>https://dev.to/yamayu-dev/shipping-a-libmpv-based-video-player-on-the-mac-app-store-1f2l</guid>
      <description>&lt;h1&gt;
  
  
  Shipping a libmpv-based video player on the Mac App Store
&lt;/h1&gt;

&lt;p&gt;Most well-known mpv-based Mac players, including IINA, distribute outside the Mac App Store. When I built &lt;strong&gt;Reel&lt;/strong&gt;, a local-video player/library app for macOS, I wanted to know &lt;em&gt;why&lt;/em&gt;, and whether it could be done. It can. It took a month from first commit to approval, and almost none of the hard parts were about playing video.&lt;/p&gt;

&lt;p&gt;This is a field report of every wall I hit: a JIT crash that only happens under the App Store's rules, LGPL compliance with static linking, two sandbox traps that pass locally and fail later, and one design rejection. If you're putting any FFmpeg/libmpv-family library into a sandboxed Mac app, this should save you a week or two.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Setup:&lt;/strong&gt; Swift / SwiftUI app. Playback is AVFoundation plus &lt;strong&gt;libmpv&lt;/strong&gt; (via &lt;a href="https://github.com/mpvkit/MPVKit" rel="noopener noreferrer"&gt;MPVKit&lt;/a&gt;, statically linked, LGPL build) for containers where AVFoundation isn't enough, such as mkv, webm, and avi. Distribution: Mac App Store, sandboxed, with StoreKit 2 for a tip jar.&lt;/p&gt;




&lt;h2&gt;
  
  
  Wall 1: LuaJIT inside libmpv gets your process killed — only on the App Store build
&lt;/h2&gt;

&lt;p&gt;During development (sandbox off), libmpv worked perfectly. The moment I built with the Mac App Store configuration — App Sandbox plus Hardened-Runtime-style entitlement restrictions — the process died &lt;strong&gt;inside &lt;code&gt;mpv_initialize()&lt;/code&gt;&lt;/strong&gt;, before playing anything.&lt;/p&gt;

&lt;p&gt;The culprit is the pair of JIT entitlements:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;com.apple.security.cs.allow-jit&lt;/code&gt; — &lt;strong&gt;allowed&lt;/strong&gt; on the Mac App Store&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;com.apple.security.cs.allow-unsigned-executable-memory&lt;/code&gt; — &lt;strong&gt;not allowed&lt;/strong&gt; on the Mac App Store&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;libmpv statically links &lt;strong&gt;LuaJIT&lt;/strong&gt;, and LuaJIT's machine-code allocator requests plain RWX executable memory rather than &lt;code&gt;MAP_JIT&lt;/code&gt; memory. With only &lt;code&gt;allow-jit&lt;/code&gt;, AMFI kills the process.&lt;/p&gt;

&lt;p&gt;I confirmed it with an A/B test on the actual signed builds:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Entitlements on the binary&lt;/th&gt;
&lt;th&gt;&lt;code&gt;mpv_initialize()&lt;/code&gt;&lt;/th&gt;
&lt;th&gt;Result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;allow-jit&lt;/code&gt; only (the most MAS permits)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;SIGKILL (exit 137)&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;AMFI kills the process&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;code&gt;allow-jit&lt;/code&gt; + &lt;code&gt;allow-unsigned-executable-memory&lt;/code&gt; (dev config)&lt;/td&gt;
&lt;td&gt;succeeds, playback works&lt;/td&gt;
&lt;td&gt;fine — but unshippable&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  "Just disable Lua scripts" does not work
&lt;/h3&gt;

&lt;p&gt;My first hope was configuration: &lt;code&gt;load-scripts=no&lt;/code&gt;, &lt;code&gt;osc=no&lt;/code&gt;, &lt;code&gt;ytdl=no&lt;/code&gt;, &lt;code&gt;config=no&lt;/code&gt;. &lt;strong&gt;No effect.&lt;/strong&gt; LuaJIT's allocator grabs executable memory during init regardless of whether any script will ever run. If LuaJIT is linked in, you crash.&lt;/p&gt;

&lt;h3&gt;
  
  
  The fix: rebuild libmpv with Lua disabled
&lt;/h3&gt;

&lt;p&gt;Reel doesn't use mpv's Lua scripting at all — no OSC, no user scripts. So the fix was a one-line change to the mpv build:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight diff"&gt;&lt;code&gt;&lt;span class="gd"&gt;- -Dlua=luajit
&lt;/span&gt;&lt;span class="gi"&gt;+ -Dlua=disabled
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A script applies this and rebuilds &lt;code&gt;Libmpv.xcframework&lt;/code&gt;. Verification:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;libmpv build&lt;/th&gt;
&lt;th&gt;Entitlements&lt;/th&gt;
&lt;th&gt;&lt;code&gt;mpv_initialize()&lt;/code&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;standard (LuaJIT)&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;allow-jit&lt;/code&gt; only&lt;/td&gt;
&lt;td&gt;SIGKILL 137&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Lua disabled&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;allow-jit&lt;/code&gt; only&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;succeeds, playback works&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;ul&gt;
&lt;li&gt;Lua symbols in the rebuilt xcframework: &lt;strong&gt;0&lt;/strong&gt; (previously ~50 undefined LuaJIT references)&lt;/li&gt;
&lt;li&gt;All the mpv API I use (&lt;code&gt;mpv_create&lt;/code&gt;, &lt;code&gt;mpv_initialize&lt;/code&gt;, &lt;code&gt;mpv_render_context_create&lt;/code&gt;, …) intact — zero functional loss for this app&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A pleasant side effect: dropping LuaJIT removed the need for &lt;code&gt;allow-unsigned-executable-memory&lt;/code&gt; &lt;em&gt;and&lt;/em&gt; &lt;code&gt;disable-library-validation&lt;/code&gt; entirely, so the final signature is tighter than the dev build ever was. The shipping entitlements file is now just: &lt;code&gt;app-sandbox&lt;/code&gt;, &lt;code&gt;network.client&lt;/code&gt; (StoreKit), &lt;code&gt;files.user-selected.read-only&lt;/code&gt;, &lt;code&gt;files.bookmarks.app-scope&lt;/code&gt;, and &lt;code&gt;cs.allow-jit&lt;/code&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Lesson:&lt;/strong&gt; if you statically link any library that JITs (LuaJIT, some JS engines, …), test against App Store entitlement rules &lt;em&gt;first&lt;/em&gt;. And ask whether you actually use the feature that needs the JIT — the cheapest fix may be compiling it out.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  Wall 2: LGPL compliance with static linking
&lt;/h2&gt;

&lt;p&gt;libmpv and its FFmpeg dependencies are &lt;strong&gt;LGPL&lt;/strong&gt; (I use MPVKit's LGPL variant — no GPL components, no x264/x265, so there's no App Store incompatibility at the license level). But static linking triggers the LGPL's re-linking obligation: users must be able to relink the application against a modified version of the library.&lt;/p&gt;

&lt;p&gt;There are three classic ways to satisfy it. Dynamic linking doesn't get you anything on the Mac App Store (users can't swap a dylib inside a signed, store-installed bundle anyway) and would have meant restructuring the build, so I went with a &lt;strong&gt;written offer&lt;/strong&gt;, valid for three years:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An in-app &lt;strong&gt;Licenses&lt;/strong&gt; window (App menu → Licenses…) listing every library, its license, and upstream source URL, with the full LGPL texts bundled in &lt;code&gt;Reel.app/Contents/Resources&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;The written offer text included in the App Store description's notes: on request, I provide the machine-readable object code of the app plus linked libraries, sufficient to relink against a user-modified library&lt;/li&gt;
&lt;li&gt;A repo script that reproduces my &lt;strong&gt;only&lt;/strong&gt; modification to the libraries (&lt;code&gt;-Dlua=disabled&lt;/code&gt;), and another script that collects the &lt;code&gt;.o&lt;/code&gt; files and link inputs from the exact release build into an archive for anyone who requests it&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Not legal advice — this is the reading I implemented, and it passed review. The parts most people forget: keep the &lt;em&gt;build inputs of the shipped release&lt;/em&gt; around for the duration of the offer, and remember the offer must ship with the binary (store listing + in-app), not just sit in your repo.&lt;/p&gt;




&lt;h2&gt;
  
  
  Wall 3: two sandbox traps that pass locally and fail later
&lt;/h2&gt;

&lt;p&gt;Playing files the user picked, across relaunches, needs security-scoped bookmarks. The happy path is well documented:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="kt"&gt;NSOpenPanel&lt;/span&gt; &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bookmarkData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;withSecurityScope&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;next&lt;/span&gt; &lt;span class="n"&gt;launch&lt;/span&gt; &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="n"&gt;resolve&lt;/span&gt; &lt;span class="err"&gt;→&lt;/span&gt; &lt;span class="nf"&gt;startAccessingSecurityScopedResource&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The traps are not.&lt;/p&gt;

&lt;h3&gt;
  
  
  Trap A: the bookmark entitlement key has a decoy spelling
&lt;/h3&gt;

&lt;p&gt;The correct key is:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;com.apple.security.files.bookmarks.app-scope
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The variant &lt;strong&gt;without&lt;/strong&gt; &lt;code&gt;files.&lt;/code&gt; (&lt;code&gt;com.apple.security.bookmarks.app-scope&lt;/code&gt;) &lt;strong&gt;signs and runs locally just fine&lt;/strong&gt; — and is then rejected by App Store submission validation as an unsupported entitlement. Everything works on your machine, so nothing warns you until you upload.&lt;/p&gt;

&lt;h3&gt;
  
  
  Trap B: a dropped folder's security scope dies with the closure
&lt;/h3&gt;

&lt;p&gt;Shipped in 1.0, added folder drag-and-drop in 1.1, and hit this: opening a folder from Finder/Dock (&lt;code&gt;.onOpenURL&lt;/code&gt;) worked, but &lt;strong&gt;dropping the same folder onto the window silently did nothing&lt;/strong&gt; — drop highlight appears, nothing imports.&lt;/p&gt;

&lt;p&gt;Debug logging showed the URL &lt;em&gt;was&lt;/em&gt; arriving. The failure chain:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;NSItemProvider.loadObject(ofClass: URL.self)&lt;/code&gt; → returns nil for Finder file URLs&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;loadItem&lt;/code&gt; + &lt;code&gt;URL(dataRepresentation:)&lt;/code&gt; → yields a URL &lt;strong&gt;without&lt;/strong&gt; a security scope; &lt;code&gt;startAccessingSecurityScopedResource()&lt;/code&gt; returns false and a later &lt;code&gt;fileExists&lt;/code&gt; check quietly fails&lt;/li&gt;
&lt;li&gt;Root cause: the scoped URL from &lt;code&gt;loadInPlaceFileRepresentation&lt;/code&gt; is &lt;strong&gt;only valid until that closure returns&lt;/strong&gt;. I was hopping to the main actor with &lt;code&gt;Task { @MainActor in … }&lt;/code&gt; — by the time the task ran, the scope was gone.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The fix: mint the security-scoped bookmark &lt;em&gt;inside&lt;/em&gt; the closure, while the scope is alive, and pass the bookmark bytes onward:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight swift"&gt;&lt;code&gt;&lt;span class="n"&gt;provider&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loadInPlaceFileRepresentation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;forTypeIdentifier&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kt"&gt;UTType&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;folder&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;identifier&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;_&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt;
    &lt;span class="k"&gt;guard&lt;/span&gt; &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;url&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;ok&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;startAccessingSecurityScopedResource&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;defer&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;ok&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stopAccessingSecurityScopedResource&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
    &lt;span class="c1"&gt;// The scope is alive *now* — capture it as a bookmark before leaving the closure.&lt;/span&gt;
    &lt;span class="k"&gt;let&lt;/span&gt; &lt;span class="nv"&gt;bookmark&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;try&lt;/span&gt;&lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;bookmarkData&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nv"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;withSecurityScope&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
                                         &lt;span class="nv"&gt;includingResourceValuesForKeys&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;relativeTo&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;nil&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="kt"&gt;Task&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="kd"&gt;@MainActor&lt;/span&gt; &lt;span class="k"&gt;in&lt;/span&gt; &lt;span class="n"&gt;store&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;importDroppedBookmark&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;bookmark&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nv"&gt;fallback&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The asymmetry that makes this hard to spot: URLs from &lt;code&gt;.onOpenURL&lt;/code&gt; come via LaunchServices and their scope persists, so the "same" feature works through one door and not the other.&lt;/p&gt;




&lt;h2&gt;
  
  
  Wall 4: App Review — Guideline 4, the un-reopenable main window
&lt;/h2&gt;

&lt;p&gt;First 1.0 submission was rejected under &lt;strong&gt;Guideline 4 (Design)&lt;/strong&gt;: close the main window with the red button and there's no menu item to get it back. Fair.&lt;/p&gt;

&lt;p&gt;The fix:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Made the main window a dedicated single &lt;code&gt;Window&lt;/code&gt; scene (not &lt;code&gt;WindowGroup&lt;/code&gt;), so the system lists it in the Window menu and reopening works&lt;/li&gt;
&lt;li&gt;Added an explicit &lt;strong&gt;Window → Main Window (⌘0)&lt;/strong&gt; item that also brings it forward when a player window covers it&lt;/li&gt;
&lt;li&gt;The app keeps its menu bar alive with the main window closed&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In the resubmission notes I wrote exactly what changed and how I verified it ("clean build; close main window with the red button; ⌘0 reopens it"). Plain reproduction steps, no argument. Approved.&lt;/p&gt;

&lt;p&gt;One postscript: an older branch still had the &lt;code&gt;WindowGroup&lt;/code&gt; implementation, and I later nearly merged it back over the reviewed fix. Whatever implementation passed review is the canonical one — guard it.&lt;/p&gt;




&lt;h2&gt;
  
  
  Smaller things that also cost time
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;You can't attach a new build to an approved version.&lt;/strong&gt; New changes require a new version number. I learned this trying to add drag-and-drop to the already-approved 1.0 → it shipped as 1.1.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;App Store screenshots must have no alpha channel&lt;/strong&gt; (macOS: 2880×1800). Screen captures have alpha; converting to JPEG kills it reliably. Also, a plain UI dump looks like nothing at search-result thumbnail size — I compose the first screenshot (headline, rounded corners, shadow, dark background) with a small Python/PIL script over real captures.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;String Catalogs:&lt;/strong&gt; only strings that go through &lt;code&gt;LocalizedStringKey&lt;/code&gt; are auto-extracted, and &lt;code&gt;xcodebuild&lt;/code&gt; won't write keys back into the catalog for you. Budget manual passes.&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  The checklist I wish I'd had
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Statically linking anything that JITs? Test &lt;code&gt;mpv_initialize()&lt;/code&gt; (or equivalent) under &lt;strong&gt;App Store entitlements&lt;/strong&gt;, not your dev entitlements. &lt;code&gt;allow-unsigned-executable-memory&lt;/code&gt; is not available to you.&lt;/li&gt;
&lt;li&gt;Do you actually &lt;em&gt;use&lt;/em&gt; the JIT-dependent feature? Compiling it out may be the whole fix.&lt;/li&gt;
&lt;li&gt;LGPL + static linking → written offer + relink materials + bundled license texts, and keep the release's build inputs.&lt;/li&gt;
&lt;li&gt;Bookmark entitlement key is &lt;code&gt;com.apple.security.files.bookmarks.app-scope&lt;/code&gt; — the &lt;code&gt;files.&lt;/code&gt;-less spelling only fails at submission.&lt;/li&gt;
&lt;li&gt;Security-scoped URLs from drag-and-drop die with the provider closure — bookmark them in place.&lt;/li&gt;
&lt;li&gt;Close your main window with the red button. Can a reviewer get it back from the menu bar?&lt;/li&gt;
&lt;li&gt;Approved version = new build needs a new version number.&lt;/li&gt;
&lt;li&gt;Screenshots: no alpha, and make the first one legible at thumbnail size.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Building the player was the easy part. Getting it — safely, license-compliantly, review-provenly — into people's hands is where the month went. But the walls are all climbable, documented above, and mostly one-time costs.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Reel, a local video player for macOS, is available on the &lt;a href="https://apps.apple.com/us/app/reel-video-player/id6779986647" rel="noopener noreferrer"&gt;Mac App Store&lt;/a&gt;. Happy to answer questions about any of the above.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>swift</category>
      <category>appstore</category>
      <category>libmpv</category>
      <category>macos</category>
    </item>
  </channel>
</rss>
