# macOS

## Vad det är

macOS är operativsystemet som Apple levererar med varje Mac. Det användaren ser är skrivbordet,
Finder och de medföljande apparna; under dem ligger ett Unix-system med terminal, skal och de
kommandoradsverktyg som en utvecklare känner igen från andra system. Apple bygger systemet för sin
egen hårdvara och säljer de två tillsammans, så den som väljer macOS väljer i praktiken också en
Mac.

Grunden heter Darwin, och Apple publicerar dess källkod. Dess systemkärna, XNU, är en hybrid:
Mach-kärnan från Carnegie Mellon University kombinerad med komponenter från FreeBSD och IOKit, ett
ramverk för drivrutiner skrivet i C++. Namnet utläses ”X is Not Unix”. Stora delar av användarlandet
kommer också från FreeBSD, och manualsidorna för `sed` och `date` på en Mac beskriver
FreeBSD-versionerna av de verktygen. Sedan macOS 10.15 Catalina är zsh standardskalet.

Under systemet har hårdvaran bytts ut på senare år. I slutet av 2020 började Apple flytta Mac från
processorer från Intel till egna chip, Apple silicon, och i dag har varje Mac-serie, från MacBook
Air till Mac Pro, modeller med Apple silicon. De chippen är arm64, och det får betydelse i samma
stund som programvara som byggts på en Mac ska köras på en x86-64-server.

På bärbara och stationära datorer ställs macOS mot Windows och Linux.

## Varför det spelar roll

För ett företag som skriver programvara har macOS två roller som lätt flyter ihop. Det är maskinen
som många utvecklare arbetar på, och det är en plattform som kunder kör programvara på.

Som arbetsstation skiljer sig en Mac från Linux-servrarna den driftsätter till på tre punkter.
Kommandoradsverktygen kommer från FreeBSD och inte från GNU, så samma flagga kan betyda olika saker
i ett skript. Processorn är arm64 där servern kanske är x86-64. Och det finns ingen Linux-kärna,
medan containrar delar systemkärna med sin värddator, så containerverktyg på en Mac startar först en
virtuell Linux-maskin. Apple har ett eget verktyg, `container`, som kräver Apple silicon och macOS
26 och ger varje container en egen lätt virtuell maskin. Detta utesluter ändå inte en Mac. Det
betyder att man testar på systemet man driftsätter till, i stället för att lita på en grön körning
på en bärbar dator.

Som plattform väcker macOS frågan om hur man bygger för den. Ett plattformsoberoende skal, alltså en
kodbas med en inbäddad webbläsarmotor som levereras till Windows, Linux och Mac, håller flera system
i takt till låg kostnad. På en Mac bär det dock med sig en körmiljö för en webbläsare som en native
applikation inte har någon användning för, och det ritar egna kontroller i stället för systemets. En
native applikation i Swift och SwiftUI använder systemets kontroller och material och följer
användarens inställningar för utseende, accentfärg, kontrast, textstorlek och minskad rörelse utan
extra kod. Priset är en separat klient och personer som kan plattformen. Native lönar sig när
kunderna sitter vid en Mac.

Att leverera till Mac innebär också att passera en grind. Programvara som distribueras utanför Mac
App Store signeras med ett Developer ID-certifikat och skickas till notarietjänsten hos Apple, en
automatisk granskning som letar efter skadligt innehåll och problem med kodsigneringen och som är
skild från App Review. Sedan macOS 10.15 måste all programvara som byggts efter den 1 juni 2019 och
distribueras med Developer ID vara notariserad. Apple skickar tillbaka en biljett (”ticket”) som
häftas vid programvaran och publicerar samma biljett på nätet, där Gatekeeper letar efter den när
användaren öppnar appen första gången. Certifikatet ingår i Apple Developer Program, som kostar 99
dollar om året. Räkna in medlemskapet och signeringssteget redan inför den första releasen.

## Så ser det ut i praktiken

En meddelandeprodukt hade på macOS en skrivbordsupplevelse som byggde på ett plattformsoberoende
skal och som varken såg ut eller betedde sig som en Mac-app. Produktens vältestade kärna i Rust
exponerar ett C-gränssnitt, och Swift kan anropa C, så
[en native klient i Swift och SwiftUI](/sv/portfolio/built-a-native-macos-messaging-client-in-swift-6-154/)
byggdes direkt på det gränssnittet. Rust-kärnan länkas in som ett statiskt arkiv från en låst
uppströmsrevision, vilket håller bygget reproducerbart, och en tunn wrapper äger varje pekare som
korsar gränsen mellan språken. Klienten startar som en Mac-app, använder plattformens egna material
och kontroller och bär inte på någon inbäddad webbläsare. Sidan säger rakt ut vad som återstår: det
finns ingen notariserad distribution och ingen uppdateringskanal, så det är en fungerande app
snarare än en produkt som andra kör. Notariseringen ovan är steget mellan de två.

Plattformens utseende kan upprätthållas i stället för att imiteras.
[Ett designsystem med 48 namngivna tokens](/sv/portfolio/built-a-48-token-design-system-160/) i sex
grupper bygger färg och material på plattformens semantiska färger och glasmaterial i stället för
fasta värden. Därför följer gränssnittet systemets utseende, accentfärgen och
kontrastinställningarna utan någon kod som bevakar dem, och textstilarna skalas med användarens
storleksinställning. En linter avvisar ett rått tal där en token hör hemma, en hårdkodad
teckenstorlek och en animation som struntar i inställningen för minskad rörelse. Sidan anger också
gränsen: tokens gör ett gränssnitt konsekvent, och det är inte detsamma som att göra det bra.

