Developing AR Product Catalog with 3D Models
AR catalog is when user selects sofa on website and can place it in their living room via phone, not leaving page. Or selects sneakers and tries on foot. Difference between "looked in catalog" and "saw at home" — conversion.
Technically three linked tasks: managing 3D model library, AR scene with placement and interaction, integration with existing catalog/CMS.
Loading and Managing 3D Assets
AR catalog with hundreds of products can't store all models in app bundle. Need on-demand download from CDN. Standard approach:
- Product catalog from CMS contains
model_url— link to USDZ (iOS) or GLB (Android) - On product card opening — check local cache (
FileManager) - If no — download in background via
URLSession.downloadTask, progress bar - After loading — initialize AR scene
func loadModel(url: URL, completion: @escaping (ModelEntity?) -> Void) {
let cacheURL = cacheDirectory.appendingPathComponent(url.lastPathComponent)
if FileManager.default.fileExists(atPath: cacheURL.path) {
completion(try? ModelEntity.loadModel(contentsOf: cacheURL))
return
}
URLSession.shared.downloadTask(with: url) { tempURL, _, _ in
guard let tempURL else { completion(nil); return }
try? FileManager.default.moveItem(at: tempURL, to: cacheURL)
DispatchQueue.main.async {
completion(try? ModelEntity.loadModel(contentsOf: cacheURL))
}
}.resume()
}
Model size matters. Furniture: 5–15 MB USDZ — normal. 50+ MB — user waits and leaves. Optimization: TextureConverter for texture compression to ASTC, Draco geometry compression (via usdz_converter or Blender pipeline).
AR Placement: From Plane to Interaction
Basic plane detection → raycast → placement well described in ARKit docs. In AR catalog added:
Scaling preserving real size. USDZ models should have correct scale: 2-meter sofa should be 2 meters in AR. If size in CMS metadata exists — apply via ModelEntity.scale. If no — set via model config. User shouldn't scale themselves — destroys "real" size feeling.
One-finger rotation. EntityRotationGestureRecognizer in RealityKit — simplest path. But rotation only on Y axis (vertical): furniture doesn't tilt, rotates around itself. Fix axis via constraints:
entity.components[PhysicsMotionComponent.self] = PhysicsMotionComponent()
// Rotation constraint — Y only
Variant/color change without reloading model. Sofa available in 5 colors. Loading 5 models — expensive. Correct: one geometry, different materials. In USDZ materials changeable via ModelComponent.materials:
var materials = entity.model?.materials ?? []
materials[0] = SimpleMaterial(color: .init(selectedColor), isMetallic: false)
entity.model?.materials = materials
For PBR materials with textures — PhysicallyBasedMaterial with loadable textures.
Integration with Catalog
Typical scenario: Shopify, WooCommerce or custom CMS backend. Add custom field ar_model_url to product. On mobile client — "View in AR" button appears only if field filled.
For 3D content management without programmers — simple CMS or admin panel with USDZ/GLB upload and server-side auto-optimization (conversion, compression, CDN upload). Removes developer dependency when adding new products.
Case from Practice
Online furniture store, 2000+ products. Priority: cover 100 top SKUs with AR models. Phased: first iOS (ARKit + USDZ), after 2 months — Android (SceneViewer + GLB). Converting from supplier FBX models to USDZ/GLB — automated pipeline via Blender Python scripts + reality-converter CLI. Processing time per model — 3–7 minutes.
Main non-technical problem: 3D models from suppliers in arbitrary units (1 unit = 1 inch for one, 1 centimeter for other). Wrote normalization script with real size validation from product spec.
Timeline
| Functionality | Timeline |
|---|---|
| Basic AR viewer for single product | 1–2 weeks |
| AR catalog with caching and color variants | 4–6 weeks |
| Full system with CMS, analytics, iOS+Android | 3–5 months |
Cost calculated individually considering catalog volume and model conversion necessity.







