← All courses ← Course home

Lesson 04 of 07

Keep SwiftUI thin: it only feeds the model

PhotosPicker and preprocessing are not the model. They exist to meet the tensor contract that the runtime and Metal path need.

Agent brief (llms.md)

Start at the runtime contract from lesson 03: image is float32 NCHW [1, 1, 28, 28], pixels are in 0...1, and logits has ten values. The SwiftUI screen is a thin helper. It gets a photo, turns it into those bytes, and shows the result. The screen does not change the model.

Read the picker backward

UIPhotosPicker
Preprocess28×28 grayscale
Contractfloat32 NCHW 0–1
Runtimeimage → logits

Resize and draw the chosen image into a 28×28 grayscale buffer. Scale channel values to float32 in 0...1. Add the batch and channel dimensions without swapping them. The result is not “an image for Core AI”. It is the exact NDArray contract described by the original course's SwiftUI clothing app lesson.

Sort the bugs by layer

You can see these failures in the UI, but the fixes belong to the contract. Do not hide them with a different label list or a larger model. Check the array shape, element type, layout, and values first.

The label also sits after the model

Runtimelogits [1,10]
Displayargmax / softmax
UIten clothing names

Read the ten logits, pick the highest score, and use the same Fashion-MNIST class order used during training: T-shirt/top, Trouser, Pullover, Dress, Coat, Sandal, Shirt, Sneaker, Bag, Ankle boot. Softmax is useful if you want to show confidence, but you do not need it to pick the winner.

Thin means you can test it. Keep one known sample image next to the photo picker. If that sample stops giving a sensible label, test preprocessing and the runtime contract before you change the SwiftUI view.

Now move below the runtime to the file that made this contract possible. Conversion exists so the runtime can run. Compare it with the original conversion lesson.

Key concepts

  • SwiftUI is a thin client. PhotosPicker and preprocessing exist to meet the tensor contract.
  • The path is photo to 28x28 grayscale to float32 NCHW 0-1 to image to logits.
  • Wrong channels, 0-255 bytes, or infer before prepare are contract breaks.
  • The ten logits become names only through the fixed Fashion-MNIST order.

Takeaways

  • UI bugs that change the tensor are runtime contract breaks, not model bugs.
  • Keep a known bundled sample to test preprocessing before you change the view.
  • Thin UI means you can isolate prep and the runtime call from layout code.