Reading in a JSON File Using Swift - json

I'm really struggling with trying to read a JSON file into Swift so I can play around with it. I've spent the best part of 2 days re-searching and trying different methods but no luck as of yet so I have signed up to StackOverFlow to see if anyone can point me in the right direction.....
My JSON file is called test.json and contains the following:
{
"person":[
{
"name": "Bob",
"age": "16",
"employed": "No"
},
{
"name": "Vinny",
"age": "56",
"employed": "Yes"
}
]
}
The file is stored in the documents directly and I access it using the following code:
let file = "test.json"
let dirs : String[] = NSSearchPathForDirectoriesInDomains(
NSSearchpathDirectory.DocumentDirectory,
NSSearchPathDomainMask.AllDomainMask,
true) as String[]
if (dirs != nil) {
let directories: String[] = dirs
let dir = directories[0]
let path = dir.stringByAppendingPathComponent(file)
}
var jsonData = NSData(contentsOfFile:path, options: nil, error: nil)
println("jsonData \(jsonData)" // This prints what looks to be JSON encoded data.
var jsonDict = NSJSONSerialization.JSONObjectWithData(jsonData, options: nil, error: nil) as? NSDictionary
println("jsonDict \(jsonDict)") - This prints nil.....
If anyone can just give me a push in the right direction on how I can de-serialize the JSON file and put it in an accessible Swift object I will be eternally grateful!
Kind Regards,
Krivvenz.

Follow the below code :
if let path = NSBundle.mainBundle().pathForResource("test", ofType: "json")
{
if let jsonData = NSData(contentsOfFile: path, options: .DataReadingMappedIfSafe, error: nil)
{
if let jsonResult: NSDictionary = NSJSONSerialization.JSONObjectWithData(jsonData, options: NSJSONReadingOptions.MutableContainers, error: nil) as? NSDictionary
{
if let persons : NSArray = jsonResult["person"] as? NSArray
{
// Do stuff
}
}
}
}
The array "persons" will contain all data for key person. Iterate throughs to fetch it.
Swift 4.0:
if let path = Bundle.main.path(forResource: "test", ofType: "json") {
do {
let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe)
let jsonResult = try JSONSerialization.jsonObject(with: data, options: .mutableLeaves)
if let jsonResult = jsonResult as? Dictionary<String, AnyObject>, let person = jsonResult["person"] as? [Any] {
// do stuff
}
} catch {
// handle error
}
}

Swift 4.x and 5.x using Decodable
struct ResponseData: Decodable {
var person: [Person]
}
struct Person : Decodable {
var name: String
var age: String
var employed: String
}
func loadJson(filename fileName: String) -> [Person]? {
if let url = Bundle.main.url(forResource: fileName, withExtension: "json") {
do {
let data = try Data(contentsOf: url)
let decoder = JSONDecoder()
let jsonData = try decoder.decode(ResponseData.self, from: data)
return jsonData.person
} catch {
print("error:\(error)")
}
}
return nil
}
Swift 3
func loadJson(filename fileName: String) -> [String: AnyObject]? {
if let url = Bundle.main.url(forResource: fileName, withExtension: "json") {
do {
let data = try Data(contentsOf: url)
let object = try JSONSerialization.jsonObject(with: data, options: .allowFragments)
if let dictionary = object as? [String: AnyObject] {
return dictionary
}
} catch {
print("Error!! Unable to parse \(fileName).json")
}
}
return nil
}

If anyone is looking for SwiftyJSON Answer:
Update:
For Swift 3/4:
if let path = Bundle.main.path(forResource: "assets/test", ofType: "json") {
do {
let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .alwaysMapped)
let jsonObj = try JSON(data: data)
print("jsonData:\(jsonObj)")
} catch let error {
print("parse error: \(error.localizedDescription)")
}
} else {
print("Invalid filename/path.")
}

Swift 5.1, Xcode 11
You can use this:
struct Person : Codable {
let name: String
let lastName: String
let age: Int
}
func loadJson(fileName: String) -> Person? {
let decoder = JSONDecoder()
guard
let url = Bundle.main.url(forResource: fileName, withExtension: "json"),
let data = try? Data(contentsOf: url),
let person = try? decoder.decode(Person.self, from: data)
else {
return nil
}
return person
}

Xcode 8 Swift 3 read json from file update:
if let path = Bundle.main.path(forResource: "userDatabseFakeData", ofType: "json") {
do {
let jsonData = try NSData(contentsOfFile: path, options: NSData.ReadingOptions.mappedIfSafe)
do {
let jsonResult: NSDictionary = try JSONSerialization.jsonObject(with: jsonData as Data, options: JSONSerialization.ReadingOptions.mutableContainers) as! NSDictionary
if let people : [NSDictionary] = jsonResult["person"] as? [NSDictionary] {
for person: NSDictionary in people {
for (name,value) in person {
print("\(name) , \(value)")
}
}
}
} catch {}
} catch {}
}

