Zum Inhalt springen

Meine App fuer Swift 6 vorbereiten

Wie du den Swift-6-Modus fuer deine Xcode-Projekte und SwiftPM-Module schon heute aktivieren kannst. Und wie die Migrationserfahrung so ist.

Meine App fuer Swift 6 vorbereiten

Was ist der “Swift-6-Modus”?

Swift 6 wird nicht mehr 2023 veroeffentlicht, das wurde von Doug Gregor aus der Swift Language Workgroup klargestellt. Aber wusstest du, dass Apple bereits Teile von Swift 6 in Version 5.8 ausgeliefert hat? Ja, das stimmt. Einige Teile von Swift, die mit Xcode 14.3 vor ein paar Wochen ausgeliefert wurden, sind standardmaessig deaktiviert. Sie werden eingeschaltet, sobald Swift 6 erscheint, was noch ein Jahr dauern koennte – oder sogar laenger.

Diese Features fuehren einige Breaking Changes in Swift ein, zum Beispiel durch Umbenennung bekannter APIs, Anpassung ihres Verhaltens oder neue Sicherheitspruefungen im Compiler. Aber sie werden alle irgendwann aktiviert, um unsere Codebasen zu verbessern. Und wir werden unsere Projekte entsprechend anpassen muessen.

Ich dachte mir, es waere eine gute Idee, alle Features in meinen Projekten zu aktivieren, um zu sehen, ob es Breaking Changes fuer meine Codebasis gibt, die ich kennen sollte. Und natuerlich koennte ich auch einige neue Features nutzen, wie BareSlashRegexLiterals, das die /.../-Regex-Literal-Syntax fuer kompakte Regex-Initialisierung verfuegbar macht.

Gluecklicherweise gibt es mit Swift 5.8 jetzt einen einheitlichen Weg, diese Optionen zu aktivieren: Wir muessen einfach -enable-upcoming-feature an Swift uebergeben, indem wir es in den OTHER_SWIFT_FLAGS in den Build Settings unseres Projekts in Xcode angeben. Aber wir muessen auch wissen, welche Features verfuegbar sind, und ich konnte keinen Ort mit einer guten Uebersicht finden (noch nicht). Der Vorschlag, der diese einheitliche Option eingefuehrt hat, enthaelt eine solche Liste, aber sie wird nicht mit neueren Optionen aktualisiert, die spaeter hinzugefuegt werden, wie die von SE-0384. Wo koennen wir also derzeit zuverlaessig eine Liste aller unterstuetzten Optionen bekommen?

UPDATE: Du kannst jetzt direkt auf Swift.org nach Upcoming Flags filtern.

Swift ist Open Source, also scheint der zuverlaessigste Ort das Swift-GitHub-Repository zu sein! Es enthaelt eine Features.def-Datei, die Eintraege (Link zum release/5.8-Branch) namens UPCOMING_FEATURE beinhaltet, einschliesslich der zugehoerigen Swift-Evolution-Vorschlagsnummer und der Swift-Version, in der sie aktiviert werden:

UPCOMING_FEATURE(ConciseMagicFile, 274, 6)
UPCOMING_FEATURE(ForwardTrailingClosures, 286, 6)
UPCOMING_FEATURE(BareSlashRegexLiterals, 354, 6)
UPCOMING_FEATURE(ExistentialAny, 335, 6)

Hier sind die Optionen mit einer kurzen Beschreibung, was sie tun:

Aus irgendeinem Grund scheinen zwei Optionen dort nicht aufgefuehrt zu sein (ich untersuche es):

Weitere Optionen werden mit spaeteren Versionen ausgeliefert, z. B. ImportObjcForwardDeclarations.

Um meinen Code zusaetzlich auf korrekte Concurrency-Unterstuetzung zu pruefen, habe ich mich entschieden, auch -warn-concurrency (das sollte eigentlich dasselbe wie StrictConcurrency sein, falls das tatsaechlich funktioniert) und -enable-actor-data-race-checks zu uebergeben.

Mein Projekt migrieren

Wenn du wie ich alle 5.8-Optionen in deinem Projekt aktivieren moechtest, kopiere (Cmd+C) den folgenden Textblock, gehe dann zum Tab “Build Settings” deines Xcode-Projekts, suche nach “Other Swift Flags”, waehle diese Option im Editor aus und fuege sie ein (Cmd+V):

//:configuration = Debug
OTHER_SWIFT_FLAGS = -enable-upcoming-feature BareSlashRegexLiterals -enable-upcoming-feature ConciseMagicFile -enable-upcoming-feature ExistentialAny -enable-upcoming-feature ForwardTrailingClosures -enable-upcoming-feature ImplicitOpenExistentials -enable-upcoming-feature StrictConcurrency -warn-concurrency -enable-actor-data-race-checks

