simslim

package module
v0.5.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 24, 2026 License: MIT Imports: 16 Imported by: 0

README

simslim

Run a lot more iOS simulators on one Mac by turning off the background daemons a simulator doesn't need.

A freshly booted iOS simulator starts around 180 background services: Siri, Spotlight indexing, photo analysis, News, wallpaper posters, iCloud sync, and so on. None of it matters when you're using the simulator for development, testing, or CI. simslim switches those services off, which cuts each simulator's memory roughly 4x. On the same laptop you go from a handful of simulators to a screenful.

19 iOS simulators running at once on a 16 GB Mac

19 iOS simulators, all under automation, on a 16 GB MacBook Pro. Stock simulators start thrashing at around 5.

Numbers

One simulator, booted stock and then slimmed, same device and settle time (M1 Pro, 16 GB):

Stock Slim
Processes 258 70
Memory 4.0 GB 0.9 GB

Memory here is phys_footprint, the figure Activity Monitor shows, which counts compressed and swapped pages. That's what decides how many simulators fit before the machine starts swapping. Run simslim measure <udid> to see it for any booted simulator.

Install

brew install mobai-app/tap/simslim

or

go install github.com/mobai-app/simslim/cmd/simslim@latest

macOS only, and you need Xcode with an iOS Simulator runtime, since simslim drives simulators through xcrun simctl.

macOS app

The SwiftUI app bundles the CLI and adds:

  • Searchable simulator status, disk-size, and live RAM columns.
  • Searchable service profiles with per-daemon controls and purpose summaries.
  • Read-only disk analysis plus confirmed cleanup of allowlisted data.
  • Clone, rename, erase, delete, and Finder shortcuts.

Build it locally with Go and Xcode:

make app
open build/SimSlim.app

Memory estimates are guidance rather than additive savings; see the measurement method. SimSlim recommends cloning before service or disk changes so the copy can serve as a backup.

Usage

simslim list             # simulators and their slim status (--booted to filter)
simslim profiles         # what a slim boot turns off
simslim profiles <id>    # the daemons in one category
simslim on <udid>        # slim a simulator and reboot it slim
simslim off <udid>       # put it back to stock
simslim status <udid>    # how slim a booted simulator is
simslim doctor <udid> --requires push,storekit,universal-links
simslim measure <udid>   # a booted simulator's memory footprint
simslim top              # live fleet monitor; enter a sim for per-daemon RAM/CPU
simslim size <udid>      # total allocated simulator size
simslim disk-plan <udid> # measure reclaimable data; read-only
simslim disk-clean --categories caches,logs --confirm <udid>
simslim clone <udid> <name>
simslim rename <udid> <name>
simslim boot <udid>      # boot a simulator and wait for its services
simslim shutdown <udid>  # shut down a booted simulator
simslim erase <udid>     # erase apps, data, settings, and slimming overrides
simslim delete <udid>    # permanently delete a simulator

Read-only and simulator-management commands accept --json for integrations and the macOS app.

Slow CI runners

simslim on boots the simulator, disables ~170 daemons one launchctl call at a time, then reboots — all under a single 10-minute deadline. Shared CI runners (like GitHub-hosted macOS runners) are slower and less predictable, and can blow that deadline mid-reconfigure with context deadline exceeded errors. Raise it with the global --boot-timeout flag or the SIMSLIM_BOOT_TIMEOUT environment variable:

simslim on <udid> --boot-timeout 15m
# or, for the whole job:
export SIMSLIM_BOOT_TIMEOUT=15m

Disk cleanup

Disk cleanup is permanent and separate from service slimming. disk-plan is read-only. disk-clean shuts down the exact simulator, clears only allowlisted per-device directories, and refuses to run without --confirm.

simslim disk-categories
simslim disk-plan <udid>
simslim disk-clean --categories caches,logs,temporary --confirm <udid>
# Optional: also remove on-demand language models
simslim disk-clean --categories linguistic-data --confirm <udid>

Built-in apps and core OS language resources are part of a signed iOS runtime shared by every simulator using that version, so simslim never modifies them. Required Siri assets are measured only because iOS restores them on launch; on-demand language data is opt-in and may download again when needed.

disk-plan also reports a read-only storage breakdown for installed app bundles, Documents, app data, and user media. Those durable rows are never eligible for cleanup. See the disk cleanup safety model for recovery behavior, safeguards, and Xcode 26.6 validation results.

Keep a category you actually need, like Spotlight search:

simslim on <udid> --except search

Or keep one specific daemon, like push notifications:

simslim on <udid> --keep com.apple.apsd
Profile files

For a repeatable setup, commit a JSON profile alongside your project and apply it per run. A ci.json and a dev.json can slim differently for each purpose:

{
  "name": "ci",
  "description": "UI test runs",
  "except": ["search", "store"],
  "keep": ["com.apple.apsd"]
}
simslim on <udid> --profile ci.json

except and keep mirror the flags of the same name; name and description are for whoever reads the file. Unknown fields, unknown category IDs, and labels no category disables are rejected, so a typo fails loudly. --profile is the single source of truth for its run and cannot be combined with --except or --keep.