Simplifying the example provided by Peter Kreinz. Works with Swift 4.2.
The extension function:
extension Decodable {
static func parse(jsonFile: String) -> Self? {
guard let url = Bundle.main.url(forResource: jsonFile, withExtension: "json"),
let data = try? Data(contentsOf: url),
let output = try? JSONDecoder().decode(self, from: data)
else {
return nil
}
return output
}
}
The example model:
struct Service: Decodable {
let name: String
}
The example usage:
/// service.json
/// { "name": "Home & Garden" }
guard let output = Service.parse(jsonFile: "service") else {
// do something if parsing failed
return
}
// use output if all good
The example will work with arrays, too:
/// services.json
/// [ { "name": "Home & Garden" } ]
guard let output = [Service].parse(jsonFile: "services") else {
// do something if parsing failed
return
}
// use output if all good
Notice how we don't provide any unnecessary generics, thus we don't need to cast the result of parse.

Updated names for Swift 3.0
Based on Abhishek's answer and Druva's answer
func loadJson(forFilename fileName: String) -> NSDictionary? {
if let url = Bundle.main.url(forResource: fileName, withExtension: "json") {
if let data = NSData(contentsOf: url) {
do {
let dictionary = try JSONSerialization.jsonObject(with: data as Data, options: .allowFragments) as? NSDictionary
return dictionary
} catch {
print("Error!! Unable to parse \(fileName).json")
}
}
print("Error!! Unable to load \(fileName).json")
}
return nil
}

