MobDev.IosLayoutPlist (mob_dev v0.7.21)

Copy Markdown View Source

The Info.plist keys that decide where an iOS app runs and how it may lay out: UIDeviceFamily (iPhone 1, iPad 2), UISupportedInterfaceOrientations (iPhone), UISupportedInterfaceOrientations~ipad, and UIRequiresFullScreen.

mob.exs overrides

Three config :mob_dev keys override the project's ios/Info.plist in the built app bundle (the dev simulator and device builds and mix mob.release). The project file is never rewritten, so the setting reaches new and existing apps alike:

  • ios_target_devices — [:iphone, :ipad] (universal) or [:iphone]. Sets UIDeviceFamily. Without iPad, an iPad runs the app letterboxed in iPhone compatibility mode, and it can't join Split View. An App Store app that has shipped iPad support can't drop it in an update.
  • ios_orientations — :all, :portrait or :landscape. Sets the iPhone UISupportedInterfaceOrientations (and removes any UISupportedInterfaceOrientations~iphone). iPad always declares all four (UISupportedInterfaceOrientations~ipad): iPadOS 26 rotates resizable apps freely, and iPad multitasking requires all four.
  • multi_window — true or false (MOB-245). true sets UIApplicationSceneManifest → UIApplicationSupportsMultipleScenes, so iPad users can open several windows of the app, each with its own navigation (Mob.Scene in mob). It needs the plist's existing UISceneConfigurations → UIWindowSceneSessionRoleApplication entry (the generated SceneDelegate); the build refuses a plist without one rather than add a manifest that would switch the app to a scene lifecycle with no delegate. false removes the key, which iOS reads as false. Needs a mob release with Mob.Scene.

Unset keys leave ios/Info.plist as written. UIRequiresFullScreen has no override: the only supported value is false (or no key), and mix mob.doctor warns when a plist sets it.

See decisions/2026-10-01-ios-layout-plist-keys.md.

Summary

Types

The layout keys of a parsed Info.plist; nil when the key is absent.

Functions

Applies plist_commands/1 to the Info.plist at path with PlistBuddy (macOS). Raises when a command other than Delete fails.

The mix mob.doctor rows for an app's Info.plist text and mob.exs config: one :ok row when the app can run full-screen on iPad, rotate and join Split View, otherwise a :warn per problem with the exact fix. Problems a mob.exs setting chose on purpose (ios_target_devices: [:iphone], ios_orientations: :portrait) aren't warned about.

The PlistBuddy commands that stamp the mob.exs overrides into a bundle's Info.plist, in order. A Delete of a key the plist lacks fails harmlessly; every other command must succeed, including the Print that checks a multi_window: true plist has an application scene configuration. Raises Mix.Error on an invalid setting.

Reads the layout keys from an XML Info.plist. {:error, reason} when the text isn't an XML property list (a binary plist, say).

Validates the mob.exs overrides. Returns the resolved settings, nil for each key that is unset.

The keys of an XML property list's top-level <dict>, parsed structurally (comments, CDATA and nested dictionaries contribute nothing).

Types

check()

@type check() :: {:ok | :warn | :fail, String.t(), String.t(), String.t() | nil}

One mix mob.doctor row.

keys()

@type keys() :: %{
  device_family: [integer()] | nil,
  orientations: [String.t()] | nil,
  iphone_orientations: [String.t()] | nil,
  ipad_orientations: [String.t()] | nil,
  requires_full_screen: boolean() | nil,
  scene_configurations?: boolean()
}

The layout keys of a parsed Info.plist; nil when the key is absent.

Functions

apply!(path, cfg)

@spec apply!(
  Path.t(),
  keyword()
) :: :ok

Applies plist_commands/1 to the Info.plist at path with PlistBuddy (macOS). Raises when a command other than Delete fails.

audit(xml, cfg)

@spec audit(
  String.t(),
  keyword()
) :: [check()]

The mix mob.doctor rows for an app's Info.plist text and mob.exs config: one :ok row when the app can run full-screen on iPad, rotate and join Split View, otherwise a :warn per problem with the exact fix. Problems a mob.exs setting chose on purpose (ios_target_devices: [:iphone], ios_orientations: :portrait) aren't warned about.

plist_commands(cfg)

@spec plist_commands(keyword()) :: [String.t()]

The PlistBuddy commands that stamp the mob.exs overrides into a bundle's Info.plist, in order. A Delete of a key the plist lacks fails harmlessly; every other command must succeed, including the Print that checks a multi_window: true plist has an application scene configuration. Raises Mix.Error on an invalid setting.

read(xml)

@spec read(String.t()) :: {:ok, keys()} | {:error, String.t()}

Reads the layout keys from an XML Info.plist. {:error, reason} when the text isn't an XML property list (a binary plist, say).

settings(cfg)

@spec settings(keyword()) ::
  {:ok,
   %{
     device_family: [integer()] | nil,
     orientations: [String.t()] | nil,
     multi_window: boolean() | nil
   }}
  | {:error, String.t()}

Validates the mob.exs overrides. Returns the resolved settings, nil for each key that is unset.

top_level_keys(xml)

@spec top_level_keys(String.t()) :: {:ok, [String.t()]} | {:error, String.t()}

The keys of an XML property list's top-level <dict>, parsed structurally (comments, CDATA and nested dictionaries contribute nothing).