To build one interactively, run simslim profile ci.json: name it, then use the arrow keys and space to tick whole features to keep enabled, or press to open a feature and keep individual daemons within it. Point it at a directory to save <name>.json there, or omit the path to print to stdout.

Checking a simulator with doctor

Slimming a simulator turns features off on purpose, so a test suite that needs one of them wants a fast way to catch a mis-slimmed simulator before it runs. doctor checks a booted simulator against the features you name and exits non-zero if any of them are broken, which makes it a natural CI preflight:

simslim doctor <udid> --requires push,storekit,universal-links
<udid>: 2/3 required features OK
  ok     push
  ok     universal-links
  BROKEN storekit — com.apple.storekitd disabled

Feature IDs are finer-grained than the slimming categories: each maps to just the daemons that back one capability. Run simslim doctor --list to see them all. Both the check and the list support --json.

Use as a Go library

The repo root is an importable package with no external dependencies, so you can drive simulators from your own tooling instead of shelling out to the CLI:

go get github.com/mobai-app/simslim
package main

import (
	"context"
	"fmt"

	"github.com/mobai-app/simslim"
)

func main() {
	ctx := context.Background()

	devices, err := simslim.ListDevices(ctx)
	if err != nil {
		panic(err)
	}

	// Slim every booted simulator, keeping Siri and search enabled.
	profile, err := simslim.BuildProfile("", "siri,search", "")
	if err != nil {
		panic(err)
	}
	for _, d := range devices {
		if d.State != "Booted" {
			continue
		}
		changed, err := simslim.EnableSlim(ctx, d.Set, d.UDID, profile, func(msg string) {
			fmt.Println(d.UDID, msg)
		})
		fmt.Println(d.UDID, "changed:", changed, "err:", err)
	}
}

The package never writes to stdout — it returns values and reports progress through the simslim.Reporter callback you pass in. simslim.Categories, simslim.Features, and simslim.SlimmableSet() expose the same allowlist the CLI uses. macOS only, since everything runs through xcrun simctl.

How it works

simslim on writes persistent launchctl disable entries for the chosen daemons into the simulator's own launchd database, then reboots it. The entries stick across reboots, so the simulator comes up slim in a single boot from then on. simslim off clears them and reboots back to stock. Your Mac is never touched, only the simulator you point it at, and only daemons that are safe to disable. Core workflow services such as sharingd, plus the handful that wedge a simulator when turned off, are left running.

This is per-simulator state, not a global setting. The daemon disables live in that one simulator's launchd database. simslim clone preserves them, but erase, delete and recreate, or "Erase All Content and Settings" reset the simulator to stock, so you'll need to run simslim on again and its memory will climb back to stock until you do. A simulator created from a new or updated runtime also starts stock. Run simslim list to see which simulators are currently slim.

What you lose

Turning services off is fine for most development, UI automation, and CI, but some features genuinely stop working. The ones worth knowing:

  • Spotlight and in-Settings search return nothing (search).
  • Push notifications need apsd, StoreKit testing needs storekitd (store).
  • Universal links need swcd (web).
  • The Contacts, Photos, and Calendar pickers can act up without their categories.

simslim profiles lists every category, so you can keep a category with --except or individual daemons with --keep.

Why

Testing is shifting. Once agents are writing apps, you want agents running them too, and the place an iOS app runs is a simulator. One agent, one simulator. So how much work you get through at once comes down to how many simulators a machine can hold, and stock simulators are heavy enough that a laptop fills up fast. Slimming them is the cheapest way to raise that ceiling: more simulators on the box means more agents working in parallel on it.

Built for MobAI to run more simulators on one machine.

License

MIT, copyright Interlap.

Documentation

Index

Constants

View Source
const ShutdownTimeout = 30 * time.Second

Variables

View Source
var BootTimeout = 10 * time.Minute

BootTimeout bounds a full boot-and-reconfigure (boots twice on a first slim); a var, not a const, so `--boot-timeout` / SIMSLIM_BOOT_TIMEOUT can raise it for CI.

