diff --git a/Xamarin.MacDev/RuntimeService.cs b/Xamarin.MacDev/RuntimeService.cs
new file mode 100644
index 0000000..cf436d6
--- /dev/null
+++ b/Xamarin.MacDev/RuntimeService.cs
@@ -0,0 +1,94 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using Xamarin.MacDev.Models;
+
+#nullable enable
+
+namespace Xamarin.MacDev;
+
+///
+/// Manages simulator runtimes via xcrun simctl and xcrun xcodebuild.
+///
+public class RuntimeService {
+
+ static readonly string XcrunPath = "/usr/bin/xcrun";
+
+ readonly ICustomLogger log;
+ readonly SimCtl simctl;
+
+ public RuntimeService (ICustomLogger log)
+ {
+ this.log = log ?? throw new ArgumentNullException (nameof (log));
+ simctl = new SimCtl (log);
+ }
+
+ ///
+ /// Lists installed simulator runtimes. Optionally filters by availability.
+ ///
+ public List List (bool availableOnly = false)
+ {
+ var json = simctl.Run ("list", "runtimes", "--json");
+ if (json is null)
+ return new List ();
+
+ var runtimes = SimctlOutputParser.ParseRuntimes (json, log);
+
+ if (availableOnly)
+ runtimes.RemoveAll (r => !r.IsAvailable);
+
+ log.LogInfo ("Found {0} simulator runtime(s).", runtimes.Count);
+ return runtimes;
+ }
+
+ ///
+ /// Lists runtimes for a specific platform (e.g. "iOS", "tvOS", "watchOS", "visionOS").
+ ///
+ public List ListByPlatform (string platform, bool availableOnly = false)
+ {
+ if (string.IsNullOrEmpty (platform))
+ throw new ArgumentException ("Platform must not be null or empty.", nameof (platform));
+
+ var all = List (availableOnly);
+ return all.Where (r => string.Equals (r.Platform, platform, StringComparison.OrdinalIgnoreCase)).ToList ();
+ }
+
+ ///
+ /// Downloads a platform runtime using xcrun xcodebuild -downloadPlatform.
+ ///
+ /// The platform to download (e.g. "iOS", "tvOS", "watchOS", "visionOS").
+ /// Optional specific version to download (e.g. "17.5").
+ /// True if the download command succeeded.
+ public bool DownloadPlatform (string platform, string? version = null)
+ {
+ if (string.IsNullOrEmpty (platform))
+ throw new ArgumentException ("Platform must not be null or empty.", nameof (platform));
+
+ log.LogInfo ("Downloading {0} platform runtime via xcodebuild...", platform);
+
+ try {
+ var args = string.IsNullOrEmpty (version)
+ ? new [] { "xcodebuild", "-downloadPlatform", platform }
+ : new [] { "xcodebuild", "-downloadPlatform", platform, "-buildVersion", version! };
+
+ log.LogInfo ("Executing: {0} {1}", XcrunPath, string.Join (" ", args));
+ var (exitCode, _, stderr) = ProcessUtils.Exec (XcrunPath, args);
+ if (exitCode != 0) {
+ log.LogInfo ("xcodebuild -downloadPlatform {0} failed (exit {1}): {2}", platform, exitCode, stderr.Trim ());
+ return false;
+ }
+
+ log.LogInfo ("Successfully downloaded {0} platform runtime.", platform);
+ return true;
+ } catch (System.ComponentModel.Win32Exception ex) {
+ log.LogInfo ("Could not run xcodebuild: {0}", ex.Message);
+ return false;
+ } catch (InvalidOperationException ex) {
+ log.LogInfo ("Could not run xcodebuild: {0}", ex.Message);
+ return false;
+ }
+ }
+}
diff --git a/tests/RuntimeServiceTests.cs b/tests/RuntimeServiceTests.cs
new file mode 100644
index 0000000..1e26988
--- /dev/null
+++ b/tests/RuntimeServiceTests.cs
@@ -0,0 +1,52 @@
+// Copyright (c) Microsoft Corporation.
+// Licensed under the MIT License.
+
+using System;
+using NUnit.Framework;
+using Xamarin.MacDev;
+
+#nullable enable
+
+namespace tests;
+
+[TestFixture]
+public class RuntimeServiceTests {
+
+ [Test]
+ public void Constructor_ThrowsOnNullLogger ()
+ {
+ Assert.Throws (() => new RuntimeService (null!));
+ }
+
+ [Test]
+ public void ListByPlatform_ThrowsOnNullPlatform ()
+ {
+ var svc = new RuntimeService (ConsoleLogger.Instance);
+ Assert.Throws (() => svc.ListByPlatform (null!));
+ Assert.Throws (() => svc.ListByPlatform (""));
+ }
+
+ [Test]
+ public void DownloadPlatform_ThrowsOnNullPlatform ()
+ {
+ var svc = new RuntimeService (ConsoleLogger.Instance);
+ Assert.Throws (() => svc.DownloadPlatform (null!));
+ Assert.Throws (() => svc.DownloadPlatform (""));
+ }
+
+ [Test]
+ [Platform ("MacOsX")]
+ public void List_DoesNotThrow ()
+ {
+ var svc = new RuntimeService (ConsoleLogger.Instance);
+ Assert.DoesNotThrow (() => svc.List ());
+ }
+
+ [Test]
+ [Platform ("MacOsX")]
+ public void ListByPlatform_DoesNotThrow ()
+ {
+ var svc = new RuntimeService (ConsoleLogger.Instance);
+ Assert.DoesNotThrow (() => svc.ListByPlatform ("iOS"));
+ }
+}