Reading a JSON File in Swift [Bundle and Documents Directory]

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.



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 GUIDE

Table 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 GUIDE

Leave a Comment