← Notes

The day it shipped, it was already ten days old

August 5, 2026 · Foldic development notes

Foldic for iPhone was approved today. We submitted it on July 25 — eleven days in the queue.

On the day it went in, that app had only ever run on a simulator and through a few short sessions on a real device. During the eleven days of waiting, I put it on an actual iPhone, plugged in an actual USB drive, and pointed it at a real library: 46 albums, 17,351 photos. That is when I found out how much of the version I had already shipped was wrong.

Here is what those eleven days changed. None of it is in the 0.4.0 that went live today — that build is who we were ten days ago.

Don't decide to scan on the user's behalf

The first version scanned the whole library the moment you opened it.

On a Mac that decision is harmless: local disk, a few seconds. On an iPhone with a USB drive attached it is a different thing entirely — every already-backed-up file on the drive has to be enumerated, which takes minutes across 46 albums, and the user may have opened the app just to look at it. Worse, there was no way to stop: you waited it out or you force-quit.

Now opening the app does one cheap thing: it lists your albums (using PhotoKit's estimated counts, no membership reads) in about a second. To scan, you tap Scan All at the bottom. Every album row also has its own Scan — to check one album you no longer scan the library.

The 30-minute timer and full-library change events are quiet on iPhone too. On this device, scanning happens when you ask for it.

When you press cancel, it has to actually stop

So I added cancellation. The first implementation checked two places: between albums, and inside the loop that walks the destination drive — the slowest part of a scan on USB storage. It tested well.

Then someone who actually uses it reported: I pressed cancel and that album kept scanning.

The place I had missed was the third one, and it was the longest: asking PhotoKit for an album's membership. That enumeration looks up the resources of every single photo, which runs for minutes on a large album — and it sat between my two checkpoints. The flag was being set, and the scan did respect it; it just had to wait for that entire enumeration to finish before anything looked at it.

The native enumeration now checks the flag every 100 photos and between albums. Cancel lands in a second or two.

The incidental lesson mattered more than the cancellation: data from an abandoned pass must never be written down. A truncated file listing, if you use it for counts, produces the confident conclusion "this album is missing 8,000 files" — and the next sync acts on that conclusion. So an interrupted album is discarded whole, as if this pass never reached it. Its previous record stays intact.

The screen was on for three hours

Syncing on iPhone is foreground-only — there is no reliable access to external storage in the background. So while a sync runs we have to keep the screen from sleeping, or iOS locks the device, freezes the process, and the backup stops halfway.

That is correct, and it has a cost: a sync of ten thousand photos runs for hours, and the screen burns at whatever brightness you set for those hours.

There is now a power-saving screen (on by default). Thirty seconds without a touch while work runs, and the backlight fades to minimum over half a second while the display switches to a black screen with progress in near-black text. Black pixels on OLED emit nothing; with the backlight at minimum that is about as little power as this device can draw while still working. Any touch restores your exact brightness.

UIScreen.brightness is system state — change it and it does not come back on its own, not even if the app is killed. So restoration is wired to four paths: the user's touch, work finishing, the app resigning active (a native notification observer), and page visibility. When a feature reaches for something that isn't ours, the recovery path deserves more care than the happy path.

The bug the power-saving screen caught

The day after that feature shipped I hit something contradictory: the numbers on the power-saving screen were advancing while the normal interface looked frozen.

Both read the same data. The power-saving screen only puts it somewhere else.

The cause was layout. The progress row is a horizontal flex row, and its title — "[45 queued] Syncing 'All Photos'" — is set not to wrap. In CSS, text that cannot wrap also cannot shrink below its own width, so on a narrow phone the title claimed the whole row and pushed the counts, the one thing the user actually watches, outside the viewport. The numbers had been advancing the whole time, just past the right edge of the screen.

On a phone, invisible is indistinguishable from broken. The user's conclusion — "it's stuck" — was entirely correct given the information available to them.

The fix is small: let the title ellipsize, pin the counts where they can be seen. The interesting part is that a feature built to save battery became the diagnostic for a different bug, purely because it put the same data in a different place.

Silence is not the same as dead

The same investigation exposed something else: our diagnostic log wrote nothing at all between "queued" and "done".

"All Photos" holds 12,372 photos, 239 of them already backed up. The remaining twelve thousand each have to be pulled from iCloud as originals and written to the USB drive — that is hours of work. And for those hours the log was silent, which is indistinguishable from a hang.

It now writes one progress line a minute. One line — but it turns "working" and "wedged" into two different things.

And the files you cannot see

A user (me) got a wall of red text after a sync: 619 files moved to the Trash, 2 failed.

Those were AppleDouble files — the ._ twins macOS writes on exFAT/FAT drives when the filesystem cannot hold extended attributes. They carry no photo data, but our file-identity logic was treating them as photos: the tail of a ._ name happens to satisfy the same identifier pattern, so a lone sidecar could convince Foldic that a photo was still present.

Sidecars are now bound to their data file: alive while the file is, orphaned once it is gone (the file-name migration leaves these behind), and removed together when the file itself leaves as an orphan. A failed move to the Trash is retried once — those 2 failures were iOS's external-storage helper dropping requests under load, and a retry clears them. Whatever still fails is summarised in one quiet line instead of dumping two full container paths into the user's face.

Writing the lessons down

These fixes are in TestFlight now and go out with the next submission. The iPhone version on the App Store today, 0.4.0, is that ten-day-old snapshot — it works, but none of the above is in it yet.

Foldic is $29.99 once, for Mac and iPhone together.

← 開發筆記

上架那天,它已經是十天前的版本

2026 年 8 月 5 日 · Foldic 開發筆記

Foldic 的 iPhone 版今天通過審核。我們在 7 月 25 日送審——中間隔了十一天。

送出去的那天,這個 App 只在模擬器上跑過,加上幾次短暫的實機測試。等待審核的這十一天裡,我把它裝上一支真的 iPhone、接上一顆真的 USB 隨身碟、對著一個真實的圖庫跑:46 本相簿、17,351 張照片。然後我才發現,已經送出去的那一版有多少地方是錯的。

以下是這十一天改掉的東西。它們全都不在今天上架的 0.4.0 裡——那一版是十天前的我們。

不要替使用者決定要掃描

第一版一打開就自動掃描整個圖庫。

在 Mac 上這個決定無傷大雅:本機磁碟,幾秒鐘的事。在 iPhone 上接一顆 USB 碟就完全是另一回事——磁碟上每一個已備份的檔案都要被列舉一次,46 本相簿要跑好幾分鐘,而使用者可能只是想打開來看一眼。更糟的是沒得停:你只能等它跑完,或者強制關掉 App。

現在打開只做一件便宜的事:把相簿列出來(用 PhotoKit 的估計張數,不讀成員清單),大約一秒鐘。要掃描,按底部那顆「掃描全部」。每一列相簿也各有自己的「掃描」——只想確認一本,不必掃整個圖庫。

30 分鐘的定時掃描、圖庫變動觸發的全庫重掃,在 iPhone 上也一律安靜。在這台裝置上,掃描只在你要求時發生。

取消鍵按下去,要真的停

於是我加了取消。第一版檢查兩個地方:每掃完一本相簿之間,以及走訪目的地磁碟的檔案迴圈裡——那是 USB 碟上掃描最慢的部分。測起來很好。

然後真的在用它的人回報:我按了取消,那本相簿還在掃。

漏掉的是第三個地方,而它才是最長的:向 PhotoKit 索取一本相簿的成員清單。那段列舉會逐張查詢每張照片的資源,大相簿要跑上好幾分鐘——而它正好夾在我那兩個檢查點中間。取消旗標確實被設定了,掃描也確實尊重它,只是得等那段列舉整個跑完,才輪得到有人去看那個旗標。

現在原生層的列舉每 100 張、以及每換一本相簿都會檢查旗標。取消在一兩秒內生效。

而順帶學到的一件事比取消本身更重要:中途放棄的資料絕對不能被寫下來。一份被截斷的檔案清單,如果拿去算數量,會產生一個非常有自信的結論——「這本相簿少了 8,000 個檔案」——然後下一次同步就照著這個結論行動。所以被打斷的相簿是整本丟掉的,當作這一輪從沒碰到它,它上一次的記錄完好無損。

螢幕亮了三個小時

iPhone 上的同步只能在前景進行——背景沒有可靠的外接儲存存取。所以同步期間我們必須阻止螢幕休眠,否則 iOS 會鎖屏、程序被凍結、備份停在半路。

這是對的,但它有代價:一個上萬張照片的同步要跑好幾個小時,而螢幕就用你設定的亮度亮著好幾個小時。

現在有省電畫面(預設開啟):工作進行中、30 秒沒有觸碰,背光會在半秒內漸暗到最低,畫面同時切換成純黑底、近黑色文字的進度顯示。OLED 上的黑色像素完全不發光,加上最低背光,這大概就是這台裝置一邊工作一邊能達到的最省電狀態。碰螢幕任何位置,立刻恢復你原本的亮度。

UIScreen.brightness 是系統層級的狀態——改了它不會自己還原,就算 App 被殺掉也不會。所以還原鋪了四條路:使用者觸碰、工作完成、App 退到背景(原生層的通知觀察者)、以及頁面可見性變化。當一個功能去動了不屬於自己的東西,它的恢復路徑要比正常路徑更講究。

省電模式抓到的 bug

省電畫面上線的隔天,我遇到一件矛盾的事:省電畫面上的數字在推進,正常介面卻看起來凍住了。

兩邊讀的是同一份資料。省電畫面只是把它放在別的地方。

原因在版面。進度列是一個橫向 flex 排版,而它的標題——「[45 個排隊中] 正在同步『All Photos』」——設定了不換行。CSS 裡不能換行的文字,也沒辦法縮到比自己的文字更窄,所以在 iPhone 的窄螢幕上,標題佔滿了整行,把使用者真正盯著看的計數推到了畫面外。數字一直在跳,只是跳在螢幕右邊看不到的地方。

在手機上,看不見等於壞掉。使用者的結論是「它卡住了」——而在他能拿到的資訊範圍內,這個結論完全正確。

修法很小:讓標題可以被省略號截斷,把計數釘在看得見的位置。但有意思的是,一個為了省電做的功能,變成了另一個 bug 的診斷工具——純粹因為它把同一份資料放在了不同的地方。

沉默不等於當機

同一次追查還暴露了另一件事:我們的診斷記錄在任務「排入」和「完成」之間,什麼都不寫。

「All Photos」有 12,372 張照片,其中 239 張已經備份。剩下的一萬兩千多張,每一張都要先從 iCloud 取回原始檔、再寫進 USB 碟——這是好幾個小時的工作。而在這幾個小時裡,記錄檔一片死寂,跟真的當機無法區分。

現在它每分鐘寫一行進度。就一行,但它讓「正在工作」和「已經死了」變成兩件分辨得出來的事。

還有那些你看不見的檔案

有個使用者(就是我)在同步後看到一整片紅字:619 個檔案成功移到垃圾桶,2 個失敗。

那些是 AppleDouble 檔案——macOS 在 exFAT/FAT 磁碟上放不下延伸屬性時,會另外寫一個 ._ 開頭的隱藏孿生檔。它們本身沒有照片資料,但我們的檔名識別邏輯把它們當成了照片:._ 檔名的尾段恰好符合我們的識別碼格式,所以一個孤零零的側檔就可能讓 Foldic 相信「這張照片還在」。

現在側檔和它的本體綁在一起:本體在,它就是活的;本體不在了(檔名轉換留下的殘骸就是這種),它才是孤兒;本體自己是孤兒時,兩個一起走。移到垃圾桶失敗會自動重試一次——那 2 個失敗是 iOS 外接儲存的系統輔助程序在高負載下偶爾掉了請求,重試就過了。真正還是失敗的,現在收斂成一行安靜的摘要,而不是把兩條完整的容器路徑倒在使用者臉上。

把教訓寫下來

這些修正現在都在 TestFlight,會隨下一次送審一起出去。今天在 App Store 上架的 iPhone 版 0.4.0 就是那個十天前的快照——它可以用,但上面這些它都還沒有。

Foldic 一次購買 $29.99,Mac 與 iPhone 通用

← 開発ノート

公開された日、それはすでに10日前のバージョンだった

2026年8月5日 · Foldic 開発ノート

Foldic の iPhone 版が今日承認されました。提出したのは7月25日——審査に11日間かかりました。

提出した時点で、そのアプリはシミュレータと数回の短い実機テストしか経験していませんでした。待っている11日間で、私は実機の iPhone に入れ、実際の USB ドライブを挿し、本物のライブラリ——46アルバム、17,351枚——に向けて動かしました。そこで、すでに提出済みのバージョンがどれだけ間違っていたかを知ることになります。

以下がこの11日間で変わったことです。どれも今日公開された 0.4.0 には入っていません。あのビルドは10日前の私たちです。

スキャンするかどうかを、ユーザーの代わりに決めない

最初のバージョンは、開いた瞬間にライブラリ全体をスキャンしていました。

Mac ではこの判断は無害です。ローカルディスク、数秒の話。しかし USB ドライブを挿した iPhone では話がまったく違います。ドライブ上のバックアップ済みファイルをすべて列挙する必要があり、46アルバムでは数分かかります。ユーザーはただ様子を見たかっただけかもしれません。さらに悪いのは、止める手段がなかったこと。待ちきるか、強制終了するかの二択でした。

今は、開いたときに行うのは安価な処理ひとつだけです。アルバム一覧の表示(PhotoKit の推定枚数を使い、メンバーシップは読みません)に約1秒。スキャンしたいときは下の「すべてスキャン」をタップします。各アルバム行にも「スキャン」があり、1つのアルバムを確認するためにライブラリ全体をスキャンする必要はもうありません。

30分間隔のタイマーも、ライブラリ全体の変更イベントも、iPhone では静かにしています。この端末では、スキャンはあなたが求めたときに起こります。

キャンセルを押したら、本当に止まらなければならない

そこでキャンセルを追加しました。最初の実装は2箇所を確認していました。アルバムの合間と、保存先ドライブを走査するループの中——USB ストレージでのスキャンで最も遅い部分です。テストはうまくいきました。

ところが、実際に使っている人から報告が来ます。キャンセルを押したのに、そのアルバムはスキャンを続けている。

見落としていた3番目の場所が、いちばん長い処理でした。PhotoKit にアルバムのメンバーシップを要求する部分です。この列挙は写真1枚ごとにリソースを問い合わせるため、大きなアルバムでは数分かかります。そしてそれは、私の2つのチェックポイントのにありました。フラグは立っていたし、スキャンもそれを尊重していた。ただ、誰かがそれを見るまでに、その列挙が終わるのを待つ必要があったのです。

ネイティブ側の列挙は、写真100枚ごとと、アルバムの切り替えごとにフラグを確認するようになりました。キャンセルは1〜2秒で効きます。

そして副産物の教訓が、キャンセルそのものより重要でした。中断された処理のデータは、絶対に書き込んではならない。切り詰められたファイル一覧を件数の計算に使えば、「このアルバムは8,000ファイル足りない」という自信満々の結論が生まれ、次の同期はその結論に従って動きます。だから中断されたアルバムは丸ごと破棄され、この回は触れなかったものとして扱われます。前回の記録はそのまま残ります。

画面が3時間ついていた

iPhone での同期はフォアグラウンド専用です。バックグラウンドでは外部ストレージへの確実なアクセスがありません。ですから同期中は画面がスリープしないようにしなければならず、さもなければ iOS がロックし、プロセスが凍結し、バックアップは途中で止まります。

これは正しい。ただし代償があります。1万枚規模の同期は数時間走り、その間ずっと画面はあなたの設定した明るさで点灯し続けます。

そこで省電力画面を追加しました(デフォルトでオン)。作業中に30秒間タッチがなければ、バックライトは0.5秒かけて最低輝度まで落ち、画面は黒地にほぼ黒い文字で進捗を表示するものに切り替わります。OLED の黒いピクセルは発光しません。最低バックライトと合わせれば、動作しながら消費できる電力としてはほぼ最小です。どこかに触れれば、あなたの元の輝度に即座に戻ります。

UIScreen.brightness はシステムの状態です。変更したら自動では戻りません。アプリが強制終了されても戻りません。だから復元は4つの経路に配線しました。ユーザーのタッチ、作業の完了、アプリの非アクティブ化(ネイティブの通知オブザーバ)、そしてページの可視性変化。自分のものでない状態に手を伸ばす機能では、復帰経路のほうが正常系より丁寧に作る価値があります。

省電力画面が捕まえたバグ

その機能を入れた翌日、矛盾に出会いました。省電力画面の数字は進んでいるのに、通常の画面は止まって見える。

両方が読んでいるのは同じデータです。省電力画面はそれを別の場所に置いているだけ。

原因はレイアウトでした。進捗行は横方向の flex レイアウトで、そのタイトル——「[45件待機中] 『All Photos』を同期中」——は折り返さない設定です。CSS では、折り返せないテキストは自身の幅より狭く縮むこともできません。だから狭いスマートフォンの画面では、タイトルが行を占領し、ユーザーが実際に見ている件数を画面の外へ押し出していました。数字はずっと進んでいたのです。画面の右端の外側で。

スマートフォンでは、見えないことは壊れていることと区別できません。「止まっている」というユーザーの結論は、彼が得られる情報の範囲では完全に正しかった。

修正は小さいものです。タイトルを省略記号で切り、件数を見える位置に固定する。興味深いのは、バッテリーを節約するために作った機能が、別のバグの診断ツールになったこと——同じデータを違う場所に置いた、それだけの理由で。

沈黙は死んでいることと同じではない

同じ調査で、もうひとつ明らかになりました。診断ログは「キュー投入」と「完了」の間に、何も書いていなかったのです。

「All Photos」には12,372枚の写真があり、そのうち239枚はバックアップ済み。残る1万2千枚あまりは、1枚ずつ iCloud からオリジナルを取得し、USB ドライブに書き込む必要があります。これは数時間の作業です。そしてその数時間、ログは沈黙していました。ハングと区別がつきません。

今は1分ごとに進捗を1行書きます。たった1行ですが、「動いている」と「死んでいる」を別のものにしてくれます。

そして、見えないファイルたち

あるユーザー(私です)が同期後に赤い文字の壁を見ました。619ファイルをゴミ箱に移動、2件失敗。

それは AppleDouble ファイル——exFAT/FAT ドライブで拡張属性を保持できないときに macOS が書く ._ の双子です。写真データは持っていませんが、私たちのファイル識別ロジックはそれを写真として扱っていました。._ 名の末尾がたまたま同じ識別子パターンを満たすため、孤立したサイドカー1つで「その写真はまだある」と Foldic を納得させられてしまうのです。

サイドカーは今、そのデータファイルと結び付けられています。ファイルがある間は生きていて、なくなれば孤立(ファイル名の移行がこれを残します)、ファイル自身が孤立ファイルとして去るときは一緒に去る。ゴミ箱への移動失敗は1回リトライします——あの2件は iOS の外部ストレージヘルパーが高負荷でリクエストを落としたもので、リトライで解決しました。それでも失敗したものは、コンテナのフルパスをユーザーの顔に叩きつける代わりに、静かな1行に要約されます。

教訓を書き留める

これらの修正はすでに TestFlight にあり、次の提出で出ていきます。今日 App Store で公開された iPhone 版 0.4.0 は、あの10日前のスナップショットです。動きますが、上記のどれもまだ入っていません。

Foldic は一度の購入で $29.99、Mac と iPhone の両方に。

← 개발 노트

출시된 날, 그것은 이미 열흘 전의 버전이었다

2026년 8월 5일 · Foldic 개발 노트

Foldic iPhone 버전이 오늘 승인되었습니다. 제출은 7월 25일 — 심사에 11일이 걸렸습니다.

제출한 날, 그 앱은 시뮬레이터와 몇 번의 짧은 실기기 테스트만 경험한 상태였습니다. 기다리는 11일 동안 저는 실제 iPhone에 넣고, 실제 USB 드라이브를 꽂고, 진짜 보관함 — 46개 앨범, 17,351장 — 을 향해 돌렸습니다. 그리고 이미 제출한 버전이 얼마나 잘못되어 있었는지 알게 되었습니다.

다음은 그 11일이 바꾼 것들입니다. 오늘 공개된 0.4.0에는 하나도 들어 있지 않습니다. 그 빌드는 열흘 전의 우리입니다.

스캔할지를 사용자 대신 결정하지 말 것

첫 버전은 열자마자 보관함 전체를 스캔했습니다.

Mac에서는 무해한 결정입니다. 로컬 디스크, 몇 초. 하지만 USB 드라이브를 연결한 iPhone에서는 완전히 다른 이야기입니다. 드라이브의 이미 백업된 파일을 모두 열거해야 하고, 46개 앨범이면 몇 분이 걸립니다. 사용자는 그냥 한번 열어보려던 것일 수도 있습니다. 더 나쁜 건 멈출 방법이 없었다는 점입니다. 끝날 때까지 기다리거나 강제 종료하는 것뿐이었습니다.

이제 앱을 열면 값싼 일 하나만 합니다. 앨범 목록 표시(PhotoKit의 추정 개수 사용, 멤버십은 읽지 않음)에 약 1초. 스캔하려면 아래의 '전체 스캔'을 누릅니다. 각 앨범 행에도 '스캔'이 있어서, 앨범 하나를 확인하려고 보관함 전체를 스캔할 필요가 없습니다.

30분 타이머와 보관함 전체 변경 이벤트도 iPhone에서는 조용합니다. 이 기기에서 스캔은 당신이 요청할 때 일어납니다.

취소를 누르면 정말로 멈춰야 한다

그래서 취소를 추가했습니다. 첫 구현은 두 곳을 확인했습니다. 앨범 사이, 그리고 대상 드라이브를 순회하는 루프 안 — USB 저장장치 스캔에서 가장 느린 부분입니다. 테스트는 잘 되었습니다.

그런데 실제로 쓰는 사람이 보고했습니다. 취소를 눌렀는데 그 앨범은 계속 스캔한다.

놓친 세 번째 장소가 가장 긴 작업이었습니다. PhotoKit에 앨범 멤버십을 요청하는 부분입니다. 이 열거는 사진 한 장마다 리소스를 조회하므로 큰 앨범에서는 몇 분이 걸립니다. 그리고 그것은 제 두 검사 지점 사이에 있었습니다. 플래그는 설정되었고 스캔도 그것을 존중했습니다. 다만 누군가 그것을 보기까지, 그 열거가 끝나기를 기다려야 했습니다.

이제 네이티브 열거는 사진 100장마다, 그리고 앨범이 바뀔 때마다 플래그를 확인합니다. 취소는 1~2초 안에 적용됩니다.

그리고 부수적으로 얻은 교훈이 취소 자체보다 중요했습니다. 중단된 작업의 데이터는 절대 기록해서는 안 된다. 잘린 파일 목록을 개수 계산에 쓰면 '이 앨범은 8,000개 파일이 없다'는 확신에 찬 결론이 나오고, 다음 동기화는 그 결론에 따라 움직입니다. 그래서 중단된 앨범은 통째로 버려지고, 이번 패스는 그것에 닿지 않은 것으로 취급됩니다. 이전 기록은 그대로 남습니다.

화면이 세 시간 동안 켜져 있었다

iPhone에서 동기화는 포그라운드 전용입니다. 백그라운드에서는 외부 저장장치에 안정적으로 접근할 수 없습니다. 그래서 동기화 중에는 화면이 잠들지 않게 해야 합니다. 그러지 않으면 iOS가 잠기고, 프로세스가 얼고, 백업은 중간에 멈춥니다.

이는 옳습니다. 다만 대가가 있습니다. 만 장 규모의 동기화는 몇 시간을 돌고, 그 시간 내내 화면은 당신이 설정한 밝기로 켜져 있습니다.

이제 절전 화면이 있습니다(기본 켜짐). 작업 중 30초간 터치가 없으면 백라이트가 0.5초에 걸쳐 최저 밝기로 떨어지고, 화면은 검은 배경에 거의 검은 글자로 진행 상황을 보여주는 것으로 바뀝니다. OLED의 검은 픽셀은 빛을 내지 않습니다. 최저 백라이트와 합치면, 작동하면서 쓸 수 있는 가장 적은 전력에 가깝습니다. 어디든 터치하면 원래 밝기로 즉시 돌아옵니다.

UIScreen.brightness는 시스템 상태입니다. 바꾸면 스스로 돌아오지 않습니다. 앱이 강제 종료되어도 돌아오지 않습니다. 그래서 복원을 네 경로에 연결했습니다. 사용자의 터치, 작업 완료, 앱의 비활성화(네이티브 알림 옵서버), 그리고 페이지 가시성 변화. 우리 것이 아닌 상태에 손을 뻗는 기능이라면, 복귀 경로가 정상 경로보다 더 정성을 들일 만합니다.

절전 화면이 잡아낸 버그

그 기능을 넣은 다음 날, 모순을 만났습니다. 절전 화면의 숫자는 올라가는데, 일반 화면은 멈춰 보였습니다.

둘은 같은 데이터를 읽습니다. 절전 화면은 그것을 다른 자리에 놓을 뿐입니다.

원인은 레이아웃이었습니다. 진행 줄은 가로 flex 레이아웃이고, 제목 — '[45개 대기 중] 'All Photos' 동기화 중' — 은 줄바꿈하지 않도록 설정되어 있습니다. CSS에서 줄바꿈할 수 없는 텍스트는 자기 너비보다 좁게 줄어들 수도 없습니다. 그래서 좁은 휴대폰 화면에서 제목이 줄 전체를 차지하고, 사용자가 실제로 보고 있는 개수를 화면 밖으로 밀어냈습니다. 숫자는 계속 올라가고 있었습니다. 화면 오른쪽 바깥에서.

휴대폰에서 보이지 않는 것은 고장난 것과 구별되지 않습니다. '멈췄다'는 사용자의 결론은, 그가 가진 정보 안에서는 완전히 옳았습니다.

수정은 작습니다. 제목을 줄임표로 자르고, 개수를 보이는 자리에 고정합니다. 흥미로운 점은 배터리를 아끼려고 만든 기능이 다른 버그의 진단 도구가 되었다는 것입니다 — 같은 데이터를 다른 곳에 두었다는 이유만으로.

침묵은 죽음과 같지 않다

같은 조사에서 또 하나가 드러났습니다. 진단 로그는 '대기열 추가'와 '완료' 사이에 아무것도 쓰지 않았습니다.

'All Photos'에는 12,372장이 있고 그중 239장은 이미 백업되어 있습니다. 남은 1만 2천여 장은 각각 iCloud에서 원본을 받아 USB 드라이브에 써야 합니다. 몇 시간의 작업입니다. 그리고 그 몇 시간 동안 로그는 침묵했습니다. 멈춤과 구별할 수 없습니다.

이제 1분마다 진행 상황을 한 줄씩 씁니다. 한 줄이지만, '작동 중'과 '죽음'을 서로 다른 것으로 만들어 줍니다.

그리고 보이지 않는 파일들

어떤 사용자(저입니다)가 동기화 후 붉은 글자의 벽을 봤습니다. 619개 파일을 휴지통으로, 2개 실패.

그것은 AppleDouble 파일 — exFAT/FAT 드라이브에서 확장 속성을 담을 수 없을 때 macOS가 쓰는 ._ 쌍둥이입니다. 사진 데이터는 없지만, 우리 파일 식별 로직은 그것을 사진으로 취급했습니다. ._ 이름의 끝부분이 우연히 같은 식별자 패턴을 만족하기 때문에, 홀로 남은 사이드카 하나로 '그 사진은 아직 있다'고 Foldic을 납득시킬 수 있었습니다.

이제 사이드카는 데이터 파일과 묶여 있습니다. 파일이 있는 동안은 살아 있고, 사라지면 고아가 되며(파일 이름 마이그레이션이 이런 것들을 남깁니다), 파일 자신이 고아로 떠날 때 함께 떠납니다. 휴지통 이동 실패는 한 번 재시도합니다 — 그 2개는 iOS의 외부 저장장치 헬퍼가 고부하에서 요청을 놓친 것이고, 재시도로 해결되었습니다. 그래도 실패한 것은 컨테이너 전체 경로를 사용자 얼굴에 들이붓는 대신 조용한 한 줄로 요약됩니다.

교훈을 적어 두기

이 수정들은 이미 TestFlight에 있고 다음 제출과 함께 나갑니다. 오늘 App Store에 공개된 iPhone 버전 0.4.0은 그 열흘 전의 스냅샷입니다. 작동하지만, 위의 어느 것도 아직 들어 있지 않습니다.

Foldic은 한 번 구매로 $29.99, Mac과 iPhone 모두.