What is Data, and how do you convert it to and from text?

2.3.5 · L1 · Interview

Predict first

What does this print?

print(Data("héllo".utf8).count)
  • 5
  • 6
  • 7

Predict first

What does this print?

let bad = Data([0xff])
print(String(data: bad, encoding: .utf8) as Any, String(decoding: bad, as: UTF8.self))
  • nil �
  • � �
  • nil nil

Mental model. Data is a sealed envelope of bytes. Whether it is text, an image or a JSON blob depends on who opens it.

Data is a value type holding a run of bytes, with copy-on-write storage. It is the currency of file, network and crypto APIs. Convert text with Data(string.utf8) and back with String(data:encoding:), which returns an optional (invalid UTF-8 gives nil) or String(decoding:as:), which never fails and substitutes U+FFFD for bad bytes. base64EncodedString() and Data(base64Encoded:) handle Base64.

A Data slice keeps its parent’s indices, exactly like an ArraySlice, so data[2...] starts at index 2. For large files, read with .mappedIfSafe so the file is memory-mapped instead of copied, or stream through a FileHandle.

Five characters, but é is two bytes in UTF-8, so six bytes.

String(data:encoding:) refuses invalid UTF-8 and returns nil. String(decoding:) repairs it with the replacement character.

Common traps

  • Using count of a string as its byte length. "héllo".utf8.count is 6 because é takes two bytes.
  • Indexing a Data slice from 0. The slice’s startIndex is not 0.

They might follow up with

  • When do you want [UInt8] instead? For pure in-memory byte work. Use Data at API boundaries.
  • How do you read a struct from bytes? withUnsafeBytes and load(as:), with care about alignment.

Related questions

  • 2.1.6 What is an ArraySlice, and why does slice[0] crash?
  • 2.3.7 How do you read and write files safely, and why is fileExists-then-act a bug?

← Why use URLComponents and URLQueryItem, and what do they still not…   Where should an iOS app put files: Documents, Application Support… →