View Source
var Categories = []Category{
	{
		ID:             "widgets",
		Name:           "Widgets & Wallpaper",
		Description:    "Home and lock screen posters, widgets, and Live Activities.",
		Downside:       "Home and Lock Screen widgets, wallpaper posters, and Live Activities stop updating.",
		ApproxMemoryMB: 675,
		Labels: []string{
			"com.apple.PosterBoard",
			"com.apple.chronod",
			"com.apple.liveactivitiesd",
		},
	},
	{
		ID:             "siri",
		Name:           "Siri & Intelligence",
		Description:    "Siri, Apple Intelligence, speech, and on-device ML model services.",
		Downside:       "Siri, speech features, and Apple Intelligence services are unavailable.",
		ApproxMemoryMB: 265,
		Labels: []string{
			"com.apple.assistantd",
			"com.apple.assistant_cdmd",
			"com.apple.assistant_service",
			"com.apple.siriactionsd",
			"com.apple.siriinferenced",
			"com.apple.siriknowledged",
			"com.apple.sirittsd",
			"com.apple.siri.context.service",
			"com.apple.siri.acousticsignature",
			"com.apple.corespeechd",
			"com.apple.voiced",
			"com.apple.voicebankingd",
			"com.apple.speechmodeltrainingd",
			"com.apple.intelligenceplatformd",
			"com.apple.intelligencecontextd",
			"com.apple.intelligenceflowd",
			"com.apple.intelligencetasksd",
			"com.apple.generativeexperiencesd",
			"com.apple.knowledgeconstructiond",
			"com.apple.naturallanguaged",
			"com.apple.textunderstandingd",
			"com.apple.modelcatalogd",
			"com.apple.modelmanagerd",
			"com.apple.mlhostd",
			"com.apple.mlruntimed",
			"com.apple.suggestd",
			"com.apple.parsecd",
			"com.apple.parsec-fbf",
			"com.apple.proactiveeventtrackerd",
		},
	},
	{
		ID:             "search",
		Name:           "Spotlight & Search",
		Description:    "On-device Spotlight and in-Settings search services.",
		Downside:       "Spotlight and Settings search return no results.",
		ApproxMemoryMB: 50,
		Labels: []string{
			"com.apple.searchd",
			"com.apple.searchtoold",
			"com.apple.spotlightknowledged",
			"com.apple.spotlightknowledged.updater",
			"com.apple.corespotlightservice",
		},
	},
	{
		ID:             "icloud",
		Name:           "iCloud & Apple Account",
		Description:    "iCloud sync, Apple Account, keychain, and backup services.",
		Downside:       "iCloud sync, Apple Account, Keychain, and backup workflows will not work.",
		ApproxMemoryMB: 100,
		Labels: []string{
			"com.apple.appleaccountd",
			"com.apple.appleaccounttransparencyd",
			"com.apple.appleidsetupd",
			"com.apple.akd",
			"com.apple.amsaccountsd",
			"com.apple.amsengagementd",
			"com.apple.amsondevicestoraged",
			"com.apple.cloudd",
			"com.apple.cloudphotod",
			"com.apple.ckdiscretionaryd",
			"com.apple.cloudsettingssyncagent",
			"com.apple.bird",
			"com.apple.syncdefaultsd",
			"com.apple.cdpd",
			"com.apple.sosd",
			"com.apple.SecureBackupDaemon",
			"com.apple.TrustedPeersHelper",
			"com.apple.protectedcloudstorage.protectedcloudkeysyncing",
			"com.apple.icloudmailagent",
			"com.apple.icloudsubscriptionoptimizerd",
			"com.apple.communicationtrustd",
		},
	},
	{
		ID:             "store",
		Name:           "App Store, Push & Media",
		Description:    "App Store, push notification, StoreKit, and media services.",
		Downside:       "Remote push notifications and StoreKit or App Store testing will not work.",
		ApproxMemoryMB: 80,
		Labels: []string{
			"com.apple.appstored",
			"com.apple.appstorecomponentsd",
			"com.apple.apsd",
			"com.apple.itunescloudd",
			"com.apple.itunesstored",
			"com.apple.storekitd",
			"com.apple.videosubscriptionsd",
			"com.apple.assetsubscriptiond",
			"com.apple.musicd",
		},
	},
	{
		ID:             "pim",
		Name:           "Mail, Calendar & Contacts",
		Description:    "Mail, Calendar, Contacts, Reminders, and related sync services.",
		Downside:       "Contacts, Calendar, Reminders, and Mail-backed pickers or sync may fail.",
		ApproxMemoryMB: 80,
		Labels: []string{
			"com.apple.email.maild",
			"com.apple.exchangesyncd",
			"com.apple.dataaccess.dataaccessd",
			"com.apple.calaccessd",
			"com.apple.remindd",
			"com.apple.contactsd",
			"com.apple.contacts.postersyncd",
			"com.apple.peopled",
		},
	},
	{
		ID:             "web",
		Name:           "Safari Sync & Web Services",
		Description:    "Safari sync, web push, privacy, and universal-link services.",
		Downside:       "Universal links and Safari sync or background web services will not work.",
		ApproxMemoryMB: 50,
		Labels: []string{
			"com.apple.SafariBookmarksSyncAgent",
			"com.apple.Safari.History",
			"com.apple.Safari.passwordbreachd",
			"com.apple.Safari.SafeBrowsing.Service",
			"com.apple.safarifetcherd",
			"com.apple.WebBookmarks.webbookmarksd",
			"com.apple.webkit.adattributiond",
			"com.apple.webkit.webpushd",
			"com.apple.webprivacyd",
			"com.apple.swcd",
		},
	},
	{
		ID:             "family",
		Name:           "Family & Screen Time",
		Description:    "Family Sharing, Screen Time, and usage tracking.",
		Downside:       "Family Sharing, Screen Time, and usage tracking stop working.",
		ApproxMemoryMB: 65,
		Labels: []string{
			"com.apple.familycircled",
			"com.apple.FamilyControlsAgent",
			"com.apple.familynotification",
			"com.apple.askpermissiond",
			"com.apple.asktod",
			"com.apple.ScreenTimeAgent",
			"com.apple.ScreenTimeSettingsAgent",
			"com.apple.UsageTrackingAgent",
		},
	},
	{
		ID:             "health",
		Name:           "Health, Home & Fitness",
		Description:    "HealthKit, HomeKit, and Fitness services.",
		Downside:       "HealthKit, HomeKit, and Fitness integrations will not work.",
		ApproxMemoryMB: 135,
		Labels: []string{
			"com.apple.healthd",
			"com.apple.healthappd",
			"com.apple.healthcontentd",
			"com.apple.healtheventsd",
			"com.apple.healthrecordsd",
			"com.apple.finhealthd",
			"com.apple.homed",
			"com.apple.homeeventsd",
			"com.apple.fitcore",
			"com.apple.fitcore.session",
			"com.apple.fitnesscoachingd",
			"com.apple.fitnessintelligenced",
			"com.apple.activityawardsd",
			"com.apple.activitysharingd",
		},
	},
	{
		ID:             "photos",
		Name:           "Photos & Media Analysis",
		Description:    "Photos library, photo analysis, and media analysis services.",
		Downside:       "Photo picker, Photos-library workflows, and media analysis may fail.",
		ApproxMemoryMB: 60,
		Labels: []string{
			"com.apple.photoanalysisd",
			"com.apple.photosface",
			"com.apple.mediaanalysisd",
			"com.apple.mediaanalysisd.service",
			"com.apple.mediastream.mstreamd",
			"com.apple.medialibraryd",
			"com.apple.assetsd",
			"com.apple.assetsd.nebulad",
		},
	},
	{
		ID:             "apps",
		Name:           "News, Weather, Maps & Games",
		Description:    "News, Weather, Maps, Tips, and game services.",
		Downside:       "News, Weather, Maps background data, and game-controller services are unavailable.",
		ApproxMemoryMB: 90,
		Labels: []string{
			"com.apple.newsd",
			"com.apple.weatherd",
			"com.apple.Maps.mapssyncd",
			"com.apple.Maps.mapspushd",
			"com.apple.Maps.geocorrectiond",
			"com.apple.maps.destinationd",
			"com.apple.MapKit.SnapshotService",
			"com.apple.jetpackassetd",
			"com.apple.tipsd",
			"com.apple.gamed",
			"com.apple.gamesaved",
			"com.apple.GameController.gamecontrollerd",
		},
	},
	{
		ID:             "messaging",
		Name:           "Messaging & FaceTime",
		Description:    "iMessage, FaceTime, call, and identity services.",
		Downside:       "iMessage, FaceTime, and related identity services will not work.",
		ApproxMemoryMB: 60,
		Labels: []string{
			"com.apple.identityservicesd",
			"com.apple.ids_simd",
			"com.apple.imautomatichistorydeletionagent",
			"com.apple.imcore.imtransferagent",
			"com.apple.imdpersistence.IMDPersistenceAgent",
			"com.apple.facetimemessagestored",
			"com.apple.telephonyutilities.callservicesd",
		},
	},
	{
		ID:             "connectivity",
		Name:           "Sharing & Device Connectivity",
		Description:    "AirDrop, Continuity, CarPlay, Watch, and Find My services.",
		Downside:       "AirDrop, Continuity, CarPlay, Watch, and Find My connectivity will not work.",
		ApproxMemoryMB: 65,
		Labels: []string{
			"com.apple.rapportd",
			"com.apple.companiond",
			"com.apple.carkitd",
			"com.apple.wcd",
			"com.apple.tvremoted",
			"com.apple.avatarsd",
			"com.apple.stickersd",
			"com.apple.sociallayerd",
			"com.apple.announced",
			"com.apple.navd",
			"com.apple.findmy.findmylocated",
		},
		AlwaysEnabled: []AlwaysEnabledService{
			{
				Label:  "com.apple.sharingd",
				Reason: "Required for system share sheets.",
			},
		},
	},
	{
		ID:             "telemetry",
		Name:           "Ads, Diagnostics & Telemetry",
		Description:    "DeviceCheck, ad privacy, analytics, diagnostics, and feedback services.",
		Downside:       "DeviceCheck plus analytics, diagnostics, and feedback services are unavailable.",
		ApproxMemoryMB: 105,
		Labels: []string{
			"com.apple.ap.adprivacyd",
			"com.apple.ap.promotedcontentd",
			"com.apple.diagnosticextensionsd",
			"com.apple.feedbackd",
			"com.apple.rtcreportingd",
			"com.apple.securityuploadd",
			"com.apple.geoanalyticsd",
			"com.apple.triald",
			"com.apple.followupd",
			"com.apple.purplebuddy.budd",
			"com.apple.devicecheckd",
		},
	},
	{
		ID:             "other",
		Name:           "Other Background Services",
		Description:    "Wallet, business services, assets, and miscellaneous background daemons.",
		Downside:       "Wallet, merchant, business, asset, and miscellaneous background services are unavailable.",
		ApproxMemoryMB: 195,
		Labels: []string{
			"com.apple.financed",
			"com.apple.passd",
			"com.apple.merchantd",
			"com.apple.coreidvd",
			"com.apple.businessservicesd",
			"com.apple.deviceaccessd",
			"com.apple.replicatord",
			"com.apple.linkd",
			"com.apple.ind",
			"com.apple.storagedatad",
			"com.apple.StatusKitAgent",
			"com.apple.countryd",
			"com.apple.mobileassetd",
			"com.apple.managedconfiguration.passcodenagd",
		},
	},
}