Swift 2.1 answer (based on Abhishek's) :
if let path = NSBundle.mainBundle().pathForResource("test", ofType: "json") {
do {
let jsonData = try NSData(contentsOfFile: path, options: NSDataReadingOptions.DataReadingMappedIfSafe)
do {
let jsonResult: NSDictionary = try NSJSONSerialization.JSONObjectWithData(jsonData, options: NSJSONReadingOptions.MutableContainers) as! NSDictionary
if let people : [NSDictionary] = jsonResult["person"] as? [NSDictionary] {
for person: NSDictionary in people {
for (name,value) in person {
print("\(name) , \(value)")
}
}
}
} catch {}
} catch {}
}

Swift 3.0, Xcode 8, iOS 10
if let path = Bundle.main.url(forResource: "person", withExtension: "json") {
do {
let jsonData = try Data(contentsOf: path, options: .mappedIfSafe)
do {
if let jsonResult = try JSONSerialization.jsonObject(with: jsonData, options: JSONSerialization.ReadingOptions(rawValue: 0)) as? NSDictionary {
if let personArray = jsonResult.value(forKey: "person") as? NSArray {
for (_, element) in personArray.enumerated() {
if let element = element as? NSDictionary {
let name = element.value(forKey: "name") as! String
let age = element.value(forKey: "age") as! String
let employed = element.value(forKey: "employed") as! String
print("Name: \(name), age: \(age), employed: \(employed)")
}
}
}
}
} catch let error as NSError {
print("Error: \(error)")
}
} catch let error as NSError {
print("Error: \(error)")
}
}
Output:
Name: Bob, age: 16, employed: No
Name: Vinny, age: 56, employed: Yes

This worked great with me
func readjson(fileName: String) -> NSData{
let path = NSBundle.mainBundle().pathForResource(fileName, ofType: "json")
let jsonData = NSData(contentsOfMappedFile: path!)
return jsonData!
}

Here is my solution using SwiftyJSON
if let path : String = NSBundle.mainBundle().pathForResource("filename", ofType: "json") {
if let data = NSData(contentsOfFile: path) {
let json = JSON(data: data)
}
}

fileprivate class BundleTargetingClass {}
func loadJSON<T>(name: String) -> T? {
guard let filePath = Bundle(for: BundleTargetingClass.self).url(forResource: name, withExtension: "json") else {
return nil
}
guard let jsonData = try? Data(contentsOf: filePath, options: .mappedIfSafe) else {
return nil
}
guard let json = try? JSONSerialization.jsonObject(with: jsonData, options: .allowFragments) else {
return nil
}
return json as? T
}
👆🏻 copy-paste ready, 3rd party framework independent solution.
usage 👇🏻
let json:[[String : AnyObject]] = loadJSON(name: "Stations")!

Swift 4: Try out my solution:
test.json
{
"person":[
{
"name": "Bob",
"age": "16",
"employed": "No"
},
{
"name": "Vinny",
"age": "56",
"employed": "Yes"
}
]
}
RequestCodable.swift
import Foundation
struct RequestCodable:Codable {
let person:[PersonCodable]
}
PersonCodable.swift
import Foundation
struct PersonCodable:Codable {
let name:String
let age:String
let employed:String
}
Decodable+FromJSON.swift
import Foundation
extension Decodable {
static func fromJSON<T:Decodable>(_ fileName: String, fileExtension: String="json", bundle: Bundle = .main) throws -> T {
guard let url = bundle.url(forResource: fileName, withExtension: fileExtension) else {
throw NSError(domain: NSURLErrorDomain, code: NSURLErrorResourceUnavailable)
}
let data = try Data(contentsOf: url)
return try JSONDecoder().decode(T.self, from: data)
}
}
Example:
let result = RequestCodable.fromJSON("test") as RequestCodable?
result?.person.compactMap({ print($0) })
/*
PersonCodable(name: "Bob", age: "16", employed: "No")
PersonCodable(name: "Vinny", age: "56", employed: "Yes")
*/

Use this generic function
func readJSONFromFile<T: Decodable>(fileName: String, type: T.Type) -> T? {
if let url = Bundle.main.url(forResource: fileName, withExtension: "json") {
do {
let data = try Data(contentsOf: url)
let decoder = JSONDecoder()
let jsonData = try decoder.decode(T.self, from: data)
return jsonData
} catch {
print("error:\(error)")
}
}
return nil
}
with this line of code:
let model = readJSONFromFile(fileName: "Model", type: Model.self)
for this type:
struct Model: Codable {
let tall: Int
}

I'm providing another answer because none of the ones here are geared toward loading the resource from the test bundle. If you are consuming a remote service that puts out JSON and want to unit test parsing the results without hitting the actual service, you take one or more responses and put them into files in the Tests folder in your project.
func testCanReadTestJSONFile() {
let path = NSBundle(forClass: ForecastIOAdapterTests.self).pathForResource("ForecastIOSample", ofType: "json")
if let jsonData = NSData(contentsOfFile:path!) {
let json = JSON(data: jsonData)
if let currentTemperature = json["currently"]["temperature"].double {
println("json: \(json)")
XCTAssertGreaterThan(currentTemperature, 0)
}
}
}
This also uses SwiftyJSON but the core logic of getting the test bundle and loading the file is the answer to the question.

The following code works for me. I am using Swift 5
let path = Bundle.main.path(forResource: "yourJSONfileName", ofType: "json")
var jsonData = try! String(contentsOfFile: path!).data(using: .utf8)!
Then, if your Person Struct (or Class) is Decodable (and also all of its properties), you can simply do:
let person = try! JSONDecoder().decode(Person.self, from: jsonData)
I avoided all the error handling code to make the code more legible.

Updated for Swift 3 with safest way
private func readLocalJsonFile() {
if let urlPath = Bundle.main.url(forResource: "test", withExtension: "json") {
do {
let jsonData = try Data(contentsOf: urlPath, options: .mappedIfSafe)
if let jsonDict = try JSONSerialization.jsonObject(with: jsonData, options: .mutableContainers) as? [String: AnyObject] {
if let personArray = jsonDict["person"] as? [[String: AnyObject]] {
for personDict in personArray {
for (key, value) in personDict {
print(key, value)
}
print("\n")
}
}
}
}
catch let jsonError {
print(jsonError)
}
}
}

Latest swift 3.0 absolutely working
func loadJson(filename fileName: String) -> [String: AnyObject]?
{
if let url = Bundle.main.url(forResource: fileName, withExtension: "json")
{
if let data = NSData(contentsOf: url) {
do {
let object = try JSONSerialization.jsonObject(with: data as Data, options: .allowFragments)
if let dictionary = object as? [String: AnyObject] {
return dictionary
}
} catch {
print("Error!! Unable to parse \(fileName).json")
}
}
print("Error!! Unable to load \(fileName).json")
}
return nil
}

Swift 4 JSON to Class with Decodable - for those who prefer classes
Define the classes as follows:
class People: Decodable {
var person: [Person]?
init(fileName : String){
// url, data and jsonData should not be nil
guard let url = Bundle.main.url(forResource: fileName, withExtension: "json") else { return }
guard let data = try? Data(contentsOf: url) else { return }
guard let jsonData = try? JSONDecoder().decode(People.self, from: data) else { return }
// assigns the value to [person]
person = jsonData.person
}
}
class Person : Decodable {
var name: String
var age: String
var employed: String
}
Usage, pretty abstract:
let people = People(fileName: "people")
let personArray = people.person
This allow methods for both People and Person classes, variables (attributes) and methods can also marked as private if needed.

Swift 5+
Decoding the jsonData with your Struct
if let jsonData = readFile(forName: <your file name>) {
do {
let decodedData = try JSONDecoder().decode(<your struct name>.self, from: jsonData)
return decodedData.<what you expect>
} catch { print("JSON decode error") }
}
This will read the file and return jsonData
In case you are actually in another bundle (test for instance), use:
let bundlePath = Bundle(for: type(of: self)).path(forResource: name, ofType: "json")
private func readFile(forName name: String) -> Data? {
do {
if let bundlePath = Bundle.main.path(forResource: name, ofType: "json"),
let jsonData = try String(contentsOfFile: bundlePath).data(using: .utf8) {
return jsonData
}
} catch {
print(error)
}
return nil
}

I wasted my time in locating file which was located in my project with name Jsondata.json. But I weren't able to locate my File through code....
Solution: Make sure that your Jsondata.json file is added in Project> Build Phases> Copy Bundle Resources. Otherwise you wont be able to get file and Bundle.main.url(forResource: fileName, withExtension: "json") will give you nil always.

One more answer here???
Ok. Hold on! All of the answers before were about using JSONSerialization, or returns nil, or ignores errors.
What is the different
"My solution" (is is not really my, this is a mix of the solutions above) contains:
Modern way to return values: Result<Value,Error> (returns Value or Error)
Avoids nil usage
Contains a slightly verbose error
Uses extension to have pretty/intuitive interface: Model.from(localJSON: "myJsonFile")
Gives possibility to select bundle
Details
Xcode 14
Swift 5.6.1
Solution 1. JSON file -> Decodable
enum JSONParseError: Error {
case fileNotFound
case dataInitialisation(error: Error)
case decoding(error: Error)
}
extension Decodable {
static func from(localJSON filename: String,
bundle: Bundle = .main) -> Result<Self, JSONParseError> {
guard let url = bundle.url(forResource: filename, withExtension: "json") else {
return .failure(.fileNotFound)
}
let data: Data
do {
data = try Data(contentsOf: url)
} catch let error {
return .failure(.dataInitialisation(error: error))
}
do {
return .success(try JSONDecoder().decode(self, from: data))
} catch let error {
return .failure(.decoding(error: error))
}
}
}
Solution 1 Usage
struct Model: Decodable {
let uuid: String
let name: String
}
switch Model.from(localJSON: "myjsonfile") {
case .success(let value):
print(value)
case .failure(let error):
print(error)
}
Solution 2. JSON file -> Dictionary
extension Dictionary where Key == String, Value == Any {
enum JSONParseError: Error {
case fileNotFound(filename: String)
case dataInitialisation(Error)
case jsonSerialization(Error)
case mappingFail(value: Any, toType: Any)
}
static func from(JSONfile url: URL) -> Result<Self, JSONParseError> {
let data: Data
do {
data = try Data(contentsOf: url)
} catch let error {
return .failure(.dataInitialisation(error))
}
let jsonObject: Any
do {
jsonObject = try JSONSerialization.jsonObject(with: data, options: .mutableLeaves)
} catch let error {
return .failure(.jsonSerialization(error))
}
guard let jsonResult = jsonObject as? Self else {
return .failure(.mappingFail(value: jsonObject, toType: Self.Type.self))
}
return .success(jsonResult)
}
static func from(localJSONfile name: String) -> Result<Self, JSONParseError> {
let fileType = "json"
let fullFileName = name + (name.contains(fileType) ? "" : ".\(fileType)")
guard let path = Bundle.main.path(forResource: fullFileName, ofType: "") else {
return .failure(.fileNotFound(filename: fullFileName))
}
return from(JSONfile: URL(fileURLWithPath: path))
}
}
Solution 2 Usage
switch [String: Any].from(localJSONfile: "file.json") {
// OR switch [String: Any].from(localJSONfile: "file.json") {
// OR switch [String: Any].from(JSONfile: url) {
case let .success(dictionary):
print(dictionary)
case let .failure(error):
print("ERROR: \(error)")
}

Based on Abhishek's answer, for iOS 8 this would be:
let masterDataUrl: NSURL = NSBundle.mainBundle().URLForResource("masterdata", withExtension: "json")!
let jsonData: NSData = NSData(contentsOfURL: masterDataUrl)!
let jsonResult: NSDictionary = NSJSONSerialization.JSONObjectWithData(jsonData, options: nil, error: nil) as! NSDictionary
var persons : NSArray = jsonResult["person"] as! NSArray

This worked for me with XCode 8.3.3
func fetchPersons(){
if let pathURL = Bundle.main.url(forResource: "Person", withExtension: "json"){
do {
let jsonData = try Data(contentsOf: pathURL, options: .mappedIfSafe)
let jsonResult = try JSONSerialization.jsonObject(with: jsonData, options: .mutableContainers) as! [String: Any]
if let persons = jsonResult["person"] as? [Any]{
print(persons)
}
}catch(let error){
print (error.localizedDescription)
}
}
}

Swift 4.1 Updated Xcode 9.2
if let filePath = Bundle.main.path(forResource: "fileName", ofType: "json"), let data = NSData(contentsOfFile: filePath) {
do {
let json = try JSONSerialization.jsonObject(with: data as Data, options: JSONSerialization.ReadingOptions.allowFragments)
}
catch {
//Handle error
}
}

//change type based on your struct and right JSON file
let quoteData: [DataType] =
load("file.json")
func load<T: Decodable>(_ filename: String, as type: T.Type = T.self) -> T {
let data: Data
guard let file = Bundle.main.url(forResource: filename, withExtension: nil)
else {
fatalError("Couldn't find \(filename) in main bundle.")
}
do {
data = try Data(contentsOf: file)
} catch {
fatalError("Couldn't load \(filename) from main bundle:\n\(error)")
}
do {
let decoder = JSONDecoder()
return try decoder.decode(T.self, from: data)
} catch {
fatalError("Couldn't parse \(filename) as \(T.self):\n\(error)")
}
}

Swift 5 answer worked for me, except that is missing that i must add a empty file, rename it as xxx.json after it works, and using generics.
func loadJson<T:Codable>(filename fileName: String) -> T? {
if let url = Bundle.main.url(forResource: fileName, withExtension: "json") {
do {
let data = try Data(contentsOf: url)
let decoder = JSONDecoder()
return try decoder.decode(T.self, from: data)
} catch {
print("error:\(error)")
}
}
return nil
}
code

I’ve used below code to fetch JSON from FAQ-data.json file present in project directory .
I’m implementing in Xcode 7.3 using Swift.
func fetchJSONContent() {
if let path = NSBundle.mainBundle().pathForResource("FAQ-data", ofType: "json") {
if let jsonData = NSData(contentsOfFile: path) {
do {
if let jsonResult: NSDictionary = try NSJSONSerialization.JSONObjectWithData(jsonData, options: NSJSONReadingOptions.MutableContainers) as? NSDictionary {
if let responseParameter : NSDictionary = jsonResult["responseParameter"] as? NSDictionary {
if let response : NSArray = responseParameter["FAQ"] as? NSArray {
responseFAQ = response
print("response FAQ : \(response)")
}
}
}
}
catch { print("Error while parsing: \(error)") }
}
}
}
override func viewWillAppear(animated: Bool) {
fetchFAQContent()
}
Structure of JSON file :
{
"status": "00",
"msg": "FAQ List ",
"responseParameter": {
"FAQ": [
{
"question": “Question No.1 here”,
"answer": “Answer goes here”,
"id": 1
},
{
"question": “Question No.2 here”,
"answer": “Answer goes here”,
"id": 2
}
. . .
]
}
}

I might also recommend Ray Wenderlich's Swift JSON Tutorial (which also covers the awesome SwiftyJSON alternative, Gloss). An excerpt (which granted, by itself, does not fully answer the poster, but the added value of this answer is the link, so no -1's for that, please):
In Objective-C, parsing and deserializing JSON is fairly straightforward:
NSArray *json = [NSJSONSerialization JSONObjectWithData:JSONData
options:kNilOptions error:nil];
NSString *age = json[0][#"person"][#"age"];
NSLog(#"Dani's age is %#", age);
In Swift, parsing and deserializing JSON is a little more tedious due to Swift optionals and type-safety [but as] part of Swift 2.0 the guard statement was introduced to help get rid of nested if statements:
var json: Array!
do {
json = try NSJSONSerialization.JSONObjectWithData(JSONData, options: NSJSONReadingOptions()) as? Array
} catch {
print(error)
}
guard let item = json[0] as? [String: AnyObject],
let person = item["person"] as? [String: AnyObject],
let age = person["age"] as? Int else {
return;
}
print("Dani's age is \(age)")
Of course, in XCode 8.x, you just double-tap the space bar and say "Hey, Siri, please deserialize this JSON for me in Swift 3.0 with space/tab-indents."

SWIFTYJSON VERSION SWIFT 3
func loadJson(fileName: String) -> JSON {
var dataPath:JSON!
if let path : String = Bundle.main.path(forResource: fileName, ofType: "json") {
if let data = NSData(contentsOfFile: path) {
dataPath = JSON(data: data as Data)
}
}
return dataPath
}

Related

Save JSON response as JSON file

I want to save my JSON response to a JSON file in document or any other directory.
Earlier I was trying to save the response in coreData but that was a heavy and slow task.
//API Manager function
func loadEmployees(urlString: String, completion: #escaping ((Any?,Error?) -> ())){
guard let url = URL(string: urlString) else { return }
var request = URLRequest(url: url)
request.httpMethod = RequestMethod.get.rawValue
let session = URLSession.shared
let sessionTask = session.dataTask(with: request) { (data, response, error) in
if error == nil {
let result = try? JSONDecoder().decode([EmployeeDetails].self, from: data!)
completion(result, nil)
}
else {
completion(nil, ServiceError.customError("Please check your internet connection"))
}
}
sessionTask.resume()
}
//I am calling it in my View Controller
NetworkManager.sharedInstance.loadEmployees(urlString: EMPLOYEEBASEURL, completion: { (data, responseError) in
if let error = responseError {
self.showToast(controller: self, message: error.localizedDescription, seconds: 1.6)
}else{
if data != nil {
DispatchQueue.global().async {
self.employeeListArray = data as! [EmployeeDetails]
self.filteredEmployeeArray = self.employeeListArray
DispatchQueue.main.async {
self.loader.isHidden = true
self.employeeTableView.reloadData()
}
}
}
}
})
//My model
struct EmployeeDetails: Decodable {
let id: String?
let name: String?
let salary: String?
let age: String?
let profileImage: String?
enum CodingKeys: String, CodingKey {
case id = "id"
case name = "employee_name"
case salary = "employee_salary"
case age = "employee_age"
case profileImage = "profile_image"
}
}
Now Instead of parsing it directly I want to save the response in a json file and parse from the file.
I can install any pods if required, my Project is in Swift 5.0 so newer methods are also acceptable.
To save:-
func saveJsonFile(_ name:String, data:Data) {
// Get the url of File in document directory
guard let documentDirectoryUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return }
let fileUrl = documentDirectoryUrl.appendingPathComponent(name + ".json")
// Transform array into data and save it into file
do {
//let data = try JSONSerialization.data(withJSONObject: list, options: [])
try data.write(to: fileUrl, options: .completeFileProtection)
} catch {
print(error)
}
}
Retrive:-
func retrieveFromJsonFile(_ name:String) -> [JSONObject]? {
// Get the url of File in document directory
guard let documentsDirectoryUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return nil}
let fileUrl = documentsDirectoryUrl.appendingPathComponent(name + ".json")
// Check for file in file manager.
guard (FileManager.default.fileExists(atPath: fileUrl.path))else {return nil}
// Read data from .json file and transform data into an array
do {
let data = try Data(contentsOf: fileUrl, options: [])
guard let list = try JSONSerialization.jsonObject(with: data, options: []) as? [JSONObject] else { return nil}
//print(list)
return list
} catch {
print(error)
return nil
}
}
Delete json file:-
func removeFile(with name: String){
// Path for the file.
guard let documentsDirectoryUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first else { return}
let fileUrl = documentsDirectoryUrl.appendingPathComponent(name + ".json")
if (FileManager.default.fileExists(atPath: fileUrl.absoluteString)){
do{
try FileManager.default.removeItem(at: fileUrl)
}catch{
print(error.localizedDescription)
}
}
}
where JSONObject:- [String: Any]