Hur macOS hanterar minne syntes tydligt i
[arbetet med en macOS-app](/sv/portfolio/stopped-an-application-filling-memory-at-41mb-a-second-155/)
som växte med ungefär 41 MB i sekunden innan systemet till slut avslutade processen. Maskinen hade
36 GB fysiskt minne, men i den registrerade incidenten låg 111 GB minnessidor i komprimerad form.
Komprimerat minne och swap lät processen växa långt förbi de fysiska 36 GB, och när den till slut
dödades hade resten av skrivbordet redan slutat svara. Tre orsaker multiplicerade varandra: en
obegränsad händelseström, att varje prenumerant fick varje händelse och en loggning utan budget.
Appen fick begränsade buffertar, lyssnare som bara väcks för de händelsetyper de vill ha och en
budget för loggtakten som slår ihop upprepningar, och ett regressionstest håller minnestaket.
Sessionen som skrev 610 996 loggrader skriver nu 1 411, och sidan redovisar avvägningen: ett snabbt
fel lämnar numera medvetet ofullständiga spår i loggen.

## Var det går fel

**Aktivitetskontroll visar nästan inget ledigt minne på en Mac som går alldeles utmärkt.**
Programmet heter Activity Monitor på engelska. macOS cachar nyligen använda filer i minne som annars
skulle stå oanvänt, och när minnet fylls komprimeras inaktiva appar för att ge plats åt de aktiva.
Att använt minne ligger nära det fysiska och att det komprimerade minnet är stort är alltså normalt,
och enligt Apple gör ledigt minne inte nödvändigtvis en Mac snabbare. Läs i stället diagrammet
Minnestryck: grönt betyder att RAM-minnet används effektivt, gult att datorn så småningom kan behöva
mer och rött att den behöver mer. Swap är den tydligaste signalen:

```sh
sysctl vm.swapusage
vm_stat | grep -E '^Swap(ins|outs)'
```

Räknarna i `vm_stat` räknar från uppstart, så jämför två avläsningar med en minuts mellanrum.
Swapouts som fortsätter att stiga betyder att arbetet har vuxit ur RAM-minnet.

**Ett skript som fungerar på en Mac stannar med ett märkligt fel på Linux-servern, eller tvärtom.**
Den FreeBSD-härledda `sed` på en Mac tolkar ordet efter `-i` som ändelsen för en reservkopia, så
GNU-vanan `sed -i 's/a/b/' file` tar uttrycket som ändelse och filnamnet som skript. Mac-vanan
`sed -i '' …` får i sin tur GNU-versionen av `sed` att läsa uttrycket som ett filnamn. En ändelse
som sitter ihop med flaggan fungerar på båda:

```sh
sed -i.bak 's/^port=80$/port=8080/' app.conf && rm app.conf.bak
```

Den varaktiga lösningen är att köra skripten i CI på samma system som de möter i produktion.

**Gatekeeper stoppar ett bygge som delats utanför teamet när mottagaren öppnar det.** Appen
signerades för utveckling, eller signerades men notariserades aldrig. Signera den med ett Developer
ID-certifikat, med härdad körmiljö (”hardened runtime”) påslagen och en säker tidsstämpel, skicka
den till notarietjänsten och häfta biljetten vid appen så att Gatekeeper hittar den även utan
nätverk. Ett ZIP-arkiv kan notariseras men kan inte bära en biljett, så det är appen inuti som får
den:

```sh
ditto -c -k --keepParent App.app App.zip
xcrun notarytool submit App.zip --keychain-profile notary --wait
xcrun stapler staple App.app
```

**En container-image går på utvecklarens Mac och dör på servern med `exec format error`.** Docker
bygger som standard för plattformen där BuildKit-daemonen kör, och på en Mac med Apple silicon kör
den daemonen i en virtuell arm64-maskin. En container delar värdens systemkärna, så utan emulering
kör en x86-64-värd bara x86-64-kod. Kontrollera servern med `uname -m` och bygg sedan för den med
`docker build --platform linux/amd64`, eller bygg i CI på samma processortyp som servern.

## Läs mer

- [Mac User Guide](https://support.apple.com/guide/mac-help/welcome/mac) — handboken för macOS från
  Apple själva, uppdaterad för varje aktuell version.
- [Check if your Mac needs more RAM](https://support.apple.com/guide/activity-monitor/check-if-your-mac-needs-more-ram-actmntr34865/mac)
  — diagrammet Minnestryck och vad dess tre färger betyder.
- [Notarizing macOS software before distribution](https://developer.apple.com/documentation/security/notarizing-macos-software-before-distribution)
  — Developer ID, den härdade körmiljön och hur Gatekeeper använder biljetten.
- [Multi-platform builds](https://docs.docker.com/build/building/multi-platform/) — varför värdar
  med arm64 och amd64 behöver olika images, och hur ett enda bygge tar fram båda.
- [container](https://github.com/apple/container) — verktyget från Apple som kör Linux-containrar på
  en Mac, var och en i en egen lätt virtuell maskin.
- [XNU](https://github.com/apple-oss-distributions/xnu) — källkoden till systemkärnan i macOS, med
  en kort redogörelse för hur Mach, FreeBSD och IOKit hänger ihop.

<https://engineer.company/sv/glossary/macos/>