Categories is the complete allowlist of daemons a profile may disable. ApproxMemoryMB values are rounded median increases in phys_footprint when only that category is kept on versus a fully slim iOS 26.5 clean boot. The estimates vary by runtime and workload and are not additive.

View Source
var DiskCleanupCategories = []DiskCleanupCategory{
	{
		ID:              "caches",
		Name:            "System & App Caches",
		Description:     "Generated cache files belonging to iOS and installed apps.",
		Downside:        "Next launches may be slower; downloaded or offline cache content can disappear.",
		Recovery:        "Old cache contents stay deleted; apps build new caches as needed.",
		Risk:            "Lower risk",
		DefaultSelected: true,
		CanClean:        true,
	},
	{
		ID:              "logs",
		Name:            "Logs & Diagnostics",
		Description:     "Unified logs, signposts, symbol text, crash logs, and app log folders.",
		Downside:        "Existing diagnostic and crash history is deleted.",
		Recovery:        "Old history stays deleted; future runs create new logs.",
		Risk:            "Lower risk",
		DefaultSelected: true,
		CanClean:        true,
	},
	{
		ID:              "temporary",
		Name:            "Temporary Files",
		Description:     "Files in simulator and app temporary directories.",
		Downside:        "Apps that misuse temporary storage may lose in-progress work.",
		Recovery:        "Old contents stay deleted; apps create new temporary files as needed.",
		Risk:            "Lower risk",
		DefaultSelected: true,
		CanClean:        true,
	},
	{
		ID:              "linguistic-data",
		Name:            "Downloaded Language Data",
		Description:     "On-demand language models used by Siri, Search, and text analysis.",
		Downside:        "Language-aware features may be limited until iOS downloads the package again.",
		Recovery:        "iOS downloads the language package again when a feature needs it.",
		Risk:            "Restored on demand",
		DefaultSelected: false,
		CanClean:        true,
	},
	{
		ID:              "required-siri-assets",
		Name:            "Required Siri Assets",
		Description:     "Siri understanding, speech, voice, and accessibility downloads restored by iOS.",
		Downside:        "Manual deletion is unsupported, and iOS promptly downloads required assets again.",
		Recovery:        "Required assets return automatically after boot.",
		Risk:            "System managed",
		DefaultSelected: false,
		CanClean:        false,
	},
}
View Source
var Features = []Feature{
	{ID: "push", Name: "Push notifications", Labels: []string{"com.apple.apsd"}},
	{ID: "storekit", Name: "StoreKit / in-app purchase", Labels: []string{"com.apple.storekitd"}},
	{ID: "app-store", Name: "App Store", Labels: []string{"com.apple.appstored", "com.apple.itunesstored"}},
	{ID: "universal-links", Name: "Universal links / associated domains", Labels: []string{"com.apple.swcd"}},
	{ID: "spotlight", Name: "Spotlight & Settings search", Labels: []string{"com.apple.searchd", "com.apple.searchtoold"}},
	{ID: "siri", Name: "Siri & speech", Labels: []string{"com.apple.assistantd", "com.apple.corespeechd"}},
	{ID: "icloud", Name: "iCloud sync", Labels: []string{"com.apple.cloudd"}},
	{ID: "keychain-sync", Name: "iCloud Keychain", Labels: []string{"com.apple.akd"}},
	{ID: "contacts", Name: "Contacts", Labels: []string{"com.apple.contactsd"}},
	{ID: "calendar", Name: "Calendar", Labels: []string{"com.apple.calaccessd"}},
	{ID: "reminders", Name: "Reminders", Labels: []string{"com.apple.remindd"}},
	{ID: "mail", Name: "Mail", Labels: []string{"com.apple.email.maild"}},
	{ID: "photos", Name: "Photos library & analysis", Labels: []string{"com.apple.assetsd", "com.apple.photoanalysisd"}},
	{ID: "health", Name: "HealthKit", Labels: []string{"com.apple.healthd"}},
	{ID: "homekit", Name: "HomeKit", Labels: []string{"com.apple.homed"}},
	{ID: "imessage", Name: "iMessage & FaceTime", Labels: []string{"com.apple.identityservicesd"}},
	{ID: "widgets", Name: "Widgets & Live Activities", Labels: []string{"com.apple.chronod", "com.apple.liveactivitiesd"}},
	{ID: "wallet", Name: "Wallet & passes", Labels: []string{"com.apple.passd"}},
	{ID: "maps", Name: "Maps background services", Labels: []string{"com.apple.Maps.mapssyncd"}},
	{ID: "weather", Name: "Weather", Labels: []string{"com.apple.weatherd"}},
	{ID: "news", Name: "News", Labels: []string{"com.apple.newsd"}},
	{ID: "game-center", Name: "Game Center", Labels: []string{"com.apple.gamed"}},
	{ID: "find-my", Name: "Find My", Labels: []string{"com.apple.findmy.findmylocated"}},
	{ID: "screen-time", Name: "Screen Time", Labels: []string{"com.apple.ScreenTimeAgent"}},
}