How to parse JSON using swift 4

I am confusing to getting detail of fruit
{
"fruits": [
{
"id": "1",
"image": "https://cdn1.medicalnewstoday.com/content/images/headlines/271/271157/bananas.jpg",
"name": "Banana"
},
{
"id": "2",
"image": "http://soappotions.com/wp-content/uploads/2017/10/orange.jpg",
"title": "Orange"
}
]
}
Want to parse JSON using "Decodable"
struct Fruits: Decodable {
let Fruits: [fruit]
}
struct fruit: Decodable {
let id: Int?
let image: String?
let name: String?
}
let url = URL(string: "https://www.JSONData.com/fruits")
URLSession.shared.dataTask(with: url!) { (data, response, error) in
guard let data = data else { return }
do{
let fruits = try JSONDecoder().decode(Fruits.self, from: data)
print(Fruits)
}catch {
print("Parse Error")
}
also can you please suggest me cocoapod library for fastly download images
The issue you are facing is because your JSON is returning different data for your Fruits.
For the 1st ID it returns a String called name, but in the 2nd it returns a String called title.
In addition when parsing the JSON the ID appears to be a String and not an Int.
Thus you have two optional values from your data.
As such your Decodable Structure should look something like this:
struct Response: Decodable {
let fruits: [Fruits]
}
struct Fruits: Decodable {
let id: String
let image: String
let name: String?
let title: String?
}
Since your URL doesn't seem to be valid, I created the JSON file in my main bundle and was able to parse it correctly like so:
/// Parses The JSON
func parseJSON(){
if let path = Bundle.main.path(forResource: "fruits", ofType: "json") {
do {
let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe)
let jsonResult = try JSONDecoder().decode(Response.self, from: data)
let fruitsArray = jsonResult.fruits
for fruit in fruitsArray{
print("""
ID = \(fruit.id)
Image = \(fruit.image)
""")
if let validName = fruit.name{
print("Name = \(validName)")
}
if let validTitle = fruit.title{
print("Title = \(validTitle)")
}
}
} catch {
print(error)
}
}
}
Hope it helps...
// Parse Json using decodable
// First in create Structure depends on json
//
//
//
struct Countory : Decodable {
let name: String
let capital: String
let region: String
}
let url = "https://restcountries.eu/rest/v2/all"
let urlObj = URL(string: url)!
URLSession.shared.dataTask(with: urlObj) {(data, responds, Error) in
do {
var countories = try JSONDecoder().decode([Countory].self, from: data!)
for country in countories {
print("Country",country.name)
print("###################")
print("Capital",country.capital)
}
} catch {
print(" not ")
}
}.resume()
Model sample:
public struct JsonData: Codable{
let data: [Data]?
let meta: MetaValue?
let linksData: LinksValue?
private enum CodingKeys: String, CodingKey{
case data
case meta
case linksData = "links"
}
}
enum BackendError: Error {
case urlError(reason: String)
case objectSerialization(reason: String)
}
struct APIServiceRequest {
static func serviceRequest<T>(reqURLString: String,
resultStruct: T.Type,
completionHandler:#escaping ((Any?, Error?) -> ())) where T : Decodable {
guard let url = URL(string: reqURLString) else {
print("Error: cannot create URL")
let error = BackendError.urlError(reason: "Could not construct URL")
completionHandler(nil, error)
return
}
let urlRequest = URLRequest(url: url)
let session = URLSession.shared
let task = session.dataTask(with: urlRequest) { (data, response, error) in
guard error == nil else {
completionHandler(nil, error)
return
}
guard let responseData = data else {
print("Error: did not receive data")
let error = BackendError.objectSerialization(reason: "No data in response")
completionHandler(nil, error)
return
}
let decoder = JSONDecoder()
do {
let books = try decoder.decode(resultStruct, from: responseData)
completionHandler(books, nil)
} catch {
print("error trying to convert data to JSON")
print(error)
completionHandler(nil, error)
}
}
task.resume()
}
}
To Access:
let apiService = APIServiceRequest()
var dataArray: [String: Any]? //global var
apiService.serviceRequest(reqURLString: endPoint, resultStruct: VariantsModel.self, completionHandler: {dataArray,Error in})
POST Method
func loginWS(endpoint: String, completionHandler: #escaping (Any?) -> Swift.Void) {
guard let sourceUrl = URL(string: endpoint) else { return }
let request = NSMutableURLRequest(url: sourceUrl)
let session = URLSession.shared
request.httpMethod = "POST"
request.addValue(vehiceHeader, forHTTPHeaderField: "X-Vehicle-Type")
request.addValue(contentHeader, forHTTPHeaderField: "Content-Type")
let task = session.dataTask(with: request as URLRequest) { data, response, error in
guard let data = data else { return }
do {
let responseData = try JSONDecoder().decode(JsonData.self, from: data)
print("response data:", responseData)
completionHandler(responseData)
} catch let err {
print("Err", err)
}
}.resume()
}

