Send a stream of data to a server.
Streaming media apps and long-running apps that send continual updates use an ongoing stream to upload data, rather than sending a single block of data or a flat file. You can configure an instance of
URLSession (a subclass of
URLSession) to work with a stream that you provide, and then fill this stream with data indefinitely.
The task gets the stream by calling your session’s delegate, so you need to create a session and set your own code as its delegate.
Create a URL Session
Begin by creating a URLSession and providing it with a delegate. Listing 1 creates a URL session with the default
URLSession and sets
self as the delegate. You’ll implement
URLSession later, in Provide the Stream to the Upload Task.
Create a Streaming Upload Task
Create the upload task with the
upload. This takes a
URLRequest specifying the URL you want to upload to, along with other parameters. You start the task by calling
resume(). Listing 2 shows how to create and start an upload task, connecting to a server on the local machine (
127) listening on port
Use a Bound Pair of Streams to Provide an Input Stream
You provide the streaming data to the upload task as an
Input. The task reads data from this stream and uploads it to the destination.
A good way to provide data to the input stream is to use a bound pair of streams. The bound pair contains an
Output that you write data to. Thanks to the binding of the streams, the data you write to the output stream is made available to the input stream, which the task can then read from. Figure 1 shows this arrangement.
Listing 3 shows a structure called
Streams that consists of an
Input and an
Output. The listing creates a property of this type, called
bound, by calling the
get method of the
Stream class, passing in in-out references for the input and output streams.
When you create the bound pair, make sure you specify a buffer size large enough to hold any data you write to the output stream, prior to the data being read from the input stream. Listing 3 uses a 4096-byte buffer.
The listing also sets
self as the output stream’s delegate. Declare that your class implements the
Stream protocol in order to receive events that indicate when the output stream is ready to receive new data. You’ll provide the implementation of
Stream later, in Write Data to the Stream When It’s Ready.
Provide the Stream to the Upload Task
You provide the input stream to the upload task in your implementation of the
url, which is called after you start the upload task by calling
resume(). The callback passes in a completion handler, which you call directly, passing in the
bound stream you created earlier. Listing 4 shows an implementation of this method.
Write Data to the Stream When It’s Ready
Write data to an output stream only when the stream is ready for it. You get notified of the stream’s readiness in the
stream(_:. When this callback sends
has as its
event parameter, the stream is ready to accept more data.
If you’re not ready to write while handling the event, and would prefer to write on your own schedule, you can set a flag variable and check it later to determine whether it’s is safe to write to the stream. Listing 5 illustrates this technique. It handles the
has event by just setting a private
can property to
While handling stream events, also check whether
error. This means that the stream has failed. When this happens, close the streams and abandon the upload.
Once you’re handling the
has event, you can write to the stream whenever you know it’s ready to receive more data. You write to the stream by calling its
write(_: method, providing a reference to the raw bytes to be written, and the maximum number of bytes to write.
Listing 6 uses a timer to wait for the private
can property to become true. Once this is the case, the code creates a string representing the current date and converts it to raw bytes. The listing then calls
write(_: to send these bytes to the output stream. Because this output stream is bound to an input stream, the upload task can then automatically read these bytes from the input stream and send them to the destination URL.
Once you write to the output stream, you can’t write again until your
Stream receives a new
has event. This example enforces this constraint by setting the class’
can property to
false. It will be reset to
true when a new
has event is received by the output stream's delegate, as shown earlier in Listing 5.