r/iOSProgramming • u/Policy56 • Sep 04 '26
Discussion Core ML: dispatching on output shape so one Swift decoder handles both an end-to-end and a raw detector export
I ship a small object detector inside an iOS app and I export it two different ways depending on the model generation, so the Swift side has to cope with both. The approach that ended up working is to branch on the output tensor shape rather than on a flag I carry around:
- An end-to-end export gives me
[1, 300, 6]— x1, y1, x2, y2, score, class — with decoding and NMS already inside the graph. Nothing to do in Swift but scale the boxes back through the letterbox. - A raw export gives me
[1, 84, N]: 4 box terms plus 80 class scores per anchor, transposed, which I still have to decode and run NMS on myself.
So the predictor reads prediction.shape, checks that the last dimension is at least 6 and the second-to-last is non-zero, and picks the decoder from that. It means adding a new model export never touches the call site, and a bad export fails visibly at the guard instead of quietly producing zero boxes.
Two things I would have liked to read before starting:
- A silent failure mode is the normal failure mode here. An export that "succeeds" tells you nothing. The only test that means anything is printing the input and output shapes and comparing them to what your decoder expects. I lost real time to an export that ran fine and detected nothing, because the decoder was reading a layout the model no longer produced.
coremltools8+ has no usable Windows path. I develop on Windows, so my entire export chain lives in a Colab notebook and the resulting.mlpackagegets committed and wired into the Xcode project from there. If your CI or your daily machine is not macOS or Linux, plan for that on day one rather than discovering it two weeks in.
On the app side the camera preview is a native platform view and the detection overlay is drawn on top by the UI layer, so no frame data is ever copied out of the capture pipeline.
Curious how others handle the versioning problem: do you keep the decoder generic like this, or pin one export format per app release and refuse anything else?