Encode/Decode JSON locally from documentDirectory

I am fairly new to SWIFT and am trying to use data from a JSON file (financetoday.json) to populate a table and for the user to update and have the data stored on the device. The table has collapsing cells (using XIB) and each cell has an embedded UISlider to update the values. On my initial work, I have the table loading the JSON file successfully from the bundle, populating the table, and the slider changes each value. Now the hard part. In order to save/change the data I need to move the JSON file into the documentDirectory, then have any changes to the data from this file. Once the user starts the app for the first time, I no longer need to use the JSON file in the bundle, just the version in the documentDirectory. I have been unable to get table to read the JSON file in the documentDirectory. Any help would be appreciated.
Here is where I have added a method in AppDelegate to move the JSON file in the document Directory
// Move json file from bundle to documents folder
var finalDatabaseURL:String = String()
func copyDatabaseIfNeeded() {
let fileManager = FileManager.default
let documentsUrl = fileManager.urls(for: .documentDirectory,
in: .userDomainMask)
guard documentsUrl.count != 0 else {
return // Could not find documents URL
}
let finalDatabaseURL = documentsUrl.first!.appendingPathComponent("financetoday.json")
if !( (try? finalDatabaseURL.checkResourceIsReachable()) ?? false) {
print("DB does not exist in documents folder")
let documentsURL = Bundle.main.resourceURL?.appendingPathComponent("financetoday.json")
do {
try fileManager.copyItem(atPath: (documentsURL?.path)!, toPath: finalDatabaseURL.path)
} catch let error as NSError {
print("Couldn't copy file to final location! Error:\(error.description)")
}
} else {
print("Database file found at path: \(finalDatabaseURL.path)")
}
}
Then I added to applicationDidBecomeActive
self.copyDatabaseIfNeeded()
In my data model this is what it looks like loading JSON data from the bundle, but I need to change the code in method dataFromFile to use JSON file in documentDirectory...not the bundle. All my attempts to change results in a blank table. So for now I am pointing to the JSON in the bundle. Any help would be appreciated.
import Foundation
public func dataFromFile(_ filename: String) -> Data? {
#objc class TestClass: NSObject { }
let bundle = Bundle(for: TestClass.self)
if let path = bundle.path(forResource: filename, ofType: "json") {
return (try? Data(contentsOf: URL(fileURLWithPath: path)))
}
return nil
}
class Plan {
var yeardata: Int?
var incomedata = [Income]()
var expensedata = [Expense]()
var assetdata = [Asset]()
var liabilitydata = [Liability]()
var profiledata = [Profile]()
var assumptiondata = [Assumption]()
init?(data: Data) {
do {
if let json = try JSONSerialization.jsonObject(with: data) as? [String: Any], let myplan = json["data"] as? [String: Any] {
if let incomedata = myplan["incomedata"] as? [[String: Any]] {
self.incomedata = incomedata.map { Income(json: $0) }
}
if let expensedata = myplan["expensedata"] as? [[String: Any]] {
self.expensedata = expensedata.map { Expense(json: $0) }
}
if let assetdata = myplan["assetdata"] as? [[String: Any]] {
self.assetdata = assetdata.map { Asset(json: $0) }
}
if let liabilitydata = myplan["liabilitydata"] as? [[String: Any]] {
self.liabilitydata = liabilitydata.map { Liability(json: $0) }
}
if let profiledata = myplan["profiledata"] as? [[String: Any]] {
self.profiledata = profiledata.map { Profile(json: $0) }
}
if let assumptiondata = myplan["assumptiondata"] as? [[String: Any]] {
self.assumptiondata = assumptiondata.map { Assumption(json: $0) }
}
}
} catch {
print("Error deserializing JSON: \(error)")
return nil
}
}
}
class Income {
var key: String?
var value: Any?
init(json: [String: Any]) {
self.key = json["key"] as? String
self.value = json["value"] as Any
}
}
class Expense {
var key: String?
var value: Any?
init(json: [String: Any]) {
self.key = json["key"] as? String
self.value = json["value"] as Any
}
}
class Asset {
var key: String?
var value: Any?
init(json: [String: Any]) {
self.key = json["key"] as? String
self.value = json["value"] as Any
}
}
class Liability {
var key: String?
var value: Any?
init(json: [String: Any]) {
self.key = json["key"] as? String
self.value = json["value"] as Any
}
}
class Profile {
var key: String?
var value: Any?
init(json: [String: Any]) {
self.key = json["key"] as? String
self.value = json["value"] as Any
}
}
class Assumption {
var key: String?
var value: Any?
init(json: [String: Any]) {
self.key = json["key"] as? String
self.value = json["value"] as Any
}
}
This will read in the json. The dictionary conversion I am less familiar with because I have started using the Codable protocol which I highly recommend.
if let path = Bundle.main.path(forResource: "FileName", ofType: "json") {
do {
let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .alwaysMapped)
let jsonString = String(data: value, encoding: .utf8)
print("json as string: \(jsonString)")
let json = try JSONSerialization.data(withJSONObject: data, options: []) as? [String: Any]
print("json as dictionary: \(json)")
} catch let error {
print("parse error: \(error.localizedDescription)")
}
}
How to decode codable Data:
let decoder = JSONDecoder()
do {
let decodableJSON = try decoder.decode(ObjectConformingToCodable.self, from: data)
print(decodableJSON)
} catch let error {
print(error.localizedDescription)
}
not sure if this is relevant but could try:
let documentsDirectoryPathString = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!
let documentsDirectoryPath = NSURL(string: documentsDirectoryPathString)!
let jsonFilePath = documentsDirectoryPath.appendingPathComponent("test.json")
let fileManager = FileManager.default
var isDirectory: ObjCBool = false
// creating a .json file in the Documents folder
if fileManager.fileExists(atPath: (jsonFilePath?.absoluteString)!, isDirectory: &isDirectory) {
print("File exists")
}
I also found: JSONSaveLoad.swift on Gist:
https://gist.github.com/norsez/aa3f11c0e875526e5270e7791f3891fb
I'm sure there are other similar examples on Github

