Apps often need to read the content of JSON files for different reasons.
You can save static JSON data inside an Xcode project to populate your app or use it in SwiftUI previews.
Apps can also save data inside the device’s documents directory.
- This article is part of the Parsing JSON in Swift series.
FREE GUIDE: Parsing JSON in Swift - The Ultimate Cheat Sheet
Learn the fundamentals of JSON parsing in Swift, advanced decoding techniques, and tasks for app development
DOWNLOAD THE FREE GUIDETable of contents
Getting sample JSON data to model your Swift types
Most of the time, JSON data comes from a REST API.
If your API does not require authentication, you can open a resource’s URL directly in your browser. Otherwise, you need to use a more complex solution like cURL or, better, a GUI client like Postman or Bruno.
JSON data is just text. However, REST APIs strip all the white space to save bandwidth, so raw JSON data is not readable for humans. If that’s the case, pass it through an online JSON formatter.
In this article, I will use data from the example user URL from the GitHub REST API.
{
"login": "octocat",
"name": "The Octocat",
"company": "@github",
"location": "San Francisco"
}
How to encode and decode the contents of a JSON file in Swift
To read JSON data from a file, you must first create a Swift type to decode the data.
struct User: Codable {
let login: String
let name: String
let company: String
let location: String
}
To decode JSON data, your Swift type must conform to the Decodable protocol. If you want to encode it, it must conform to the Encodable protocol instead. If you need both, you can use the Codable typealias.
No matter where your file is situated, it will usually be identified using a file URL. Read its content using the Data type and decode it using a JSONDecoder instance.
func readUserFile(with url: URL) throws -> User {
let data = try Data(contentsOf: url)
return try JSONDecoder().decode(User.self, from: data)
}
Writing a JSON file mirrors the steps above. First, encode your values into JSON data using a JSONEncoder instance and then write the data to a file URL.
func write(user: User, at url: URL) throws {
let data = try JSONEncoder().encode(user)
try data.write(to: url)
}
You can make your functions generic if you need to read and write multiple decodable types.
func readJSONFile<T: Decodable>(with url: URL) throws -> T {
let data = try Data(contentsOf: url)
return try JSONDecoder().decode(T.self, from: data)
}
func write<T: Encodable>(value: T, at url: URL) throws {
let data = try JSONEncoder().encode(value)
try data.write(to: url)
}
You often need to save data to a JSON file to cache it after fetching it from a URL. If you want the contents of the JSON file to be human-readable, add options for pretty-printing to the JSONEncoder
instance.
Reading a JSON file from the app’s main bundle
The most common place to place JSON files is inside the app bundle. Bundle files help populate your app with data and provide data for SwiftUI previews.
First, place the JSON file inside your Xcode project. You can drag an existing file into the project navigator or create a new empty file with the .json extension.
You can then retrieve the file URL using the main
Bundle instance.
func readUserFromBundle() -> User? {
guard let url = Bundle.main.url(forResource: "User", withExtension: "json") else {
return nil
}
return try? readJSONFile(with: url)
}
If you need to use your JSON file only for SwiftUI previews, place it inside the Preview Content folder of your Xcode project.
extension User {
static var preview: User {
readUserFromBundle()!
}
}
struct UserView: View {
let user: User
var body: some View {
Form {
LabeledContent {
Text(user.name)
} label: {
Label("Name", systemImage: "pencil.and.scribble")
}
LabeledContent {
Text(user.company)
} label: {
Label("Company", systemImage: "building.2")
}
LabeledContent {
Text(user.location)
} label: {
Label("Location", systemImage: "mappin.and.ellipse")
}
}
}
}
#Preview {
UserView(user: .preview)
}
Reading a JSON file from the app’s document directory
Many apps must save and retrieve data to preserve user-generated information or cache data downloaded from the Internet. On iOS, apps must save their data to the system-assigned documents directory.
The URL type sports several static properties, locating several system directories. You can use those to assemble the full file URL, which you can then use to read and save data, as we have seen above.
extension User {
static let documentsURL = URL.documentsDirectory
.appendingPathComponent("User")
.appendingPathExtension("json")
}
func writeUserIntoDocuments(user: User) {
try? write(value: user, at: User.documentsURL)
}
func readUserFromDocuments() -> User? {
return try? readJSONFile(with: User.documentsURL)
}
If you want a quick reference of all these code snippets to read and write JSON files, together with other JSON parsing techniques, download my free cheat sheet below.
FREE GUIDE: Parsing JSON in Swift - The Ultimate Cheat Sheet
Learn the fundamentals of JSON parsing in Swift, advanced decoding techniques, and tasks for app development
DOWNLOAD THE FREE GUIDEMatteo has been developing apps for iOS since 2008. He has been teaching iOS development best practices to hundreds of students since 2015 and he is the developer of Vulcan, a macOS app to generate SwiftUI code. Before that he was a freelance iOS developer for small and big clients, including TomTom, Squla, Siilo, and Layar. Matteo got a master’s degree in computer science and computational logic at the University of Turin. In his spare time he dances and teaches tango.