From 87d9828598015f683ae8293317ff212abdd051ba Mon Sep 17 00:00:00 2001 From: farshad jahanmanesh Date: Sun, 30 Sep 2018 18:43:42 +0330 Subject: [PATCH] Dynamic Member Lookup introduced in Swift 4.2, now we can use dot, instead of brackets (#986) * This proposal introduces a new @dynamicMemberLookup attribute. Types that use it provide "dot" syntax for arbitrary names which are resolved at runtime - in a completely type safe way. This provides syntactic sugar that allows the user to write **[introduce-user-defined-dynamic-member-lookup-types]**(https://github.com/apple/swift-evolution/blob/master/proposals/0195-dynamic-member-lookup.md#introduce-user-defined-dynamic-member-lookup-types) i've added a subscript function with dynamic member, so users can use dot instead of [], ``` let name = json.name The same as: let name = json["name"] let phoneNumber = json.user.contactInfo.phoneNumber The same as: let name = json["user"]["contactInfo"]["phoneNumber"] ``` * resolve houndci-bot conversation * update swift version to 4.2 * change travis swift version to 4.2.0 * set travis simulator ios version * check for ios 12 * redu changes to travis.yml * new test file added, DynamicMemberLookupTests.swift * email address changed to some random email address * remove a test because it was duplicate * resolve hound comma --- .travis.yml | 2 +- Source/SwiftyJSON.swift | 26 +++++++- SwiftyJSON.podspec | 2 +- SwiftyJSON.xcodeproj/project.pbxproj | 8 ++- .../DynamicMemberLookupTests.swift | 65 +++++++++++++++++++ Tests/SwiftyJSONTests/SubscriptTests.swift | 2 +- 6 files changed, 99 insertions(+), 6 deletions(-) create mode 100644 Tests/SwiftyJSONTests/DynamicMemberLookupTests.swift diff --git a/.travis.yml b/.travis.yml index 34acdea7..be1d25b9 100644 --- a/.travis.yml +++ b/.travis.yml @@ -5,4 +5,4 @@ script: - set -o pipefail - travis_retry xcodebuild -workspace SwiftyJSON.xcworkspace -scheme "SwiftyJSON iOS" -destination "platform=iOS Simulator,name=iPhone 6" build-for-testing test | xcpretty - travis_retry xcodebuild -workspace SwiftyJSON.xcworkspace -scheme "SwiftyJSON macOS" build-for-testing test | xcpretty -- travis_retry xcodebuild -workspace SwiftyJSON.xcworkspace -scheme "SwiftyJSON tvOS" -destination "platform=tvOS Simulator,name=Apple TV" build-for-testing test | xcpretty +- travis_retry xcodebuild -workspace SwiftyJSON.xcworkspace -scheme "SwiftyJSON tvOS" -destination "platform=tvOS Simulator,name=Apple TV" build-for-testing test | xcpretty \ No newline at end of file diff --git a/Source/SwiftyJSON.swift b/Source/SwiftyJSON.swift index f3553fe5..ebd40ba1 100644 --- a/Source/SwiftyJSON.swift +++ b/Source/SwiftyJSON.swift @@ -94,7 +94,7 @@ public enum Type: Int { } // MARK: - JSON Base - +@dynamicMemberLookup public struct JSON { /** @@ -538,6 +538,30 @@ extension JSON { self[path] = newValue } } + + /** + Find a json in the complex data structures by using dynamic member name. + + - parameter path: The target json's path. Example: + + you seperate your path by . (dot) + + let name = json.name + The same as: let name = json["name"] + + let phoneNumber = json.user.contactInfo.phoneNumber + The same as: let name = json["user"]["contactInfo"]["phoneNumber"] + + - returns: Return a json found by the path or a null json with error + */ + public subscript(dynamicMember member: String) -> JSON { + get { + return self[sub:member] + } + set { + self[sub:member] = newValue + } + } } // MARK: - LiteralConvertible diff --git a/SwiftyJSON.podspec b/SwiftyJSON.podspec index b1cdf26f..03e453b3 100644 --- a/SwiftyJSON.podspec +++ b/SwiftyJSON.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "SwiftyJSON" - s.version = "4.1.0" + s.version = "4.2.0" s.summary = "SwiftyJSON makes it easy to deal with JSON data in Swift" s.homepage = "https://github.com/SwiftyJSON/SwiftyJSON" s.license = { :type => "MIT" } diff --git a/SwiftyJSON.xcodeproj/project.pbxproj b/SwiftyJSON.xcodeproj/project.pbxproj index 1ec04ff6..a0561636 100644 --- a/SwiftyJSON.xcodeproj/project.pbxproj +++ b/SwiftyJSON.xcodeproj/project.pbxproj @@ -34,6 +34,7 @@ 712921F12004E4EB00DA6340 /* CodableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 712921EE2004E4EB00DA6340 /* CodableTests.swift */; }; 7236B4EE1BAC14150020529B /* SwiftyJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8491E1D19CD6DAE00CCFAE6 /* SwiftyJSON.swift */; }; 7236B4F11BAC14150020529B /* SwiftyJSON.h in Headers */ = {isa = PBXBuildFile; fileRef = 2E4FEFE019575BE100351305 /* SwiftyJSON.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 8ED1DB49215FB47C002423BF /* DynamicMemberLookupTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8ED1DB48215FB47C002423BF /* DynamicMemberLookupTests.swift */; }; 9C459EF41A910334008C9A41 /* SwiftyJSON.h in Headers */ = {isa = PBXBuildFile; fileRef = 2E4FEFE019575BE100351305 /* SwiftyJSON.h */; settings = {ATTRIBUTES = (Public, ); }; }; 9C459EF51A910361008C9A41 /* SwiftyJSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8491E1D19CD6DAE00CCFAE6 /* SwiftyJSON.swift */; }; 9C459EF81A9103C1008C9A41 /* Tests.json in Resources */ = {isa = PBXBuildFile; fileRef = A885D1DA19CFCFF0002FD4C3 /* Tests.json */; }; @@ -123,6 +124,7 @@ 712921EE2004E4EB00DA6340 /* CodableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CodableTests.swift; sourceTree = ""; }; 7236B4F61BAC14150020529B /* SwiftyJSON.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SwiftyJSON.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 7236B4F71BAC14150020529B /* Info-tvOS.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "Info-tvOS.plist"; sourceTree = ""; }; + 8ED1DB48215FB47C002423BF /* DynamicMemberLookupTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DynamicMemberLookupTests.swift; sourceTree = ""; }; 9C459EF61A9103B1008C9A41 /* Info-macOS.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = "Info-macOS.plist"; sourceTree = ""; }; 9C7DFC5B1A9102BD005AA3F7 /* SwiftyJSON.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = SwiftyJSON.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 9C7DFC651A9102BD005AA3F7 /* SwiftyJSON macOS Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "SwiftyJSON macOS Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -263,6 +265,7 @@ A819C49619E1A7DD00ADCC3D /* LiteralConvertibleTests.swift */, A819C49819E1B10300ADCC3D /* RawRepresentableTests.swift */, A87080E519E3DF7800CDE086 /* ComparableTests.swift */, + 8ED1DB48215FB47C002423BF /* DynamicMemberLookupTests.swift */, A87080E919E43C0700CDE086 /* StringTests.swift */, A87080E719E439DA00CDE086 /* NumberTests.swift */, A863BE2719EED46F0092A41F /* RawTests.swift */, @@ -622,6 +625,7 @@ A819C49719E1A7DD00ADCC3D /* LiteralConvertibleTests.swift in Sources */, A87080EA19E43C0700CDE086 /* StringTests.swift in Sources */, A87080E619E3DF7800CDE086 /* ComparableTests.swift in Sources */, + 8ED1DB49215FB47C002423BF /* DynamicMemberLookupTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -853,7 +857,7 @@ SKIP_INSTALL = YES; SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_SWIFT3_OBJC_INFERENCE = Default; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 4.2; }; name = Debug; }; @@ -877,7 +881,7 @@ PRODUCT_NAME = SwiftyJSON; SKIP_INSTALL = YES; SWIFT_SWIFT3_OBJC_INFERENCE = Default; - SWIFT_VERSION = 4.0; + SWIFT_VERSION = 4.2; }; name = Release; }; diff --git a/Tests/SwiftyJSONTests/DynamicMemberLookupTests.swift b/Tests/SwiftyJSONTests/DynamicMemberLookupTests.swift new file mode 100644 index 00000000..1aef831a --- /dev/null +++ b/Tests/SwiftyJSONTests/DynamicMemberLookupTests.swift @@ -0,0 +1,65 @@ +// +// DynamicMemberLookupTests.swift +// SwiftyJSON iOS Tests +// +// Created by ios 4 on 9/29/18. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +import XCTest +import SwiftyJSON +class DynamicMemberLookupTests: XCTestCase { + func testDictionaryDynamicLookupString() { + var json: JSON = JSON(rawValue: ["a": "aoo", "bb": "bpp", "z": "zoo"] as NSDictionary)! + XCTAssertTrue(json.a == "aoo") + XCTAssertEqual(json.bb, JSON("bpp")) + XCTAssertTrue(json.z == "zoo") + json.bb = "update" + XCTAssertTrue(json.a == "aoo") + XCTAssertTrue(json.bb == "update") + XCTAssertTrue(json.z == "zoo") + } + + func testMultilevelDynamicLookUpSetterAndGetter() { + var json: JSON = ["user": ["id": 987654, + "info":["name": "farshad","email": "sam@gmail.com"], + "feeds": [98833, 23443, 213239, 23232]]] + json.user.info.name = "jim" + XCTAssertEqual(json.user.id, 987654) + XCTAssertEqual(json.user.info.name, "jim") + XCTAssertEqual(json.user.info.email, "sam@gmail.com") + XCTAssertEqual(json.user.feeds, [98833, 23443, 213239, 23232]) + json.user.info.email = "jim@hotmail.com" + XCTAssertEqual(json.user.id, 987654) + XCTAssertEqual(json.user.info.name, "jim") + XCTAssertEqual(json.user.info.email, "jim@hotmail.com") + XCTAssertEqual(json.user.feeds, [98833, 23443, 213239, 23232]) + json.user.info = ["name": "tom", "email": "tom@qq.com"] + XCTAssertEqual(json.user.id, 987654) + XCTAssertEqual(json.user.info.name, "tom") + XCTAssertEqual(json.user.info.email, "tom@qq.com") + XCTAssertEqual(json.user.feeds, [98833, 23443, 213239, 23232]) + json.user.feeds = [77323, 2313, 4545, 323] + XCTAssertEqual(json.user.id, 987654) + XCTAssertEqual(json.user.info.name, "tom") + XCTAssertEqual(json.user.info.email, "tom@qq.com") + XCTAssertEqual(json.user.feeds, [77323, 2313, 4545, 323]) + } + +} diff --git a/Tests/SwiftyJSONTests/SubscriptTests.swift b/Tests/SwiftyJSONTests/SubscriptTests.swift index 5b399f40..8d7580c5 100644 --- a/Tests/SwiftyJSONTests/SubscriptTests.swift +++ b/Tests/SwiftyJSONTests/SubscriptTests.swift @@ -214,7 +214,7 @@ class SubscriptTests: XCTestCase { XCTAssertEqual(json["Type"]["Value"].error, SwiftyJSONError.notExist) XCTAssertEqual(json["Type", "Value"].error, SwiftyJSONError.notExist) } - + func testMultilevelGetter() { let json: JSON = [[[[["one": 1]]]]] XCTAssertEqual(json[[0, 0, 0, 0, "one"]].int!, 1)