Features maps commonly required capabilities to the daemons they need running.

Functions

func BootAndWait

func BootAndWait(ctx context.Context, set, udid string) error

bootAndWait boots the device (tolerating an already-booted one) and blocks on bootstatus until its services are ready.

func CloneDevice

func CloneDevice(ctx context.Context, udid, name string) (newUDID string, err error)

cloneDevice temporarily shuts down a booted source because CoreSimulator can only clone a stable device, then restores the source's original boot state.

func DeleteDevice

func DeleteDevice(ctx context.Context, udid string) error

func DisableSlim

func DisableSlim(ctx context.Context, set, udid string, report Reporter) (bool, error)

disableSlim re-enables every managed daemon, returning the device to stock.

func EnableSlim

func EnableSlim(ctx context.Context, set, udid string, p Profile, report Reporter) (bool, error)

enableSlim disables the profile's daemons and boots the device slim.

func EraseDevice

func EraseDevice(ctx context.Context, udid string) error

func ExtraDeviceSetTokens

func ExtraDeviceSetTokens() []string

ExtraDeviceSetTokens returns the tokens registered with RegisterDeviceSet, beyond the well-known default and testing sets.

func MarshalProfile

func MarshalProfile(sp SlimProfile) ([]byte, error)

marshalProfile renders a profile as indented JSON with a trailing newline.

