-
Nouveautés dans Swift
Rejoignez-nous pour une mise à jour sur Swift. Nous parlerons des améliorations apportées au workflow qui vous rendront plus productif, ainsi que des nouvelles API de bibliothèque modernisées pour les tâches de programmation fondamentales. Nous montrerons des exemples d'adoption de Swift à travers d'autres couches de la pile logicielle. Enfin, nous explorerons de nouvelles fonctionnalités de langage visant à améliorer l'utilisation de la concurrence et à atteindre des performances optimales lorsque vous en avez besoin.
Chapitres
- 0:00 - Introduction et ordre du jour
- 0:48 - Mises à jour de swiftlang
- 3:06 - Workflow de développement : Écriture de code
- 4:40 - Workflow de développement : Création
- 7:36 - Workflow de développement : Débogage
- 9:14 - Bibliothèques : Sous-processus
- 10:45 - Bibliothèques : Foundation
- 12:31 - Bibliothèques : Observation
- 14:13 - Bibliothèques : Test
- 16:08 - Swift à tous les niveaux : Swift embarqué
- 18:00 - Swift à tous les niveaux : Sécurité
- 19:37 - Swift à tous les niveaux : Serveur
- 23:23 - Swift à tous les niveaux : Plates-formes
- 26:11 - Évolution du langage : Performances
- 30:28 - Évolution du langage : Concurrence
- 37:15 - Conclusion
Ressources
Vidéos connexes
WWDC25
- Adoptez la concurrence avec Swift
- Améliorer l’utilisation de la mémoire et les performances avec Swift
- Code-along : Améliorez une app avec la concurrence Swift
- Découvrez Containerization
- Découvrir l’interopérabilité entre Swift et Java
- Mixez en toute sécurité C, C++ et Swift
WWDC24
-
Rechercher dans cette vidéo…
-
-
9:44 - Subprocess: Call `run` with string
import Subprocess let result = try await run( .name("pwd") ) -
10:04 - Subprocess: Call `run` with file path
import Subprocess let swiftPath = FilePath("/usr/bin/swift") let result = try await run( .path(swiftPath), arguments: ["--version"] ) -
10:05 - Subprocess: Accessing standard output
import Subprocess let swiftPath = FilePath("/usr/bin/swift") let result = try await run( .path(swiftPath), arguments: ["--version"] ) let swiftVersion = result.standardOutput -
10:51 - NotificationCenter: Dynamic types
import UIKit @MainActor class KeyboardObserver { func registerObserver(screen: UIScreen) { let center = NotificationCenter.default let token = center.addObserver( forName: UIResponder.keyboardWillShowNotification, object: screen, queue: .main ) { notification in guard let userInfo = notification.userInfo else { return } let startFrame = userInfo[UIResponder.keyboardFrameBeginUserInfoKey] as? CGRect let endFrame = userInfo[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect guard let startFrame, let endFrame else { return } self.keyboardWillShow(startFrame: startFrame, endFrame: endFrame) } } func keyboardWillShow(startFrame: CGRect, endFrame: CGRect) {} } -
11:34 - NotificationCenter: Concrete types
import UIKit @MainActor class KeyboardObserver { func registerObserver(screen: UIScreen) { let center = NotificationCenter.default let token = center.addObserver( of: screen, for: .keyboardWillShow ) { keyboardState in let startFrame = keyboardState.startFrame let endFrame = keyboardState.endFrame self.keyboardWillShow(startFrame: startFrame, endFrame: endFrame) } } func keyboardWillShow(startFrame: CGRect, endFrame: CGRect) {} } -
12:01 - NotificationCenter: Conformances
extension UIResponder { public struct KeyboardWillShowMessage: NotificationCenter.MainActorMessage } extension HTTPCookieStorage { public struct CookiesChangedMessage: NotificationCenter.AsyncMessage } -
12:48 - Observation: The @Observable macro
import Observation enum Item { case none case banana case star } @Observable class Player { let name: String var score: Int = 0 var item: Item = .none init(name: String) { self.name = name } } -
12:58 - Observation: The Observations type
import Observation enum Item { case none case banana case star } @Observable class Player { let name: String var score: Int = 0 var item: Item = .none init(name: String) { self.name = name } } let player = Player(name: "Holly") let values = Observations { let score = "\(player.score) points" let item = switch player.item { case .none: "no item" case .banana: "a banana" case .star: "a star" } return "\(score) and \(item)" } -
13:56 - Observation: Transactional updates
import Observation enum Item { case none case banana case star } @Observable class Player { let name: String var score: Int = 0 var item: Item = .none init(name: String) { self.name = name } } let player = Player(name: "Holly") let values = Observations { let score = "\(player.score) points" let item = switch player.item { case .none: "no item" case .banana: "a banana" case .star: "a star" } return "\(score) and \(item)" } player.score += 2 player.item = .banana -
14:05 - Observation: AsyncSequence
import Observation enum Item { case none case banana case star } @Observable class Player { let name: String var score: Int = 0 var item: Item = .none init(name: String) { self.name = name } } let player = Player(name: "Holly") let values = Observations { let score = "\(player.score) points" let item = switch player.item { case .none: "no item" case .banana: "a banana" case .star: "a star" } return "\(score) and \(item)" } player.score += 2 player.item = .banana for await value in values { print(value) } -
14:17 - Swift Testing
import Testing import Foundation import EvolutionMetadataModel @Test func validateProposalID() async throws { let (data, _) = try await URLSession.shared.data(from: evolutionJSONMetadataURL) let jsonDecoder = JSONDecoder() let metadata = try jsonDecoder.decode(EvolutionMetadata.self, from: data) for proposal in metadata.proposals { #expect(proposal.id.starts(with: "SE")) } } -
14:54 - Swift Testing: Attachments
import Testing import Foundation import EvolutionMetadataModel @Test func validateProposalID() async throws { let (data, _) = try await URLSession.shared.data(from: evolutionJSONMetadataURL) Attachment.record(data, named: "evolution-metadata.json") let jsonDecoder = JSONDecoder() let metadata = try jsonDecoder.decode(EvolutionMetadata.self, from: data) for proposal in metadata.proposals { #expect(proposal.id.starts(with: "SE")) } } -
15:23 - Exit Tests: Preconditions
extension Proposal { public var number: Int { let components = id.split(separator: "-") precondition( components.count == 2 && components[1].allSatisfy(\.isNumber), "Invalid proposal ID format \(id); expected SE-<Number>" ) return Int(components[1])! } } -
15:34 - Exit Tests: processExitsWith argument
import Testing import EvolutionMetadataModel @Test func invalidProposalPrefix() async throws { await #expect(processExitsWith: .failure) { let proposal = Proposal(id: "SE-NNNN") _ = proposal.number } } -
31:06 - Concurrency: Async function error message
class PhotoProcessor { func extractSticker(data: Data, with id: String?) async -> Sticker? { } } @MainActor final class StickerModel { let photoProcessor = PhotoProcessor() func extractSticker(_ item: PhotosPickerItem) async throws -> Sticker? { guard let data = try await item.loadTransferable(type: Data.self) else { return nil } return await photoProcessor.extractSticker(data: data, with: item.itemIdentifier) } } -
32:06 - Concurrency: Run async functions on the caller's actor
// Run async functions on the caller's actor class PhotoProcessor { func extractSticker(data: Data, with id: String?) async -> Sticker? {} } @MainActor final class StickerModel { let photoProcessor = PhotoProcessor() func extractSticker(_ item: PhotosPickerItem) async throws -> Sticker? { guard let data = try await item.loadTransferable(type: Data.self) else { return nil } return await photoProcessor.extractSticker(data: data, with: item.itemIdentifier) } } -
32:36 - Concurrency: Conformance error
protocol Exportable { func export() } extension StickerModel: Exportable { // error: Conformance of 'StickerModel' to protocol 'Exportable' crosses into main actor-isolated code and can cause data races func export() { photoProcessor.exportAsPNG() } } -
33:04 - Concurrency: Isolated conformances
// Isolated conformances protocol Exportable { func export() } extension StickerModel: @MainActor Exportable { func export() { photoProcessor.exportAsPNG() } } -
33:20 - Concurrency: Isolated conformance use
// Isolated conformances @MainActor struct ImageExporter { var items: [any Exportable] mutating func add(_ item: StickerModel) { items.append(item) } func exportAll() { for item in items { item.export() } } } -
33:31 - Concurrency: Isolated conformance error
// Isolated conformances nonisolated struct ImageExporter { var items: [any Exportable] mutating func add(_ item: StickerModel) { items.append(item) // error: Main actor-isolated conformance of 'StickerModel' to 'Exportable' cannot be used in nonisolated context } func exportAll() { for item in items { item.export() } } } -
33:51 - Concurrency: Unsafe static variable
final class StickerLibrary { static let shared: StickerLibrary = .init() // error: Static property 'shared' is not concurrency-safe because non-'Sendable' type 'StickerLibrary' may have shared mutable state } -
34:01 - Concurrency: Protecting static variables
final class StickerLibrary { @MainActor static let shared: StickerLibrary = .init() } -
34:05 - Concurrency: Protecting classes
@MainActor final class StickerLibrary { static let shared: StickerLibrary = .init() } -
34:15 - Concurrency: A single-threaded program
@MainActor final class StickerLibrary { static let shared: StickerLibrary = .init() } @MainActor final class StickerModel { let photoProcessor: PhotoProcessor var selection: [PhotosPickerItem] } extension StickerModel: @MainActor Exportable { func export() { photoProcessor.exportAsPNG() } } -
34:22 - Concurrency: Mode to infer main actor by default
// Mode to infer main actor by default final class StickerLibrary { static let shared: StickerLibrary = .init() } final class StickerModel { let photoProcessor: PhotoProcessor var selection: [PhotosPickerItem] } extension StickerModel: Exportable { func export() { photoProcessor.exportAsPNG() } } -
35:06 - Concurrency: Explicitly offloading async work
// Explicitly offloading async work class PhotoProcessor { var cachedStickers: [String: Sticker] func extractSticker(data: Data, with id: String) async -> Sticker { if let sticker = cachedStickers[id] { return sticker } let sticker = await Self.extractSubject(from: data) cachedStickers[id] = sticker return sticker } @concurrent static func extractSubject(from data: Data) async -> Sticker {} }
-
-
- 0:00 - Introduction et ordre du jour
Découvrez les nouveautés de Swift 6.2. Parmi les points forts : améliorations des processus, nouvelles API de bibliothèque, adoption étendue de la pile logicielle et fonctionnalités de langage améliorées pour la gestion de la simultanéité pour un codage plus productif.
- 0:48 - Mises à jour de swiftlang
L’organisation swiftlang sur GitHub s’est considérablement développée, englobant plus de 50 projets, dont le compilateur Swift, le système de build de Xcode Swift Build et le site web Swift.org. Swift Build, désormais en open source, prend en charge le processus de compilation pour les systèmes d’exploitation Apple et est utilisé par Swift Package Manager. Un nouveau gestionnaire de versions, Swiftly, développé à l’origine pour Linux, prend désormais en charge macOS et simplifie la gestion de la chaîne d’outils Swift. La page d’accueil Swift.org a un nouveau look pour vous aider dans différents domaines.
- 3:06 - Workflow de développement : Écriture de code
La dernière version Swift améliore les flux de développement sur toutes les plateformes, avec des améliorations spécifiques pour les utilisateurs de l’extension Swift dans VS Code, notamment une validation officielle et une distribution par Swift.org. Désormais, l’indexation en arrière-plan permet des fonctionnalités en temps réel dans l’éditeur, la saisie semi-automatique du code est améliorée, le débogage est simplifié grâce à la prise en charge automatique de LLDB, et un nouveau panneau de projet ainsi que des aperçus en direct de DocC sont disponibles.
- 4:40 - Workflow de développement : Création
Swift 6.2 introduit plusieurs améliorations pour renforcer votre productivité. Les temps de compilation à neuf pour les projets utilisant des API basées sur des macros sont améliorés, car la bibliothèque swift-syntax n’a plus besoin d’être reconstruite lors des builds à neuf. Cette optimisation, prise en charge par Swift PM et Xcode, peut réduire les temps de compilation de plusieurs minutes. Cette version améliore aussi la documentation des diagnostics du compilateur en fournissant des explications plus détaillées sur les avertissements et erreurs courants, ce qui les rend plus faciles à comprendre et à corriger. Vous disposez désormais d’un contrôle accru sur les avertissements du compilateur : vous pouvez spécifier ceux à traiter comme des erreurs, une fonctionnalité issue des retours de la communauté.
- 7:36 - Workflow de développement : Débogage
Le débogage est amélioré dans Swift 6.2, en particulier pour le code asynchrone, avec des fonctionnalités LLDB améliorées, la possibilité de nommer les tâches et à une visibilité accrue dans les profils Instruments. Les temps de réponse du débogueur sont également plus rapides, grâce aux modules explicitement compilés activés par défaut dans Xcode 26.
- 9:14 - Bibliothèques : Sous-processus
Cette version introduit le nouveau package Subprocess, avec lequel vous pouvez lancer et gérer des sous-processus directement à partir du code Swift. Le package vous donne un contrôle précis sur l’exécution des processus, vous permettant de spécifier le chemin de l’exécutable. Lorsque le sous-processus se termine, vous pouvez inspecter l’état de sortie, la sortie standard et d’autres infos sur l’exécution du processus, la version 0.1 étant désormais disponible et ouverte aux commentaires.
- 10:45 - Bibliothèques : Foundation
Le groupe de travail Foundation a amélioré le développement d’apps iOS en introduisant des types concrets pour les noms et les charges utiles des notifications, ce qui simplifie le code, réduit les erreurs et renforce la sécurité des threads, pour les notifications du framework comme pour les notifications personnalisées. Vous pouvez également définir des types de notification concrets pour vos propres notifications.
- 12:31 - Bibliothèques : Observation
La bibliothèque Observation de Swift utilise le modèle d’observateur pour suivre les changements d’état dans un graphe d’objets. La macro Observable permet d’activer le suivi des observations. Swift 6.2 introduit le type Observations, qui permet de créer une AsyncSequence diffusant les changements d’état à partir d’une fermeture calculant une valeur à partir de propriétés observables. Les mises à jour sont transactionnelles, garantissant un état cohérent, et peuvent être parcourues à l’aide d’une boucle for await.
- 14:13 - Bibliothèques : Test
Avec Swift Testing, une bibliothèque multiplateforme, vous pouvez écrire et organiser vos tests à l’aide de macros. Swift 6.2 renforce cette capacité avec des pièces jointes personnalisées pour un meilleur diagnostic des pannes, notamment en environnement distant, et des tests de sortie pour valider le code qui se termine dans des conditions spécifiques. Ces fonctionnalités permettent de tester du code Swift portable, au-delà du simple code de votre application.
- 16:08 - Swift à tous les niveaux : Swift embarqué
Swift 6.2 améliore Embedded Swift, en vous permettant d’écrire du code pour des appareils embarqués, des serveurs et des composants critiques en matière de sécurité. Il inclut désormais les API complètes pour les chaînes de caractères, les types any de Swift pour les protocoles contraints aux classes, ainsi que de nouvelles API comme InlineArray et Span pour travailler efficacement avec les régions de mémoire. Apple utilise Embedded Swift dans certains des logiciels de bas niveau sur l’iPhone, et la communauté propose des exemples disponibles sur GitHub, dans le dépôt swift-embedded-examples.
- 18:00 - Swift à tous les niveaux : Sécurité
Swift 6.2 introduit le mode strict-memory-safety, activable manuellement, qui exige une validation explicite des API non sûres et aide à repérer le code sensible sur le plan de la sécurité. Apple adopte ce mode dans WebKit et dans un sous-système de l’app Messages, tous deux exposés à des données non fiables.
- 19:37 - Swift à tous les niveaux : Serveur
Swift est largement utilisé dans l’écosystème des serveurs, en particulier chez Apple, où il alimente les services backend traitant des millions de requêtes par seconde. Un exemple notable est un service d’alerte par mot de passe, auparavant développé en Java, qui a connu une augmentation de 40 % du débit et une réduction de 50 % des besoins matériels après avoir été réécrit en Swift. D’autres entreprises, comme Cultured Code, ont également tiré des bénéfices de l’adoption de Swift. Le backend de Things Cloud, réécrit en Swift, a permis de diviser par 3 les coûts de calcul et d’améliorer les temps de réponse moyens de 400 %. L’écosystème de packages de Swift, son interopérabilité avec C, Objective-C et C++, ainsi que les nouveaux projets open source comme Swift-Java et une bibliothèque de conteneurisation, permettent aux développeurs de créer des serveurs backends efficaces et performants, et d’intégrer de manière transparente Swift aux bases de code existantes, en particulier en Java.
- 23:23 - Swift à tous les niveaux : Plates-formes
Swift 6.2 prend officiellement en charge FreeBSD et WebAssembly (Wasm), ce qui permet de créer des applications client et serveur pour les navigateurs et d’autres environnements d’exécution. Cette prise en charge, née d’un projet communautaire, permet de compiler du code Swift et de l’exécuter dans le navigateur, comme le montre une app de rendu 3D utilisant WebGPU et JavaScriptKit. Grâce à sa sécurité, sa simplicité d’usage et ses performances, Swift s’impose comme un choix attractif dans toute la pile logicielle.
- 26:11 - Évolution du langage : Performances
La nouvelle version introduit également 2 nouveaux types, InlineArray et Span, pour améliorer les performances du code critique. InlineArray est un tableau de taille fixe qui stocke directement ses éléments, supprimant ainsi le recours à une allocation sur le tas. Cela améliore les performances, notamment dans les chemins critiques, et permet d’autres optimisations, comme l’élimination de la vérification des limites. Le système définit la taille d’un InlineArray comme faisant partie de son type, et peut la déduire à partir de littéraux de tableau. Span offre un accès rapide et direct à une mémoire contiguë, sans compromettre la sécurité mémoire. Il permet aux fonctions d’opérer efficacement et en toute sécurité sur le stockage sous-jacent de différents types de conteneurs, comme Array et InlineArray. Grâce à des vérifications effectuées à la compilation, Span garantit la validité de la mémoire et évite les problèmes courants inhérents aux pointeurs.
- 30:28 - Évolution du langage : Concurrence
La simultanéité est un aspect complexe de la programmation, en raison du risque de conflits d’accès aux données lorsque plusieurs tâches partagent la mémoire. Swift 6 a introduit la sécurité contre les conflits d’accès dès la compilation, mais cela entraînait souvent des erreurs du compilateur, car le langage déléguait implicitement certaines opérations en arrière-plan, même lorsque l’exécution parallèle n’était pas nécessaire. Swift 6.2 corrige ce problème en adoptant une nouvelle approche par défaut : le code reste monothread jusqu’à ce que vous choisissiez d’introduire la simultanéité. Cette approche permet, par défaut, d’écrire du code naturellement exempt de conflits d’accès aux données. Le langage permet désormais aux fonctions async non liées à un acteur spécifique de se poursuivre dans le même acteur que celui depuis lequel elles ont été appelées, éliminant ainsi les conflits d’accès aux données. Cette version introduit également des conformités isolées, permettant aux types d’acteurs principaux de se conformer aux protocoles tout en garantissant que le compilateur protège l’état de l’acteur principal. De plus, il existe désormais un mode opt-in pour déduire l’acteur principal par défaut, réduisant ainsi les annotations de simultanéité dans le code monothread. Si vous avez besoin de simultanéité pour améliorer les performances (pour décharger les tâches gourmandes en ressources CPU en arrière-plan), Swift 6.2 fournit des outils pour le faire en toute sécurité. L’attribut concurrent garantit que certaines fonctions s’exécutent dans le pool de threads concurrents, libérant ainsi l’acteur actuel pour exécuter d’autres tâches en parallèle. Ces changements contribuent à rendre la simultanéité plus accessible et plus facile à mettre en œuvre dans les projets Swift.
- 37:15 - Conclusion
Les retours des utilisateurs ont permis d’améliorer la gestion de la simultanéité dans Swift, la rendant plus intuitive et conviviale. La communauté est invitée à participer aux forums de Swift.org pour contribuer au développement du langage et de son écosystème, partager ses projets et rester informée des évènements liés à Swift.