Turning JSON file into an array in Swift/Xcode

I have been struggling all week. I am new to programming. I cannot turn a simple JSON file into a dictionary in Xcode. There is little simplified documentation online using the new method of Codable. So I am using a walkthrough, which has the following code.
import UIKit
class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
guard let path = Bundle.main.path(forResource: "menu", ofType: "json") else { return }
let url = URL(fileURLWithPath: path)
do {
let data = try Data(contentsOf: url)
let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers)
//print(json)
guard let array = json as? [Any] else { return }
for user in array {
guard let userDict = user as? [String: Any] else { return }
guard let drinks = userDict["drinks"] as? String else { print("not a String"); return }
guard let junkFood = userDict["junk-food"] as? String else { return }
print(drinks)
print(junkFood)
print(" ")
}
}
catch {
print(error)
}
}
}
The below code is what my JSON looks like.
{"menu": {
"drinks": [
{"coke": "20"},
{"pepsi": "20"},
{"water": "20"}
],
"junk-food": [
{"hamburger": "40"},
{"fries": "20"},
{"pizza": "20"}
]
}}
Can anyone please walk me through, or show me some simplified documentation as to how I can turn the JSON into a dictionary that I can later map the data from? I am using Xcode and trying to work out Swift 4.
Thanks in advance for your patience.
My guess is that your json is actually a Dictionary not an Array. So guard let array = json as? [Any] else { return } is falling through because the json is [String: Any]. You can get to the array with the "menu" key.
Here's an updated version of your code:
do {
let data = try Data(contentsOf: url)
let json = try JSONSerialization.jsonObject(with: data, options: .mutableContainers)
//print(json)
guard let menuDict = json as? [String: Any] else { return }
guard let drinks = menuDict["drinks"] as? [[String: Any]] else {
print("not an array of dictionaries")
return
}
guard let junkFood = menuDict["junk-food"] as? [[String: Any]] else {
print("not an array of dictionaries")
return
}
print(drinks)
print(junkFood)
print(" ")
}
Try that, let me know if it works. This is just the do block by the way.