func MeasureMany

func MeasureMany(ctx context.Context, udids []string) (map[string]Measurement, map[string]string)

measureMany takes one process and footprint snapshot for every requested simulator. This keeps the GUI's RAM column current without running the relatively expensive `top` command once per booted device.

func NormalizeSimulatorName

func NormalizeSimulatorName(name string) (string, error)

func ProfileFileName

func ProfileFileName(name string) string

profileFileName derives a JSON filename from a profile name, for when the command targets a directory. Non-filename runs collapse to a hyphen; an empty name falls back to profile.json.

func RegisterDeviceSet

func RegisterDeviceSet(value string)

registerDeviceSet adds a --set value to the scanned sets ignoring any that duplicate an already-known token.

func RenameDevice

func RenameDevice(ctx context.Context, udid, name string) error

func ResetDeviceSets

func ResetDeviceSets()

ResetDeviceSets drops every set registered with RegisterDeviceSet.

func Shutdown

func Shutdown(ctx context.Context, set, udid string) error

func SlimmableSet

func SlimmableSet() map[string]bool

slimmableSet is every label a service profile may disable.

func SplitList

func SplitList(s string) []string

SplitList parses the comma-separated list syntax shared by the --except, --keep, --requires and --categories flags, dropping blank entries.

func ValidateDiskCleanupSelection

func ValidateDiskCleanupSelection(ids []string) ([]string, error)

func WaitShutdown

func WaitShutdown(ctx context.Context, set, udid string, timeout time.Duration) error

Types

type AlwaysEnabledService

type AlwaysEnabledService struct {
	Label  string `json:"label"`
	Reason string `json:"reason"`
}

AlwaysEnabledService is shown alongside a category for transparency but is never included in a slim profile. Earlier versions may have disabled it, so it remains in the mutation allowlist solely to repair that legacy state.

type Category

type Category struct {
	ID                  string                 `json:"id"`
	Name                string                 `json:"name"`
	Description         string                 `json:"description"`
	Downside            string                 `json:"downside"`
	ApproxMemoryMB      int                    `json:"approxMemoryMB"`
	Labels              []string               `json:"labels"`
	ServiceDescriptions map[string]string      `json:"serviceDescriptions"`
	AlwaysEnabled       []AlwaysEnabledService `json:"alwaysEnabled,omitempty"`
}

Category groups launchd daemon labels that a slim boot disables together.

func CategoryByID

func CategoryByID(id string) (Category, bool)

type Device

type Device struct {
	UDID      string `json:"udid"`
	Name      string `json:"name"`
	State     string `json:"state"` // "Booted" or "Shutdown"
	OSVersion string `json:"osVersion"`
	Set       string `json:"set"`
	DataPath  string `json:"-"`
}

Device is a simulator as reported by `simctl list`.

func FindDevice

func FindDevice(ctx context.Context, udid, set string) (Device, error)

findDevice locates a simulator by UDID. An empty set searches every known set. A non-empty set looks only there, so repeated lookups need not rescan.

func ListDevices

func ListDevices(ctx context.Context) ([]Device, error)

The default set is mandatory; a secondary set that cannot be listed (e.g. it does not exist) is skipped rather than failing the whole listing.

type DeviceSummary

