From b49c04b2a0116c2075b9b743420f1e62d6961225 Mon Sep 17 00:00:00 2001 From: Edwin Vermeer Date: Fri, 28 Oct 2016 14:15:38 +0200 Subject: [PATCH] Move functionality to EVReflectable protocol --- EVReflection.podspec | 2 +- .../EVReflection.xcodeproj/project.pbxproj | 46 +- .../EVReflectionEVObjectTests.swift | 1 + .../EVReflectionIssue107.swift | 2 +- .../EVReflectionIssue86.swift | 2 +- .../EVReflectionIssue91.swift | 1 - .../EVReflectionIssue99.swift | 4 +- .../EVReflectionJsonTests.swift | 4 +- .../EVReflectionMappingTest.swift | 6 +- .../EVReflectionNestedObjectsTests.swift | 2 +- .../EVReflectionTests/EVReflectionTests.swift | 4 +- ...flectionWorkaroundSwiftGenericsTests.swift | 8 + .../EVReflectionWorkaroundsTests.swift | 2 +- .../EVRelfectionEnheritanceTests.swift | 2 +- .../EVReflectionTests/TestObjects.swift | 8 +- EVReflection/EVReflectionTests/sample.json | 1 + ...xtensions.swift => EVArrayExtension.swift} | 61 +-- EVReflection/pod/EVObject.swift | 439 +++-------------- EVReflection/pod/EVReflectable.swift | 466 ++++++++++++++++++ EVReflection/pod/EVReflection.swift | 82 +-- 20 files changed, 670 insertions(+), 473 deletions(-) create mode 100644 EVReflection/EVReflectionTests/sample.json rename EVReflection/pod/{EVExtensions.swift => EVArrayExtension.swift} (66%) create mode 100644 EVReflection/pod/EVReflectable.swift diff --git a/EVReflection.podspec b/EVReflection.podspec index 8162942e..f85a669c 100644 --- a/EVReflection.podspec +++ b/EVReflection.podspec @@ -8,7 +8,7 @@ Pod::Spec.new do |s| # s.name = "EVReflection" -s.version = "3.1.4" +s.version = "3.2.0" s.summary = "iOS: Swift helper library with reflection functions" s.description = "Swift helper library with reflection functions including support for NSCoding, Printable, Hashable, Equatable and JSON" s.homepage = "https://github.com/evermeer/EVReflection" diff --git a/EVReflection/EVReflection.xcodeproj/project.pbxproj b/EVReflection/EVReflection.xcodeproj/project.pbxproj index e1caf997..4d117a84 100644 --- a/EVReflection/EVReflection.xcodeproj/project.pbxproj +++ b/EVReflection/EVReflection.xcodeproj/project.pbxproj @@ -11,11 +11,11 @@ 2DC8AA401C24215500FA6B8E /* EVReflection.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC8AA3F1C24215500FA6B8E /* EVReflection.h */; settings = {ATTRIBUTES = (Public, ); }; }; 2DC8AA541C2421D600FA6B8E /* EVReflection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F07C5841AF10284000DF1F3 /* EVReflection.swift */; }; 2DC8AA551C2421D600FA6B8E /* EVObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F0E5E591AF4D94000A10E51 /* EVObject.swift */; }; - 2DC8AA571C2421D600FA6B8E /* EVExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F1BA5261B971CBF009F9DB8 /* EVExtensions.swift */; }; + 2DC8AA571C2421D600FA6B8E /* EVArrayExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F1BA5261B971CBF009F9DB8 /* EVArrayExtension.swift */; }; 2DC8AA661C242E0600FA6B8E /* EVReflectionOSX.h in Headers */ = {isa = PBXBuildFile; fileRef = 2DC8AA651C242E0600FA6B8E /* EVReflectionOSX.h */; settings = {ATTRIBUTES = (Public, ); }; }; 2DC8AA7C1C242ECC00FA6B8E /* EVReflection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F07C5841AF10284000DF1F3 /* EVReflection.swift */; }; 2DC8AA7D1C242ECE00FA6B8E /* EVObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F0E5E591AF4D94000A10E51 /* EVObject.swift */; }; - 2DC8AA7F1C242ED200FA6B8E /* EVExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F1BA5261B971CBF009F9DB8 /* EVExtensions.swift */; }; + 2DC8AA7F1C242ED200FA6B8E /* EVArrayExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F1BA5261B971CBF009F9DB8 /* EVArrayExtension.swift */; }; 422668011CB77DD800145E17 /* EVReflectionCustomInitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 422667FF1CB77DD000145E17 /* EVReflectionCustomInitTests.swift */; }; 422668021CB7802C00145E17 /* EVReflectionCustomInitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 422667FF1CB77DD000145E17 /* EVReflectionCustomInitTests.swift */; }; 422668031CB7802D00145E17 /* EVReflectionCustomInitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 422667FF1CB77DD000145E17 /* EVReflectionCustomInitTests.swift */; }; @@ -92,6 +92,9 @@ 7F83A6721D132F6200A4C508 /* EVReflectionIssue96.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F83A66F1D132F5400A4C508 /* EVReflectionIssue96.swift */; }; 7F83A6731D132F6300A4C508 /* EVReflectionIssue96.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F83A66F1D132F5400A4C508 /* EVReflectionIssue96.swift */; }; 7F8818F91C04447100E17072 /* EVReflectionPropertyMappingTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F8818F81C04447100E17072 /* EVReflectionPropertyMappingTest.swift */; }; + 7F925AB61DC325E50059F4F2 /* EVReflectable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7FF033F71DC2A0DF00B25070 /* EVReflectable.swift */; }; + 7F925AB71DC325E60059F4F2 /* EVReflectable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7FF033F71DC2A0DF00B25070 /* EVReflectable.swift */; }; + 7F925AB81DC325E70059F4F2 /* EVReflectable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7FF033F71DC2A0DF00B25070 /* EVReflectable.swift */; }; 7F9336951C23F2EB00AF7076 /* EVReflectionEVObjectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F59F9DA1C07999300C14CF9 /* EVReflectionEVObjectTests.swift */; }; 7F9336961C23F2F100AF7076 /* EVReflectionPropertyMappingTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F8818F81C04447100E17072 /* EVReflectionPropertyMappingTest.swift */; }; 7F9336971C23F2F700AF7076 /* EVReflectionAssociatedTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F59F9D81C07986200C14CF9 /* EVReflectionAssociatedTests.swift */; }; @@ -100,7 +103,7 @@ 7F9336B11C28227400AF7076 /* EVReflectionTVOS.h in Headers */ = {isa = PBXBuildFile; fileRef = 7F9336B01C28227400AF7076 /* EVReflectionTVOS.h */; settings = {ATTRIBUTES = (Public, ); }; }; 7F9336C51C28229C00AF7076 /* EVReflection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F07C5841AF10284000DF1F3 /* EVReflection.swift */; }; 7F9336C61C28229F00AF7076 /* EVObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F0E5E591AF4D94000A10E51 /* EVObject.swift */; }; - 7F9336C81C2822A700AF7076 /* EVExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F1BA5261B971CBF009F9DB8 /* EVExtensions.swift */; }; + 7F9336C81C2822A700AF7076 /* EVArrayExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F1BA5261B971CBF009F9DB8 /* EVArrayExtension.swift */; }; 7F9336C91C2822B200AF7076 /* TestObjects.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F41ECA41B91F2E0003A2236 /* TestObjects.swift */; }; 7F9336CA1C2822B700AF7076 /* EVReflectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F41ECA81B91F306003A2236 /* EVReflectionTests.swift */; }; 7F9336CB1C2822B700AF7076 /* EVReflectionEVObjectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F59F9DA1C07999300C14CF9 /* EVReflectionEVObjectTests.swift */; }; @@ -131,10 +134,11 @@ 7FD67D8E1C288B560071234B /* EVReflectionWatchOS.h in Headers */ = {isa = PBXBuildFile; fileRef = 7FD67D8D1C288B560071234B /* EVReflectionWatchOS.h */; settings = {ATTRIBUTES = (Public, ); }; }; 7FD67D931C288B760071234B /* EVReflection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F07C5841AF10284000DF1F3 /* EVReflection.swift */; }; 7FD67D941C288B7A0071234B /* EVObject.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F0E5E591AF4D94000A10E51 /* EVObject.swift */; }; - 7FD67D961C288B840071234B /* EVExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F1BA5261B971CBF009F9DB8 /* EVExtensions.swift */; }; - 7FDE786B1DC2154700C2AC1C /* sample.json in Resources */ = {isa = PBXBuildFile; fileRef = 7FDE78661DC2144700C2AC1C /* sample.json */; }; - 7FDE786C1DC2154900C2AC1C /* sample.json in Resources */ = {isa = PBXBuildFile; fileRef = 7FDE78661DC2144700C2AC1C /* sample.json */; }; - 7FDE786D1DC2154B00C2AC1C /* sample.json in Resources */ = {isa = PBXBuildFile; fileRef = 7FDE78661DC2144700C2AC1C /* sample.json */; }; + 7FD67D961C288B840071234B /* EVArrayExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7F1BA5261B971CBF009F9DB8 /* EVArrayExtension.swift */; }; + 7FDC390F1DC358A40091D27E /* sample.json in Resources */ = {isa = PBXBuildFile; fileRef = 7FDC390D1DC3582F0091D27E /* sample.json */; }; + 7FDC39101DC358A60091D27E /* sample.json in Resources */ = {isa = PBXBuildFile; fileRef = 7FDC390D1DC3582F0091D27E /* sample.json */; }; + 7FDC39111DC358A70091D27E /* sample.json in Resources */ = {isa = PBXBuildFile; fileRef = 7FDC390D1DC3582F0091D27E /* sample.json */; }; + 7FF033F81DC2A0E000B25070 /* EVReflectable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7FF033F71DC2A0DF00B25070 /* EVReflectable.swift */; }; 7FF4BEAF1CF0FBF50086F999 /* EVReflectionIssue84.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7FF4BEAD1CF0FB920086F999 /* EVReflectionIssue84.swift */; }; 7FF4BEB01CF0FBF60086F999 /* EVReflectionIssue84.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7FF4BEAD1CF0FB920086F999 /* EVReflectionIssue84.swift */; }; 7FF4BEB11CF0FBF80086F999 /* EVReflectionIssue84.swift in Sources */ = {isa = PBXBuildFile; fileRef = 7FF4BEAD1CF0FB920086F999 /* EVReflectionIssue84.swift */; }; @@ -203,7 +207,7 @@ 7F07C58E1AF11861000DF1F3 /* LICENSE */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = ""; }; 7F0E5E591AF4D94000A10E51 /* EVObject.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = EVObject.swift; sourceTree = ""; xcLanguageSpecificationIdentifier = xcode.lang.swift; }; 7F159DFB1BA2FA2F00247CB1 /* .gitignore */ = {isa = PBXFileReference; lastKnownFileType = text; path = .gitignore; sourceTree = ""; }; - 7F1BA5261B971CBF009F9DB8 /* EVExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EVExtensions.swift; sourceTree = ""; }; + 7F1BA5261B971CBF009F9DB8 /* EVArrayExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EVArrayExtension.swift; sourceTree = ""; }; 7F1DD7071D90946800E99D4D /* EVReflectionIssue124.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EVReflectionIssue124.swift; sourceTree = ""; }; 7F1DD7091D90949100E99D4D /* EVReflectionIssue124.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = EVReflectionIssue124.plist; sourceTree = ""; }; 7F1E778F1CD343D300F88A9A /* EVReflectionConversionOptionsTest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EVReflectionConversionOptionsTest.swift; sourceTree = ""; }; @@ -262,7 +266,8 @@ 7FD67D8B1C288B560071234B /* EVReflection.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = EVReflection.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 7FD67D8D1C288B560071234B /* EVReflectionWatchOS.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = EVReflectionWatchOS.h; sourceTree = ""; }; 7FD67D8F1C288B560071234B /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; - 7FDE78661DC2144700C2AC1C /* sample.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; name = sample.json; path = ../../../AlamofireJsonToObjects/AlamofireJsonToObjectsTests/sample.json; sourceTree = ""; }; + 7FDC390D1DC3582F0091D27E /* sample.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = sample.json; sourceTree = ""; }; + 7FF033F71DC2A0DF00B25070 /* EVReflectable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EVReflectable.swift; sourceTree = ""; }; 7FF4BEAD1CF0FB920086F999 /* EVReflectionIssue84.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EVReflectionIssue84.swift; sourceTree = ""; }; 7FF4BEB31CF2DA6E0086F999 /* EVReflectionIssue86.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = EVReflectionIssue86.swift; sourceTree = ""; }; 7FFCCC361BB440F400EE9312 /* coverage.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = coverage.png; sourceTree = ""; }; @@ -380,7 +385,7 @@ 7F07C5751AF101A9000DF1F3 /* EVReflectionTests */ = { isa = PBXGroup; children = ( - 7FDE78661DC2144700C2AC1C /* sample.json */, + 7FDC390D1DC3582F0091D27E /* sample.json */, 7F41ECA41B91F2E0003A2236 /* TestObjects.swift */, 7F41ECA81B91F306003A2236 /* EVReflectionTests.swift */, 7F59F9DA1C07999300C14CF9 /* EVReflectionEVObjectTests.swift */, @@ -421,7 +426,8 @@ children = ( 7F07C5841AF10284000DF1F3 /* EVReflection.swift */, 7F0E5E591AF4D94000A10E51 /* EVObject.swift */, - 7F1BA5261B971CBF009F9DB8 /* EVExtensions.swift */, + 7FF033F71DC2A0DF00B25070 /* EVReflectable.swift */, + 7F1BA5261B971CBF009F9DB8 /* EVArrayExtension.swift */, 7F9E68111C67728200F0F4D3 /* EVWorkaroundHelpers.swift */, ); path = pod; @@ -773,7 +779,7 @@ buildActionMask = 2147483647; files = ( 7F1DD70A1D90949100E99D4D /* EVReflectionIssue124.plist in Resources */, - 7FDE786B1DC2154700C2AC1C /* sample.json in Resources */, + 7FDC390F1DC358A40091D27E /* sample.json in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -782,7 +788,7 @@ buildActionMask = 2147483647; files = ( 7F1DD70B1D90949100E99D4D /* EVReflectionIssue124.plist in Resources */, - 7FDE786C1DC2154900C2AC1C /* sample.json in Resources */, + 7FDC39101DC358A60091D27E /* sample.json in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -810,7 +816,7 @@ buildActionMask = 2147483647; files = ( 7F1DD70C1D90949100E99D4D /* EVReflectionIssue124.plist in Resources */, - 7FDE786D1DC2154B00C2AC1C /* sample.json in Resources */, + 7FDC39111DC358A70091D27E /* sample.json in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -863,7 +869,8 @@ 7F9E68121C67728200F0F4D3 /* EVWorkaroundHelpers.swift in Sources */, 2DC8AA541C2421D600FA6B8E /* EVReflection.swift in Sources */, 2DC8AA551C2421D600FA6B8E /* EVObject.swift in Sources */, - 2DC8AA571C2421D600FA6B8E /* EVExtensions.swift in Sources */, + 7FF033F81DC2A0E000B25070 /* EVReflectable.swift in Sources */, + 2DC8AA571C2421D600FA6B8E /* EVArrayExtension.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -872,8 +879,9 @@ buildActionMask = 2147483647; files = ( 4A779EC01C77D505006CE342 /* EVWorkaroundHelpers.swift in Sources */, - 2DC8AA7F1C242ED200FA6B8E /* EVExtensions.swift in Sources */, + 2DC8AA7F1C242ED200FA6B8E /* EVArrayExtension.swift in Sources */, 2DC8AA7C1C242ECC00FA6B8E /* EVReflection.swift in Sources */, + 7F925AB61DC325E50059F4F2 /* EVReflectable.swift in Sources */, 2DC8AA7D1C242ECE00FA6B8E /* EVObject.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -968,9 +976,10 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 7F9336C81C2822A700AF7076 /* EVExtensions.swift in Sources */, + 7F9336C81C2822A700AF7076 /* EVArrayExtension.swift in Sources */, 7F9336C51C28229C00AF7076 /* EVReflection.swift in Sources */, 7F625FD51D7031DF00F9A304 /* EVWorkaroundHelpers.swift in Sources */, + 7F925AB71DC325E60059F4F2 /* EVReflectable.swift in Sources */, 7F9336C61C28229F00AF7076 /* EVObject.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -1017,9 +1026,10 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - 7FD67D961C288B840071234B /* EVExtensions.swift in Sources */, + 7FD67D961C288B840071234B /* EVArrayExtension.swift in Sources */, 7FD67D931C288B760071234B /* EVReflection.swift in Sources */, 7F625FD61D7031E000F9A304 /* EVWorkaroundHelpers.swift in Sources */, + 7F925AB81DC325E70059F4F2 /* EVReflectable.swift in Sources */, 7FD67D941C288B7A0071234B /* EVObject.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; diff --git a/EVReflection/EVReflectionTests/EVReflectionEVObjectTests.swift b/EVReflection/EVReflectionTests/EVReflectionEVObjectTests.swift index 1fd5319c..a6928cb7 100644 --- a/EVReflection/EVReflectionTests/EVReflectionEVObjectTests.swift +++ b/EVReflection/EVReflectionTests/EVReflectionEVObjectTests.swift @@ -87,6 +87,7 @@ class EVReflectionEVObjectTests: XCTestCase { let result = NSKeyedUnarchiver.unarchiveObject(withFile: filePath) as? TestObject2 // Test if the objects are the same + XCTAssert(theObject.isEqual(result), "Pass") XCTAssert(theObject == result, "Pass") } diff --git a/EVReflection/EVReflectionTests/EVReflectionIssue107.swift b/EVReflection/EVReflectionTests/EVReflectionIssue107.swift index 6cc33ba4..f5a8c1ad 100644 --- a/EVReflection/EVReflectionTests/EVReflectionIssue107.swift +++ b/EVReflection/EVReflectionTests/EVReflectionIssue107.swift @@ -102,7 +102,7 @@ class EVSubConfigurationModel: EVObject { var evening: String? var morning: String? - override internal func initValidation(_ dict: NSDictionary) { + internal override func initValidation(_ dict: NSDictionary) { self.initMayNotContainKeys(["desc","morning","evening"], dict: dict) } } diff --git a/EVReflection/EVReflectionTests/EVReflectionIssue86.swift b/EVReflection/EVReflectionTests/EVReflectionIssue86.swift index 8e1059da..9a9b4291 100644 --- a/EVReflection/EVReflectionTests/EVReflectionIssue86.swift +++ b/EVReflection/EVReflectionTests/EVReflectionIssue86.swift @@ -87,7 +87,7 @@ class Supports: EVObject { var _attribute: String = "" var _value: YesNoEnum = .none - override internal func propertyConverters() -> [(String?, ((Any?) -> ())?, (() -> Any?)?)] { + override func propertyConverters() -> [(String?, ((Any?) -> ())?, (() -> Any?)?)] { return [ ( "_type" diff --git a/EVReflection/EVReflectionTests/EVReflectionIssue91.swift b/EVReflection/EVReflectionTests/EVReflectionIssue91.swift index 3ddf7314..43aa3474 100644 --- a/EVReflection/EVReflectionTests/EVReflectionIssue91.swift +++ b/EVReflection/EVReflectionTests/EVReflectionIssue91.swift @@ -46,7 +46,6 @@ class TestIssue91: XCTestCase { } class BaseObject: EVObject { - } class SimpleObject: BaseObject { diff --git a/EVReflection/EVReflectionTests/EVReflectionIssue99.swift b/EVReflection/EVReflectionTests/EVReflectionIssue99.swift index a1c59d1b..a749a6fd 100644 --- a/EVReflection/EVReflectionTests/EVReflectionIssue99.swift +++ b/EVReflection/EVReflectionTests/EVReflectionIssue99.swift @@ -132,7 +132,7 @@ public class MyPrimaryObject: EVObject { public var secondaryObjects: [MySecondaryObject]? - override public func propertyMapping() -> [(String?, String?)] { + public override func propertyMapping() -> [(String?, String?)] { return [("myObjectDescription","Description")] } @@ -157,7 +157,7 @@ public class PublicInfusion: EVObject { public var vodkaHistory: [PublicInfusionCheckmark] = [] - override public func propertyMapping() -> [(String?, String?)] { + public override func propertyMapping() -> [(String?, String?)] { return [("infusionDescription","Description")] } } diff --git a/EVReflection/EVReflectionTests/EVReflectionJsonTests.swift b/EVReflection/EVReflectionTests/EVReflectionJsonTests.swift index 4ce4834a..a613e7e7 100644 --- a/EVReflection/EVReflectionTests/EVReflectionJsonTests.swift +++ b/EVReflection/EVReflectionTests/EVReflectionJsonTests.swift @@ -216,9 +216,9 @@ class EVReflectionJsonTests: XCTestCase { let path = Bundle(for: type(of: self)).url(forResource: "sample", withExtension: "json") let data = try! Data(contentsOf: path!) let a = TestObject4(data: data) - XCTAssertEqual(a.myString, "1", "myString should contain 1") + XCTAssertEqual(a.myString, "test", "myString should contain 1") XCTAssertEqual(a.myInt, 2, "myInt should contain 2") - XCTAssertEqual(a.myFloat, 2.1, "myFloat should contain 2.1") + XCTAssertEqual(a.myFloat, 2.5, "myFloat should contain 2.5") XCTAssertEqual(a.myBool, true, "myBool should contain true") } diff --git a/EVReflection/EVReflectionTests/EVReflectionMappingTest.swift b/EVReflection/EVReflectionTests/EVReflectionMappingTest.swift index ffbd8e5f..5e718d76 100644 --- a/EVReflection/EVReflectionTests/EVReflectionMappingTest.swift +++ b/EVReflection/EVReflectionTests/EVReflectionMappingTest.swift @@ -40,7 +40,7 @@ class EVReflectionMappingTests: XCTestCase { player.gamesPlayed = 123 player.rating = 76 - NSLog("\n\n===> This will output a warning because GameAdministrator does not have the propery gamesPlayes") + NSLog("\n\n===> This will output a warning because GameAdministrator does not have the propery gamesPlayed") let administrator: GameAdministrator = player.mapObjectTo() // Remember that printing will use the property converter and multiply the administrator level with 4. So it will print the same as the player. @@ -107,11 +107,11 @@ public class GameAdministrator: GameUser { var usersBanned: Int = 0 var level: Int = 0 - override public func propertyMapping() -> [(String?, String?)] { + public override func propertyMapping() -> [(String?, String?)] { return [("level","rating")] } - override public func propertyConverters() -> [(String?, ((Any?)->())?, (() -> Any?)? )] { + public override func propertyConverters() -> [(String?, ((Any?)->())?, (() -> Any?)? )] { return [ ( // We want a custom converter for the field isGreat "level", diff --git a/EVReflection/EVReflectionTests/EVReflectionNestedObjectsTests.swift b/EVReflection/EVReflectionTests/EVReflectionNestedObjectsTests.swift index ac3838fd..00772ccd 100644 --- a/EVReflection/EVReflectionTests/EVReflectionNestedObjectsTests.swift +++ b/EVReflection/EVReflectionTests/EVReflectionNestedObjectsTests.swift @@ -75,7 +75,7 @@ public class B: EVObject { } } - override public func propertyMapping() -> [(String?, String?)] { + public override func propertyMapping() -> [(String?, String?)] { return [("intProp", nil)] } } diff --git a/EVReflection/EVReflectionTests/EVReflectionTests.swift b/EVReflection/EVReflectionTests/EVReflectionTests.swift index fd929c5f..407bf447 100644 --- a/EVReflection/EVReflectionTests/EVReflectionTests.swift +++ b/EVReflection/EVReflectionTests/EVReflectionTests.swift @@ -119,11 +119,11 @@ class EVReflectionTests: XCTestCase { Test the convenience methods for getting a dictionary and creating an object based on a dictionary. */ func testClassToAndFromDictionaryConvenienceMethods() { - let theObject = TestObject2() + let theObject: TestObject2 = TestObject2() theObject.objectValue = "testing" let toDict = theObject.toDictionary() NSLog("toDictionary = \(toDict)") - let result = TestObject2(dictionary: toDict) + let result: TestObject2 = TestObject2(dictionary: toDict) XCTAssert(theObject == result, "Pass") } diff --git a/EVReflection/EVReflectionTests/EVReflectionWorkaroundSwiftGenericsTests.swift b/EVReflection/EVReflectionTests/EVReflectionWorkaroundSwiftGenericsTests.swift index 97b846f4..7539779c 100644 --- a/EVReflection/EVReflectionTests/EVReflectionWorkaroundSwiftGenericsTests.swift +++ b/EVReflection/EVReflectionTests/EVReflectionWorkaroundSwiftGenericsTests.swift @@ -102,6 +102,10 @@ public class MyGenericObject: MyGenericBase, EVGenericsKVC where T:NSObject { super.init() } + required public init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + public func setGenericValue(_ value: AnyObject!, forUndefinedKey key: String) { switch key { case "data": @@ -126,6 +130,10 @@ public class MyIncorrectGenericObject: MyGenericBase, EVGenericsKVC where T:N required public init() { super.init() } + + required public init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } public func setGenericValue(_ value: AnyObject!, forUndefinedKey key: String) { // Not setting anything diff --git a/EVReflection/EVReflectionTests/EVReflectionWorkaroundsTests.swift b/EVReflection/EVReflectionTests/EVReflectionWorkaroundsTests.swift index b7043240..48f0feb3 100644 --- a/EVReflection/EVReflectionTests/EVReflectionWorkaroundsTests.swift +++ b/EVReflection/EVReflectionTests/EVReflectionWorkaroundsTests.swift @@ -197,7 +197,7 @@ class WorkaroundObject: EVObject, EVArrayConvertable { } // Implementation of the EVDictionaryConvertable protocol for handling a Swift dictionary. - override func convertDictionary(_ field: String, dict: Any) -> NSDictionary { + func convertDictionary(_ field: String, dict: Any) -> NSDictionary { assert(field == "dict", "convertDictionary for key \(field) should be handled.") let returnDict = NSMutableDictionary() diff --git a/EVReflection/EVReflectionTests/EVRelfectionEnheritanceTests.swift b/EVReflection/EVReflectionTests/EVRelfectionEnheritanceTests.swift index 166ad8eb..e08cb27e 100644 --- a/EVReflection/EVReflectionTests/EVRelfectionEnheritanceTests.swift +++ b/EVReflection/EVReflectionTests/EVRelfectionEnheritanceTests.swift @@ -80,7 +80,7 @@ class Foo: EVObject { var allFoo: String = "all Foo" // What you need to do to get the correct type for when you deserialize enherited classes - override func getSpecificType(_ dict: NSDictionary) -> EVObject { + override func getSpecificType(_ dict: NSDictionary) -> EVReflectable { if dict["justBar"] != nil { return Bar() } else if dict["justBaz"] != nil { diff --git a/EVReflection/EVReflectionTests/TestObjects.swift b/EVReflection/EVReflectionTests/TestObjects.swift index 31ad4caf..f2f97b27 100644 --- a/EVReflection/EVReflectionTests/TestObjects.swift +++ b/EVReflection/EVReflectionTests/TestObjects.swift @@ -117,7 +117,7 @@ public class TestObject5: EVObject { var propertyInObject: String = "" // will be written to or read from keyInJson var ignoredProperty: String = "" // Will not be written or read to/from json - override public func propertyMapping() -> [(String?, String?)] { + public override func propertyMapping() -> [(String?, String?)] { return [("ignoredProperty", nil), ("propertyInObject", "keyInJson")] } } @@ -129,7 +129,7 @@ For testing the custom property conversion public class TestObject6: EVObject { var isGreat: Bool = false - override public func propertyConverters() -> [(String?, ((Any?)->())?, (() -> Any?)? )] { + public override func propertyConverters() -> [(String?, ((Any?)->())?, (() -> Any?)? )] { return [ ( // We want a custom converter for the field isGreat "isGreat", @@ -189,7 +189,7 @@ public class TestObjectWithNilConverters: EVObject { var optionalValue: String? - override public func propertyConverters() -> [(String?, ((Any?)->())?, (() -> Any?)? )] { + public override func propertyConverters() -> [(String?, ((Any?)->())?, (() -> Any?)? )] { return [("optionalValue", nil, nil)] } } @@ -235,7 +235,7 @@ public class ValidateObject: EVObject { var optionalKey2: String? var optionalKey3: String? - override public func initValidation(_ dict: NSDictionary) { + public override func initValidation(_ dict: NSDictionary) { self.initMayNotContainKeys(["error"], dict: dict) self.initMustContainKeys(["requiredKey1", "requiredKey2", "requiredKey3"], dict: dict) if dict.value(forKey: "requiredKey1") as? String == dict.value(forKey: "optionalKey1") as? String { diff --git a/EVReflection/EVReflectionTests/sample.json b/EVReflection/EVReflectionTests/sample.json new file mode 100644 index 00000000..122f117a --- /dev/null +++ b/EVReflection/EVReflectionTests/sample.json @@ -0,0 +1 @@ +{"myString":"test", "myInt":2, "myFloat":2.5, "myBool":true} diff --git a/EVReflection/pod/EVExtensions.swift b/EVReflection/pod/EVArrayExtension.swift similarity index 66% rename from EVReflection/pod/EVExtensions.swift rename to EVReflection/pod/EVArrayExtension.swift index ed3453f3..8175d430 100644 --- a/EVReflection/pod/EVExtensions.swift +++ b/EVReflection/pod/EVArrayExtension.swift @@ -1,5 +1,5 @@ // -// EVExtensions.swift +// ArrayExtension.swift // EVReflection // // Created by Edwin Vermeer on 9/2/15. @@ -9,32 +9,7 @@ import Foundation /** -Implementation for Equatable == - -- parameter lhs: The object at the left side of the == -- parameter rhs: The object at the right side of the == - - - returns: True if the objects are the same, otherwise false. -*/ -public func == (lhs: EVObject, rhs: EVObject) -> Bool { - return EVReflection.areEqual(lhs, rhs: rhs) -} - -/** -Implementation for Equatable != - -- parameter lhs: The object at the left side of the == -- parameter rhs: The object at the right side of the == - - - returns: False if the objects are the the same, otherwise true. -*/ -public func != (lhs: EVObject, rhs: EVObject) -> Bool { - return !EVReflection.areEqual(lhs, rhs: rhs) -} - - -/** -Extending Array with an initializer with a json string +Extending Array with an some EVReflection functions where the elements can be of type NSObject */ public extension Array where Element: NSObject { @@ -63,10 +38,8 @@ public extension Array where Element: NSObject { self.init() for item in dictionaryArray { let arrayTypeInstance = getArrayTypeInstance(self) - if arrayTypeInstance is EVObject { - EVReflection.setPropertiesfromDictionary(item, anyObject: arrayTypeInstance as! EVObject) - self.append(arrayTypeInstance) - } + EVReflection.setPropertiesfromDictionary(item, anyObject: arrayTypeInstance) + self.append(arrayTypeInstance) } } @@ -107,17 +80,25 @@ public extension Array where Element: NSObject { return NSStringFromClass(type(of:item)) } - /** - Convert this array to a json string - - - parameter conversionOptions: Option set for the various conversion options. - - returns: The json string - */ +} + + +/** + Extending Array with an some EVReflection functions where the elements can be of type EVReflectable + */ +public extension Array where Element: EVReflectable { + /** + Convert this array to a json string + + - parameter conversionOptions: Option set for the various conversion options. + + - returns: The json string + */ public func toJsonString(_ conversionOptions: ConversionOptions = .DefaultSerialize) -> String { - return "[\n" + self.map({($0 as? EVObject ?? EVObject()).toJsonString(conversionOptions)}).joined(separator: ", \n") + "\n]" + return "[\n" + self.map({($0).toJsonString(conversionOptions)}).joined(separator: ", \n") + "\n]" } - + /** Returns the dictionary representation of this array. @@ -126,6 +107,6 @@ public extension Array where Element: NSObject { - returns: The array of dictionaries */ public func toDictionaryArray(_ conversionOptions: ConversionOptions = .DefaultSerialize) -> NSArray { - return self.map({($0 as? EVObject ?? EVObject()).toDictionary(conversionOptions)}) as NSArray + return self.map({($0).toDictionary(conversionOptions)}) as NSArray } } diff --git a/EVReflection/pod/EVObject.swift b/EVReflection/pod/EVObject.swift index b6ebd496..44d7f6a8 100644 --- a/EVReflection/pod/EVObject.swift +++ b/EVReflection/pod/EVObject.swift @@ -7,435 +7,160 @@ import Foundation + /** - Object that will support NSCoding, Printable, Hashable and Equeatable for all properties. Use this object as your base class + Object that implements EVReflectable and NSCoding. Use this object as your base class instead of NSObject and you wil automatically have support for all these protocols. -*/ -open class EVObject: NSObject, NSCoding { // These are redundant in Swift 2+: CustomDebugStringConvertible, CustomStringConvertible, Hashable, Equatable - - /** - This basic init override is needed so we can use EVObject as a base class. - */ - public required override init() { - super.init() - } - - /** - Decode any object - - This method is in EVObject and not in NSObject because you would get the error: Initializer requirement 'init(coder:)' can - only be satisfied by a `required` initializer in the definition of non-final class 'NSObject' - - -parameter coder: The NSCoder that will be used for decoding the object. - */ - public convenience required init?(coder: NSCoder) { - self.init() - EVReflection.decodeObjectWithCoder(self, aDecoder: coder, conversionOptions: .DefaultNSCoding) - } - - /** - Convenience init for creating an object whith the property values of a dictionary. - - - parameter dictionary: The dictionary that will be used to create this object - - parameter conversionOptions: Option set for the various conversion options. - */ - public convenience required init(dictionary: NSDictionary, conversionOptions: ConversionOptions = .DefaultDeserialize) { - self.init() - EVReflection.setPropertiesfromDictionary(dictionary, anyObject: self, conversionOptions: conversionOptions) - } - - /** - Convenience init for creating an object whith the contents of a json string. - - - parameter json: The json string that will be used to create this object - - parameter conversionOptions: Option set for the various conversion options. - */ - public convenience required init(json: String?, conversionOptions: ConversionOptions = .DefaultDeserialize) { - self.init() - let jsonDict = EVReflection.dictionaryFromJson(json) - EVReflection.setPropertiesfromDictionary(jsonDict, anyObject: self, conversionOptions: conversionOptions) - } + */ +open class EVObject: NSObject, NSCoding, EVReflectable { + // These are redundant in Swift 2+: CustomDebugStringConvertible, CustomStringConvertible, Hashable, Equatable /** - Convenience init for creating an object whith the property values of json Data. + Implementation of the setValue forUndefinedKey so that we can catch exceptions for when we use an optional Type like Int? in our object. Instead of using Int? you should use NSNumber? - - parameter dictionary: The dictionary that will be used to create this object - - parameter conversionOptions: Option set for the various conversion options. - */ - public convenience required init(data: Data, conversionOptions: ConversionOptions = .DefaultDeserialize) { - self.init() - let dictionary: NSDictionary = try! JSONSerialization.jsonObject(with: data, options: []) as? NSDictionary ?? NSDictionary() - EVReflection.setPropertiesfromDictionary(dictionary, anyObject: self, conversionOptions: conversionOptions) - } - - - /** - Encode this object using a NSCoder - - - parameter aCoder: The NSCoder that will be used for encoding the object - */ - open func encode(with aCoder: NSCoder) { - EVReflection.encodeWithCoder(self, aCoder: aCoder, conversionOptions: .DefaultNSCoding) - } - - /** - Initialize this object from an archived file from the temp directory - - - parameter fileNameInTemp: The filename - - parameter conversionOptions: Option set for the various conversion options. - */ - public convenience init(fileNameInTemp: String, conversionOptions: ConversionOptions = .DefaultNSCoding) { - self.init() - let filePath = (NSTemporaryDirectory() as NSString).appendingPathComponent(fileNameInTemp) - if let temp = NSKeyedUnarchiver.unarchiveObject(withFile: filePath) as? EVObject { - EVReflection.setPropertiesfromDictionary( temp.toDictionary(conversionOptions), anyObject: self, conversionOptions: conversionOptions) - } - } - - /** - Initialize this object from an archived file from the documents directory - - - parameter fileNameInDocuments: The filename - - parameter conversionOptions: Option set for the various conversion options. - */ - public convenience init(fileNameInDocuments: String, conversionOptions: ConversionOptions = .DefaultNSCoding) { - self.init() - let filePath = (NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString).appendingPathComponent(fileNameInDocuments) - if let temp = NSKeyedUnarchiver.unarchiveObject(withFile: filePath) as? EVObject { - EVReflection.setPropertiesfromDictionary( temp.toDictionary(conversionOptions), anyObject: self, conversionOptions: conversionOptions) - } - } - - - /** - Returns the pritty description of this object - - - returns: The pritty description - */ - open override var description: String { - get { - return EVReflection.description(self) - } - } - - /** - Returns the pritty description of this object - - - returns: The pritty description - */ - open override var debugDescription: String { - get { - return EVReflection.description(self) - } - } - - /** - Returns the hashvalue of this object - - - returns: The hashvalue of this object - */ - open override var hashValue: Int { - get { - return Int(EVReflection.hashValue(self)) - } - } - - /** - Function for returning the hash for the NSObject based functionality - - - returns: The hashvalue of this object - */ - open override var hash: Int { - get { - return self.hashValue - } - } - - /** - Save this object to a file in the temp directory - - - parameter fileName: The filename - - - returns: Nothing - */ - @discardableResult - open func saveToTemp(_ fileName: String) -> Bool { - let filePath = (NSTemporaryDirectory() as NSString).appendingPathComponent(fileName) - return NSKeyedArchiver.archiveRootObject(self, toFile: filePath) - } - - - - #if os(tvOS) - // Save to documents folder is not supported on tvOS - #else - /** - Save this object to a file in the documents directory - - - parameter fileName: The filename + This method is in EVObject and not in NSObject extension because you would get the error: method conflicts with previous declaration with the same Objective-C selector - - returns: true if successfull - */ - @discardableResult - open func saveToDocuments(_ fileName: String) -> Bool { - let filePath = (NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString).appendingPathComponent(fileName) - return NSKeyedArchiver.archiveRootObject(self, toFile: filePath) - } - #endif - - - - - /** - Implementation of the NSObject isEqual comparisson method - - This method is in EVObject and not in NSObject extension because you would get the error: method conflicts with previous declaration with the same Objective-C selector - - - parameter object: The object where you want to compare with - - - returns: Returns true if the object is the same otherwise false - */ - open override func isEqual(_ object: Any?) -> Bool { // for isEqual: - if let dataObject = object as? EVObject { - return dataObject == self // just use our "==" function - } - return false - } - - - /** - Implementation of the setValue forUndefinedKey so that we can catch exceptions for when we use an optional Type like Int? in our object. Instead of using Int? you should use NSNumber? - - This method is in EVObject and not in NSObject extension because you would get the error: method conflicts with previous declaration with the same Objective-C selector - - - parameter value: The value that you wanted to set - - parameter key: The name of the property that you wanted to set - */ + - parameter value: The value that you wanted to set + - parameter key: The name of the property that you wanted to set + */ open override func setValue(_ value: Any!, forUndefinedKey key: String) { if let kvc = self as? EVGenericsKVC { kvc.setGenericValue(value as AnyObject!, forUndefinedKey: key) } else { self.addStatusMessage(.IncorrectKey, message: "The class '\(EVReflection.swiftStringFromClass(self))' is not key value coding-compliant for the key '\(key)'") print("\nWARNING: The class '\(EVReflection.swiftStringFromClass(self))' is not key value coding-compliant for the key '\(key)'\n There is no support for optional type, array of optionals or enum properties.\nAs a workaround you can implement the function 'setValue forUndefinedKey' for this. See the unit tests for more information\n") - + } } /** - Override this method when you want custom property mapping. - - This method is in EVObject and not in extension of NSObject because a functions from extensions cannot be overwritten yet - - - returns: Return an array with value pairs of the object property name and json key name. - */ - open func propertyMapping() -> [(String?, String?)] { - return [] - } - - /** - Override this method when you want custom property value conversion - - This method is in EVObject and not in extension of NSObject because a functions from extensions cannot be overwritten yet - - - returns: Returns an array where each item is a combination of the folowing 3 values: A string for the property name where the custom conversion is for, a setter function and a getter function. - */ - open func propertyConverters() -> [(String?, ((Any?)->())?, (() -> Any?)? )] { - return [] - } - - /** - This is a general functon where you can filter for specific values (like nil or empty string) when creating a dictionary + Implementation of the NSObject isEqual comparisson method - - parameter value: The value that we will test - - parameter key: The key for the value + This method is in EVObject and not in NSObject extension because you would get the error: method conflicts with previous declaration with the same Objective-C selector - - returns: True if the value needs to be ignored. + - parameter object: The object where you want to compare with + + - returns: Returns true if the object is the same otherwise false */ - open func skipPropertyValue(_ value: Any, key: String) -> Bool { + + open override func isEqual(_ object: Any?) -> Bool { // for isEqual: + if let obj = object as? EVObject { + return EVReflection.areEqual(self, rhs: obj) + } return false } - + /** - When a property is declared as a base type for multiple enherited classes, then this function will let you pick the right specific type based on the suplied dictionary. + Returns the pritty description of this object - - parameter dict: The dictionary for the specific type - - - returns: The specific type + - returns: The pritty description */ - open func getSpecificType(_ dict: NSDictionary) -> EVObject { - return self - } - - - - // MARK: - The code below was originally in a NSObject extension. - - - /** - Returns the dictionary representation of this object. - - - parameter conversionOptions: Option set for the various conversion options. - - - returns: The dictionary - */ - open func toDictionary(_ conversionOptions: ConversionOptions = .DefaultSerialize) -> NSDictionary { - let (reflected, _) = EVReflection.toDictionary(self, conversionOptions: conversionOptions) - return reflected + open override var description: String { + get { + return EVReflection.description(self) + } } /** - Convert this object to a json string - - - parameter conversionOptions: Option set for the various conversion options. + Returns the pritty description of this object - - returns: The json string + - returns: The pritty description */ - open func toJsonString(_ conversionOptions: ConversionOptions = .DefaultSerialize) -> String { - return EVReflection.toJsonString(self, conversionOptions: conversionOptions) + open override var debugDescription: String { + get { + return EVReflection.description(self) + } } /** - Convenience method for instantiating an array from a json string. - - - parameter json: The json string - - parameter conversionOptions: Option set for the various conversion options. - - - returns: An array of objects + This basic init override is needed so we can use EVObject as a base class. */ - open class func arrayFromJson(_ json: String?, conversionOptions: ConversionOptions = .DefaultDeserialize) -> [T] where T:NSObject { - return EVReflection.arrayFromJson(nil, type: T(), json: json, conversionOptions: conversionOptions) + public required override init() { + super.init() } - - /** - Auto map an opbject to an object of an other type. - Properties with the same name will be mapped automattically. - Automattic cammpelCase, PascalCase, snake_case conversion - Supports propperty mapping and conversion when using EVObject as base class - - - parameter conversionOptions: Option set for the various conversion options. - - returns: The targe object with the mapped values - */ - open func mapObjectTo(_ conversionOptions: ConversionOptions = .DefaultDeserialize) -> T where T:NSObject { - let nsobjectype: NSObject.Type = T.self as NSObject.Type - let nsobject: NSObject = nsobjectype.init() - let dict = self.toDictionary() - let result = EVReflection.setPropertiesfromDictionary(dict, anyObject: nsobject, conversionOptions: conversionOptions) - return result as? T ?? T() - } - /** - Get the type for a given property name or `nil` if there aren't any properties matching said name. + Decode any object - - parameter propertyName: The property name + This method is in EVObject and not in NSObject because you would get the error: Initializer requirement 'init(coder:)' can + only be satisfied by a `required` initializer in the definition of non-final class 'NSObject' - - returns: The type for the property + -parameter coder: The NSCoder that will be used for decoding the object. */ - open func typeForKey(_ propertyName: String) -> Any.Type? { - let mirror = Mirror(reflecting: self) - return typeForKey(propertyName, mirror: mirror) + public convenience required init?(coder: NSCoder) { + self.init() + EVReflection.decodeObjectWithCoder(self, aDecoder: coder, conversionOptions: .DefaultNSCoding) } + /// This property will contain an array with deserialization statussses with a description. + open var evReflectionStatuses: [(DeserializationStatus, String)] = [] + /** - get the type of a property - - - parameter propertyName: The property key - - parameter mirror: The mirror of this object + Encode this object using a NSCoder - - returns: The type of the property + - parameter aCoder: The NSCoder that will be used for encoding the object */ - fileprivate func typeForKey(_ propertyName: String, mirror: Mirror) -> Any.Type? { - for (label, value) in mirror.children { - if propertyName == label { - return Mirror(reflecting: value).subjectType - } - } - - guard let superclassMirror = mirror.superclassMirror else { - return nil - } - - return typeForKey(propertyName, mirror: superclassMirror) + open func encode(with aCoder: NSCoder) { + EVReflection.encodeWithCoder(self , aCoder: aCoder, conversionOptions: .DefaultNSCoding) } + + + //MARK - Default implementation of protocol functions that we can override + /** By default there is no aditional validation. Override this function to add your own class level validation rules - parameter dict: The dictionary with keys where the initialisation is called with */ - open func initValidation(_ dict: NSDictionary) { + public func initValidation(_ dict: NSDictionary) { } /** - Validation function that you will probably call from the initValidation function. This function will make sure - the passed on keys are not in the dictionary used for initialisation. - The result of this validation is stored in evReflectionStatus. + Override this method when you want custom property mapping. - - parameter keys: The fields that may not be in the dictionary (like an error key) - - parameter dict: The dictionary that is passed on from the initValidation function + This method is in EVObject and not in extension of NSObject because a functions from extensions cannot be overwritten yet + + - returns: Return an array with value pairs of the object property name and json key name. */ - open func initMayNotContainKeys(_ keys: [String], dict: NSDictionary) { - for key in keys { - if dict[key] != nil { - addStatusMessage(.IncorrectKey, message: "Invalid key: \(key)") - } - } + public func propertyMapping() -> [(String?, String?)] { + return [] } /** - Validation function that you will probably call from the initValidation function. This function will make sure - the passed on keys are in the dictionary used for initialisation. - The result of this validation is stored in evReflectionStatus. + Override this method when you want custom property value conversion - - parameter keys: The fields that may not be in the dictionary (like an error key) - - parameter dict: The dictionary that is passed on from the initValidation function + This method is in EVObject and not in extension of NSObject because a functions from extensions cannot be overwritten yet + + - returns: Returns an array where each item is a combination of the folowing 3 values: A string for the property name where the custom conversion is for, a setter function and a getter function. */ - open func initMustContainKeys(_ keys: [String], dict: NSDictionary) { - for key in keys { - if dict[key] == nil { - addStatusMessage(.MissingKey, message: "Missing key: \(key)") - } - } + public func propertyConverters() -> [(String?, ((Any?)->())?, (() -> Any?)? )] { + return [] } - /// This property will contain an array with deserialization statussses with a description - open var evReflectionStatuses: [(DeserializationStatus, String)] = [] /** - Return a merged status out of the status array + This is a general functon where you can filter for specific values (like nil or empty string) when creating a dictionary - - returns: the deserialization status for the object - */ - open func evReflectionStatus() -> DeserializationStatus { - var status: DeserializationStatus = .None - for (s, _) in evReflectionStatuses { - status = [status, s] - } - return status - } - /** - Convenience function for adding a new status message to the evReflectionStatus array + - parameter value: The value that we will test + - parameter key: The key for the value - - parameter type: A string to specify the message type - - parameter message: The message for the status. + - returns: True if the value needs to be ignored. */ - open func addStatusMessage(_ type: DeserializationStatus, message: String) { - evReflectionStatuses.append(type, message) + public func skipPropertyValue(_ value: Any, key: String) -> Bool { + return false } /** - Convert a Swift dictionary to a NSDictionary. + When a property is declared as a base type for multiple enherited classes, then this function will let you pick the right specific type based on the suplied dictionary. - - parameter key: Key of the property that is the dictionary. Can be used when overriding this function - - parameter dict: The Swift dictionary + - parameter dict: The dictionary for the specific type - - returns: The dictionary converted to a NSDictionary + - returns: The specific type */ - open func convertDictionary(_ key: String, dict: Any) -> NSDictionary { - let returnDict = NSMutableDictionary() - for (key, value) in dict as? NSDictionary ?? NSDictionary() { - returnDict[key as? String ?? ""] = value - } - return returnDict + public func getSpecificType(_ dict: NSDictionary) -> EVReflectable { + return self } - } + + + diff --git a/EVReflection/pod/EVReflectable.swift b/EVReflection/pod/EVReflectable.swift new file mode 100644 index 00000000..06d313e9 --- /dev/null +++ b/EVReflection/pod/EVReflectable.swift @@ -0,0 +1,466 @@ +// +// EVReflectable.swift +// EVReflection +// +// Created by Edwin Vermeer on 27/10/2016. +// Copyright © 2016 evict. All rights reserved. +// + +// MARK: - Protocol with the overridable functions. All functionality is added to this in the extension below. +public protocol EVReflectable: class, NSObjectProtocol { + /** + By default there is no aditional validation. Override this function to add your own class level validation rules + + - parameter dict: The dictionary with keys where the initialisation is called with + */ + func initValidation(_ dict: NSDictionary) + + /** + Override this method when you want custom property mapping. + + This method is in EVObject and not in extension of NSObject because a functions from extensions cannot be overwritten yet + + - returns: Return an array with value pairs of the object property name and json key name. + */ + func propertyMapping() -> [(String?, String?)] + + /** + Override this method when you want custom property value conversion + + This method is in EVObject and not in extension of NSObject because a functions from extensions cannot be overwritten yet + + - returns: Returns an array where each item is a combination of the folowing 3 values: A string for the property name where the custom conversion is for, a setter function and a getter function. + */ + func propertyConverters() -> [(String?, ((Any?)->())?, (() -> Any?)? )] + + /** + This is a general functon where you can filter for specific values (like nil or empty string) when creating a dictionary + + - parameter value: The value that we will test + - parameter key: The key for the value + + - returns: True if the value needs to be ignored. + */ + func skipPropertyValue(_ value: Any, key: String) -> Bool + + /** + When a property is declared as a base type for multiple enherited classes, then this function will let you pick the right specific type based on the suplied dictionary. + + - parameter dict: The dictionary for the specific type + + - returns: The specific type + */ + func getSpecificType(_ dict: NSDictionary) -> EVReflectable + + /** + Declaration for Equatable == + + - parameter lhs: The object at the left side of the == + - parameter rhs: The object at the right side of the == + + - returns: True if the objects are the same, otherwise false. + */ + static func ==(lhs: EVReflectable, rhs: EVReflectable) -> Bool + + /** + Delclaration for Equatable != + + - parameter lhs: The object at the left side of the == + - parameter rhs: The object at the right side of the == + + - returns: False if the objects are the the same, otherwise true. + */ + static func !=(lhs: EVReflectable, rhs: EVReflectable) -> Bool +} + + +// MARK: - extending EVReflectable with the initialisation functions (which need NSObject) + + +extension EVReflectable where Self: NSObject { + + /** + Convenience init for creating an object whith the property values of a dictionary. + + - parameter dictionary: The dictionary that will be used to create this object + - parameter conversionOptions: Option set for the various conversion options. + */ + public init(dictionary: NSDictionary, conversionOptions: ConversionOptions = .DefaultDeserialize) { + self.init() + EVReflection.setPropertiesfromDictionary(dictionary, anyObject: self, conversionOptions: conversionOptions) + } + + /** + Convenience init for creating an object whith the contents of a json string. + + - parameter json: The json string that will be used to create this object + - parameter conversionOptions: Option set for the various conversion options. + */ + public init(json: String?, conversionOptions: ConversionOptions = .DefaultDeserialize) { + self.init() + let jsonDict = EVReflection.dictionaryFromJson(json) + EVReflection.setPropertiesfromDictionary(jsonDict, anyObject: self, conversionOptions: conversionOptions) + } + + /** + Convenience init for creating an object whith the property values of json Data. + + - parameter dictionary: The dictionary that will be used to create this object + - parameter conversionOptions: Option set for the various conversion options. + */ + public init(data: Data, conversionOptions: ConversionOptions = .DefaultDeserialize) { + self.init() + let dictionary: NSDictionary = (((try! JSONSerialization.jsonObject(with: data, options: []) as? NSDictionary)) ?? NSDictionary())! + EVReflection.setPropertiesfromDictionary(dictionary, anyObject: self, conversionOptions: conversionOptions) + } + + + /** + Initialize this object from an archived file from the temp directory + + - parameter fileNameInTemp: The filename + - parameter conversionOptions: Option set for the various conversion options. + */ + public init(fileNameInTemp: String, conversionOptions: ConversionOptions = .DefaultNSCoding) { + self.init() + let filePath = (NSTemporaryDirectory() as NSString).appendingPathComponent(fileNameInTemp) + if let temp = NSKeyedUnarchiver.unarchiveObject(withFile: filePath) as? EVObject { + EVReflection.setPropertiesfromDictionary( temp.toDictionary(conversionOptions), anyObject: self, conversionOptions: conversionOptions) + } + } + + /** + Initialize this object from an archived file from the documents directory + + - parameter fileNameInDocuments: The filename + - parameter conversionOptions: Option set for the various conversion options. + */ + public init(fileNameInDocuments: String, conversionOptions: ConversionOptions = .DefaultNSCoding) { + self.init() + let filePath = (NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString).appendingPathComponent(fileNameInDocuments) + if let temp = NSKeyedUnarchiver.unarchiveObject(withFile: filePath) as? EVObject { + EVReflection.setPropertiesfromDictionary( temp.toDictionary(conversionOptions), anyObject: self, conversionOptions: conversionOptions) + } + } + + /** + Returns the hashvalue of this object + + - returns: The hashvalue of this object + */ + public var hashValue: Int { + get { + return Int(EVReflection.hashValue(self)) + } + } + + /** + Function for returning the hash for the NSObject based functionality + + - returns: The hashvalue of this object + */ + public var hash: Int { + get { + return self.hashValue + } + } + +} + + +// MARK: - extending EVReflectable with most of EVReflection functionality + + +extension EVReflectable { + /** + Implementation for Equatable == + + - parameter lhs: The object at the left side of the == + - parameter rhs: The object at the right side of the == + + - returns: True if the objects are the same, otherwise false. + */ + static public func == (lhs: EVReflectable, rhs: EVReflectable) -> Bool { + if let lhso = lhs as? NSObject, let rhso = rhs as? NSObject { + return EVReflection.areEqual(lhso, rhs: rhso) + } + return lhs.isEqual(rhs) + } + + /** + Implementation for Equatable != + + - parameter lhs: The object at the left side of the == + - parameter rhs: The object at the right side of the == + + - returns: False if the objects are the the same, otherwise true. + */ + static public func != (lhs: EVReflectable, rhs: EVReflectable) -> Bool { + if let lhso = lhs as? NSObject, let rhso = rhs as? NSObject { + return !EVReflection.areEqual(lhso, rhs: rhso) + } + return !lhs.isEqual(rhs) + } + + // MARK: - extending the base implementation for the overridable functions + + + /** + By default there is no aditional validation. Override this function to add your own class level validation rules + + - parameter dict: The dictionary with keys where the initialisation is called with + */ + public func initValidation(_ dict: NSDictionary) { + } + + /** + Override this method when you want custom property mapping. + + This method is in EVObject and not in extension of NSObject because a functions from extensions cannot be overwritten yet + + - returns: Return an array with value pairs of the object property name and json key name. + */ + public func propertyMapping() -> [(String?, String?)] { + return [] + } + + /** + Override this method when you want custom property value conversion + + This method is in EVObject and not in extension of NSObject because a functions from extensions cannot be overwritten yet + + - returns: Returns an array where each item is a combination of the folowing 3 values: A string for the property name where the custom conversion is for, a setter function and a getter function. + */ + public func propertyConverters() -> [(String?, ((Any?)->())?, (() -> Any?)? )] { + return [] + } + + /** + This is a general functon where you can filter for specific values (like nil or empty string) when creating a dictionary + + - parameter value: The value that we will test + - parameter key: The key for the value + + - returns: True if the value needs to be ignored. + */ + public func skipPropertyValue(_ value: Any, key: String) -> Bool { + return false + } + + /** + When a property is declared as a base type for multiple enherited classes, then this function will let you pick the right specific type based on the suplied dictionary. + + - parameter dict: The dictionary for the specific type + + - returns: The specific type + */ + public func getSpecificType(_ dict: NSDictionary) -> EVReflectable { + return self + } + + // MARK: - extension methods + + + /** + Save this object to a file in the temp directory + + - parameter fileName: The filename + + - returns: Nothing + */ + @discardableResult + public func saveToTemp(_ fileName: String) -> Bool { + let filePath = (NSTemporaryDirectory() as NSString).appendingPathComponent(fileName) + return NSKeyedArchiver.archiveRootObject(self, toFile: filePath) + } + + + + #if os(tvOS) + // Save to documents folder is not supported on tvOS + #else + /** + Save this object to a file in the documents directory + + - parameter fileName: The filename + + - returns: true if successfull + */ + @discardableResult + public func saveToDocuments(_ fileName: String) -> Bool { + let filePath = (NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as NSString).appendingPathComponent(fileName) + return NSKeyedArchiver.archiveRootObject(self, toFile: filePath) + } + #endif + + + /** + Returns the dictionary representation of this object. + + - parameter conversionOptions: Option set for the various conversion options. + + - returns: The dictionary + */ + public func toDictionary(_ conversionOptions: ConversionOptions = .DefaultSerialize) -> NSDictionary { + if let obj = self as? NSObject { + let (reflected, _) = EVReflection.toDictionary(obj, conversionOptions: conversionOptions) + return reflected + } + print("ERROR: You should only extend object with EVReflectable that are derived from NSObject!") + return NSDictionary() + } + + /** + Convert this object to a json string + + - parameter conversionOptions: Option set for the various conversion options. + + - returns: The json string + */ + public func toJsonString(_ conversionOptions: ConversionOptions = .DefaultSerialize) -> String { + if let obj = self as? NSObject { + return EVReflection.toJsonString(obj, conversionOptions: conversionOptions) + } + print("ERROR: You should only extend object with EVReflectable that are derived from NSObject!") + return "{}" + } + + /** + Convenience method for instantiating an array from a json string. + + - parameter json: The json string + - parameter conversionOptions: Option set for the various conversion options. + + - returns: An array of objects + */ + public static func arrayFromJson(_ json: String?, conversionOptions: ConversionOptions = .DefaultDeserialize) -> [T] where T:NSObject { + return EVReflection.arrayFromJson(nil, type: T(), json: json, conversionOptions: conversionOptions) + } + + /** + Auto map an opbject to an object of an other type. + Properties with the same name will be mapped automattically. + Automattic cammpelCase, PascalCase, snake_case conversion + Supports propperty mapping and conversion when using EVObject as base class + + - parameter conversionOptions: Option set for the various conversion options. + + - returns: The targe object with the mapped values + */ + public func mapObjectTo(_ conversionOptions: ConversionOptions = .DefaultDeserialize) -> T where T:NSObject { + let nsobjectype: NSObject.Type = T.self as NSObject.Type + let nsobject: NSObject = nsobjectype.init() + let dict = self.toDictionary() + let result = EVReflection.setPropertiesfromDictionary(dict, anyObject: nsobject, conversionOptions: conversionOptions) + return result as? T ?? T() + } + + /** + Get the type for a given property name or `nil` if there aren't any properties matching said name. + + - parameter propertyName: The property name + + - returns: The type for the property + */ + public func typeForKey(_ propertyName: String) -> Any.Type? { + let mirror = Mirror(reflecting: self) + return typeForKey(propertyName, mirror: mirror) + } + + /** + get the type of a property + + - parameter propertyName: The property key + - parameter mirror: The mirror of this object + + - returns: The type of the property + */ + fileprivate func typeForKey(_ propertyName: String, mirror: Mirror) -> Any.Type? { + for (label, value) in mirror.children { + if propertyName == label { + return Mirror(reflecting: value).subjectType + } + } + + guard let superclassMirror = mirror.superclassMirror else { + return nil + } + + return typeForKey(propertyName, mirror: superclassMirror) + } + + + /** + Convert a Swift dictionary to a NSDictionary. + + - parameter key: Key of the property that is the dictionary. Can be used when overriding this function + - parameter dict: The Swift dictionary + + - returns: The dictionary converted to a NSDictionary + */ + public func convertDictionary(_ key: String, dict: Any) -> NSDictionary { + let returnDict = NSMutableDictionary() + for (key, value) in dict as? NSDictionary ?? NSDictionary() { + returnDict[key as? String ?? ""] = value + } + return returnDict + } + + + // MARK: - extending serialization status functions + + + /** + Validation function that you will probably call from the initValidation function. This function will make sure + the passed on keys are not in the dictionary used for initialisation. + The result of this validation is stored in evReflectionStatus. + + - parameter keys: The fields that may not be in the dictionary (like an error key) + - parameter dict: The dictionary that is passed on from the initValidation function + */ + public func initMayNotContainKeys(_ keys: [String], dict: NSDictionary) { + for key in keys { + if dict[key] != nil { + addStatusMessage(.IncorrectKey, message: "Invalid key: \(key)") + } + } + } + + /** + Validation function that you will probably call from the initValidation function. This function will make sure + the passed on keys are in the dictionary used for initialisation. + The result of this validation is stored in evReflectionStatus. + + - parameter keys: The fields that may not be in the dictionary (like an error key) + - parameter dict: The dictionary that is passed on from the initValidation function + */ + public func initMustContainKeys(_ keys: [String], dict: NSDictionary) { + for key in keys { + if dict[key] == nil { + addStatusMessage(.MissingKey, message: "Missing key: \(key)") + } + } + } + /** + Return a merged status out of the status array + + - returns: the deserialization status for the object + */ + public func evReflectionStatus() -> DeserializationStatus { + var status: DeserializationStatus = .None + for (s, _) in (self as? EVObject)?.evReflectionStatuses ?? [] { + status = [status, s] + } + return status + } + + /** + Convenience function for adding a new status message to the evReflectionStatus array + + - parameter type: A string to specify the message type + - parameter message: The message for the status. + */ + public func addStatusMessage(_ type: DeserializationStatus, message: String) { + (self as? EVObject)?.evReflectionStatuses.append(type, message) + } +} diff --git a/EVReflection/pod/EVReflection.swift b/EVReflection/pod/EVReflection.swift index 0f20dc4e..ec86deab 100644 --- a/EVReflection/pod/EVReflection.swift +++ b/EVReflection/pod/EVReflection.swift @@ -27,8 +27,10 @@ final public class EVReflection { */ public class func fromDictionary(_ dictionary: NSDictionary, anyobjectTypeString: String, conversionOptions: ConversionOptions = .DefaultDeserialize) -> NSObject? { if var nsobject = swiftClassFromString(anyobjectTypeString) { - if let evResult = nsobject as? EVObject { - nsobject = evResult.getSpecificType(dictionary) + if let evResult = nsobject as? EVReflectable { + if let specific = evResult.getSpecificType(dictionary) as? NSObject { + nsobject = specific + } } nsobject = setPropertiesfromDictionary(dictionary, anyObject: nsobject, conversionOptions: conversionOptions) return nsobject @@ -48,13 +50,13 @@ final public class EVReflection { */ @discardableResult public class func setPropertiesfromDictionary(_ dictionary: NSDictionary, anyObject: T, conversionOptions: ConversionOptions = .DefaultDeserialize) -> T where T: NSObject { - (anyObject as? EVObject)?.initValidation(dictionary) + (anyObject as? EVReflectable)?.initValidation(dictionary) let (keyMapping, _, types) = getKeyMapping(anyObject, dictionary: dictionary, conversionOptions: .PropertyMapping) for (k, v) in dictionary { var skipKey = false if conversionOptions.contains(.PropertyMapping) { - if let evObject = anyObject as? EVObject { - if let mapping = evObject.propertyMapping().filter({$0.0 == k as? String}).first { + if let reflectable = anyObject as? EVReflectable { + if let mapping = reflectable.propertyMapping().filter({$0.0 == k as? String}).first { if mapping.1 == nil { skipKey = true } @@ -99,8 +101,8 @@ final public class EVReflection { var keyMapping: Dictionary = Dictionary() for (objectKey, _) in properties { if conversionOptions.contains(.PropertyMapping) { - if let evObject = anyObject as? EVObject { - if let mapping = evObject.propertyMapping().filter({$0.1 == objectKey as? String}).first { + if let reflectable = anyObject as? EVReflectable { + if let mapping = reflectable.propertyMapping().filter({$0.1 == objectKey as? String}).first { keyMapping[objectKey as? String ?? ""] = mapping.0 } } @@ -295,7 +297,7 @@ final public class EVReflection { - parameter aCoder: The NSCoder that will be used for encoding the object. - parameter conversionOptions: Option set for the various conversion options. */ - public class func encodeWithCoder(_ theObject: EVObject, aCoder: NSCoder, conversionOptions: ConversionOptions = .DefaultNSCoding) { + public class func encodeWithCoder(_ theObject: NSObject, aCoder: NSCoder, conversionOptions: ConversionOptions = .DefaultNSCoding) { let (hasKeys, _) = toDictionary(theObject, conversionOptions: conversionOptions) for (key, value) in hasKeys { aCoder.encode(value, forKey: key as? String ?? "") @@ -309,7 +311,7 @@ final public class EVReflection { - parameter aDecoder: The NSCoder that will be used for decoding the object. - parameter conversionOptions: Option set for the various conversion options. */ - public class func decodeObjectWithCoder(_ theObject: EVObject, aDecoder: NSCoder, conversionOptions: ConversionOptions = .DefaultNSCoding) { + public class func decodeObjectWithCoder(_ theObject: NSObject, aDecoder: NSCoder, conversionOptions: ConversionOptions = .DefaultNSCoding) { let (hasKeys, _) = toDictionary(theObject, conversionOptions: conversionOptions, isCachable: true) let dict = NSMutableDictionary() for (key, _) in hasKeys { @@ -564,7 +566,7 @@ final public class EVReflection { */ public class func valueForAny(_ parentObject: Any? = nil, key: String? = nil, anyValue: Any, conversionOptions: ConversionOptions = .DefaultDeserialize, isCachable: Bool = false, parents: [NSObject] = []) -> (value: AnyObject, type: String, isObject: Bool) { var theValue = anyValue - var valueType: String = "EVObject" + var valueType: String = "" var mi: Mirror = Mirror(reflecting: theValue) if mi.displayStyle == .optional { @@ -604,13 +606,13 @@ final public class EVReflection { let convertedValue = arrayConverter.convertArray(key!, array: theValue) return (convertedValue, valueType, false) } - (parentObject as? EVObject)?.addStatusMessage(.MissingProtocol, message: "An object with a property of type Array with optional objects should implement the EVArrayConvertable protocol. type = \(valueType) for key \(key)") + (parentObject as? EVReflectable)?.addStatusMessage(.MissingProtocol, message: "An object with a property of type Array with optional objects should implement the EVArrayConvertable protocol. type = \(valueType) for key \(key)") print("WARNING: An object with a property of type Array with optional objects should implement the EVArrayConvertable protocol. type = \(valueType) for key \(key)") return (NSNull(), "NSNull", false) } } else if mi.displayStyle == .dictionary { valueType = String(reflecting: type(of:theValue)) - if let dictionaryConverter = parentObject as? EVObject { + if let dictionaryConverter = parentObject as? EVReflectable { let convertedValue = dictionaryConverter.convertDictionary(key!, dict: theValue) return (convertedValue, valueType, false) } @@ -621,14 +623,14 @@ final public class EVReflection { let convertedValue = arrayConverter.convertArray(key!, array: theValue) return (convertedValue, valueType, false) } - (parentObject as? EVObject)?.addStatusMessage(.MissingProtocol, message: "An object with a property of type Set should implement the EVArrayConvertable protocol. type = \(valueType) for key \(key)") + (parentObject as? EVReflectable)?.addStatusMessage(.MissingProtocol, message: "An object with a property of type Set should implement the EVArrayConvertable protocol. type = \(valueType) for key \(key)") print("WARNING: An object with a property of type Set should implement the EVArrayConvertable protocol. type = \(valueType) for key \(key)") return (NSNull(), "NSNull", false) } } else if mi.displayStyle == .struct { valueType = String(reflecting: type(of:theValue)) if valueType.contains("Dictionary") { - if let dictionaryConverter = parentObject as? EVObject { + if let dictionaryConverter = parentObject as? EVReflectable { let convertedValue = dictionaryConverter.convertDictionary(key!, dict: theValue) return (convertedValue, valueType, false) } @@ -712,11 +714,11 @@ final public class EVReflection { if theValue is NSArray { return (theValue as! NSArray, valueType, false) } - if theValue is EVObject { + if theValue is EVReflectable && theValue is NSObject { if valueType.contains("<") { - return (theValue as! EVObject, swiftStringFromClass(theValue as! EVObject), true) + return (theValue as! EVReflectable, swiftStringFromClass(theValue as! NSObject), true) } - return (theValue as! EVObject, valueType, true) + return (theValue as! EVReflectable, valueType, true) } if theValue is NSObject { if valueType.contains("<") { @@ -731,7 +733,7 @@ final public class EVReflection { return ((parentObject as! EVArrayConvertable).convertArray(key ?? "_unknownKey", array: theValue), valueType, false) } - (parentObject as? EVObject)?.addStatusMessage(.InvalidType, message: "valueForAny unkown type \(valueType) for value: \(theValue).") + (parentObject as? EVReflectable)?.addStatusMessage(.InvalidType, message: "valueForAny unkown type \(valueType) for value: \(theValue).") print("ERROR: valueForAny unkown type \(valueType) for value: \(theValue).") return (NSNull(), "NSNull", false) } @@ -760,7 +762,7 @@ final public class EVReflection { } if conversionOptions.contains(.PropertyConverter) { - if let (_, propertySetter, _) = (anyObject as? EVObject)?.propertyConverters().filter({$0.0 == key}).first { + if let (_, propertySetter, _) = (anyObject as? EVReflectable)?.propertyConverters().filter({$0.0 == key}).first { guard let propertySetter = propertySetter else { return // if the propertySetter is nil, skip setting the property } @@ -789,7 +791,7 @@ final public class EVReflection { } else if (typeInObject == "NSDate" || typeInObject == "Date") && (type == "String" || type == "NSString") { if let convertedValue = value as? String { guard let date = getDateFormatter().date(from: convertedValue) else { - (anyObject as? EVObject)?.addStatusMessage(.InvalidValue, message: "The dateformatter returend nil for value \(convertedValue)") + (anyObject as? EVReflectable)?.addStatusMessage(.InvalidValue, message: "The dateformatter returend nil for value \(convertedValue)") print("WARNING: The dateformatter returend nil for value \(convertedValue)") return } @@ -815,7 +817,7 @@ final public class EVReflection { try anyObject.validateValue(&setValue, forKey: key) anyObject.setValue(setValue, forKey: key) } catch _ { - (anyObject as? EVObject)?.addStatusMessage(.InvalidValue, message: "Not a valid value for object `\(NSStringFromClass(type(of: (anyObject as AnyObject))))`, type `\(type)`, key `\(key)`, value `\(value)`") + (anyObject as? EVReflectable)?.addStatusMessage(.InvalidValue, message: "Not a valid value for object `\(NSStringFromClass(type(of: (anyObject as AnyObject))))`, type `\(type)`, key `\(key)`, value `\(value)`") print("INFO: Not a valid value for object `\(NSStringFromClass(type(of: (anyObject as AnyObject))))`, type `\(type)`, key `\(key)`, value `\(value)`") } @@ -1025,7 +1027,7 @@ final public class EVReflection { array.append(dictValue as? NSDictionary ?? NSDictionary()) dictValue = dictArrayToObjectArray(anyObject, key: key, type: type, array: array as NSArray, conversionOptions: conversionOptions) as NSArray } - } else if let _ = type.range(of: "_NativeDictionaryStorageOwner"), let dict = dictValue as? NSDictionary, let org = anyObject as? EVObject { + } else if let _ = type.range(of: "_NativeDictionaryStorageOwner"), let dict = dictValue as? NSDictionary, let org = anyObject as? EVReflectable { dictValue = org.convertDictionary(key, dict: dict) } else if type != "NSDictionary" && dictValue as? NSDictionary != nil { //TODO this too? && original is NSObject let (dict, isValid) = dictToObject(type, original: original as? NSObject, dict: dictValue as? NSDictionary ?? NSDictionary(), conversionOptions: conversionOptions) @@ -1034,14 +1036,14 @@ final public class EVReflection { } else if type.range(of: "") == nil && dictValue as? [NSDictionary] != nil { // Array of objects dictValue = dictArrayToObjectArray(anyObject, key: key, type: type, array: dictValue as? [NSDictionary] as NSArray? ?? [NSDictionary]() as NSArray, conversionOptions: conversionOptions) as NSArray - } else if original is EVObject && dictValue is String && original is NSObject { + } else if dictValue is String && original is NSObject && original is EVReflectable { // fixing the conversion from XML without properties let (dict, isValid) = dictToObject(type, original:original as? NSObject, dict: ["__text": dictValue as? String ?? ""], conversionOptions: conversionOptions) dictValue = dict ?? dictValue valid = isValid } else if !type.hasPrefix("Swift.Array<") && !type.hasPrefix("Swift.Set<") { if let array = dictValue as? NSArray { - if let org = anyObject as? EVObject { + if let org = anyObject as? EVReflectable { org.addStatusMessage(DeserializationStatus.InvalidType, message: "Did not expect an array for \(key). Will use the first item instead.") print("WARNING: Did not expect an array for \(key). Will use the first item instead.") } @@ -1067,13 +1069,13 @@ final public class EVReflection { - returns: The object that is created from the dictionary */ - fileprivate class func dictToObject(_ type: String, original: T?, dict: NSDictionary, conversionOptions: ConversionOptions = .DefaultDeserialize) -> (T?, Bool) where T:NSObject { + fileprivate class func dictToObject(_ type: String, original: T?, dict: NSDictionary, conversionOptions: ConversionOptions = .DefaultDeserialize) -> (T?, Bool) where T: NSObject { if var returnObject = original { if type != "NSNumber" && type != "NSString" && type != "NSDate" && type != "Struct" && type.contains("Dictionary<") == false { returnObject = setPropertiesfromDictionary(dict, anyObject: returnObject, conversionOptions: conversionOptions) } else { if type.contains("Dictionary<") == false && type != "Struct" { - (original as? EVObject)?.addStatusMessage(.InvalidClass, message: "Cannot set values on type \(type) from dictionary \(dict)") + (original as? EVReflectable)?.addStatusMessage(.InvalidClass, message: "Cannot set values on type \(type) from dictionary \(dict)") print("WARNING: Cannot set values on type \(type) from dictionary \(dict)") } return (returnObject, false) @@ -1089,15 +1091,17 @@ final public class EVReflection { useType = subtype } if var returnObject: NSObject = swiftClassFromString(useType) { - if let evResult = returnObject as? EVObject { - returnObject = evResult.getSpecificType(dict) + if let evResult = returnObject as? EVReflectable { + if let specific = evResult.getSpecificType(dict) as? NSObject { + returnObject = specific + } } returnObject = setPropertiesfromDictionary(dict, anyObject: returnObject, conversionOptions: conversionOptions) return (returnObject as? T, true) } if useType != "Struct" { - (original as? EVObject)?.addStatusMessage(.InvalidClass, message: "Could not create an instance for type \(type)\ndict:\(dict)") + (original as? EVReflectable)?.addStatusMessage(.InvalidClass, message: "Could not create an instance for type \(type)\ndict:\(dict)") print("ERROR: Could not create an instance for type \(useType)\ndict:\(dict)") } return (nil, false) @@ -1113,7 +1117,7 @@ final public class EVReflection { - returns: The array of objects that is created from the array of dictionaries */ fileprivate class func dictArrayToObjectArray(_ anyObject: NSObject, key: String, type: String, array: NSArray, conversionOptions: ConversionOptions = .DefaultDeserialize) -> NSArray { - var subtype = "EVObject" + var subtype = "" if type.components(separatedBy: "<").count > 1 { // Remove the Array prefix subtype = type.substring(from: (type.components(separatedBy: "<") [0] + "<").endIndex) @@ -1132,8 +1136,10 @@ final public class EVReflection { for item in array { //var org = result.getTypeInstance() does not work !? var org = swiftClassFromString(subtype) - if let evResult = org as? EVObject { - org = evResult.getSpecificType(item as? NSDictionary ?? NSDictionary()) + if let evResult = org as? EVReflectable { + if let specific = evResult.getSpecificType(item as? NSDictionary ?? NSDictionary()) as? NSObject { + org = specific + } } if let evResult = anyObject as? EVGenericsKVC { org = evResult.getGenericType() @@ -1180,8 +1186,8 @@ final public class EVReflection { skipThisKey = true } if conversionOptions.contains(.PropertyMapping) { - if let evObject = theObject as? EVObject { - if let mapping = evObject.propertyMapping().filter({$0.0 == originalKey}).first { + if let reflectable = theObject as? EVReflectable { + if let mapping = reflectable.propertyMapping().filter({$0.0 == originalKey}).first { if mapping.1 == nil { skipThisKey = true } else { @@ -1198,7 +1204,7 @@ final public class EVReflection { if conversionOptions.contains(.PropertyConverter) { // If there is a properyConverter, then use the result of that instead. - if let (_, _, propertyGetter) = (theObject as? EVObject)?.propertyConverters().filter({$0.0 == originalKey}).first { + if let (_, _, propertyGetter) = (theObject as? EVReflectable)?.propertyConverters().filter({$0.0 == originalKey}).first { guard let propertyGetter = propertyGetter else { continue // if propertyGetter is nil, skip getting the property @@ -1235,8 +1241,8 @@ final public class EVReflection { } if conversionOptions.contains(.SkipPropertyValue) { - if let evObject = theObject as? EVObject { - if !evObject.skipPropertyValue(unboxedValue, key: mapKey) { + if let reflectable = theObject as? EVReflectable { + if !reflectable.skipPropertyValue(unboxedValue, key: mapKey) { propertiesDictionary.setValue(unboxedValue, forKey: mapKey) propertiesTypeDictionary[mapKey] = valueType } @@ -1296,7 +1302,7 @@ final public class EVReflection { case let ok as NSDictionary: return convertDictionaryForJsonSerialization(ok, theObject: theObject) default: - (theObject as? EVObject)?.addStatusMessage(.InvalidType, message: "Unexpected type while converting value for JsonSerialization: \(value)") + (theObject as? EVReflectable)?.addStatusMessage(.InvalidType, message: "Unexpected type while converting value for JsonSerialization: \(value)") NSLog("ERROR: Unexpected type while converting value for JsonSerialization: \(value)") return "\(value)" as AnyObject }