From a7fe036b988969a94cd8359dae8a24ade2ee8964 Mon Sep 17 00:00:00 2001 From: Lois Di Qual Date: Wed, 23 Nov 2016 01:29:57 -0800 Subject: [PATCH 1/2] Added init(jsonString) and made init(data) throw --- README.md | 12 +++--- Source/SwiftyJSON.swift | 43 +++++++++++-------- Tests/SwiftyJSONTests/BaseTests.swift | 43 ++++++++++++++----- Tests/SwiftyJSONTests/PerformanceTests.swift | 24 +++++------ Tests/SwiftyJSONTests/SequenceTypeTests.swift | 33 +++++++------- 5 files changed, 89 insertions(+), 66 deletions(-) diff --git a/README.md b/README.md index 3a43aaa1..05bd2566 100644 --- a/README.md +++ b/README.md @@ -56,7 +56,7 @@ An unreadable mess--for something that should really be simple! With SwiftyJSON all you have to do is: ```swift -let json = JSON(data: dataFromNetworking) +let json = try JSON(data: dataFromNetworking) if let userName = json[0]["user"]["name"].string { //Now you got your value } @@ -65,7 +65,7 @@ if let userName = json[0]["user"]["name"].string { And don't worry about the Optional Wrapping thing. It's done for you automatically. ```swift -let json = JSON(data: dataFromNetworking) +let json = try JSON(data: dataFromNetworking) if let userName = json[999999]["wrong_key"]["wrong_name"].string { //Calm down, take it easy, the ".string" property still produces the correct Optional String type with safety } else { @@ -139,17 +139,15 @@ import SwiftyJSON ``` ```swift -let json = JSON(data: dataFromNetworking) +let json = try JSON(data: data) ``` ```swift -let json = JSON(jsonObject) +let json = try JSON(jsonString: string) ``` ```swift -if let dataFromString = jsonString.data(using: .utf8, allowLossyConversion: false) { - let json = JSON(data: dataFromString) -} +let json = JSON(jsonObject) ``` #### Subscript diff --git a/Source/SwiftyJSON.swift b/Source/SwiftyJSON.swift index f74e21b5..649d7f81 100644 --- a/Source/SwiftyJSON.swift +++ b/Source/SwiftyJSON.swift @@ -64,27 +64,32 @@ public struct JSON { - returns: The created JSON */ - public init(data:Data, options opt: JSONSerialization.ReadingOptions = .allowFragments, error: NSErrorPointer = nil) { + public init(data: Data, options: JSONSerialization.ReadingOptions = .allowFragments) throws { do { - let object: Any = try JSONSerialization.jsonObject(with: data, options: opt) + let object: Any = try JSONSerialization.jsonObject(with: data, options: options) self.init(object) - } catch let aError as NSError { - if error != nil { - error?.pointee = aError - } - self.init(NSNull()) - } - } - - /** - Creates a JSON from JSON string - - parameter string: Normal json string like '{"a":"b"}' - - - returns: The created JSON - */ - public static func parse(_ string:String) -> JSON { - return string.data(using: String.Encoding.utf8) - .flatMap{ JSON(data: $0) } ?? JSON(NSNull()) + } catch let error as NSError { + throw NSError(domain: ErrorDomain, code: ErrorInvalidJSON, userInfo: [ + NSLocalizedDescriptionKey: "Provided data is not a valid JSON", + NSLocalizedFailureReasonErrorKey: "\(error.localizedDescription). \(error.localizedFailureReason)" + ]) + } + } + + /// Initializes using a JSON string. + /// + /// - Parameters: + /// - jsonString: JSON string to parse + /// - encoding: String encoding to use when parsing the provided JSON. Must be one of the following: + /// .utf8, .utf16LittleEndian, .utf16BigEndian, .utf32LittleEndian, .utf32BigEndian + /// as specified in https://developer.apple.com/reference/foundation/jsonserialization/1415493-jsonobject + /// - options: JSON reading options + /// - Throws: Throws if the string cannot be converted to raw data, or if it is not a valid JSON string + public init(jsonString: String, encoding: String.Encoding = .utf8, options: JSONSerialization.ReadingOptions = .allowFragments) throws { + guard let data = jsonString.data(using: encoding) else { + throw NSError(domain: ErrorDomain, code: ErrorInvalidJSON, userInfo: [NSLocalizedDescriptionKey: "Provided string is not a valid JSON string"]) + } + try self.init(data: data, options: options) } /** diff --git a/Tests/SwiftyJSONTests/BaseTests.swift b/Tests/SwiftyJSONTests/BaseTests.swift index 0ec670ed..28e8e1a8 100644 --- a/Tests/SwiftyJSONTests/BaseTests.swift +++ b/Tests/SwiftyJSONTests/BaseTests.swift @@ -26,6 +26,7 @@ import XCTest class BaseTests: XCTestCase { var testData: Data! + var testString: String! override func setUp() { @@ -33,6 +34,7 @@ class BaseTests: XCTestCase { if let file = Bundle(for:BaseTests.self).path(forResource: "Tests", ofType: "json") { self.testData = try? Data(contentsOf: URL(fileURLWithPath: file)) + self.testString = try? String(contentsOf: URL(fileURLWithPath: file)) } else { XCTFail("Can't find the test JSON file") } @@ -42,8 +44,8 @@ class BaseTests: XCTestCase { super.tearDown() } - func testInit() { - let json0 = JSON(data:self.testData) + func testInitWithData() throws { + let json0 = try JSON(data:self.testData) XCTAssertEqual(json0.array!.count, 3) XCTAssertEqual(JSON("123").description, "123") XCTAssertEqual(JSON(["1":"2"])["1"].string!, "2") @@ -51,12 +53,31 @@ class BaseTests: XCTestCase { dictionary.setObject(NSNumber(value: 1.0), forKey: "number" as NSString) dictionary.setObject(NSNull(), forKey: "null" as NSString) _ = JSON(dictionary) + + let object: Any = try JSONSerialization.jsonObject(with: self.testData, options: []) + let json2 = JSON(object) + XCTAssertEqual(json0, json2) + } + + func testInitWithJsonString() { + do { - let object: Any = try JSONSerialization.jsonObject(with: self.testData, options: []) + let json1 = try JSON(jsonString: testString) + let object = try JSONSerialization.jsonObject(with: self.testData, options: []) let json2 = JSON(object) - XCTAssertEqual(json0, json2) - } catch _ { + XCTAssertEqual(json1, json2) + } catch { + XCTFail("Should not throw an error") } + + do { + _ = try JSON(jsonString: "{\"foo\":\"bar\"}") + } catch { + XCTFail("Should not throw an error") + } + + XCTAssertThrowsError(try JSON(jsonString: "{\"foo\":")) + XCTAssertThrowsError(try JSON(jsonString: "[")) } func testCompare() { @@ -75,8 +96,8 @@ class BaseTests: XCTestCase { XCTAssertNotEqual(JSON(NSNull()), JSON(123)) } - func testJSONDoesProduceValidWithCorrectKeyPath() { - let json = JSON(data:self.testData) + func testJSONDoesProduceValidWithCorrectKeyPath() throws { + let json = try JSON(data:self.testData) let tweets = json let tweets_array = json.array @@ -224,8 +245,8 @@ class BaseTests: XCTestCase { XCTAssertFalse(jsonForArray["someValue"].exists()) } - func testErrorHandle() { - let json = JSON(data:self.testData) + func testErrorHandle() throws { + let json = try JSON(data: self.testData) if let _ = json["wrong-type"].string { XCTFail("Should not run into here") } else { @@ -244,8 +265,8 @@ class BaseTests: XCTestCase { } } - func testReturnObject() { - let json = JSON(data:self.testData) + func testReturnObject() throws { + let json = try JSON(data: self.testData) XCTAssertNotNil(json.object) } diff --git a/Tests/SwiftyJSONTests/PerformanceTests.swift b/Tests/SwiftyJSONTests/PerformanceTests.swift index 80b3c028..e05e8b69 100644 --- a/Tests/SwiftyJSONTests/PerformanceTests.swift +++ b/Tests/SwiftyJSONTests/PerformanceTests.swift @@ -45,28 +45,28 @@ class PerformanceTests: XCTestCase { func testInitPerformance() { self.measure() { for _ in 1...100 { - let json = JSON(data:self.testData) - XCTAssertTrue(json != JSON.null) + let json = try? JSON(data:self.testData) + XCTAssertNotNil(json) } } } func testObjectMethodPerformance() { - let json = JSON(data:self.testData) + let json = try? JSON(data:self.testData) self.measure() { for _ in 1...100 { - let object:Any? = json.object - XCTAssertTrue(object != nil) + let object:Any? = json?.object + XCTAssertNotNil(object) } } } func testArrayMethodPerformance() { - let json = JSON(data:self.testData) + let json = try? JSON(data:self.testData) self.measure() { for _ in 1...100 { autoreleasepool{ - if let array = json.array { + if let array = json?.array { XCTAssertTrue(array.count > 0) } } @@ -75,11 +75,11 @@ class PerformanceTests: XCTestCase { } func testDictionaryMethodPerformance() { - let json = JSON(data:testData)[0] + let json = try? JSON(data:testData)[0] self.measure() { for _ in 1...100 { autoreleasepool{ - if let dictionary = json.dictionary { + if let dictionary = json?.dictionary { XCTAssertTrue(dictionary.count > 0) } } @@ -88,12 +88,12 @@ class PerformanceTests: XCTestCase { } func testRawStringMethodPerformance() { - let json = JSON(data:testData) + let json = try? JSON(data:testData) self.measure() { for _ in 1...100 { autoreleasepool{ - let string = json.rawString() - XCTAssertTrue(string != nil) + let string = json?.rawString() + XCTAssertNotNil(string) } } } diff --git a/Tests/SwiftyJSONTests/SequenceTypeTests.swift b/Tests/SwiftyJSONTests/SequenceTypeTests.swift index 62bd7dcf..920ef22f 100644 --- a/Tests/SwiftyJSONTests/SequenceTypeTests.swift +++ b/Tests/SwiftyJSONTests/SequenceTypeTests.swift @@ -26,24 +26,23 @@ import SwiftyJSON class SequenceTypeTests: XCTestCase { - func testJSONFile() { - if let file = Bundle(for:BaseTests.self).path(forResource: "Tests", ofType: "json") { - let testData = try? Data(contentsOf: URL(fileURLWithPath: file)) - let json = JSON(data:testData!) - for (index, sub) in json { - switch (index as NSString).integerValue { - case 0: - XCTAssertTrue(sub["id_str"] == "240558470661799936") - case 1: - XCTAssertTrue(sub["id_str"] == "240556426106372096") - case 2: - XCTAssertTrue(sub["id_str"] == "240539141056638977") - default: - continue - } + func testJSONFile() throws { + guard let file = Bundle(for:BaseTests.self).path(forResource: "Tests", ofType: "json") else { + return XCTFail("Can't find the test JSON file") + } + let testData = try Data(contentsOf: URL(fileURLWithPath: file)) + let json = try JSON(data: testData) + for (index, sub) in json { + switch (index as NSString).integerValue { + case 0: + XCTAssertTrue(sub["id_str"] == "240558470661799936") + case 1: + XCTAssertTrue(sub["id_str"] == "240556426106372096") + case 2: + XCTAssertTrue(sub["id_str"] == "240539141056638977") + default: + continue } - } else { - XCTFail("Can't find the test JSON file") } } From a4effb18acecdbc49cbd46c5bee03512c0702979 Mon Sep 17 00:00:00 2001 From: Lois Di Qual Date: Sat, 3 Dec 2016 14:54:47 -0800 Subject: [PATCH 2/2] Updated changelog --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 7b15b869..3c726383 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,8 +32,14 @@ - It seems not easy to manipulate an array or dictionary? [\#110](https://github.com/SwiftyJSON/SwiftyJSON/issues/110) +- Document JSON.parse() usage in README [\#459](https://github.com/SwiftyJSON/SwiftyJSON/issues/459) + **Merged pull requests:** +- **Breaking**: Removed `SwiftyJSON.parse` and added `init(jsonString:)`. [\#731](https://github.com/SwiftyJSON/SwiftyJSON/pull/731) + +- **Breaking**: `init(data:)` now throws if the data cannot be converted to a proper JSON object. [\#731](https://github.com/SwiftyJSON/SwiftyJSON/pull/731) + - Fix for xcode 6.3..1 issue [\#224](https://github.com/SwiftyJSON/SwiftyJSON/pull/224) ([datomnurdin](https://github.com/datomnurdin)) - Update the first two examples snippets [\#223](https://github.com/SwiftyJSON/SwiftyJSON/pull/223) ([kmikael](https://github.com/kmikael))