This question has been asked quite a few times over the years, but it has changed again in Swift 5, particularly in the last two betas.
Reading a JSON file seems to be quite simple:
func readJSONFileData(_ fileName: String) -> Array<Dictionary<String, Any>> {
var resultArr: Array<Dictionary<String, Any>> = []
if let url = Bundle.main.url(forResource: "file", withExtension: "json") {
if let data = try? Data(contentsOf: url) {
print("Data raw: ", data)
if let json = try? (JSONSerialization.jsonObject(with: data, options: []) as! NSArray) {
print("JSON: ", json)
if let arr = json as? Array<Any> {
print("Array: ", arr)
resultArr = arr.map { $0 as! Dictionary<String, Any> }
}
}
}
}
return resultArr
}
But writing is incredibly difficult, and all of the previous methods found on this site have failed in Swift 5 on Xcode 11 betas 5 and 6.
How can I write data to a JSON file in Swift 5?
I tried these approaches:
There weren't any errors except for deprecation warnings, and when I fixed those, it simply didn't work.
Xcode 11 supports on-device debugging for iOS 8 and later, tvOS 9 and later, and watchOS 2 and later. Xcode 11 requires a Mac running macOS Mojave 10.14. 4 or later.
You can certainly go on playing with Xcode 11 if that is what you feel like doing. But that's all you will be doing - playing. You won't be able to do anything with an app that you create that way.
Let’s assume for a second that you had some random collection (either arrays or dictionaries or some nested combination thereof):
let dictionary: [String: Any] = ["bar": "qux", "baz": 42]
Then you could save it as JSON in the “Application Support” directory like so:
do {
let fileURL = try FileManager.default
.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
.appendingPathComponent("example.json")
try JSONSerialization.data(withJSONObject: dictionary)
.write(to: fileURL)
} catch {
print(error)
}
For rationale why we now use “Application Support” directory rather than the “Documents” folder, see the iOS Storage Best Practices video or refer to the File System Programming Guide. But, regardless, we use those folders, not the Application’s “bundle” folder, which is read only.
And to read that JSON file:
do {
let fileURL = try FileManager.default
.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
.appendingPathComponent("example.json")
let data = try Data(contentsOf: fileURL)
let dictionary = try JSONSerialization.jsonObject(with: data)
print(dictionary)
} catch {
print(error)
}
That having been said, we generally prefer to use strongly typed custom types rather than random dictionaries where the burden falls upon the programmer to make sure there aren’t typos in the key names. Anyway, we make these custom struct
or class
types conform to Codable
:
struct Foo: Codable {
let bar: String
let baz: Int
}
Then we’d use JSONEncoder
rather than the older JSONSerialization
:
let foo = Foo(bar: "qux", baz: 42)
do {
let fileURL = try FileManager.default
.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
.appendingPathComponent("example.json")
try JSONEncoder().encode(foo)
.write(to: fileURL)
} catch {
print(error)
}
And to read that JSON file:
do {
let fileURL = try FileManager.default
.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
.appendingPathComponent("example.json")
let data = try Data(contentsOf: fileURL)
let foo = try JSONDecoder().decode(Foo.self, from: data)
print(foo)
} catch {
print(error)
}
For more information about preparing JSON from custom types, see the Encoding and Decoding Custom Types article or the Using JSON with Custom Types sample code.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With