type DeviceSummary struct {
	Device
	ManagedDisabled *int         `json:"managedDisabled,omitempty"`
	ManagedTotal    int          `json:"managedTotal"`
	StatusError     string       `json:"statusError,omitempty"`
	Memory          *Measurement `json:"memory,omitempty"`
	MemoryError     string       `json:"memoryError,omitempty"`
}

DeviceSummary is the stable, machine-readable representation used by the macOS app and other integrations. managedDisabled is omitted for shutdown simulators because launchd state can only be read while a simulator is booted.

type DiskCleanupCategory

type DiskCleanupCategory struct {
	ID              string `json:"id"`
	Name            string `json:"name"`
	Description     string `json:"description"`
	Downside        string `json:"downside"`
	Recovery        string `json:"recovery"`
	Risk            string `json:"risk"`
	DefaultSelected bool   `json:"defaultSelected"`
	CanClean        bool   `json:"canClean"`
}

DiskCleanupCategory describes one tightly allowlisted class of per-device data. Runtime files are deliberately outside this model: an iOS runtime is shared by many simulators and must only be managed by Xcode/simctl.

type DiskCleanupCategoryMeasurement

type DiskCleanupCategoryMeasurement struct {
	DiskCleanupCategory
	Bytes   int64 `json:"bytes"`
	Targets int   `json:"targets"`
}

type DiskCleanupPlan

type DiskCleanupPlan struct {
	UDID           string                           `json:"udid"`
	TotalBytes     int64                            `json:"totalBytes"`
	CleanableBytes int64                            `json:"cleanableBytes"`
	Categories     []DiskCleanupCategoryMeasurement `json:"categories"`
	Storage        []DiskStorageMeasurement         `json:"storage"`
}

func PlanDiskCleanup

func PlanDiskCleanup(ctx context.Context, udid string) (DiskCleanupPlan, error)

type DiskCleanupResult

type DiskCleanupResult struct {
	UDID              string   `json:"udid"`
	CategoryIDs       []string `json:"categoryIds"`
	BeforeBytes       int64    `json:"beforeBytes"`
	AfterBytes        int64    `json:"afterBytes"`
	ReclaimedBytes    int64    `json:"reclaimedBytes"`
	WasBooted         bool     `json:"wasBooted"`
	BootStateRestored bool     `json:"bootStateRestored"`
}

func CleanDeviceDisk

func CleanDeviceDisk(ctx context.Context, udid string, categoryIDs []string, preserveBootState bool) (DiskCleanupResult, error)

type DiskMeasurement

type DiskMeasurement struct {
	Bytes int64 `json:"bytes"`
}

func DeviceDiskUsage

func DeviceDiskUsage(ctx context.Context, udid string) (DiskMeasurement, error)

deviceDiskUsage reports allocated filesystem blocks for one exact simulator device directory. It deliberately resolves the UDID through simctl first so aliases such as "all" can never become filesystem paths.

type DiskStorageMeasurement

type DiskStorageMeasurement struct {
	ID          string `json:"id"`
	Name        string `json:"name"`
	Description string `json:"description"`
	Bytes       int64  `json:"bytes"`
}

DiskStorageMeasurement is a read-only breakdown of durable per-simulator storage. These rows are deliberately separate from cleanup categories so documents, app bundles, and user data can never become selected for deletion.

type DoctorOutput

type DoctorOutput struct {
	UDID     string          `json:"udid,omitempty"`
	OK       bool            `json:"ok"`
	Features []FeatureStatus `json:"features"`
}

DoctorOutput reports whether each required feature survives the device's current slimming. OK is false when any required feature has a disabled daemon.

func DiagnoseFeatures

func DiagnoseFeatures(features []Feature, disabled map[string]bool) DoctorOutput

diagnoseFeatures reports, for each feature, which of its daemons the device currently has disabled. A feature is OK only when none of them are.

type DroppedCategory

type DroppedCategory struct {
	ID       string   `json:"id"`
	Name     string   `json:"name"`
	Downside string   `json:"downside"`
	Labels   []string `json:"labels"`
}

DroppedCategory lists the managed daemons a category has disabled on a simulator, alongside the feature that stops working as a result.

func DroppedCategories

func DroppedCategories(disabled map[string]bool) []DroppedCategory

droppedCategories groups the disabled managed daemons by category, in category order, omitting categories with nothing disabled.

type Feature

type Feature struct {
	ID     string   `json:"id"`
	Name   string   `json:"name"`
	Labels []string `json:"labels"`
}

Feature is a user-facing capability backed by specific launchd daemons. Every label must also live in a Category (enforced in features_test.go), so a feature only ever names daemons the tool could actually have turned off.

func ResolveFeatures

func ResolveFeatures(ids []string) ([]Feature, error)

resolveFeatures maps requested IDs to their Features, erroring on the first unknown one so a typo in --requires fails loudly instead of passing silently.

type FeatureStatus

type FeatureStatus struct {
	ID       string   `json:"id"`
	Name     string   `json:"name"`
	OK       bool     `json:"ok"`
	Disabled []string `json:"disabled,omitempty"`
}

FeatureStatus is one feature's verdict: the daemons it needs that are down.

type Measurement

