
-
Exploitez la concurrence structurée avec le framework Network
Le framework Network est le meilleur moyen d'établir des connexions réseau de bas niveau sur les plates-formes Apple – et dans iOS, iPadOS et macOS 26, il s'intègre parfaitement à votre code de concurrence structurée. Nous verrons comment vous pouvez établir des connexions, envoyer et recevoir des données et des messages structurés, écouter les connexions entrantes et parcourir le réseau à la recherche de services. Nous aborderons également les bonnes pratiques essentielles tout au long du processus.
Chapitres
- 0:00 - Bienvenue
- 0:45 - Établir des connexions
- 7:22 - Envoyer et recevoir
- 14:22 - Accepter les connexions entrantes
- 16:05 - Rechercher d’autres appareils
Ressources
Vidéos connexes
WWDC25
WWDC18
-
Rechercher dans cette vidéo…
Bonjour. Je m’appelle Scott et je suis ravi de partager avec vous quelques améliorations qui rendront votre code réseau plus sympa et facile à écrire que jamais. Si vous n’avez jamais écrit une ligne de code réseau, vous êtes au bon endroit. Et pour les plus expérimentés, j’ai de nouvelles API utiles à partager avec vous. J’expliquerai d’abord comment créer des connexions. Puis, je montrerai comment les utiliser pour envoyer et recevoir des données.
Je poursuivrai avec l’écoute des connexions entrantes. Enfin, j’aborderai comment trouver d’autres appareils sur le réseau et s’y connecter. Commençons par la création de connexions. Le framework Network permet d’établir des connexions sécurisées, modulables et modernes dans une app. Finis les sockets, les sockaddrs, les ioctls difficiles à retenir et les opérations bloquantes ! Le framework Network inclut la connexion par nom. Il s’occupe de la résolution des noms pour vous. Une fois qu’il résout ce nom, un mécanisme appelé Happy Eyeballs garantit qu’il choisit efficacement la meilleure adresse résolue pour la connexion dynamique.
Comme la sécurité via TLS est intégrée, vous n’avez pas à intégrer d’autres bibliothèques, souvent avec un type d’API différent, dans votre app juste pour protéger la vie privée des utilisateurs. Grâce à des transitions et proxys d’interface réseau, il permet des fonctionnalités comme l’assistance Wi-Fi et les protocoles multipath sans intervention de votre part.
Il accepte les transports modernes comme QUIC et vous permet même de combiner vos propres protocoles avec les protocoles intégrés. Vous vous concentrez ainsi sur la logique métier et l’expérience utilisateur au lieu de déboguer les erreurs de transfert de messages d’un point A à un point B. Pour ceux qui proposent des apps natives et des apps web, le framework Network offre une prise en charge robuste des sockets web, permettant à un seul serveur de gérer toutes les applications clientes.
Le framework Network a été construit à partir de zéro pour être modulable. Dès lors, quand vous faites appel au framework Network, vous profitez d’un ensemble familier d’API améliorant les performances et la confidentialité même si les protocoles réseau évoluent. Si vous avez déjà écrit du code réseau en utilisant TCP avec l’API BSD socket, vous savez que passer à QUIC impliquerait une réécriture majeure. Avec le framework Network, la transition vers QUIC est plus rapide que jamais. Dans iOS et macOS 26, le framework Network s’intègre étroitement à la prise en charge Swift des opérations asynchrones et de la simultanéité structurée. Désormais, votre code réseau s’intègre parfaitement au reste du code Swift, ce qui facilite la création et la gestion de vos apps. Pour les novices, sachez que je vais présenter certains concepts dans cet exemple qui ne sont peut-être pas familiers, mais que vous comprendrez mieux à la fin.
Vous créez une app et vous voulez communiquer avec un serveur à www.example.com sur le port 1029. Vous voulez utiliser TLS et vous ne voulez établir cette connexion que sur un réseau sans contrainte. Le point de terminaison est l’endroit où vous souhaitez vous connecter. La pile de protocoles est la façon dont vous voulez vous connecter. Les paramètres vous aident à affiner la façon d’y parvenir.
Vous les combinez pour créer une connexion réseau. Voyons un exemple pratique.
J’établis une connexion en initialisant NetworkConnection avec le point de terminaison et la pile de protocoles voulus. Dans cet exemple, je spécifie que ma pile de protocoles est TLS. Notez que TCP et IP sont déduits automatiquement. Je n’ai qu’à les spécifier si je veux les personnaliser, mais les valeurs par défaut sont correctes presque tout le temps.
Lorsque je veux modifier les valeurs par défaut, je le fais de manière déclarative. Je vais désactiver la fragmentation IP pour cette connexion à titre d’exemple.
D’abord, je dois spécifier les protocoles TCP et IP sous TLS. Puis, je peux personnaliser les options définies pour IP pour désactiver la fragmentation.
Si le mode Faibles données a été activé pour minimiser l’utilisation du réseau, je veux modifier le comportement de ma connexion pour interdire l’utilisation de ces interfaces réseau contraintes.
Je mets à jour le code afin de pouvoir modifier les paramètres de la connexion, car je n’utiliserai plus les paramètres par défaut qui ont été créés automatiquement.
La pile de protocoles reste la même, mais j’ajoute mes paramètres personnalisés pour préciser que pour cette connexion spécifique, le framework Network ne doit utiliser que des interfaces réseau qui ne sont pas en mode faibles données. OK. Avec le style déclaratif auquel SwiftUI doit sa popularité, le code réseau est désormais similaire à celui de l’interface utilisateur.
Même si nous aimerions tous que le réseau soit toujours disponible, ce n’est pas possible. Contrairement aux sockets, NetworkConnection répond aux fluctuations de son état pour vous.
Quand la connexion est lancée, elle passe à l’état de préparation pendant que les négociations de protocole sont effectuées. Lorsqu’elle sont terminées, la connexion devient prête. S’il n’y a pas de connectivité, la connexion passe de l’état de préparation à celui d’attente.
Si le framework détecte un changement des conditions réseau, il revient à l’état de préparation pendant la connexion au point de terminaison distant. Lorsque l’état de connexion devient prêt, il peut envoyer et recevoir des données.
Une fois que la connexion est prête, en cas d’erreur ou de perte de la connectivité, elle passe à l’état d’échec avec une erreur pour vous informer de ce qui s’est passé.
La fermeture ou l’annulation de la tâche associée à la connexion la fait passer à l’état annulé. Ce qui est génial, c’est que vous n’avez pas besoin de connaître les états de connexion si vous ne le voulez pas. Lorsque vous appelez Send ou Receive, NetworkConnection attend que l’état soit prêt pour mener à bien ces opérations. Pour connaître l’état de la connexion, notamment pour mettre à jour votre UI, vous pouvez installer un gestionnaire qui sera appelé quand la connexion change d’état. Vous savez maintenant établir une connexion. Utilisons-la pour envoyer et recevoir des données sur le réseau.
Send et Receive sont des fonctions asynchrones. Elles lancent la connexion si elle n’a pas déjà été établie.
Send prend un objet de données et suspend la tâche jusqu’à ce que le framework Network l’ait traité.
TLS et TCP sont des protocoles de flux. Lorsque vous recevez des données, vous devez donc définir le nombre d’octets voulus. Dans cet exemple, je spécifie exactement le nombre d’octets que je veux lire. Receive renvoie un tuple de contenu et de métadonnées, mais j’indique ici que je ne veux que le contenu.
Ces deux fonctions génèrent des erreurs si la connexion rencontre une erreur. Par exemple, si le réseau est perdu parce que le mode avion est activé, l’erreur associée permet d’expliquer pourquoi le transfert a été interrompu. Parfois, vous ne savez pas combien d’octets de données recevoir. Dans cet exemple, je charge une image à partir du réseau. Un entier 32 bits que je reçois sur ma connexion m’indique le nombre d’octets de données d’image restants à recevoir. J’utilise cette valeur pour appeler Receive plusieurs fois jusqu’à ce que l’image soit complète.
Dans le dernier exemple, j’ai utilisé la version de Receive qui spécifie un nombre exact d’octets. Dans cet exemple, j’utilise la version de Receive qui me permet de spécifier un nombre minimum et maximum d’octets à recevoir. En procédant de cette façon, le code peut analyser l’image à mesure que les octets sont reçus sans avoir à attendre la totalité. Les protocoles de flux d’octets comme TLS sont utiles, mais il est souvent préférable de délimiter les octets envoyés et reçus afin de se concentrer sur les messages. Dans l’exemple où j’ai découvert la taille de l’image en lisant une valeur 32 bits du flux d’octets avant le contenu de l’image, j’ai délimité l’image avec la longueur nécessaire pour la démarquer des images voisines. J’ai dû le faire, car les protocoles de flux ne préservent pas les limites des messages. Autrement dit, le nombre d’octets transmis à une opération d’envoi individuelle ne sera pas nécessairement le nombre d’octets transmis par les opérations de réception à l’autre extrémité de la connexion. Par exemple, si vous envoyez trois tronçons de données, la destination peut les recevoir un octet à la fois, en une seule fois ou de multiples manières. Cela peut compliquer grandement l’écriture d’apps robustes en réseau.
Le framework Network peut vous aider. Nouveau dans iOS et macOS 26, un trameur TLV intégré délimite les messages pour que les données envoyées à une extrémité de la connexion soient exactement les données reçues à l’autre extrémité.
TLV est un protocole de message simple qui encode un type, qui peut être utilisé pour décrire les données du message, et une longueur, qui est la taille des données dans le message. Vient ensuite le contenu réel du message. Les protocoles réseau courants utilisent TLV. Votre serveur le comprend donc peut-être déjà. Essayons. Pour cet exemple, je vais envoyer et recevoir des types de messages de jeu. GameMessage est une énumération que j’utilise comme type de message. Le contenu du message sera soit un personnage de jeu, soit un mouvement. Ajouter TLV est aussi simple que de l’ajouter à ma pile de protocoles. Comme TLV délimite automatiquement mes messages, l’interface d’envoi et de réception est un peu différente.
Je vais encoder ma structure GameCharacter à l’aide de JSONEncoder et l’envoyer. Notez que je spécifie le type du message avec les données encodées. Voyons maintenant comment je reçois les messages avec TLV.
Contrairement à l’exemple précédent utilisant des protocoles de flux d’octets avec TLV, je n’ai pas à spécifier le nombre d’octets à recevoir, car il analyse le message pour moi. Comme je veux connaître le type de message que j’ai reçu, j’ai reçu le tuple du contenu et les métadonnées associées au message. Avec TLV, les métadonnées incluent le type. Je l’utilise pour déterminer le type de contenu que j’ai reçu, et avec ces informations, je décode les données et imprime ce que j’ai reçu. C’est vraiment puissant, surtout lorsque j’interagis avec des serveurs et des protocoles que je ne contrôle pas. J’ai donc mes octets qui ont été délimités sans trop de problèmes, et je peux envoyer et recevoir des messages. C’est une belle amélioration par rapport à l’utilisation des protocoles de flux d’octets.
Et si je pouvais juste envoyer mes objets directement ? Une nouveauté d’iOS et de macOS 26 est la possibilité d’envoyer et de recevoir directement des types codables, simplifiant une partie de ce code répétitif.
Je peux réduire le personnage et déplacer les structures dans l’énumération GameMessage. Coder est un protocole que je peux ajouter à ma pile de protocoles. Il délimite les messages pour moi et permet d’envoyer et de recevoir des types codables sans que j’ai à les sérialiser ou à les désérialiser. Dans ce code, j’envoie des messages de jeu dans les deux sens. J’initialise donc Coder avec le type que j’envoie et reçois, et avec le mode voulu de mise en forme des données. Le framework Network prend en charge les formats JSON et de liste de propriétés. Je choisis JSON pour cet exemple. Maintenant, je peux envoyer des messages de jeu sans faire aucun encodage moi-même.
L’appel de Receive sur ma connexion renvoie directement un message de jeu, sans avoir à faire de décodage intermédiaire pour passer d’un objet de données à un objet GameMessage. Je peux désormais envoyer et recevoir des messages de jeu sans effort supplémentaire de ma part. Je peux me concentrer sur la logique métier et l’interface utilisateur de mon app sans tout encombrer avec une tonne de code réseau sur mesure.
Vous savez maintenant comment créer une connexion à un point de terminaison et comment y envoyer et y recevoir des données. Vous avez découvert les protocoles de flux d’octets comme TCP et TLS et savez comment ajouter des protocoles de délimitation à la pile de protocoles afin de se concentrer sur les messages au lieu de flux d’octets. Mais qu’en est-il des applications qui écoutent les connexions entrantes ?
Les connexions entrantes sont gérées par NetworkListener. Tout comme NetworkConnection, je l’initialise en déclarant une pile de protocoles. Contrairement à une connexion, un écouteur n’a pas de méthode d’envoi ou de réception.
Il écoute les nouvelles connexions et les transmet ensuite à l’appelant. Il possède une méthode d’exécution qui fournit les nouvelles connexions au gestionnaire transmis. Voyons comment cela fonctionne. Je crée l’écouteur réseau de manière déclarative en spécifiant une pile de protocoles. Dans cet exemple, mes connexions entrantes pourront envoyer et recevoir des objets GameMessage chiffrés par TLS.
L’appel run sur NetworkListener commence à fournir de nouvelles connexions entrantes au gestionnaire que j’ai transmis.
NetworkListener entame une nouvelle sous-tâche pour moi pour chaque nouvelle connexion. Je peux donc effectuer des opérations asynchrones dans ma fermeture sans craindre de l’empêcher de continuer à fournir de nouvelles connexions entrantes. Lorsque j’obtiens une nouvelle connexion, j’utilise la propriété messages de NetworkConnection pour gérer les messages entrants du client. J’ai donc créé une connexion réseau à un point de terminaison que j’avais déjà, et j’ai codé l’écoute de nouvelles connexions. Maintenant, je veux créer une connexion réseau dont je ne connais pas le point de terminaison. NetworkBrowser me permet d’identifier les points de terminaison que je peux utiliser lorsque je crée mes connexions. Nouveau sur iOS 26, Wi-Fi Aware est une technologie de mise en réseau P2P multiplateforme qui vous permet de découvrir une large gamme d’appareils compatibles et de vous y connecter. Le framework DeviceDiscoveryUI permet de rechercher et coupler des appareils à proximité avec Wi-Fi Aware. Vous pouvez également rechercher les services annoncés par Bonjour. Pour en savoir plus sur Wi-Fi Aware, regardez la vidéo « Supercharge device connectivity with Wi-Fi Aware ». Pour rechercher des appareils sur le réseau à proximité avec Wi-Fi Aware ou via Bonjour, utilisez NetworkBrowser. Il utilise des descripteurs de navigation, qui décrivent ce que vous essayez de trouver. Comme NetworkConnection, il utilise aussi des paramètres décrivant comment trouver ce que vous cherchez. Contrairement à NetworkConnection et NetworkListener, NetworkBrowser n’utilise pas de pile de protocoles. C’est parce que sa seule tâche est de renvoyer les points de terminaison permettant d’établir des connexions.
Dans cet exemple, je crée le navigateur réseau pour rechercher des appareils à proximité avec le service Wi-Fi Aware, Tic-Tac-Toe. L’appel de run dans le navigateur le lance et entame la distribution de points de terminaison au gestionnaire transmis.
Dans mon app, je n’ai pas de point de terminaison préféré à utiliser, je choisis donc le premier renvoyé par le navigateur. Renvoyer .finish avec le point de terminaison entraîne l’arrêt de l’exécution du navigateur et le renvoi du point de terminaison. Je peux utiliser ce point de terminaison pour initialiser une connexion réseau tout comme je l’ai fait pour initialiser une connexion réseau dans les exemples précédents. Ce point de terminaison, le navigateur l’a découvert pour moi. Je n’ai pas eu à le rechercher à l’avance.
Avec tous ces nouveaux protocoles, vous vous demandez peut-être lequel choisir dans votre app. C’est une bonne question. La réponse n’est pas aussi compliquée qu’on pourrait le croire. Si vous communiquez avec un serveur ou un autre appareil que vous ne contrôlez pas, le choix de protocole est fait pour vous. Par exemple, le protocole est IPP sur TCP pour une imprimante. Si vous communiquez avec votre app sur un autre appareil, Coder sur TLS ou QUIC sont la référence.
Notez que si vous effectuez une mise en réseau HTTP et que vous utilisez URLSession, vous n’avez pas besoin de modifier le code. Si vous utilisez l’API C du framework Network ou si vous utilisez Swift et que vous préférez des gestionnaires de complétion, vous n’avez pas non plus besoin de modifier le code. Vous bénéficiez d’une connexion par nom, d’une modularité, d’une mobilité et d’une sécurité intégrée de premier ordre chaque fois que vous utilisez URLSession ou le framework Network.
Récapitulons tout ça. Les nouveautés d’iOS et de macOS 26 sont NetworkConnection, NetworkListener et NetworkBrowser. Vous avez appris à utiliser une connexion réseau avec la délimitation TLV et la prise en charge de l’envoi et de la réception de types codables avec le protocole Coder. NetworkListener permet d’écouter les connexions entrantes. Et NetworkBrowser permet de rechercher des points de terminaison sur le réseau. Grâce à tout cela, l’écriture d’apps en réseau n’a jamais été aussi facile. Et voilà ! Ces nouvelles API sont entièrement conçues pour la simultanéité structurée de Swift. Elles sont créées de manière déclarative, un peu comme la mise en page d’UI dans SwiftUI. Elles s’exécutent dans les tâches et sont automatiquement annulées pour vous en cas d’annulation de la tâche. Essayez ces nouvelles API pour exploiter la simultanéité structurée dans Swift afin d’assainir le code et d’éliminer l’excès de code répétitif. Le tout, avec la puissance et la flexibilité que le framework Network met à votre disposition dans vos apps. Merci de votre attention !
-
-
4:04 - Make a connection with TLS
// Make a connection import Network let connection = NetworkConnection(to: .hostPort(host: "www.example.com", port: 1029)) { TLS() }
-
4:41 - Make a connection with TLS and IP options
// Make a connection import Network let connection = NetworkConnection(to: .hostPort(host: "www.example.com", port: 1029) { TLS { TCP { IP() .fragmentationEnabled(false) } } }
-
5:07 - Make a connection with customized parameters
// Make a connection import Network let connection = NetworkConnection(to: .hostPort(host: "www.example.com", port: 1029), using: .parameters { TLS { TCP { IP() .fragmentationEnabled(false) } } } .constrainedPathsProhibited(true))
-
7:30 - Send and receive on a connection
// Send and receive on a connection import Network public func sendAndReceiveWithTLS() async throws { let connection = NetworkConnection(to: .hostPort(host: "www.example.com", port: 1029)) { TLS() } let outgoingData = Data("Hello, world!".utf8) try await connection.send(outgoingData) let incomingData = try await connection.receive(exactly: 98).content print("Received data: \(incomingData)") }
-
8:29 - Send and receive on a connection
// Send and receive on a connection import Network public func sendAndReceiveWithTLS() async throws { let connection = NetworkConnection(to: .hostPort(host: "www.example.com", port: 1029)) { TLS() } let outgoingData = Data("Hello, world!".utf8) try await connection.send(outgoingData) let remaining32 = try await connection.receive(as: UInt32.self).content guard var remaining = Int(exactly: remaining32) else { /* ... throw an error ... */ } while remaining > 0 { let imageChunk = try await connection.receive(atLeast: 1, atMost: remaining).content remaining -= imageChunk.count // Parse the next portion of the image before continuing } }
-
11:06 - Tic-Tac-Toe game messages
// TicTacToe game messages import Network enum GameMessage: Int { case selectedCharacter = 0 case move = 1 } struct GameCharacter: Codable { let character: String } struct GameMove: Codable { let row: Int let column: Int }
-
11:24 - Send TicTacToe game messages with TLV
// Send TicTacToe game messages with TLV import Network public func sendWithTLV() async throws { let connection = NetworkConnection(to: .hostPort(host: "www.example.com", port: 1029)) { TLV { TLS() } } let characterData = try JSONEncoder().encode(GameCharacter(character: "🐨")) try await connection.send(characterData, type: GameMessage.selectedCharacter.rawValue) }
-
11:53 - Receive TicTacToe game messages with TLV
import Network public func receiveWithTLV() async throws { let connection = NetworkConnection(to: .hostPort(host: "www.example.com", port: 1029)) { TLV { TLS() } } let (incomingData, metadata) = try await connection.receive() switch GameMessage(rawValue: metadata.type) { case .selectedCharacter: let character = try JSONDecoder().decode(GameCharacter.self, from: incomingData) print("Character selected: \(character)") case .move: let move = try JSONDecoder().decode(GameMove.self, from: incomingData) print("Move: \(move)") case .none: print("Unknown message") } }
-
12:50 - Tic-Tac-Toe game messages with Coder
// TicTacToe game messages with Coder import Network enum GameMessage: Codable { case selectedCharacter(String) case move(row: Int, column: Int) }
-
13:13 - Send TicTacToe game messages with Coder
// Send TicTacToe game messages with Coder import Network public func sendWithCoder() async throws { let connection = NetworkConnection(to: .hostPort(host: "www.example.com", port: 1029)) { Coder(GameMessage.self, using: .json) { TLS() } } let selectedCharacter: GameMessage = .selectedCharacter("🐨") try await connection.send(selectedCharacter) }
-
13:53 - Receive TicTacToe game messages with Coder
// Receive TicTacToe game messages with Coder import Network public func receiveWithCoder() async throws { let connection = NetworkConnection(to: .hostPort(host: "www.example.com", port: 1029)) { Coder(GameMessage.self, using: .json) { TLS() } } let gameMessage = try await connection.receive().content switch gameMessage { case .selectedCharacter(let character): print("Character selected: \(character)") case .move(let row, let column): print("Move: (\(row), \(column))") } }
-
15:16 - Listen for incoming connections with NetworkListener
// Listen for incoming connections with NetworkListener import Network public func listenForIncomingConnections() async throws { try await NetworkListener { Coder(GameMessage.self, using: .json) { TLS() } }.run { connection in for try await (gameMessage, _) in connection.messages { // Handle the GameMessage } } }
-
17:39 - Browse for nearby paired Wi-Fi Aware devices
// Browse for nearby paired Wi-Fi Aware devices import Network import WiFiAware public func findNearbyDevice() async throws { let endpoint = try await NetworkBrowser(for: .wifiAware(.connecting(to: .allPairedDevices, from: .ticTacToeService))).run { endpoints in .finish(endpoints.first!) } // Make a connection to the endpoint }
-
-
- 0:00 - Bienvenue
Découvrez les nouveautés du framework Network. Apprenez à créer des connexions, envoyer et recevoir des données, écouter les connexions entrantes et découvrir les points de terminaison sur le réseau.
- 0:45 - Établir des connexions
Le framework Network simplifie la mise en réseau des apps. Il offre des fonctionnalités comme Connect by Name et Happy Eyeballs pour une résolution d’adresses efficace. Il intègre la sécurité TLS et prend en charge des protocoles modernes comme QUIC. Avec le framework Network, vous pouvez créer une pile de protocoles via une API déclarative. Le framework gère automatiquement les transitions d’interface réseau, les proxies et les états de connexion, garantissant des connexions robustes et réactives. Les objets NetworkConnection gèrent le cycle de vie de la connexion, en passant par des états tels que preparing, ready, waiting, failed ou canceled. Vous pouvez, en option, surveiller ces états pour mettre à jour l’interface.
- 7:22 - Envoyer et recevoir
L’envoi et la réception de données dans Network sont des fonctions asynchrones qui établissent une connexion si nécessaire. La fonction send suspend la tâche jusqu’au traitement des données fournies. Lors de la réception de données avec des protocoles en flux comme TLS et TCP, le nombre d’octets doit être spécifié. Des erreurs peuvent survenir lors de l’envoi ou de la réception, avec des explications fournies pour chaque interruption. Si la taille des données est inconnue, vous pouvez utiliser la fonction receive à plusieurs reprises avec des plages d’octets minimales et maximales spécifiées. Pour simplifier la gestion des messages, le framework intègre un encodeur/décodeur Type-Length-Value (TLV) qui garantit que ce qui est envoyé est bien ce qui est reçu. iOS et macOS 26 prennent désormais en charge l’envoi et la réception directes de types Codable.
- 14:22 - Accepter les connexions entrantes
NetworkListener permet à votre app d’accepter des connexions entrantes. Il s’initialise avec une pile de protocoles et, lors de l’appel à run, lance une sous-tâche pour chaque connexion entrante, transmise à un gestionnaire pour un traitement asynchrone des messages.
- 16:05 - Rechercher d’autres appareils
Dans iOS 26, le framework Network introduit NetworkBrowser, qui permet à votre app de découvrir des points de terminaison réseau. NetworkBrowser peut aussi utiliser Wi-Fi Aware, une technologie peer-to-peer ou Bonjour, pour détecter les appareils ou services à proximité. Vous pouvez créer un NetworkBrowser pour rechercher des services spécifiques via des descripteurs, et utiliser un point de terminaison découvert pour initialiser une NetworkConnection. Ces nouvelles API, conçues pour la concurrence structurée de Swift, facilitent et simplifient la création d’apps réseau, supprimant le code répétitif tout en conservant la puissance et la flexibilité du framework Network.