大多数浏览器和
Developer App 均支持流媒体播放。
-
将结构化并发代码与 Network 框架搭配使用
Network 框架是在 Apple 平台上建立底层网络连接的最佳方式,而在 iOS、iPadOS、和 macOS 26 上,它非常适合与你的结构化并发代码搭配使用。我们将探索如何建立连接、发送和接收数据与分帧处理的信息、监听传入连接以及浏览网络上的服务。在此期间,我们还将介绍一些关键的最佳做法。
章节
- 0:00 - Welcome
- 0:45 - Make connections
- 7:22 - Send and receive
- 14:22 - Accept incoming connections
- 16:05 - Find other devices
资源
相关视频
WWDC25
WWDC18
-
搜索此视频…
Hi - I’m Scott, and I’m really excited to share some improvements that make your networking code even more fun and easy to write than it’s ever been. If you’ve never written a line of networking code, you’re in the right place. And if you have, I have some exciting new APIs to share with you.
I'll start with how to create connections. Then, I’ll dive deep into using those connections to send and receive data.
Next, I’ll discuss how to listen for incoming connections. And finally, I’ll cover how to find and connect to other devices on your network. Let’s get started with making connections.
Network framework enables you to make secure, composable and modern connections in your app.
Gone are the days of sockets and sockaddrs and tough-to-remember ioctls and blocking operations. Network framework has Connect by Name, meaning it will do the work of resolving names so you don’t have to. Once it resolves that name, it has something called Happy Eyeballs, which ensures that it efficiently picks the best resolved address to connect you dynamically.
Security via TLS is baked right in, so you don’t have to integrate yet another library, usually with a completely different kind of API, into your application just to protect your users’ privacy. It also supports network interface transitions and proxies, enabling things like Wi-Fi Assist and multi-path protocols without you having to do anything at all.
It supports modern transports like QUIC, and it even lets you mix and match your own protocols with the built-in ones, enabling you to spend time on your business logic and user experience instead of debugging why messages aren’t getting from point A to point B.
For those of you who support both native apps and web apps, Network framework also has robust support for web sockets, allowing one server to service the gamut of client applications.
Network framework was built from the ground up to be composable. What that means for you is that when you reach for Network framework, you will be rewarded with a familiar set of APIs, even as network protocols evolve over time, improving performance and privacy.
If you’ve ever written networking code using TCP with the BSD socket API, you’ll know that switching to something like QUIC would entail a major rewrite. With Network framework, switching over to QUIC could be accomplished during a working lunch. In iOS and macOS 26, Network framework integrates tightly with Swift's robust support for asynchronous operations and structured concurrency. Now, your networking code will fit in smoothly with the rest of your Swift code, making your apps easier to build and maintain. For those of you who are new to networking, I'll be introducing some concepts in this example that might be unfamiliar, but don’t worry, it will all make sense in the end.
Let's say you're writing your app, and you want to talk to a server at www.example.com on port 1029. You want to use TLS, and you only want to make this connection on an unconstrained network.
Your endpoint is where you want to connect. Your protocol stack is how you want to connect. Your parameters help refine how you get there.
You combine these to make a NetworkConnection.
Let’s go through an example of how this works in practice.
I make a connection by initializing my NetworkConnection with the endpoint and the protocol stack that I want. In this example, I specify that my protocol stack is TLS. Note that TCP and IP are inferred for me. I only have to specify them if I want to customize something but the defaults are good almost all of the time.
When I want to modify the defaults, I still do it in a declarative way. I’m going to turn off IP fragmentation for this connection as an example.
First, I need to specify the TCP and IP protocols below TLS. And then, I can customize the options that are set for IP to turn fragmentation off.
If someone enables low data mode to minimize network usage, I want to modify the behavior of my connection so that it prohibits using those constrained network interfaces.
I update the code so I can modify the parameters for the connection because I will no longer be using the default parameters that were created for me automatically.
The protocol stack remains the same but I add my custom parameters, which allows me to specify that for this specific connection, I would like Network framework to only consider using network interfaces that are not in low data mode. Cool! And with the same declarative style that has made SwiftUI so popular, now my networking code has a similar feel to my user interface code.
While it would be great if the network was always available, Network conditions change all the time.
Unlike sockets, NetworkConnection will respond to these changing states for you.
When your connection is started, it will transition into the preparing state while any protocol handshakes are performed. And when those are complete, it will go to the ready state.
If there is no connectivity, the connection will instead go from preparing into a waiting state.
When Network framework detects that network conditions have changed, it will go back to preparing while it tries to connect to the remote endpoint. When your connection state becomes ready, it can send and receive data.
Once your connection is in the ready state, if it encounters an error or connectivity is lost, it will transition to the failed state with an error to let you know what happened.
Exiting or canceling the task associated with the connection will move it to the canceled state.
The great thing about this is that you don’t need to be aware of any of the connection states if you don’t want to be. You can call send and receive, and NetworkConnection will wait until its state becomes ready to complete those operations. But if you do want to know what state your connection is in, maybe to update your user interface, you can install a handler that will be called when your connection changes state.
Okay, now you know how to make a connection. So let’s start using it to send and receive data on the network.
Send and receive are both asynchronous functions, and both will start the connection if it hasn’t already been started.
Send takes a data object and suspends the task until Network framework has processed the data.
TLS and TCP are both stream protocols, so when you receive data, you have to specify how many bytes you want.
In this example, I’m specifying exactly the number of bytes I want to read. Receive returns a tuple of content and metadata but in this example, I specify I only want the content.
Both of these functions will throw errors if the connection encounters an error. For example, if the network is lost because Airplane Mode is turned on, you can use the associated error to explain why the transfer was interrupted.
Sometimes you don’t know how many bytes of data to receive.
In this example, I’m loading an image from the network. A 32-bit integer that I receive on my connection tells me how many bytes remaining of image data there are to receive. Using that value, I call receive repeatedly until the image is complete.
In the last example, I used the version of receive that specifies an exact number of bytes. In this example, I use the version of receive that allows me to specify a minimum and a maximum number of bytes to receive.
Doing it this way allows the code to parse the image as bytes come in from the network and not have to wait for the whole thing. Byte stream protocols like TLS are great but often times you’ll want to frame the bytes you’re sending and receiving, so you work with messages instead of bytes. In the example where I found out how big the image was by reading a 32-bit value from the byte stream ahead of the image contents, that was framing the image with the length to delineate it from neighboring images. I had to do this because streaming protocols don’t preserve message boundaries. What that means is that the number of bytes passed to an individual send operation will not necessarily be the number of bytes passed back from receive operations on the other end of the connection.
For example, if you call send with three chunks of data, the other side might receive those one byte at a time, all at once, or anything in between.
This can complicate writing robust, networked applications significantly.
Fortunately, Network framework can help. New in iOS and macOS 26 is a built-in type, length, value, or TLV framer that frames messages so what you send on one end of a connection is exactly what you receive on the other end.
TLV is a simple message protocol that encodes a type, which can be used to describe the data contained in the message, and a length, which is the size of the data in the message. Following that is the actual content of the message. Common network protocols use TLV, so this may be a format your server already speaks. Let's try it out. For this example, I’m going to send and receive game message types. GameMessage is an enum, which I will use as the type of the message. The content of the message will be either a game character or a game move.
Adding TLV is as simple as adding it to my protocol stack.
Because TLV will frame my messages for me, the interface for sending and receiving is slightly different.
I’m going to encode my GameCharacter struct using JSONEncoder and send it.
Notice that I’m specifying the type of the message along with the encoded data.
Now let’s look at how I receive messages using TLV.
Unlike the previous example using byte stream protocols, when I use TLV, I don't have to specify the number of bytes to receive because TLV will parse the message for me. Because I want to know the type of message I received, I received the tuple of the content and the metadata associated with the message. For TLV, the metadata includes the type.
I use the type to determine what kind of content I received, and using that information, I decode the data and print what I received. This is really powerful, especially when interoperating with existing servers and protocols that I don’t control. So now I’ve got my bytes framed without too much trouble, and I’m able to send and receive messages. This is a nice improvement over using byte stream protocols directly.
But what if I could just send my objects directly? New in iOS and macOS 26 is support for directly sending and receiving codable types, which can help simplify some of this boilerplate code.
I can collapse the character and move structs into the GameMessage enum itself.
Coder is a protocol that I can add to my protocol stack that will frame messages for me and allow me to send and receive codable types without having to serialize and deserialize them myself.
In this code, I’m sending game messages back and forth. So I'll initialize the coder with the type that I'm sending and receiving and with how I want Coder to format the data.
Network framework has built-in support for JSON and property list formats. I'll choose JSON for this example.
Now, I can send game messages without doing any of the encoding myself.
Calling receive on my connection will return a game message directly, without having to do any intermediate decoding to go from a data object to a GameMessage object.
Now, I can send and receive game messages without doing any extra work myself. I can concentrate on the business logic and user interface of my app without cluttering things up with a bunch of bespoke networking code.
So now you know how to create a connection to an endpoint and send and receive data on the connection. You learned about byte stream protocols like TCP and TLS and how to add framing protocols to your protocol stack so you work with messages instead of byte streams. But what about applications that listen for incoming connections? Incoming connections are handled by NetworkListener.
Just like NetworkConnection, I initialize it by declaring a protocol stack. Unlike a connection, a listener doesn’t have a send or receive method.
This is because a listener listens for new connections and then delivers them to the caller.
NetworkListener has a run method that will deliver new connections to the handler that is passed in. Let’s see how that works. I create my NetworkListener declaratively by specifying a protocol stack. In this example, my incoming connections will be able to send and receive GameMessage objects that are encrypted by TLS.
Calling run on NetworkListener will begin delivering new incoming connections to the handler I passed in to run.
NetworkListener will start a new subtask for me for every new connection. So I can perform async operations in my closure without worrying that I’ll be preventing the listener from continuing to deliver new incoming connections. When I get a new connection, I use the messages property on NetworkConnection to handle the incoming messages from the client.
So now I’ve created a NetworkConnection to an endpoint that I already had, and I’ve coded up listening for new connections. But now I want to create a NetworkConnection whose endpoint I don’t know ahead of time. NetworkBrowser enables me to discover endpoints that I can use when I create my connections.
New this year in iOS 26 is Wi-Fi Aware, a cross-platform peer-to-peer networking technology that will allow you to discover and connect to a wide range of compatible devices. You can use the DeviceDiscoveryUI framework to find and pair with nearby devices using Wi-Fi Aware. Alternatively, you can browse for Bonjour-advertised services. To learn more about Wi-Fi Aware, watch “Supercharge device connectivity with Wi-Fi Aware.” When you want to find devices on your network, either nearby with Wi-Fi Aware or via Bonjour, you use NetworkBrowser. NetworkBrowser takes browse descriptors, which describe what you're trying to find.
Like NetworkConnection, it also takes parameters, which describe how you want to find it. But unlike NetworkConnection and NetworkListener, NetworkBrowser doesn’t take a protocol stack.
That’s because NetworkBrowser's only job is to return endpoints which can be used to make connections.
In this example, I create my NetworkBrowser to search for nearby devices using the Wi-Fi Aware service, Tic-Tac-Toe. Calling run on the browser will cause it to start and begin to vend sets of endpoints to the handler I passed in to run.
In my app, I don’t have a preference for which endpoint to use, so I choose the first endpoint that is returned from the browser.
Returning .finish with the endpoint will cause the browser to stop running and the endpoint to be returned from run.
I can then use that endpoint to initialize a NetworkConnection in exactly the same way that I used an endpoint to initialize a NetworkConnection in the previous examples.
The neat thing about this endpoint, though, is that the browser discovered it for me, so I didn’t have to know about it ahead of time.
With all these new protocols, you might wonder how to choose which protocol to use in your app. That's a great question. The answer is not as complicated as you might think. If you’re talking to a server or some other device that you don’t control, your choice of protocol has already been made for you. For example, you might be talking to a printer via IPP over TCP.
If you’re talking to your own app on another device, you can’t go wrong with Coder over TLS or QUIC.
Note that if you're doing HTTP networking and are currently using URLSession, you don't need to change any of your code. And if you’re using Network framework C API or are working in Swift and you prefer using completion handlers, you don’t need to change any of your code either. You’ll still get first-class connect-by-name support, composability, mobility, and built-in security whenever you use URLSession or Network framework in any form.
Let's do a quick recap.
New in iOS and macOS 26 are NetworkConnection, NetworkListener, and NetworkBrowser.
You learned about using a NetworkConnection with TLV framing and support for sending and receiving codable types with the Coder protocol.
NetworkListener can be used to listen for incoming connections. And NetworkBrowser can be used to browse for endpoints on the network. All of this makes writing networked apps easier than ever.
So that’s it. These new APIs are built from the ground up for Swift's structured concurrency. They are created declaratively, much like laying out a user interface in SwiftUI.
They run in tasks and will be cancelled for you automatically when the task they are running in is cancelled.
Try out these new APIs to take full advantage of structured concurrency in Swift to make your code cleaner and eliminate tons of boilerplate.
All of that with all the same power and flexibility the Network framework gives you to use in your apps. Thanks for watching.
-
-
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 }
-