Read JSON file with Swift 3

I have a JSON file called points.json, and a read function like:
private func readJson() {
let file = Bundle.main.path(forResource: "points", ofType: "json")
let data = try? Data(contentsOf: URL(fileURLWithPath: file!))
let jsonData = try? JSONSerialization.jsonObject(with: data!, options: []) as! [String:Any]
print(jsonData)
}
It does not work, any help?
Your problem here is that you force unwrap the values and in case of an error you can't know where it comes from.
Instead, you should handle errors and safely unwrap your optionals.
And as #vadian rightly notes in his comment, you should use Bundle.main.url.
private func readJson() {
do {
if let file = Bundle.main.url(forResource: "points", withExtension: "json") {
let data = try Data(contentsOf: file)
let json = try JSONSerialization.jsonObject(with: data, options: [])
if let object = json as? [String: Any] {
// json is a dictionary
print(object)
} else if let object = json as? [Any] {
// json is an array
print(object)
} else {
print("JSON is invalid")
}
} else {
print("no file")
}
} catch {
print(error.localizedDescription)
}
}
When coding in Swift, usually, ! is a code smell. Of course there's exceptions (IBOutlets and others) but try to not use force unwrapping with ! yourself and always unwrap safely instead.
The Swift 5 / iOS 12.3 code below shows a possible rewrite of your method that avoids force unwrap on optional values and handles gently potential errors:
import Foundation
func readJson() {
// Get url for file
guard let fileUrl = Bundle.main.url(forResource: "Data", withExtension: "json") else {
print("File could not be located at the given url")
return
}
do {
// Get data from file
let data = try Data(contentsOf: fileUrl)
// Decode data to a Dictionary<String, Any> object
guard let dictionary = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else {
print("Could not cast JSON content as a Dictionary<String, Any>")
return
}
// Print result
print(dictionary)
} catch {
// Print error if something went wrong
print("Error: \(error)")
}
}