Which run-time probes identify the device (UIUserInterfaceIdiom, UIDevice, size classes), and what do they report on tvOS, Catalyst and the watch?

20.7.3 · L3 · Interview

Predict first

What do the idiom raw values and the current device report on the tvOS simulator?

import UIKit
let all: [(String, UIUserInterfaceIdiom)] = [("unspecified", .unspecified), ("phone", .phone), ("pad", .pad), ("tv", .tv), ("carPlay", .carPlay), ("mac", .mac), ("vision", .vision)]
print(all.map { "\($0.0)=\($0.1.rawValue)" }.joined(separator: " "))
print("current:", UIDevice.current.userInterfaceIdiom.rawValue, UIDevice.current.model, UIDevice.current.systemName)
  • unspecified=-1 phone=0 pad=1 tv=2 carPlay=3 mac=5 vision=6 / current: 2 Apple TV tvOS
  • unspecified=0 phone=1 pad=2 tv=3 … / current: 3
  • current: 0 Apple TV tvOS

Predict first

Does UIDevice compile for watchOS?

import UIKit
print(UIDevice.current.model)
  • Yes, UIKit is importable
  • No, UIDevice is unavailable in watchOS

Mental model. Asking a person to describe the room (size, light, noise) rather than check their ID card: the room tells you what you can do in it.

Prefer capability and layout probes to device identity, but when you need identity the facts are these. UIUserInterfaceIdiom raw values are unspecified -1, phone 0, pad 1, tv 2, carPlay 3, mac 5, vision 6 (confirmed on the iOS, tvOS and Catalyst SDKs). On an Apple TV simulator userInterfaceIdiom is .tv (2) and UIDevice.current.model is “Apple TV”; on an iPhone it is .phone (0). A bare Mac Catalyst process reported .pad with systemName “iPadOS”, because the default Catalyst mode scales an iPad interface; the .mac idiom appears when the app opts in to “Optimize interface for Mac”, which was not run here. watchOS has no UIUserInterfaceIdiom or UIDevice at all (“unavailable in watchOS”, even though UIKit imports); the watch uses WKInterfaceDevice.

For layout decisions use what the interface says rather than who it is: size classes and @Environment(\.horizontalSizeClass) (which compiles on every SwiftUI platform), the window size, the safe area, and #if os() at compile time. An iPad in Split View is compact, so “is an iPad” is the wrong question for “is there room”. For features ask the API: HKHealthStore.isHealthDataAvailable(), AVCaptureDevice.default(for:), CMMotionManager.isDeviceMotionAvailable, WCSession.isSupported(). Use identity probes for telemetry and for the few genuine contract differences (a tvOS focus screen), and keep model-identifier strings out of logic. Not run: a real Catalyst app with the Mac idiom, and a device (simulator models report the same idioms).

The raw values are fixed; tvOS reports idiom 2 and the model name Apple TV.

UIKit imports on the watch but UIDevice does not exist there.

Common traps

  • Switching on the idiom to decide layout. An iPad window can be compact; use size classes and measured width.
  • Calling UIDevice in code that builds for the watch. It is unavailable in watchOS even though canImport(UIKit) is true.

They might follow up with

  • How do I detect “Designed for iPad” on a Mac? ProcessInfo.processInfo.isiOSAppOnMac.
  • How do I support the watch in shared code? Put device probes behind a protocol with a watch leaf.

Related questions

  • 20.1.2 How do the platforms differ in input, process model and capabilities, and which entitlements and APIs change…
  • 20.1.1 How do you structure one codebase for several Apple platforms: shared cores, UI leaves, #if os, canImport and…

← What does the * in #available(iOS 17, *) mean on tvOS, watchOS and…