type Measurement struct {
	Processes int     `json:"processes"`
	Bytes     int64   `json:"bytes"` // summed phys_footprint (compressed + dirty), the number that caps how many sims fit
	CPU       float64 `json:"cpu"`   // summed %cpu across the tree (ps's decaying average; can exceed 100 on multicore)
}

Measurement is a device's real memory cost.

func Measure

func Measure(ctx context.Context, udid string) (Measurement, error)

measure sums the phys_footprint of every process in the device's launchd tree. phys_footprint (the same value Activity Monitor's "Memory" column reports) counts compressed and swapped pages, so it stays accurate under memory pressure where resident size would read misleadingly low.

type Process added in v0.5.0

type Process struct {
	PID     int     `json:"pid"`
	Command string  `json:"command"`
	Bytes   int64   `json:"bytes"`
	CPU     float64 `json:"cpu"`
}

Process is one process in a simulator's launchd tree, for the top drill-down.

func MeasureProcesses added in v0.5.0

func MeasureProcesses(ctx context.Context, udid string) ([]Process, error)

MeasureProcesses returns every process in the device's launchd tree with its own footprint and cpu, sorted by memory descending — the top drill-down view.

type Profile

type Profile struct {
	ExceptCategories map[string]bool // category IDs to leave fully enabled
	Keep             map[string]bool // individual labels to leave enabled
}

Profile selects which managed daemons a slim boot should disable.

func BuildProfile

func BuildProfile(profilePath, except, keep string) (Profile, error)

buildProfile selects the slim profile for an `on` invocation. A --profile file is the single source of truth, so it cannot be combined with --except/--keep.

func LoadSlimProfile

func LoadSlimProfile(path string) (Profile, error)

loadSlimProfile reads a profile file and resolves it to a validated Profile.

func (Profile) Desired

func (p Profile) Desired() map[string]bool

desired returns the labels this profile wants disabled.

type Reporter

type Reporter func(string)

Reporter receives human-readable progress lines. A nil Reporter is a no-op, so non-interactive callers can ignore progress entirely.

type SimulatorMutationOutput

type SimulatorMutationOutput struct {
	Action     string `json:"action"`
	UDID       string `json:"udid"`
	Name       string `json:"name,omitempty"`
	SourceUDID string `json:"sourceUdid,omitempty"`
}

SimulatorMutationOutput is returned by simulator-management commands so the GUI can refresh and, for clone, select the newly created device.

type SlimProfile

type SlimProfile struct {
	Name        string   `json:"name,omitempty"`
	Description string   `json:"description,omitempty"`
	Except      []string `json:"except,omitempty"`
	Keep        []string `json:"keep,omitempty"`
}

SlimProfile is the on-disk profile applied with `simslim on --profile <path>`. Except and Keep mirror the `--except` and `--keep` flags.

type Status

type Status struct {
	ManagedDisabled int  `json:"managedDisabled"` // managed labels currently disabled
	ManagedTotal    int  `json:"managedTotal"`    // size of the managed universe
	Booted          bool `json:"booted"`
}

Status describes how slim a device currently is.

func ReadStatus

func ReadStatus(ctx context.Context, udid string) (Status, map[string]bool, error)

status reports how slim a device is and returns the labels it currently has disabled (nil when the device is not booted).

func ReadStatusForDevice

func ReadStatusForDevice(ctx context.Context, d Device) (Status, map[string]bool, error)

type StatusOutput

type StatusOutput struct {
	Status
	Verdict string            `json:"verdict"`
	Dropped []DroppedCategory `json:"dropped,omitempty"` // only when `status --dropped` is requested
}

type TopOutput added in v0.5.0

type TopOutput struct {
	Sims       []TopSim `json:"sims"`
	TotalBytes int64    `json:"totalBytes"`
}

TopOutput is the fleet view: every booted simulator plus the summed footprint that decides how many more fit.

func FleetSnapshot added in v0.5.0

func FleetSnapshot(ctx context.Context, withDisk bool) (TopOutput, error)

FleetSnapshot gathers the live resource view of every booted simulator: its slim status and memory come from one shared process snapshot; disk usage is optional because it shells out to `du` per device and is comparatively slow.

type TopProcesses added in v0.5.0

type TopProcesses struct {
	UDID       string    `json:"udid"`
	Name       string    `json:"name,omitempty"`
	Processes  []Process `json:"processes"`
	TotalBytes int64     `json:"totalBytes"`
}

TopProcesses is one simulator's per-daemon breakdown, the top drill-down.

type TopSim added in v0.5.0

type TopSim struct {
	Device
	ManagedDisabled *int         `json:"managedDisabled,omitempty"`
	ManagedTotal    int          `json:"managedTotal"`
	StatusError     string       `json:"statusError,omitempty"`
	Memory          *Measurement `json:"memory,omitempty"`
	MemoryError     string       `json:"memoryError,omitempty"`
	DiskBytes       *int64       `json:"diskBytes,omitempty"`
}

TopSim is one booted simulator's live resource snapshot for `simslim top`.

Directories

Path Synopsis
cmd
simslim command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL