My need for this one is that I have a data file. I want my data file distributed with my app and I need a way to be able to read that file back.
This can be for all sorts of reasons, but the most obvious use case for me was that I needed a set of data that I was maintaining on the web and that I could download. However, when the app is run, I cannot guarantee that there will be a network connection so I need to have a set of defaults available until I have successfully downloaded an updated file.
So, I start by adding my 'template' file by adding it to my project:
Now it's part of my project, it will be built in to the app and will be available to me when my app runs. It becomes
and integral part of the program Bundle
. In the above case, I have chosen to put the file at the root
of the project as it saves messing around with sub-folders. You probably should mess around with
sub-folders and do things properly.
Next job is to get to the file at run time. To do that, we need it's address and that's a URL of a resource in the bundle:
let bundle = Bundle(for: EmojiDataSource.self)
let fileUrl = bundle.url(forResource: "emojiJsonFile", withExtension: "json")
The resource name is the name of the file we want access to and the extension is the file extension.
Now I have a URL, I can load the contents into a string:
let text = try String(contentsOf: <fileUrl>, encoding: .utf8)
As an aside, lets assume my file is a JSON file and I want a decoded JSON object. The code to achieve this would be
private func loadEmojiFile(fromURL: URL?) {
if let jsFile = fromURL {
do {
let jsonText = try String(contentsOf: jsFile, encoding: .utf8)
let JSONData = jsonText.data(using: String.Encoding.utf8)
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .formatted(dateFormatter())
emojiFile = try decoder.decode(EmojiFile.self, from: JSONData!)
} catch {
print(error)
}
}
}
private func dateFormatter() -> DateFormatter {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
return dateFormatter
}