//:configuration = Release
OTHER_SWIFT_FLAGS = -enable-upcoming-feature BareSlashRegexLiterals -enable-upcoming-feature ConciseMagicFile -enable-upcoming-feature ExistentialAny -enable-upcoming-feature ForwardTrailingClosures -enable-upcoming-feature ImplicitOpenExistentials -enable-upcoming-feature StrictConcurrency -warn-concurrency -enable-actor-data-race-checks

//:completeSettings = some
OTHER_SWIFT_FLAGS

.enableUpcomingFeature is only available from Swift 5.8

Wenn du wie ich eine mit SwiftPM modularisierte App verwendest oder an einem Swift-Package arbeitest, musst du zusaetzlich ein Array von .enableUpcomingFeatures an jedes Target ueber swiftSettings uebergeben:

let swiftSettings: [SwiftSetting] = [
   .enableUpcomingFeature("BareSlashRegexLiterals"),
   .enableUpcomingFeature("ConciseMagicFile"),
   .enableUpcomingFeature("ExistentialAny"),
   .enableUpcomingFeature("ForwardTrailingClosures"),
   .enableUpcomingFeature("ImplicitOpenExistentials"),
   .enableUpcomingFeature("StrictConcurrency"),
   .unsafeFlags(["-warn-concurrency", "-enable-actor-data-race-checks"]),
]

let package = Package(
   // ...
   targets: [
      // ...
      .target(
         name: "MyTarget",
         dependencies: [/* ... */],
         swiftSettings: swiftSettings
      ),
      // ...
   ]

Vergiss nicht, deine Tools-Version am Anfang der Datei auf 5.8 zu aktualisieren:

// swift-tools-version:5.8

.enableUpcomingFeature ist erst ab Swift 5.8 verfuegbar

Das liegt daran, dass Swift die Optionen, die du fuer dein Projekt-Target angegeben hast, nicht automatisch an importierte Module weitergibt. Und das sind gute Neuigkeiten, denn so muessen Swift-Packages, die du moeglicherweise in dein Projekt einbindest, nicht angepasst werden, und du kannst diese Features trotzdem fuer deinen eigenen App-Code nutzen. Und umgekehrt: Package-Autoren koennen diese Features fuer ihre Projekte aktivieren, ohne den Code in Projekten zu beeinflussen, in die sie eingebunden werden.

Nachdem ich alles aktiviert und gebaut hatte, stiess ich auf vier Arten von Problemen:

  1. Ich musste an mehreren Stellen das any-Keyword hinzufuegen – Xcode half mit einem Fix-It:

Migrating my project

Migrating my project 2

  1. Aus irgendeinem Grund erhielt ich viele Fehler bei Views mit einem .sheet-Modifier. Die Fehlerpaare besagten: “Generic parameter ‘Content’ could not be inferred” und “Missing argument for parameter ‘content’ in call”. Diese Meldungen waren aber nicht sehr hilfreich, also versuchte ich zuerst, den .sheet-Inhalt durch eine einfache Text-View zu ersetzen – aber das half nicht. Also habe ich die Optionen einzeln deaktiviert und die Fehler verschwanden, als ich ForwardTrailingClosures ausschaltete – also liess ich es deaktiviert. Ich hoffe, dass zukuenftige Swift-Versionen eine bessere Fehlermeldung erzeugen, um die Fehler spaeter zu beheben. Es eilt nicht. Ich kann es mit Swift 5.9 erneut versuchen. Ich hatte jetzt keine Zeit zum Untersuchen.

Migrating my project 3

  1. Ich musste einige meiner Funktionen, die einen TCA-WithViewStore zurueckgeben, mit dem @MainActor-Attribut markieren – aber auch hier half Xcode mit Fix-Its.

Migrating my project 4

  1. An vielen Stellen wurden Warnungen angezeigt: “Non-sendable type ‘…’ passed in call to main actor-isolated function cannot cross actor boundary”. Also habe ich diese Typen dem Sendable-Protokoll konform gemacht (mehr ueber Sendable hier).

Alles andere schien fuer meine App RemafoX mit ~35.000 Zeilen Swift-Code problemlos zu bauen. Der gesamte Prozess hat mich weniger als 3 Stunden gekostet.

Mein Projekt ist jetzt bereit fuer die Zukunft von Swift und ich kann das neue Regex-Literal-Feature nutzen (let regex = /.*@.*/). Ausserdem kann ich keinen neuen Code einfuehren, den ich spaeter zu Swift 6 migrieren muesste, da ich sofort Fehler erhalte.

Wie sieht es bei deinem Projekt aus? Welche Upcoming Features moechtest du migrieren?

Hat dir dieser Beitrag gefallen? Folge mir auf Bluesky und Mastodon für mehr Swift-Tipps und Indie-Dev-Updates.