Post

Replies

Boosts

Views

Activity

FaceGroupAnalyzer: a truncated image file reports zero faces with no error, indistinguishable from a photograph that contains no people
An image file whose data is cut in half is accepted by insertOrUpdateAssets, produces no error, and is reported as an image containing zero faces. That result is indistinguishable from a photograph that genuinely has no people in it. In my measurement the intact file reported 4 faces and a copy truncated to half its bytes reported 0 faces — both without throwing. Why this is worse than an error. A client that receives "zero faces, no error" will record the asset as analysed, no people present. That is a permanent, silent, incorrect result: the file is never revisited, and the people in it are lost from the catalogue with no diagnostic anywhere. An error would have been recorded as a failure and retried later. Silence is the one outcome that cannot be recovered from. The failure mode is also inconsistent. Other kinds of damaged input do throw — a zero-byte file, a text file with a .jpg extension, and a file whose interior bytes have been destroyed all raise MediaIntelligenceError.faceGroupProcessing. Truncation is the case that silently succeeds, and truncation is precisely the damage that partial downloads, interrupted copies and failing disks produce — the most common form of corruption in a real photo archive, and the one I have to handle in 97 libraries of mixed provenance. My current workaround is to fully decode every image myself before handing it to the framework, purely to detect truncation. That means every file is decoded twice, which roughly doubles the I/O of the analysis pass. Feedback: FB24174749
0
0
211
Aug ’26
FaceGroupAnalyzer: one unreadable asset makes `insertOrUpdateAssets` deliver zero elements, discarding results already computed for the valid assets in the batch
If a single asset in the array passed to insertOrUpdateAssets cannot be read, the returned AsyncSequence throws and delivers zero elements — including for the valid assets that appear before the offending one in the array. In my measurement a batch of 5 — two valid photographs, one zero-byte .jpg, then two more valid photographs — delivered 0 of 5 elements and reported 0 faces. The framework's own stdout log shows it had already processed the valid photographs before failing, so the work was done and then thrown away. This is the behaviour I would expect from a function returning [Result] after processing everything, not from a streaming AsyncSequence. The whole reason to expose an async sequence is to deliver results as they are produced; here the sequence produces nothing at all, which makes the streaming shape actively misleading. Why this matters at library scale. I am cataloguing 97 photo libraries, many of them archives of scanned family photographs going back to the 1970s, where a handful of damaged files is normal rather than exceptional. As the API stands, the batch size I choose for throughput is also the amount of work a single corrupt file destroys — with a batch of 100, one bad file costs 100 images. The only safe strategy is to catch the failure and re-submit the batch one asset at a time to find the culprit, which turns a rare bad file into a full re-run of that batch and makes the worst case quadratic in the number of bad files. Note also that the error gives no indication of which asset failed (see the related enhancement request on error taxonomy), so isolating the culprit by re-submission is the only option available. Feedback: FB24174733
0
0
189
Aug ’26
FaceGroupAnalyzer: `insertOrUpdateAssets` does not honour `Task` cancellation and returns successfully long after the task was cancelled
insertOrUpdateAssets(_:) is async throws and returns an AsyncSequence, so by the Swift Concurrency contract I expected it to observe cancellation of the enclosing Task and throw CancellationError. It does not. The call runs to completion and returns successfully, with the full result set, as though the cancellation had never happened. In my measurement the cancel was delivered at 2.7 s and the call returned successfully at 19.0 s, having processed all 150 assets and reported 550 faces. The practical consequence for an app is that a "Stop" button cannot stop work that is already in flight. The only way to get responsive cancellation is to slice the work into many small calls and check Task.checkCancellation() between them — which means the batch size, which should be tuned for throughput, ends up being dictated by how long a user is willing to wait after pressing Stop. For me that is the difference between a batch of 150 (≈19 s to react) and a batch of 25 (≈3 s). The store is left in a consistent state, which is good: the assets processed before cancellation remain, and state correctly becomes .stale. So this is specifically about the cancellation signal being ignored, not about data integrity. One detail worth knowing when reproducing this insertOrUpdateAssets is declared nonisolated(nonsending), so it executes on the caller's executor. My first attempt at this reproducer used a plain Task { } created from @MainActor top-level code — which inherits main-actor isolation — and the detection therefore ran on the main actor and starved the very code that was supposed to cancel it: a Task.sleep(2.5s) on the main actor did not resume until the 19-second call had already finished, so the cancel was not even delivered until 19.0 s. The attached reproducer uses Task.detached to avoid that confound, and the cancel is correctly delivered at 2.7 s. I mention it because anyone reproducing this from a @MainActor context will see a different and misleading timeline. Feedback: FB24174707
0
0
170
Aug ’26
FaceGroupAnalyzer: two live instances register the Core Data model twice and abort the process with +[MIManagedFace entity] Failed to find a unique match
Constructing a second FaceGroupAnalyzer while a first one is still alive registers the framework's Core Data managed object model a second time. Core Data can then no longer resolve +[MIManagedFace entity], and the process is terminated with SIGTRAP (exit status 133). Three things make this worse than a normal API misuse: It happens with completely separate working directories. The two instances are logically independent — different directories, different stores, no shared state that the API surface exposes. Nothing in the signature of init(workingDirectory:) suggests that two of them cannot coexist, and the parameter's existence implies the opposite. There is no way to detect or prevent it from a library. FaceGroupAnalyzer is not a singleton and offers no way to ask whether an instance already exists in the process. Any two independent subsystems in an app — say, a background analysis service and a foreground preview — that each construct an analyzer will terminate the app. In my case the app must now enforce single-instance access through its own serial queue and lock, which is a constraint the framework imposes but does not state. Construction alone does not fail, so the problem surfaces later and elsewhere. Both instances construct successfully; Core Data only emits warnings at that point. The abort comes when both are alive and one of them does real work (update() and reading the grouping). So the crash lands far from its cause, in code that is individually correct. Feedback: FB24174678
0
0
160
Aug ’26
.widgetURL modifier and interactive widgets co-existence in iOS17
Can someone tell me how to handle the conflict between the .widgetURL modifier and the interactive widget in iOS 17? The .widgetURL makes all widget tapable, meanwhile interactive widget allows for tapping a button/toggle in a specific location inside the widget. I want to mix the two: the .widgetURL modifier and the interactive widget, but right now when .widgetURL is set, the button/toggle of interactive widget doesn't work. Running Xcode 15 beta 1 with iOS 17 on Simulator. Any help is appreciated. Thanks!
0
0
635
Jun ’23
AppStoreConnect API - Error 404 when fetching build icon
Hi folks! After fetching all icons for all builds (v1/builds/{id}/icons) which returns an array of BuildIcon, I can't fetch any icon that comes inside the response. I'm using the attributes->iconAsset->templateURL as URL of the icon I want to retrieve, but I'm getting 404 for all of them. Every URL has the following prefix: https://is4-ssl.mzstatic.com/image What am I doing wrong here? Any help will be much appreciated. Thanks!
2
0
1.7k
Apr ’23
FaceGroupAnalyzer: a truncated image file reports zero faces with no error, indistinguishable from a photograph that contains no people
An image file whose data is cut in half is accepted by insertOrUpdateAssets, produces no error, and is reported as an image containing zero faces. That result is indistinguishable from a photograph that genuinely has no people in it. In my measurement the intact file reported 4 faces and a copy truncated to half its bytes reported 0 faces — both without throwing. Why this is worse than an error. A client that receives "zero faces, no error" will record the asset as analysed, no people present. That is a permanent, silent, incorrect result: the file is never revisited, and the people in it are lost from the catalogue with no diagnostic anywhere. An error would have been recorded as a failure and retried later. Silence is the one outcome that cannot be recovered from. The failure mode is also inconsistent. Other kinds of damaged input do throw — a zero-byte file, a text file with a .jpg extension, and a file whose interior bytes have been destroyed all raise MediaIntelligenceError.faceGroupProcessing. Truncation is the case that silently succeeds, and truncation is precisely the damage that partial downloads, interrupted copies and failing disks produce — the most common form of corruption in a real photo archive, and the one I have to handle in 97 libraries of mixed provenance. My current workaround is to fully decode every image myself before handing it to the framework, purely to detect truncation. That means every file is decoded twice, which roughly doubles the I/O of the analysis pass. Feedback: FB24174749
Replies
0
Boosts
0
Views
211
Activity
Aug ’26
FaceGroupAnalyzer: one unreadable asset makes `insertOrUpdateAssets` deliver zero elements, discarding results already computed for the valid assets in the batch
If a single asset in the array passed to insertOrUpdateAssets cannot be read, the returned AsyncSequence throws and delivers zero elements — including for the valid assets that appear before the offending one in the array. In my measurement a batch of 5 — two valid photographs, one zero-byte .jpg, then two more valid photographs — delivered 0 of 5 elements and reported 0 faces. The framework's own stdout log shows it had already processed the valid photographs before failing, so the work was done and then thrown away. This is the behaviour I would expect from a function returning [Result] after processing everything, not from a streaming AsyncSequence. The whole reason to expose an async sequence is to deliver results as they are produced; here the sequence produces nothing at all, which makes the streaming shape actively misleading. Why this matters at library scale. I am cataloguing 97 photo libraries, many of them archives of scanned family photographs going back to the 1970s, where a handful of damaged files is normal rather than exceptional. As the API stands, the batch size I choose for throughput is also the amount of work a single corrupt file destroys — with a batch of 100, one bad file costs 100 images. The only safe strategy is to catch the failure and re-submit the batch one asset at a time to find the culprit, which turns a rare bad file into a full re-run of that batch and makes the worst case quadratic in the number of bad files. Note also that the error gives no indication of which asset failed (see the related enhancement request on error taxonomy), so isolating the culprit by re-submission is the only option available. Feedback: FB24174733
Replies
0
Boosts
0
Views
189
Activity
Aug ’26
FaceGroupAnalyzer: `insertOrUpdateAssets` does not honour `Task` cancellation and returns successfully long after the task was cancelled
insertOrUpdateAssets(_:) is async throws and returns an AsyncSequence, so by the Swift Concurrency contract I expected it to observe cancellation of the enclosing Task and throw CancellationError. It does not. The call runs to completion and returns successfully, with the full result set, as though the cancellation had never happened. In my measurement the cancel was delivered at 2.7 s and the call returned successfully at 19.0 s, having processed all 150 assets and reported 550 faces. The practical consequence for an app is that a "Stop" button cannot stop work that is already in flight. The only way to get responsive cancellation is to slice the work into many small calls and check Task.checkCancellation() between them — which means the batch size, which should be tuned for throughput, ends up being dictated by how long a user is willing to wait after pressing Stop. For me that is the difference between a batch of 150 (≈19 s to react) and a batch of 25 (≈3 s). The store is left in a consistent state, which is good: the assets processed before cancellation remain, and state correctly becomes .stale. So this is specifically about the cancellation signal being ignored, not about data integrity. One detail worth knowing when reproducing this insertOrUpdateAssets is declared nonisolated(nonsending), so it executes on the caller's executor. My first attempt at this reproducer used a plain Task { } created from @MainActor top-level code — which inherits main-actor isolation — and the detection therefore ran on the main actor and starved the very code that was supposed to cancel it: a Task.sleep(2.5s) on the main actor did not resume until the 19-second call had already finished, so the cancel was not even delivered until 19.0 s. The attached reproducer uses Task.detached to avoid that confound, and the cancel is correctly delivered at 2.7 s. I mention it because anyone reproducing this from a @MainActor context will see a different and misleading timeline. Feedback: FB24174707
Replies
0
Boosts
0
Views
170
Activity
Aug ’26
FaceGroupAnalyzer: two live instances register the Core Data model twice and abort the process with +[MIManagedFace entity] Failed to find a unique match
Constructing a second FaceGroupAnalyzer while a first one is still alive registers the framework's Core Data managed object model a second time. Core Data can then no longer resolve +[MIManagedFace entity], and the process is terminated with SIGTRAP (exit status 133). Three things make this worse than a normal API misuse: It happens with completely separate working directories. The two instances are logically independent — different directories, different stores, no shared state that the API surface exposes. Nothing in the signature of init(workingDirectory:) suggests that two of them cannot coexist, and the parameter's existence implies the opposite. There is no way to detect or prevent it from a library. FaceGroupAnalyzer is not a singleton and offers no way to ask whether an instance already exists in the process. Any two independent subsystems in an app — say, a background analysis service and a foreground preview — that each construct an analyzer will terminate the app. In my case the app must now enforce single-instance access through its own serial queue and lock, which is a constraint the framework imposes but does not state. Construction alone does not fail, so the problem surfaces later and elsewhere. Both instances construct successfully; Core Data only emits warnings at that point. The abort comes when both are alive and one of them does real work (update() and reading the grouping). So the crash lands far from its cause, in code that is individually correct. Feedback: FB24174678
Replies
0
Boosts
0
Views
160
Activity
Aug ’26
.widgetURL modifier and interactive widgets co-existence in iOS17
Can someone tell me how to handle the conflict between the .widgetURL modifier and the interactive widget in iOS 17? The .widgetURL makes all widget tapable, meanwhile interactive widget allows for tapping a button/toggle in a specific location inside the widget. I want to mix the two: the .widgetURL modifier and the interactive widget, but right now when .widgetURL is set, the button/toggle of interactive widget doesn't work. Running Xcode 15 beta 1 with iOS 17 on Simulator. Any help is appreciated. Thanks!
Replies
0
Boosts
0
Views
635
Activity
Jun ’23
AppStoreConnect API - Error 404 when fetching build icon
Hi folks! After fetching all icons for all builds (v1/builds/{id}/icons) which returns an array of BuildIcon, I can't fetch any icon that comes inside the response. I'm using the attributes->iconAsset->templateURL as URL of the icon I want to retrieve, but I'm getting 404 for all of them. Every URL has the following prefix: https://is4-ssl.mzstatic.com/image What am I doing wrong here? Any help will be much appreciated. Thanks!
Replies
2
Boosts
0
Views
1.7k
Activity
Apr ’23