Files
Danylo 6518ba7eab Keep navd enabled with --except widgets so the Maps widget does not flood the log (#61)
chronod runs the stock Maps widget, which retries its navd XPC connection in a tight loop while navd is disabled. Keep navd in the widgets category and the widgets doctor feature.
2026-10-09 17:38:14 +02:00

97 lines
4.3 KiB
Go

package simslim
import "fmt"
// Feature is a user-facing capability backed by specific launchd daemons.
// Every label must also be managed by a Category (enforced in features_test.go),
// including required services that earlier versions could have turned off.
type Feature struct {
ID string `json:"id"`
Name string `json:"name"`
Labels []string `json:"labels"`
}
// Features maps commonly required capabilities to the daemons they need running.
var Features = []Feature{
{ID: "push", Name: "Push notifications", Labels: []string{"com.apple.apsd"}},
// In-app purchase needs the store daemons plus AMS, which runs the
// payment sheet task, plus Wallet (passd) which hosts the sheet UI and
// Finance (financed) which passd requires — products load without them
// but purchases fail at sheet presentation.
{ID: "storekit", Name: "StoreKit / in-app purchase", Labels: []string{
"com.apple.storekitd",
"com.apple.itunesstored",
"com.apple.amsaccountsd",
"com.apple.amsengagementd",
"com.apple.amsondevicestoraged",
"com.apple.passd",
"com.apple.financed",
}},
{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"}},
// The stock Maps widget needs navd: without it the widget extension
// spins on its XPC connection and floods the unified log.
{ID: "widgets", Name: "Widgets & Live Activities", Labels: []string{"com.apple.chronod", "com.apple.liveactivitiesd", "com.apple.navd"}},
{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"}},
}
func featureByID(id string) (Feature, bool) {
for _, f := range Features {
if f.ID == id {
return f, true
}
}
return Feature{}, false
}
// resolveFeatures maps requested IDs to their Features, erroring on the first
// unknown one so a typo in --requires fails loudly instead of passing silently.
func ResolveFeatures(ids []string) ([]Feature, error) {
out := make([]Feature, 0, len(ids))
for _, id := range ids {
f, ok := featureByID(id)
if !ok {
return nil, fmt.Errorf("unknown feature %q (see `simslim doctor --list`)", id)
}
out = append(out, f)
}
return out, nil
}
// diagnoseFeatures reports, for each feature, which of its daemons the device
// currently has disabled. A feature is OK only when none of them are.
func DiagnoseFeatures(features []Feature, disabled map[string]bool) DoctorOutput {
statuses := make([]FeatureStatus, 0, len(features))
allOK := true
for _, f := range features {
var down []string
for _, l := range f.Labels {
if disabled[l] {
down = append(down, l)
}
}
ok := len(down) == 0
allOK = allOK && ok
statuses = append(statuses, FeatureStatus{ID: f.ID, Name: f.Name, OK: ok, Disabled: down})
}
return DoctorOutput{OK: allOK, Features: statuses}
}