├── Example ├── .gitignore ├── ObjectMapperAdditions.xcodeproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ ├── xcshareddata │ │ └── xcschemes │ │ │ └── ObjectMapperAdditions-Example.xcscheme │ └── project.pbxproj ├── ObjectMapperAdditions.xcworkspace │ ├── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── WorkspaceSettings.xcsettings │ └── contents.xcworkspacedata ├── ObjectMapperAdditions │ ├── AppDelegate.swift │ ├── ExampleEnums.swift │ ├── MyOtherModel.swift │ ├── Images.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── MyOtherRealmModel.swift │ ├── Info.plist │ ├── Base.lproj │ │ ├── Main.storyboard │ │ └── LaunchScreen.xib │ ├── MyModel.swift │ ├── MyRealmModel.swift │ ├── MyPersistedRealmModel.swift │ └── ViewController.swift ├── Tests │ ├── Info.plist │ ├── RealmPropertyTransform_Spec.swift │ ├── ISO8601JustDateTransform_Spec.swift │ ├── RealmPropertyTypeCastTransform_Spec.swift │ ├── TimestampTransform_Spec.swift │ ├── MongoDateTransform_Spec.swift │ ├── Create_Spec.swift │ ├── Realm_Spec.swift │ └── TypeCast_Spec.swift ├── Scripts │ └── Cocoapods │ │ ├── podUpdate.command │ │ ├── utils.sh │ │ ├── podInstall.command │ │ └── podSetup.command ├── Podfile └── Podfile.lock ├── ObjectMapperAdditions ├── Assets │ └── .gitkeep ├── Classes │ ├── Core │ │ ├── Types │ │ │ ├── MappingError.swift │ │ │ ├── OptionalType.swift │ │ │ └── ASCIICodes.swift │ │ ├── Extensions │ │ │ ├── ISO8601DateFormatter+Additions.swift │ │ │ ├── Bool+Extension.swift │ │ │ ├── StringProtocol+Extension.swift │ │ │ ├── Double+Extension.swift │ │ │ ├── Map+Additions.swift │ │ │ ├── BaseMappable+Additions.swift │ │ │ ├── Int+Extension.swift │ │ │ ├── ObjectMapper+Additions.swift │ │ │ ├── String+Extension.swift │ │ │ ├── Data+Extension.swift │ │ │ ├── ImmutableMappable+Create.swift │ │ │ ├── Mappable+Create.swift │ │ │ └── BaseMappable+Create.swift │ │ └── Transforms │ │ │ ├── ISO8601JustDateTransform.swift │ │ │ ├── TimestampTransform.swift │ │ │ ├── TypeCastTransform.swift │ │ │ ├── BoolTransform.swift │ │ │ ├── IntTransform.swift │ │ │ ├── StringTransform.swift │ │ │ ├── DoubleTransform.swift │ │ │ ├── MongoDateTransform.swift │ │ │ └── EnumTypeCastTransform.swift │ └── Realm │ │ ├── Extensions │ │ └── Dictionary+RealmAdditions.swift │ │ ├── RealmListTransform.swift │ │ ├── ObjectIdTransform.swift │ │ ├── RealmPropertyTransform.swift │ │ ├── RealmTransform.swift │ │ ├── RealmPropertyTypeCastTransform.swift │ │ ├── RealmTypeCastTransform.swift │ │ ├── Realm+ObjectMapper.swift │ │ └── ObjectMapper+Realm.swift └── Privacy │ ├── ObjectMapperAdditions.Core │ └── PrivacyInfo.xcprivacy │ └── ObjectMapperAdditions.Realm │ └── PrivacyInfo.xcprivacy ├── packageUpdate.command ├── podCheck.command ├── podPush.command ├── DEPLOY PROCESS.md ├── .travis.yml ├── .cocoadocs.yml ├── .gitignore ├── LICENSE ├── checkBuild.command ├── Package.resolved ├── Package.swift ├── ObjectMapperAdditions.podspec ├── CHANGELOG.md └── README.md /Example/.gitignore: -------------------------------------------------------------------------------- 1 | 2 | /Pods/ 3 | -------------------------------------------------------------------------------- /ObjectMapperAdditions/Assets/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /packageUpdate.command: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | base_dir=$(dirname "$0") 4 | cd "$base_dir" 5 | 6 | set -e 7 | 8 | swift package update 9 | -------------------------------------------------------------------------------- /podCheck.command: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | base_dir=$(dirname "$0") 4 | cd "$base_dir" 5 | 6 | # Checking lib lint 7 | pod lib lint --verbose --no-clean --allow-warnings 8 | -------------------------------------------------------------------------------- /podPush.command: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | base_dir=$(dirname "$0") 4 | cd "$base_dir" 5 | 6 | # Pushing latest version to cocoapods 7 | pod trunk push --allow-warnings --verbose 8 | -------------------------------------------------------------------------------- /Example/ObjectMapperAdditions.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /DEPLOY PROCESS.md: -------------------------------------------------------------------------------- 1 | - Change version in podspec 2 | - Run `podUpdate.command` 3 | - Run `packageUpdate.command` 4 | - Run `checkBuild.command` 5 | - Update CHANGELOG.md 6 | - Update README.md with new version if needed 7 | - Push changes in git 8 | - Add git tag 9 | - Run `podPush.command` 10 | -------------------------------------------------------------------------------- /Example/ObjectMapperAdditions.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Example/ObjectMapperAdditions.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # references: 2 | # * http://www.objc.io/issue-6/travis-ci.html 3 | # * https://github.com/supermarin/xcpretty#usage 4 | 5 | osx_image: xcode12.4 6 | 7 | language: swift 8 | 9 | cache: 10 | directories: 11 | - Example/Pods 12 | 13 | before_install: 14 | - Example/Scripts/Cocoapods/podInstall.command 15 | 16 | script: 17 | - bash checkBuild.command 18 | -------------------------------------------------------------------------------- /Example/ObjectMapperAdditions/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // ObjectMapperAdditions 4 | // 5 | // Created by Anton Plebanovich on 07/17/2017. 6 | // Copyright (c) 2017 Anton Plebanovich. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | var window: UIWindow? 14 | } 15 | -------------------------------------------------------------------------------- /ObjectMapperAdditions/Classes/Core/Types/MappingError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MappingError.swift 3 | // ObjectMapperAdditions 4 | // 5 | // Created by Anton Plebanovich on 16.11.21. 6 | // Copyright © 2021 Anton Plebanovich. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | public enum MappingError: Error { 12 | case emptyData 13 | case invalidJSON(message: String) 14 | case unknownType 15 | } 16 | -------------------------------------------------------------------------------- /Example/ObjectMapperAdditions/ExampleEnums.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExampleEnums.swift 3 | // ObjectMapperAdditions 4 | // 5 | // Created by Anton Plebanovich on 4/4/19. 6 | // Copyright © 2019 CocoaPods. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | 12 | enum ExampleIntEnum: Int { 13 | case one = 1 14 | case two = 2 15 | case three = 3 16 | } 17 | 18 | enum ExampleStringEnum: String { 19 | case one = "1" 20 | case two = "2" 21 | case three = "3" 22 | } 23 | -------------------------------------------------------------------------------- /.cocoadocs.yml: -------------------------------------------------------------------------------- 1 | highlight-font: '"GT Walsheim", "gt_walsheim_regular", "Avant Garde Gothic ITCW01Dm", "Avant Garde", "Helvetica Neue", "Arial"' 2 | 3 | body: '"Helvetica Neue", "Arial", san-serif' 4 | code: '"Monaco", "Menlo", "Consolas", "Courier New", monospace' 5 | 6 | highlight-color: '#ED0015' 7 | highlight-dark-color: '#A90010' 8 | 9 | darker-color: '#C6B7B2' 10 | darker-dark-color: '#A8A8A8' 11 | 12 | background-color: '#F2F2F2' 13 | alt-link-color: '#B7233F' 14 | warning-color: '#B80E3D' 15 | -------------------------------------------------------------------------------- /ObjectMapperAdditions/Privacy/ObjectMapperAdditions.Core/PrivacyInfo.xcprivacy: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSPrivacyTracking 6 | 7 | NSPrivacyTrackingDomains 8 | 9 | NSPrivacyCollectedDataTypes 10 | 11 | NSPrivacyAccessedAPITypes 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /ObjectMapperAdditions/Privacy/ObjectMapperAdditions.Realm/PrivacyInfo.xcprivacy: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSPrivacyTracking 6 | 7 | NSPrivacyTrackingDomains 8 | 9 | NSPrivacyCollectedDataTypes 10 | 11 | NSPrivacyAccessedAPITypes 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /Example/ObjectMapperAdditions.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BuildSystemType 6 | Latest 7 | DisableBuildSystemDeprecationDiagnostic 8 | 9 | DisableBuildSystemDeprecationWarning 10 | 11 | PreviewsEnabled 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /ObjectMapperAdditions/Classes/Realm/Extensions/Dictionary+RealmAdditions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Dictionary+RealmAdditions.swift 3 | // Pods 4 | // 5 | // Created by Anton Plebanovich on 27.12.24. 6 | // Copyright © 2024 Anton Plebanovich. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import RealmSwift 11 | 12 | extension Dictionary where Key == String { 13 | func _getObjectID() -> ObjectId? { 14 | guard let idString = self["$oid"] as? String else { return nil } 15 | return try? ObjectId(string: idString) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Example/ObjectMapperAdditions/MyOtherModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MyOtherModel.swift 3 | // ObjectMapperAdditions 4 | // 5 | // Created by Anton Plebanovich on 7/27/17. 6 | // Copyright © Anton Plebanovich. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import ObjectMapper 11 | import ObjectMapperAdditions 12 | 13 | 14 | struct MyOtherModel: Mappable { 15 | init?(map: Map) {} 16 | mutating func mapping(map: Map) {} 17 | } 18 | 19 | //----------------------------------------------------------------------------- 20 | // MARK: - Equatable 21 | //----------------------------------------------------------------------------- 22 | 23 | extension MyOtherModel: Equatable { 24 | static func ==(lhs: MyOtherModel, rhs: MyOtherModel) -> Bool { 25 | return true 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Example/ObjectMapperAdditions/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Example/Tests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /ObjectMapperAdditions/Classes/Core/Types/OptionalType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // OptionalType.swift 3 | // ObjectMapperAdditions 4 | // 5 | // Created by Anton Plebanovich on 17.11.21. 6 | // Copyright © 2021 Anton Plebanovich. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | // Take from https://github.com/RxSwiftCommunity/RxOptional/blob/master/Sources/RxOptional/OptionalType.swift 12 | // Originally from here: https://github.com/artsy/eidolon/blob/24e36a69bbafb4ef6dbe4d98b575ceb4e1d8345f/Kiosk/Observable%2BOperators.swift#L30-L40 13 | // Credit to Artsy and @ashfurrow 14 | public protocol OptionalType { 15 | associatedtype Wrapped 16 | var value: Wrapped? { get } 17 | init(nilLiteral: ()) 18 | } 19 | 20 | extension Optional: OptionalType { 21 | /// Cast `Optional` to `Wrapped?` 22 | public var value: Wrapped? { self } 23 | } 24 | -------------------------------------------------------------------------------- /Example/ObjectMapperAdditions/MyOtherRealmModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MyOtherRealmModel.swift 3 | // ObjectMapperAdditions 4 | // 5 | // Created by mac-246 on 07/27/17. 6 | // Copyright © 2017 mac-246. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import ObjectMapper 11 | import ObjectMapperAdditions 12 | import RealmSwift 13 | 14 | 15 | class MyOtherRealmModel: Object, Mappable { 16 | @objc dynamic var string: String? 17 | required convenience init?(map: ObjectMapper.Map) { self.init() } 18 | func mapping(map: ObjectMapper.Map) { 19 | performMapping { 20 | string <- (map["string"], StringTransform.shared) 21 | } 22 | } 23 | 24 | override func isEqual(_ object: Any?) -> Bool { 25 | guard let other = object as? MyOtherRealmModel else { return false } 26 | return string == other.string 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /ObjectMapperAdditions/Classes/Core/Extensions/ISO8601DateFormatter+Additions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ISO8601DateFormatter+Additions.swift 3 | // Pods 4 | // 5 | // Created by Anton Plebanovich on 27.12.24. 6 | // Copyright © 2024 Anton Plebanovich. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension ISO8601DateFormatter { 12 | static let `default`: ISO8601DateFormatter = ISO8601DateFormatter() 13 | 14 | static let withMillisAndTimeZone: ISO8601DateFormatter = { 15 | let df = ISO8601DateFormatter() 16 | df.formatOptions = [ 17 | .withInternetDateTime, 18 | .withDashSeparatorInDate, 19 | .withColonSeparatorInTime, 20 | .withTimeZone, 21 | .withFractionalSeconds, 22 | .withColonSeparatorInTimeZone 23 | ] 24 | 25 | return df 26 | }() 27 | } 28 | -------------------------------------------------------------------------------- /ObjectMapperAdditions/Classes/Core/Types/ASCIICodes.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ASCIICodes.swift 3 | // ObjectMapperAdditions 4 | // 5 | // Created by Anton Plebanovich on 16.11.21. 6 | // Copyright © 2021 Anton Plebanovich. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | enum ASCIICodes { 12 | static let openCurlyBracket: UInt8 = "{".data(using: .ascii)!.first! 13 | static let closeCurlyBracket: UInt8 = "}".data(using: .ascii)!.first! 14 | static let openSquareBracket: UInt8 = "[".data(using: .ascii)!.first! 15 | static let closeSquareBracket: UInt8 = "]".data(using: .ascii)!.first! 16 | static let space: UInt8 = " ".data(using: .ascii)!.first! 17 | static let carriageReturn: UInt8 = "\r".data(using: .ascii)!.first! 18 | static let newLine: UInt8 = "\n".data(using: .ascii)!.first! 19 | 20 | static let whitespaceSet: Set = [ 21 | space, 22 | carriageReturn, 23 | newLine, 24 | ] 25 | } 26 | -------------------------------------------------------------------------------- /ObjectMapperAdditions/Classes/Core/Extensions/Bool+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Bool+Extension.swift 3 | // ObjectMapperAdditions 4 | // 5 | // Created by Anton Plebanovich on 28.07.22. 6 | // Copyright © 2022 Anton Plebanovich. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import RoutableLogger 11 | 12 | extension Bool { 13 | 14 | static func safeFrom(_ string: String, file: String = #file, function: String = #function, line: UInt = #line) -> Bool? { 15 | if string.isNil { 16 | RoutableLogger.logDebug("[\(file._fileName):\(line)] Received '\(string)' string instead of a Bool. Considering it as `nil`.") 17 | return nil 18 | } 19 | 20 | if let bool = string.asBool { 21 | return bool 22 | } else { 23 | RoutableLogger.logError("Unable to cast String to Bool", data: ["string": string], file: file, function: function, line: line) 24 | return nil 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /ObjectMapperAdditions/Classes/Core/Extensions/StringProtocol+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StringProtocol+Extension.swift 3 | // ObjectMapperAdditions 4 | // 5 | // Created by Anton Plebanovich on 28.07.22. 6 | // Copyright © 2022 Anton Plebanovich. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | extension StringProtocol { 12 | 13 | /// Returns `self` as `Bool` if conversion is possible. 14 | var asBool: Bool? { 15 | if let bool = Bool(asString) { 16 | return bool 17 | } 18 | 19 | switch lowercased() { 20 | case "true", "yes", "1", "enable": return true 21 | case "false", "no", "0", "disable": return false 22 | default: return nil 23 | } 24 | } 25 | 26 | /// Returns `self` as `String` 27 | var asString: String { 28 | if let string = self as? String { 29 | return string 30 | } else { 31 | return String(self) 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /ObjectMapperAdditions/Classes/Realm/RealmListTransform.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RealmListTransform.swift 3 | // ObjectMapper+Realm 4 | // 5 | // Created by Jake Peterson on 8/25/16. 6 | // Copyright © 2016 jakenberg. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import ObjectMapper 11 | import RealmSwift 12 | 13 | public struct RealmListTransform: TransformType where T: BaseMappable { 14 | 15 | public init() { } 16 | 17 | public typealias Object = List 18 | public typealias JSON = Array 19 | 20 | public func transformFromJSON(_ value: Any?) -> List? { 21 | if let objects = Mapper().mapArray(JSONObject: value) { 22 | let list = List() 23 | list.append(objectsIn: objects) 24 | return list 25 | } 26 | return nil 27 | } 28 | 29 | public func transformToJSON(_ value: Object?) -> JSON? { 30 | return value?.compactMap { $0.toJSON() } 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /ObjectMapperAdditions/Classes/Core/Extensions/Double+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Double+Extension.swift 3 | // ObjectMapperAdditions 4 | // 5 | // Created by Anton Plebanovich on 28.07.22. 6 | // Copyright © 2022 Anton Plebanovich. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import RoutableLogger 11 | 12 | extension Double { 13 | 14 | static func safeFrom(_ string: String, file: String = #file, function: String = #function, line: UInt = #line) -> Double? { 15 | if string.isNil { 16 | RoutableLogger.logDebug("[\(file._fileName):\(line)] Received '\(string)' string instead of a Double. Considering it as `nil`.") 17 | return nil 18 | } 19 | 20 | if let double = Double(string) { 21 | return double 22 | } else { 23 | RoutableLogger.logError("Unable to cast String to Double", data: ["string": string], file: file, function: function, line: line) 24 | return nil 25 | } 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OS X 2 | .DS_Store 3 | 4 | # Xcode 5 | build/ 6 | *.pbxuser 7 | !default.pbxuser 8 | *.mode1v3 9 | !default.mode1v3 10 | *.mode2v3 11 | !default.mode2v3 12 | *.perspectivev3 13 | !default.perspectivev3 14 | xcuserdata/ 15 | *.xccheckout 16 | profile 17 | *.moved-aside 18 | DerivedData 19 | *.hmap 20 | *.ipa 21 | *.d 22 | *.dia 23 | 24 | # Bundler 25 | .bundle 26 | 27 | /Carthage 28 | # We recommend against adding the Pods directory to your .gitignore. However 29 | # you should judge for yourself, the pros and cons are mentioned at: 30 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 31 | # 32 | # Note: if you ignore the Pods directory, make sure to uncomment 33 | # `pod install` in .travis.yml 34 | # 35 | Pods/ 36 | 37 | # Swift Package Manager 38 | # 39 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata 40 | # hence it is not needed unless you have added a package configuration file to your project 41 | # .swiftpm 42 | 43 | .build/ 44 | .swiftpm/ 45 | /Packages 46 | -------------------------------------------------------------------------------- /ObjectMapperAdditions/Classes/Realm/ObjectIdTransform.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ObjectIdTransform.swift 3 | // Pods 4 | // 5 | // Created by Anton Plebanovich on 12.12.21. 6 | // Copyright © 2021 Anton Plebanovich. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import ObjectMapper 11 | import RealmSwift 12 | 13 | public final class ObjectIdTransform: TransformType { 14 | public typealias Object = ObjectId 15 | public typealias JSON = [String: String] 16 | 17 | public static let shared = ObjectIdTransform() 18 | 19 | fileprivate init() {} 20 | 21 | public func transformFromJSON(_ value: Any?) -> Object? { 22 | guard let dateDict = value as? JSON, 23 | let id = dateDict._getObjectID() else { return nil } 24 | 25 | return id 26 | } 27 | 28 | public func transformToJSON(_ value: Object?) -> JSON? { 29 | if let id = value { 30 | let string = id.stringValue 31 | return ["$oid": string] 32 | } else { 33 | return nil 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Example/Scripts/Cocoapods/podUpdate.command: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Assume scripts are placed in /Scripts/Cocoapods dir 4 | _script_call_path="${BASH_SOURCE%/*}" 5 | if [[ ! -d "${_script_call_path}" ]]; then _script_call_path=$(dirname "$0"); fi 6 | cd "${_script_call_path}" 7 | 8 | . ./utils.sh 9 | 10 | cd .. 11 | cd .. 12 | 13 | # Listing available pods 14 | echo "" 15 | echo "Pods list:" 16 | 17 | # Blue color 18 | printf '\033[0;34m' 19 | 20 | # Pods list 21 | grep -o "pod \'[a-zA-Z0-9\.\/-]*\'" Podfile | sed -e "s/^pod \'//" -e "s/\'$//" | sort -fu 22 | 23 | # No color 24 | printf '\033[0m' 25 | echo "" 26 | 27 | # Asking which one to update 28 | read -p "Which pod to update? Press enter to update all: " pod_name 29 | 30 | # Check if pod has git repository attached 31 | if [ -n "${pod_name}" ] && grep -cq "\- ${pod_name}.*(from " Podfile.lock; then 32 | # Pod has git repository attached. No need to fetch pods repo. 33 | pod update ${pod_name} --no-repo-update 34 | else 35 | # Trigger specific or all pod update 36 | pod update ${pod_name} 37 | fi 38 | 39 | echo "Fixing warnings" 40 | fixWarnings 41 | -------------------------------------------------------------------------------- /ObjectMapperAdditions/Classes/Realm/RealmPropertyTransform.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RealmPropertyTransform.swift 3 | // Pods 4 | // 5 | // Created by Anton Plebanovich on 7.11.21. 6 | // Copyright © 2021 Anton Plebanovich. All rights reserved. 7 | // 8 | 9 | import RealmSwift 10 | import ObjectMapper 11 | 12 | /// Transforms Swift numeric to `RealmProperty`. 13 | /// E.g. `Int?` to `RealmProperty` or `Double` to `RealmProperty`. 14 | public class RealmPropertyTransform: TransformType { 15 | public typealias Object = RealmProperty 16 | public typealias JSON = Any 17 | 18 | public init() {} 19 | 20 | public func transformFromJSON(_ value: Any?) -> RealmProperty? { 21 | let realmProperty = RealmProperty() 22 | guard let value = value as? T else { return realmProperty } 23 | 24 | realmProperty.value = value 25 | 26 | return realmProperty 27 | } 28 | 29 | public func transformToJSON(_ value: RealmProperty?) -> Any? { 30 | return value?.value 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /ObjectMapperAdditions/Classes/Realm/RealmTransform.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RealmTransform.swift 3 | // ObjectMapperAdditions 4 | // 5 | // Created by Anton Plebanovich on 7/25/17. 6 | // Copyright © 2017 Anton Plebanovich. All rights reserved. 7 | // 8 | 9 | import RealmSwift 10 | import ObjectMapper 11 | 12 | 13 | /// Transforms Swift Arrays to Realm Arrays. E.g. [String] to List. 14 | public class RealmTransform: TransformType { 15 | public typealias Object = List 16 | public typealias JSON = [Any] 17 | 18 | public init() {} 19 | 20 | public func transformFromJSON(_ value: Any?) -> List? { 21 | guard let array = value as? [Any] else { return nil } 22 | 23 | let list = List() 24 | let realmValues: [T] = array.compactMap { $0 as? T } 25 | list.append(objectsIn: realmValues) 26 | 27 | return list 28 | } 29 | 30 | public func transformToJSON(_ value: List?) -> [Any]? { 31 | guard let value = value else { return nil } 32 | 33 | return Array(value) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2017 Anton Plebanovich 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in 11 | all copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 | THE SOFTWARE. 20 | -------------------------------------------------------------------------------- /checkBuild.command: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | base_dir=$(dirname "$0") 6 | cd "$base_dir" 7 | 8 | echo "" 9 | echo "" 10 | 11 | echo -e "\nBuilding Swift Package..." 12 | swift build 13 | 14 | echo -e "Performing tests..." 15 | simulator_id="$(xcrun simctl list devices available iPhone | grep " SE " | tail -1 | sed -e "s/.*(\([0-9A-Z-]*\)).*/\1/")" 16 | if [ -n "${simulator_id}" ]; then 17 | echo "Using iPhone SE simulator with ID: '${simulator_id}'" 18 | 19 | else 20 | simulator_id="$(xcrun simctl list devices available iPhone | grep "^ " | tail -1 | sed -e "s/.*(\([0-9A-Z-]*\)).*/\1/")" 21 | if [ -n "${simulator_id}" ]; then 22 | echo "Using iPhone simulator with ID: '${simulator_id}'" 23 | 24 | else 25 | echo >&2 "error: Please install iPhone simulator." 26 | echo " " 27 | exit 1 28 | fi 29 | fi 30 | 31 | set -o pipefail && xcodebuild -workspace "Example/ObjectMapperAdditions.xcworkspace" -sdk iphonesimulator -scheme "ObjectMapperAdditions-Example" -destination "platform=iOS Simulator,id=${simulator_id}" test | xcpretty 32 | 33 | echo "" 34 | echo "SUCCESS!" 35 | echo "" 36 | echo "" 37 | -------------------------------------------------------------------------------- /Example/Scripts/Cocoapods/utils.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Colors Constants 4 | red_color='\033[0;31m' 5 | green_color='\033[0;32m' 6 | yellow_color='\033[0;33m' 7 | blue_color='\033[0;34m' 8 | no_color='\033[0m' 9 | 10 | # Font Constants 11 | bold_text=$(tput bold) 12 | normal_text=$(tput sgr0) 13 | 14 | # First parameter - message 15 | # Second parameter - retype message. Optional. 16 | askForContinue() { 17 | local message="${1}" 18 | local retype_message="${2}" 19 | while true; do 20 | printf "${message}" 21 | read yn 22 | case $yn in 23 | [Yy]* ) continue=true; break;; 24 | [Nn]* ) continue=fase; break;; 25 | * ) [ -n "${retype_message}" ] && echo "${retype_message}";; 26 | esac 27 | done 28 | } 29 | 30 | fixWarnings() { 31 | # Project last update check fix 32 | sed -i '' -e $'s/LastUpgradeCheck = [0-9]*;/LastUpgradeCheck = 9999;\\\n\t\t\t\tLastSwiftMigration = 9999;/g' 'Pods/Pods.xcodeproj/project.pbxproj' 33 | 34 | # Schemes last update verions fix 35 | find Pods/Pods.xcodeproj/xcuserdata -type f -name '*.xcscheme' -exec sed -i '' -e 's/LastUpgradeVersion = \"[0-9]*\"/LastUpgradeVersion = \"9999\"/g' {} + 36 | } 37 | -------------------------------------------------------------------------------- /Example/Tests/RealmPropertyTransform_Spec.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RealmPropertyTransform_Spec.swift 3 | // ObjectMapperAdditions 4 | // 5 | // Created by Anton Plebanovich on 7.11.21. 6 | // Copyright © 2021 Anton Plebanovich. All rights reserved. 7 | // 8 | 9 | import Quick 10 | import Nimble 11 | import ObjectMapper 12 | import ObjectMapperAdditions 13 | import APExtensions 14 | import RealmSwift 15 | @testable import ObjectMapperAdditions_Example 16 | 17 | class RealmPropertyTransform_Spec: QuickSpec { 18 | override class func spec() { 19 | describe("RealmPropertyTransform") { 20 | it("should map from JSON") { 21 | let t = RealmPropertyTransform() 22 | expect(t.transformFromJSON(1.0)?.value) == 1.0 23 | expect(t.transformFromJSON("1.0")?.value).to(beNil()) 24 | } 25 | 26 | it("should map to JSON") { 27 | let t = RealmPropertyTransform() 28 | let property = RealmProperty() 29 | property.value = 1.0 30 | expect(t.transformToJSON(property) as? Double) == 1.0 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Example/Tests/ISO8601JustDateTransform_Spec.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ISO8601JustDateTransform_Spec.swift 3 | // ObjectMapperAdditions 4 | // 5 | // Created by Anton Plebanovich on 9/26/17. 6 | // Copyright © 2017 CocoaPods. All rights reserved. 7 | // 8 | 9 | import APExtensions 10 | import Nimble 11 | import ObjectMapper 12 | import ObjectMapperAdditions 13 | import Quick 14 | 15 | 16 | class ISO8601JustDateTransformSpec: QuickSpec { 17 | override class func spec() { 18 | describe("ISO8601JustDateTransform") { 19 | var transform: ISO8601JustDateTransform! 20 | let date = Date(timeIntervalSince1970: 0) 21 | let dateString = "1970-01-01" 22 | 23 | beforeEach { 24 | transform = ISO8601JustDateTransform.shared 25 | } 26 | 27 | it("should convert date string to Date") { 28 | expect(transform.transformFromJSON("1970-01-01")).to(equal(date)) 29 | } 30 | 31 | it("should convert Date to date string") { 32 | expect(transform.transformToJSON(date)).to(equal(dateString)) 33 | } 34 | } 35 | } 36 | } 37 | 38 | -------------------------------------------------------------------------------- /ObjectMapperAdditions/Classes/Core/Transforms/ISO8601JustDateTransform.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ISO8601Transform.swift 3 | // ObjectMapperAdditions 4 | // 5 | // Created by Anton Plebanovich on 9/26/17. 6 | // Copyright © 2017 Anton Plebanovich. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import ObjectMapper 11 | 12 | 13 | fileprivate extension DateFormatter { 14 | convenience init(withFormat format: String, timeZone: TimeZone) { 15 | self.init() 16 | 17 | self.timeZone = timeZone 18 | self.dateFormat = format 19 | } 20 | } 21 | 22 | 23 | /// Transforms ISO8601 date string to/from Date. ObjectMapper's `ISO8601DateTransform` actually is date and time transform. 24 | open class ISO8601JustDateTransform: DateFormatterTransform { 25 | static let reusableISODateFormatter = DateFormatter(withFormat: "yyyy-MM-dd", timeZone: TimeZone(secondsFromGMT: 0)!) 26 | 27 | private init() { 28 | super.init(dateFormatter: ISO8601JustDateTransform.reusableISODateFormatter) 29 | } 30 | } 31 | 32 | // ******************************* MARK: - Singleton 33 | 34 | public extension ISO8601JustDateTransform { 35 | static let shared = ISO8601JustDateTransform() 36 | } 37 | -------------------------------------------------------------------------------- /Example/Tests/RealmPropertyTypeCastTransform_Spec.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RealmPropertyTypeCastTransform_Spec.swift 3 | // ObjectMapperAdditions 4 | // 5 | // Created by Anton Plebanovich on 7.11.21. 6 | // Copyright © 2021 Anton Plebanovich. All rights reserved. 7 | // 8 | 9 | import Quick 10 | import Nimble 11 | import ObjectMapper 12 | import ObjectMapperAdditions 13 | import APExtensions 14 | import RealmSwift 15 | @testable import ObjectMapperAdditions_Example 16 | 17 | class RealmPropertyTypeCastTransform_Spec: QuickSpec { 18 | override class func spec() { 19 | describe("RealmPropertyTypeCastTransform") { 20 | it("should map from JSON") { 21 | let t = RealmPropertyTypeCastTransform() 22 | expect(t.transformFromJSON(1.0)?.value) == 1.0 23 | expect(t.transformFromJSON("1.0")?.value) == 1.0 24 | } 25 | 26 | it("should map to JSON") { 27 | let t = RealmPropertyTypeCastTransform() 28 | let property = RealmProperty() 29 | property.value = 1.0 30 | expect(t.transformToJSON(property) as? Double) == 1.0 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /ObjectMapperAdditions/Classes/Core/Transforms/TimestampTransform.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TimestampTransform.swift 3 | // ObjectMapperAdditions 4 | // 5 | // Created by Anton Plebanovich on 9/26/17. 6 | // Copyright © 2017 Anton Plebanovich. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import ObjectMapper 11 | 12 | 13 | /// Transforms UNIX timestamp (aka POSIX timestamp or epoch) to/from Date. 14 | public class TimestampTransform: TransformType { 15 | public typealias Object = Date 16 | public typealias JSON = Int 17 | 18 | private init() {} 19 | 20 | public func transformFromJSON(_ value: Any?) -> Object? { 21 | guard let timestamp = DoubleTransform.shared.transformFromJSON(value) else { return nil } 22 | 23 | let date = Date(timeIntervalSince1970: timestamp) 24 | 25 | return date 26 | } 27 | 28 | public func transformToJSON(_ value: Object?) -> JSON? { 29 | guard let timestamp = value?.timeIntervalSince1970 else { return nil } 30 | 31 | return Int(timestamp) 32 | } 33 | } 34 | 35 | // ******************************* MARK: - Singleton 36 | 37 | public extension TimestampTransform { 38 | static let shared = TimestampTransform() 39 | } 40 | -------------------------------------------------------------------------------- /Example/ObjectMapperAdditions/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /ObjectMapperAdditions/Classes/Realm/RealmPropertyTypeCastTransform.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RealmPropertyTypeCastTransform.swift 3 | // Pods 4 | // 5 | // Created by Anton Plebanovich on 7.11.21. 6 | // Copyright © 2021 Anton Plebanovich. All rights reserved. 7 | // 8 | 9 | import RealmSwift 10 | import ObjectMapper 11 | #if !COCOAPODS 12 | import ObjectMapperAdditions 13 | #endif 14 | 15 | /// Transforms Swift numeric to `RealmProperty`. 16 | /// E.g. Int? to RealmOptional. 17 | /// Additionally, it will type cast value if type mismatches. E.g. "123" String to 123 Int. 18 | public class RealmPropertyTypeCastTransform: TransformType { 19 | public typealias Object = RealmProperty 20 | public typealias JSON = Any 21 | 22 | public init() {} 23 | 24 | public func transformFromJSON(_ value: Any?) -> RealmProperty? { 25 | let realmOptional = RealmProperty() 26 | let typeCastTransform = TypeCastTransform() 27 | guard let castedValue = typeCastTransform.transformFromJSON(value) else { return realmOptional } 28 | realmOptional.value = castedValue 29 | 30 | return realmOptional 31 | } 32 | 33 | public func transformToJSON(_ value: RealmProperty?) -> Any? { 34 | return value?.value 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /ObjectMapperAdditions/Classes/Core/Extensions/Map+Additions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Map+Additions.swift 3 | // Pods 4 | // 5 | // Created by Anton Plebanovich on 8.12.23. 6 | // Copyright © 2023 Anton Plebanovich. All rights reserved. 7 | // 8 | 9 | import ObjectMapper 10 | 11 | public extension Map { 12 | 13 | func boolValue(_ key: String) -> Bool? { 14 | try? value(key, using: BoolTransform.shared) 15 | } 16 | 17 | func doubleValue(_ key: String) -> Double? { 18 | try? value(key, using: DoubleTransform.shared) 19 | } 20 | 21 | func intValue(_ key: String) -> Int? { 22 | try? value(key, using: IntTransform.shared) 23 | } 24 | 25 | func stringValue(_ key: String) -> String? { 26 | try? value(key, using: StringTransform.shared) 27 | } 28 | 29 | func value(_ type: T.Type, key: String) -> T? { 30 | try? value(key) 31 | } 32 | 33 | func value(_ type: [T].Type, key: String) -> [T]? { 34 | try? value(key) 35 | } 36 | 37 | func typeCastedValue(_ type: T.Type, key: String) -> T? { 38 | try? value(key, using: TypeCastTransform()) 39 | } 40 | 41 | func typeCastedValue(_ type: T.Type, key: String) -> T? { 42 | try? value(key, using: EnumTypeCastTransform()) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "originHash" : "163b7b864558f0e693d0e42efce6b27bcdcad8c6e1626107f1da97bb4d5d4ab6", 3 | "pins" : [ 4 | { 5 | "identity" : "objectmapper", 6 | "kind" : "remoteSourceControl", 7 | "location" : "https://github.com/tristanhimmelman/ObjectMapper", 8 | "state" : { 9 | "revision" : "6021c6035e83a306047348666f6400dc61445d3b", 10 | "version" : "4.4.3" 11 | } 12 | }, 13 | { 14 | "identity" : "realm-core", 15 | "kind" : "remoteSourceControl", 16 | "location" : "https://github.com/realm/realm-core.git", 17 | "state" : { 18 | "revision" : "cccb3ca9e26ec452a29f2f0d4050d1e38b8a3d43", 19 | "version" : "14.14.0" 20 | } 21 | }, 22 | { 23 | "identity" : "realm-swift", 24 | "kind" : "remoteSourceControl", 25 | "location" : "https://github.com/realm/realm-swift", 26 | "state" : { 27 | "revision" : "54eebd2b5e7a4a8055a7a8b55ac859a48462d722", 28 | "version" : "10.54.5" 29 | } 30 | }, 31 | { 32 | "identity" : "routablelogger", 33 | "kind" : "remoteSourceControl", 34 | "location" : "https://github.com/anton-plebanovich/RoutableLogger", 35 | "state" : { 36 | "revision" : "e544488de124ab2a72a4e2490759ab6a8a084701", 37 | "version" : "2.0.0" 38 | } 39 | } 40 | ], 41 | "version" : 3 42 | } 43 | -------------------------------------------------------------------------------- /ObjectMapperAdditions/Classes/Realm/RealmTypeCastTransform.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RealmTypeCastTransform.swift 3 | // ObjectMapperAdditions 4 | // 5 | // Created by Anton Plebanovich on 7/27/17. 6 | // Copyright © 2017 Anton Plebanovich. All rights reserved. 7 | // 8 | 9 | import RealmSwift 10 | import ObjectMapper 11 | #if !COCOAPODS 12 | import ObjectMapperAdditions 13 | #endif 14 | 15 | /// Transforms Swift Arrays to Realm Arrays. E.g. [String] to List. 16 | /// Additionally, it will type cast value if type mismatches. E.g. "123" String to 123 Int. 17 | public class RealmTypeCastTransform: TransformType { 18 | public typealias Object = List 19 | public typealias JSON = [Any] 20 | 21 | public init() {} 22 | 23 | public func transformFromJSON(_ value: Any?) -> Object? { 24 | guard let array = value as? [Any] else { return nil } 25 | 26 | let typeCastTransform = TypeCastTransform() 27 | let realmValues: [T] = array.compactMap { typeCastTransform.transformFromJSON($0) } 28 | 29 | let list = List() 30 | list.append(objectsIn: realmValues) 31 | 32 | return list 33 | } 34 | 35 | public func transformToJSON(_ value: Object?) -> JSON? { 36 | guard let value = value else { return nil } 37 | 38 | return Array(value) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Example/Tests/TimestampTransform_Spec.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TimestampTransform_Spec.swift 3 | // ObjectMapperAdditions 4 | // 5 | // Created by Anton Plebanovich on 9/26/17. 6 | // Copyright © 2017 CocoaPods. All rights reserved. 7 | // 8 | 9 | import APExtensions 10 | import Nimble 11 | import ObjectMapper 12 | import ObjectMapperAdditions 13 | import Quick 14 | 15 | 16 | class TimestampTransformSpec: QuickSpec { 17 | override class func spec() { 18 | describe("TimestampTransform") { 19 | var transform: TimestampTransform! 20 | 21 | beforeEach { 22 | transform = TimestampTransform.shared 23 | } 24 | 25 | it("should convert int timestamp to Date") { 26 | expect(transform.transformFromJSON(0)).to(equal(Date(timeIntervalSince1970: 0))) 27 | } 28 | 29 | it("should convert double timestamp to Date") { 30 | expect(transform.transformFromJSON(0.0)).to(equal(Date(timeIntervalSince1970: 0.0))) 31 | } 32 | 33 | it("should convert string timestamp to Date") { 34 | expect(transform.transformFromJSON("0.0")).to(equal(Date(timeIntervalSince1970: 0.0))) 35 | } 36 | 37 | it("should convert date to Int timestamp") { 38 | expect(transform.transformToJSON(Date(timeIntervalSince1970: 0.0))).to(equal(0)) 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /ObjectMapperAdditions/Classes/Core/Extensions/BaseMappable+Additions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BaseMappable+Additions.swift 3 | // ObjectMapperAdditions 4 | // 5 | // Created by Anton Plebanovich on 6/30/17. 6 | // Copyright © 2017 Anton Plebanovich. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import ObjectMapper 11 | 12 | public extension BaseMappable { 13 | func toJSON(shouldIncludeNilValues: Bool) -> [String: Any] { 14 | return Mapper(shouldIncludeNilValues: shouldIncludeNilValues).toJSON(self) 15 | } 16 | 17 | /// Returns the JSON Data for the object 18 | func toJSONData() -> Data? { 19 | toJSONString(prettyPrint: false)?.data(using: .utf8) 20 | } 21 | } 22 | 23 | public extension Array where Element: BaseMappable { 24 | func toJSON(shouldIncludeNilValues: Bool) -> [[String: Any]] { 25 | return Mapper(shouldIncludeNilValues: shouldIncludeNilValues).toJSONArray(self) 26 | } 27 | 28 | /// Returns the JSON Data for the object 29 | func toJSONData() -> Data? { 30 | toJSONString(prettyPrint: false)?.data(using: .utf8) 31 | } 32 | 33 | } 34 | 35 | public extension Set where Element: BaseMappable { 36 | func toJSON(shouldIncludeNilValues: Bool) -> [[String: Any]] { 37 | return Mapper(shouldIncludeNilValues: shouldIncludeNilValues).toJSONSet(self) 38 | } 39 | 40 | /// Returns the JSON Data for the object 41 | func toJSONData() -> Data? { 42 | toJSONString(prettyPrint: false)?.data(using: .utf8) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /ObjectMapperAdditions/Classes/Core/Transforms/TypeCastTransform.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TypeCastTransform.swift 3 | // ObjectMapperAdditions 4 | // 5 | // Created by Anton Plebanovich on 7/21/17. 6 | // Copyright © 2017 Anton Plebanovich. All rights reserved. 7 | // 8 | 9 | import ObjectMapper 10 | import RoutableLogger 11 | 12 | /// Transforms value of type Any to generic type. Tries to typecast if possible. 13 | public class TypeCastTransform: TransformType { 14 | public typealias Object = T 15 | public typealias JSON = T 16 | 17 | public init() {} 18 | 19 | public func transformFromJSON(_ value: Any?) -> Object? { 20 | if value == nil { 21 | return nil 22 | } else if let value = value as? T { 23 | return value 24 | } else if T.self == Int.self { 25 | return IntTransform.shared.transformFromJSON(value) as? T 26 | } else if T.self == Double.self { 27 | return DoubleTransform.shared.transformFromJSON(value) as? T 28 | } else if T.self == Bool.self { 29 | return BoolTransform.shared.transformFromJSON(value) as? T 30 | } else if T.self == String.self { 31 | return StringTransform.shared.transformFromJSON(value) as? T 32 | } else { 33 | RoutableLogger.logError("Can not cast value of type \(type(of: value!)) to type \(Object.self)", data: ["value": value]) 34 | return nil 35 | } 36 | } 37 | 38 | public func transformToJSON(_ value: Object?) -> JSON? { 39 | return value 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /ObjectMapperAdditions/Classes/Core/Transforms/BoolTransform.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BoolTransform.swift 3 | // ObjectMapperAdditions 4 | // 5 | // Created by Anton Plebanovich on 7/17/17. 6 | // Copyright © 2017 Anton Plebanovich. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import ObjectMapper 11 | import RoutableLogger 12 | 13 | /// Transforms value of type Any to Bool. Tries to typecast if possible. 14 | public class BoolTransform: TransformType { 15 | public typealias Object = Bool 16 | public typealias JSON = Bool 17 | 18 | private init() {} 19 | 20 | public func transformFromJSON(_ value: Any?) -> Object? { 21 | if value == nil { 22 | return nil 23 | } else if let bool = value as? Bool { 24 | return bool 25 | } else if let int = value as? Int { 26 | return (int != 0) 27 | } else if let double = value as? Double { 28 | return (double != 0) 29 | } else if let string = value as? String { 30 | return Bool.safeFrom(string) 31 | } else if let number = value as? NSNumber { 32 | return number.boolValue 33 | } else { 34 | RoutableLogger.logError("Can not cast value of type \(type(of: value!)) to type \(Object.self)", data: ["value": value]) 35 | return nil 36 | } 37 | } 38 | 39 | public func transformToJSON(_ value: Object?) -> JSON? { 40 | return value 41 | } 42 | } 43 | 44 | // ******************************* MARK: - Singleton 45 | 46 | public extension BoolTransform { 47 | static let shared = BoolTransform() 48 | } 49 | -------------------------------------------------------------------------------- /ObjectMapperAdditions/Classes/Core/Transforms/IntTransform.swift: -------------------------------------------------------------------------------- 1 | // 2 | // IntTransform.swift 3 | // ObjectMapperAdditions 4 | // 5 | // Created by Anton Plebanovich on 7/17/17. 6 | // Copyright © 2017 Anton Plebanovich. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import ObjectMapper 11 | import RoutableLogger 12 | 13 | /// Transforms value of type Any to Int. Tries to typecast if possible. 14 | public class IntTransform: TransformType { 15 | public typealias Object = Int 16 | public typealias JSON = Int 17 | 18 | private init() {} 19 | 20 | public func transformFromJSON(_ value: Any?) -> Object? { 21 | if value == nil { 22 | return nil 23 | } else if let int = value as? Int { 24 | return int 25 | } else if let double = value as? Double { 26 | return Int.safeFrom(double) 27 | } else if let bool = value as? Bool { 28 | return (bool ? 1 : 0) 29 | } else if let string = value as? String { 30 | return Int.safeFrom(string) 31 | } else if let number = value as? NSNumber { 32 | return number.intValue 33 | } else { 34 | RoutableLogger.logError("Can not cast value of type \(type(of: value!)) to type \(Object.self)", data: ["value": value]) 35 | return nil 36 | } 37 | } 38 | 39 | public func transformToJSON(_ value: Object?) -> JSON? { 40 | return value 41 | } 42 | } 43 | 44 | // ******************************* MARK: - Singleton 45 | 46 | public extension IntTransform { 47 | static let shared = IntTransform() 48 | } 49 | -------------------------------------------------------------------------------- /ObjectMapperAdditions/Classes/Core/Transforms/StringTransform.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StringTransform.swift 3 | // ObjectMapperAdditions 4 | // 5 | // Created by Anton Plebanovich on 7/17/17. 6 | // Copyright © 2017 Anton Plebanovich. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import ObjectMapper 11 | import RoutableLogger 12 | 13 | /// Transforms value of type Any to String. Tries to typecast if possible. 14 | public class StringTransform: TransformType { 15 | public typealias Object = String 16 | public typealias JSON = String 17 | 18 | private init() {} 19 | 20 | public func transformFromJSON(_ value: Any?) -> Object? { 21 | if value == nil { 22 | return nil 23 | } else if let string = value as? String { 24 | return string 25 | } else if let int = value as? Int { 26 | return String(int) 27 | } else if let double = value as? Double { 28 | return String(double) 29 | } else if let bool = value as? Bool { 30 | return (bool ? "true" : "false") 31 | } else if let number = value as? NSNumber { 32 | return number.stringValue 33 | } else { 34 | RoutableLogger.logError("Can not cast value of type \(type(of: value!)) to type \(Object.self)", data: ["value": value]) 35 | return nil 36 | } 37 | } 38 | 39 | public func transformToJSON(_ value: Object?) -> JSON? { 40 | return value 41 | } 42 | } 43 | 44 | // ******************************* MARK: - Singleton 45 | 46 | public extension StringTransform { 47 | static let shared = StringTransform() 48 | } 49 | -------------------------------------------------------------------------------- /ObjectMapperAdditions/Classes/Core/Transforms/DoubleTransform.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DoubleTransform.swift 3 | // ObjectMapperAdditions 4 | // 5 | // Created by Anton Plebanovich on 7/17/17. 6 | // Copyright © 2017 Anton Plebanovich. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import ObjectMapper 11 | import RoutableLogger 12 | 13 | /// Transforms value of type Any to Double. Tries to typecast if possible. 14 | public class DoubleTransform: TransformType { 15 | public typealias Object = Double 16 | public typealias JSON = Double 17 | 18 | private init() {} 19 | 20 | public func transformFromJSON(_ value: Any?) -> Object? { 21 | if value == nil { 22 | return nil 23 | } else if let double = value as? Double { 24 | return double 25 | } else if let int = value as? Int { 26 | return Double(int) 27 | } else if let bool = value as? Bool { 28 | return (bool ? 1.0 : 0.0) 29 | } else if let string = value as? String { 30 | return Double.safeFrom(string) 31 | } else if let number = value as? NSNumber { 32 | return number.doubleValue 33 | } else { 34 | RoutableLogger.logError("Can not cast value of type \(type(of: value!)) to type \(Object.self)", data: ["value": value]) 35 | return nil 36 | } 37 | } 38 | 39 | public func transformToJSON(_ value: Object?) -> JSON? { 40 | return value 41 | } 42 | } 43 | 44 | // ******************************* MARK: - Singleton 45 | 46 | public extension DoubleTransform { 47 | static let shared = DoubleTransform() 48 | } 49 | -------------------------------------------------------------------------------- /Example/Tests/MongoDateTransform_Spec.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MongoDateTransform_Spec.swift 3 | // ObjectMapperAdditions 4 | // 5 | // Created by Anton Plebanovich on 8.06.25. 6 | // Copyright © 2025 Anton Plebanovich. All rights reserved. 7 | // 8 | 9 | import Quick 10 | import Nimble 11 | import ObjectMapper 12 | import ObjectMapperAdditions 13 | @testable import ObjectMapperAdditions_Example 14 | 15 | class MongoDateTransform_Spec: QuickSpec { 16 | override class func spec() { 17 | describe("MongoDateTransform") { 18 | it("should map from JSON") { 19 | let t = MongoDateTransform.shared 20 | expect(t.transformFromJSON(["$date": "2022-01-01T00:00:00.000+00:00"])?.timeIntervalSince1970) == 1640995200 21 | expect(t.transformFromJSON(["$date": "2022-01-01T00:00:00.000Z"])?.timeIntervalSince1970) == 1640995200 22 | expect(t.transformFromJSON(["$date": "2022-01-01T00:00:00+00:00"])?.timeIntervalSince1970) == 1640995200 23 | expect(t.transformFromJSON(["$date": "2022-01-01T00:00:00Z"])?.timeIntervalSince1970) == 1640995200 24 | expect(t.transformFromJSON(["$date": ["$numberLong":1640995200000]])?.timeIntervalSince1970) == 1640995200 25 | } 26 | 27 | it("should map to JSON") { 28 | let t = MongoDateTransform.shared 29 | expect(t.transformToJSON(Date(timeIntervalSince1970: 1640995200))?.dictionary(forKey: "$date")?.int(forKey: "$numberLong")) == 1640995200000 30 | expect(t.transformToJSON(Date(timeIntervalSince1970: 1640995200.001))?.dictionary(forKey: "$date")?.int(forKey: "$numberLong")) == 1640995200001 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Example/Podfile: -------------------------------------------------------------------------------- 1 | # Deployment Target 2 | platform :ios, '13.0' 3 | install! 'cocoapods', :warn_for_unused_master_specs_repo => false 4 | 5 | # Add pods as frameworks so we could add obj-c and swift 3.0 pods 6 | use_frameworks! 7 | 8 | # https://github.com/CocoaPods/CocoaPods/issues/12574 9 | source 'https://github.com/CocoaPods/Specs.git' 10 | 11 | target 'ObjectMapperAdditions_Example' do 12 | pod 'APExtensions', :git => 'https://github.com/APUtils/APExtensions' 13 | pod 'ObjectMapperAdditions/Realm', :path => '../' 14 | 15 | target 'ObjectMapperAdditions_Tests' do 16 | inherit! :search_paths 17 | 18 | pod 'Quick' 19 | pod 'Nimble' 20 | end 21 | end 22 | 23 | post_install do |installer| 24 | # Add podInstall.command and podUpdate.command shell scripts to Pods project 25 | pods_project = installer.pods_project 26 | pods_project.new_file "../Scripts/Cocoapods/podInstall.command" 27 | pods_project.new_file "../Scripts/Cocoapods/podUpdate.command" 28 | 29 | # Silence Pods project warning 30 | installer.pods_project.build_configurations.each do |config| 31 | config.build_settings['CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED'] = 'YES' 32 | end 33 | 34 | installer.pods_project.targets.each do |target| 35 | target.build_configurations.each do |config| 36 | # Silence deployment target warnings 37 | config.build_settings.delete 'IPHONEOS_DEPLOYMENT_TARGET' 38 | end 39 | 40 | # Warning fix https://github.com/realm/realm-swift/issues/7957#issuecomment-1248556797 41 | if target.name == 'Realm' 42 | create_symlink_phase = target.shell_script_build_phases.find { |x| x.name == 'Create Symlinks to Header Folders' } 43 | create_symlink_phase.always_out_of_date = "1" 44 | end 45 | end 46 | end 47 | -------------------------------------------------------------------------------- /Example/Scripts/Cocoapods/podInstall.command: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | performPodInstallAndCaptureOutput() { 4 | # Capture variable but preserve console output - https://stackoverflow.com/a/12451419/4124265 5 | # Preserve colors - https://stackoverflow.com/a/3515296/4124265 6 | exec 5>&1 7 | set -o pipefail 8 | pod_install_output=`script -q /dev/null pod install | tee /dev/fd/5` 9 | } 10 | 11 | # Assume scripts are placed in /Scripts/Cocoapods dir 12 | _script_call_path="${BASH_SOURCE%/*}" 13 | if [[ ! -d "${_script_call_path}" ]]; then _script_call_path=$(dirname "$0"); fi 14 | cd "${_script_call_path}" 15 | 16 | . utils.sh 17 | 18 | cd .. 19 | cd .. 20 | 21 | set +e 22 | performPodInstallAndCaptureOutput 23 | exit_code=$? 24 | set -e 25 | 26 | # Check if repo needs update 27 | # * `31` Spec not found (i.e out-of-date source repos, mistyped Pod name etc...) 28 | echo "Exit code: ${exit_code}" 29 | if [ ${exit_code} -eq 31 ] || [ ${exit_code} -eq 1 ]; then 30 | echo "Fixing outdated repo" 31 | pod repo update 32 | pod_install_output=`script -q /dev/null pod install | tee /dev/fd/5` 33 | 34 | elif [ ${exit_code} -eq 0 ]; then 35 | # Break 36 | : 37 | 38 | else 39 | exit ${exit_code} 40 | fi 41 | 42 | # Check if there is a license warning caused by a broken pods cache. 43 | # [!] Unable to read the license file `LICENSE` for the spec `FirebaseCoreDiagnostics (1.3.0)` 44 | # Filter out `LogsManager` missing LICENSE because of the bug - https://github.com/leavez/cocoapods-binary/pull/122 45 | filtered_pod_install_output=$(echo "${pod_install_output}" | grep -v 'LICENSE.*LogsManager') 46 | if [[ "${filtered_pod_install_output}" == *LICENSE* ]]; then 47 | echo "Fixing broken Pods cache" 48 | rm -rf "Pods" 49 | pod cache clean --all 50 | pod install 51 | fi 52 | 53 | echo "Fixing warnings" 54 | fixWarnings 55 | -------------------------------------------------------------------------------- /ObjectMapperAdditions/Classes/Core/Extensions/Int+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Int+Extension.swift 3 | // ObjectMapperAdditions 4 | // 5 | // Created by Anton Plebanovich on 28.07.22. 6 | // Copyright © 2022 Anton Plebanovich. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import RoutableLogger 11 | 12 | extension Int { 13 | 14 | static func safeFrom(_ double: Double, file: String = #file, function: String = #function, line: UInt = #line) -> Int? { 15 | let roundedDouble = double.rounded() 16 | if let int = Int(exactly: roundedDouble) { 17 | if roundedDouble != double { 18 | RoutableLogger.logWarning("[\(file._fileName):\(line)] Double casted to Int with rounding: \(double) -> \(int)") 19 | } 20 | 21 | return int 22 | 23 | } else { 24 | RoutableLogger.logError("Unable to cast Double to Int", data: ["double": double], file: file, function: function, line: line) 25 | return nil 26 | } 27 | } 28 | 29 | static func safeFrom(_ string: String, file: String = #file, function: String = #function, line: UInt = #line) -> Int? { 30 | if string.isNil { 31 | RoutableLogger.logDebug("[\(file._fileName):\(line)] Received '\(string)' string instead of an Int. Considering it as `nil`.") 32 | return nil 33 | } 34 | 35 | if let int = Int(string) { 36 | return int 37 | 38 | } else if let double = Double.safeFrom(string, file: file, function: function, line: line) { 39 | return safeFrom(double, file: file, function: function, line: line) 40 | 41 | } else { 42 | RoutableLogger.logError("Unable to cast String to Int", data: ["string": string], file: file, function: function, line: line) 43 | return nil 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Example/ObjectMapperAdditions/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /ObjectMapperAdditions/Classes/Core/Transforms/MongoDateTransform.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MongoDateTransform.swift 3 | // Pods 4 | // 5 | // Created by Anton Plebanovich on 10.10.21. 6 | // Copyright © 2021 Anton Plebanovich. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import ObjectMapper 11 | import RoutableLogger 12 | 13 | public final class MongoDateTransform: TransformType { 14 | public typealias Object = Date 15 | public typealias JSON = [String: Any] 16 | 17 | public static let shared = MongoDateTransform() 18 | fileprivate init() {} 19 | 20 | public func transformFromJSON(_ value: Any?) -> Object? { 21 | guard let dateDict = value as? JSON, 22 | let date = dateDict._getMongoDate() else { return nil } 23 | 24 | return date 25 | } 26 | 27 | public func transformToJSON(_ value: Object?) -> JSON? { 28 | if let date = value { 29 | let timeIntervalMs = date.timeIntervalSince1970 * 1000 30 | let timestampMs = Int(exactly: timeIntervalMs.rounded()) 31 | return ["$date": ["$numberLong": timestampMs]] 32 | } else { 33 | return nil 34 | } 35 | } 36 | } 37 | 38 | fileprivate extension Dictionary where Key == String { 39 | func _getMongoDate() -> Date? { 40 | let dateValue = self["$date"] 41 | if let dateString = dateValue as? String { 42 | return ISO8601DateFormatter.default.date(from: dateString) 43 | ?? ISO8601DateFormatter.withMillisAndTimeZone.date(from: dateString) 44 | 45 | } else if let dictionary = dateValue as? [String: Any] { 46 | if let timestampMilliseconds = dictionary["$numberLong"] as? Int { 47 | let timestamp = TimeInterval(timestampMilliseconds) / 1000 48 | return Date(timeIntervalSince1970: timestamp) 49 | } else if let timestampMilliseconds = dictionary["$numberLong"] as? Int64 { 50 | let timestamp = TimeInterval(timestampMilliseconds) / 1000 51 | return Date(timeIntervalSince1970: timestamp) 52 | } 53 | } 54 | 55 | RoutableLogger.logError("Unable to map Mongo date", data: ["self": self]) 56 | return nil 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /ObjectMapperAdditions/Classes/Realm/Realm+ObjectMapper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Realm+ObjectMapper.swift 3 | // Pods 4 | // 5 | // Created by Anton Plebanovich on 23.02.23. 6 | // Copyright © 2023 Anton Plebanovich. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import ObjectMapper 11 | import RealmSwift 12 | import RoutableLogger 13 | 14 | public extension Object { 15 | 16 | /// `.toJSON()` requires Realm write transaction or it'll crash 17 | /// https://github.com/APUtils/ObjectMapperAdditions#realm-features 18 | func performMapping(file: String = #file, function: String = #function, line: UInt = #line, _ action: () -> Void) { 19 | guard !isInvalidated else { 20 | RoutableLogger.logError("Unable to perform mapping on the invalidated object", file: file, function: function, line: line) 21 | return 22 | } 23 | 24 | guard !isFrozen else { 25 | RoutableLogger.logError("Unable to perform mapping on the frozen object", file: file, function: function, line: line) 26 | return 27 | } 28 | 29 | let isWriteRequired = realm != nil && realm?.isInWriteTransaction == false 30 | if isWriteRequired { realm?.beginWrite() } 31 | action() 32 | if isWriteRequired { realm?.cancelWrite() } 33 | } 34 | } 35 | 36 | public extension EmbeddedObject { 37 | 38 | /// `.toJSON()` requires Realm write transaction or it'll crash 39 | /// https://github.com/APUtils/ObjectMapperAdditions#realm-features 40 | func performMapping(file: String = #file, function: String = #function, line: UInt = #line, _ action: () -> Void) { 41 | guard !isInvalidated else { 42 | RoutableLogger.logError("Unable to perform mapping on the invalidated object", file: file, function: function, line: line) 43 | return 44 | } 45 | 46 | guard !isFrozen else { 47 | RoutableLogger.logError("Unable to perform mapping on the frozen object", file: file, function: function, line: line) 48 | return 49 | } 50 | 51 | let isWriteRequired = realm != nil && realm?.isInWriteTransaction == false 52 | if isWriteRequired { realm?.beginWrite() } 53 | action() 54 | if isWriteRequired { realm?.cancelWrite() } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /ObjectMapperAdditions/Classes/Core/Extensions/ObjectMapper+Additions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ObjectMapper+Additions.swift 3 | // ObjectMapperAdditions 4 | // 5 | // Created by Anton Plebanovich on 6/13/17. 6 | // Copyright © 2017 Anton Plebanovich. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import ObjectMapper 11 | 12 | public extension Map { 13 | /// It asserts that value is presents in JSON. Optional values must be included as . 14 | func assureValuePresent(forKey key: String) -> Bool { 15 | if JSON[key] == nil { 16 | assertionFailure("Mandatory field for key `\(key)` is missing from JSON: \(JSON)") 17 | 18 | return false 19 | } 20 | 21 | return true 22 | } 23 | } 24 | 25 | // ******************************* MARK: - To JSON String 26 | 27 | public extension BaseMappable { 28 | 29 | var jsonDescription: String { 30 | toJSONString(options: .sortedKeysWithoutEscapingSlashesIfPossible) ?? "error" 31 | } 32 | 33 | /// Returns the JSON String for the object 34 | func toJSONString(options: JSONSerialization.WritingOptions) -> String? { 35 | let dictionary = Mapper().toJSON(self) 36 | return Mapper.toJSONData(dictionary, options: options)?.utf8String 37 | } 38 | } 39 | 40 | public extension Array where Element: BaseMappable { 41 | 42 | /// Returns the JSON String for the object 43 | var jsonDescription: String { 44 | toJSONString(options: .sortedKeysWithoutEscapingSlashesIfPossible) ?? "error" 45 | } 46 | 47 | /// Returns the JSON String for the object 48 | func toJSONString(options: JSONSerialization.WritingOptions) -> String? { 49 | let dictionary = Mapper().toJSONArray(self) 50 | return Mapper.toJSONData(dictionary, options: options)?.utf8String 51 | } 52 | } 53 | 54 | public extension JSONSerialization.WritingOptions { 55 | 56 | /// `[.sortedKeys]` on `iOS <13.0` and `[.sortedKeys, .withoutEscapingSlashes]` on `iOS >=13.0` 57 | static let sortedKeysWithoutEscapingSlashesIfPossible: JSONSerialization.WritingOptions = { 58 | if #available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *) { 59 | return [.sortedKeys, .withoutEscapingSlashes] 60 | } else { 61 | return [.sortedKeys] 62 | } 63 | }() 64 | } 65 | -------------------------------------------------------------------------------- /Example/ObjectMapperAdditions/MyModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MyModel.swift 3 | // ObjectMapperAdditions 4 | // 5 | // Created by Anton Plebanovich on 7/27/17. 6 | // Copyright © Anton Plebanovich. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import ObjectMapper 11 | import ObjectMapperAdditions 12 | 13 | struct MyModel: Mappable { 14 | var string: String? 15 | var stringsArray: [String]? 16 | var double: Double? 17 | var myOtherModel: MyOtherModel? 18 | var myOtherModelsArray: [MyOtherModel]? 19 | var intEnum: ExampleIntEnum? 20 | var stringEnum: ExampleStringEnum? 21 | 22 | init?(map: Map) {} 23 | 24 | mutating func mapping(map: Map) { 25 | // You could specify proper type transform directly 26 | string <- (map["string"], StringTransform.shared) 27 | 28 | // Or you could just use TypeCastTransform 29 | string <- (map["string"], TypeCastTransform()) 30 | 31 | // No doubt it also works with Double 32 | double <- (map["double"], TypeCastTransform()) 33 | 34 | // Works with arrays too but for TypeCastTransform you must specify type 35 | stringsArray <- (map["stringsArray"], TypeCastTransform()) 36 | 37 | // Or just use StringTransform directly 38 | stringsArray <- (map["stringsArray"], StringTransform.shared) 39 | 40 | // No need to transform your types. They should specify transforms by themselfs. 41 | myOtherModel <- map["myOtherModel"] 42 | myOtherModelsArray <- map["myOtherModelsArray"] 43 | 44 | intEnum <- (map["intEnum"], EnumTypeCastTransform()) 45 | stringEnum <- (map["stringEnum"], EnumTypeCastTransform()) 46 | } 47 | } 48 | 49 | //----------------------------------------------------------------------------- 50 | // MARK: - Equatable 51 | //----------------------------------------------------------------------------- 52 | 53 | extension MyModel: Equatable { 54 | static func ==(lhs: MyModel, rhs: MyModel) -> Bool { 55 | return lhs.string == rhs.string 56 | && lhs.stringsArray == rhs.stringsArray 57 | && lhs.double == rhs.double 58 | && lhs.myOtherModel == rhs.myOtherModel 59 | && lhs.myOtherModelsArray == rhs.myOtherModelsArray 60 | && lhs.intEnum == rhs.intEnum 61 | && lhs.stringEnum == rhs.stringEnum 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Example/Scripts/Cocoapods/podSetup.command: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | ### Script to setup Cocoapods for a project target ### 4 | 5 | # Any subsequent(*) commands which fail will cause the shell script to exit immediately 6 | set -e 7 | 8 | _post_instal_phase="\npost_install do |installer|\n # Add podInstall.command and podUpdate.command shell scripts to Pods project\n pods_project = installer.pods_project\n pods_project.new_file \"../Scripts/Cocoapods/podInstall.command\"\n pods_project.new_file \"../Scripts/Cocoapods/podUpdate.command\"\n\n # Silence Pods project warning\n installer.pods_project.build_configurations.each do |config|\n config.build_settings['CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED'] = 'YES'\n end\nend\n" 9 | addPostIstallPhase() { 10 | printf "${_post_instal_phase}" >> "Podfile" 11 | } 12 | 13 | # Assume scripts are placed in /Scripts/Cocoapods dir 14 | _script_call_path="${BASH_SOURCE%/*}" 15 | if [[ ! -d "${_script_call_path}" ]]; then _script_call_path=$(dirname "$0"); fi 16 | cd "${_script_call_path}" 17 | 18 | . ./utils.sh 19 | 20 | cd .. 21 | cd .. 22 | 23 | # Podfile Update 24 | printf >&2 "\n${blue_color}Updating Podfile...${no_color}\n" 25 | touch "Podfile" 26 | if ! $(grep -q -F 'pods_project.new_file "../Scripts/Cocoapods/podInstall.command"' "Podfile"); then 27 | # Need to update Podfile 28 | if ! $(grep -q -F 'post_install' "Podfile"); then 29 | # Need to create post_install phase 30 | addPostIstallPhase 31 | 32 | else 33 | askForContinue "${yellow_color}Your Podfile already have \'post_install\' phase. Do you want to override it? (Y/n) ${no_color}" 34 | if [ "${continue}" = "true" ]; then 35 | # Remove post_install phase first 36 | sed -i '' '/^post_install/,/^end/d' 'Podfile' 37 | addPostIstallPhase 38 | 39 | else 40 | # Need to update post_install phase 41 | printf >&2 "${yellow_color}You have to manually add those lines:${no_color}\n" 42 | printf "${_post_instal_phase}\n" 43 | fi 44 | fi 45 | fi 46 | 47 | # .gitignore Update 48 | printf >&2 "\n${blue_color}Updating .gitignore...${no_color}\n" 49 | if ! grep -q -F "Pods/" ".gitignore"; then 50 | printf "\n/Pods/\n" >> ".gitignore" 51 | elif ! grep -q -F "/Pods/" ".gitignore"; then 52 | sed -i '' 's/Pods\//\/Pods\//g' ".gitignore" 53 | fi 54 | 55 | printf >&2 "\n${blue_color}Executing 'pod install'...${no_color}\n" 56 | bash "Scripts/Cocoapods/podInstall.command" 57 | 58 | # Success 59 | printf >&2 "\n${bold_text}PROJECT SETUP SUCCESS${normal_text}\n\n" 60 | -------------------------------------------------------------------------------- /ObjectMapperAdditions/Classes/Core/Extensions/String+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // String+Extension.swift 3 | // Pods 4 | // 5 | // Created by Anton Plebanovich on 7.04.22. 6 | // Copyright © 2022 Anton Plebanovich. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | // ******************************* MARK: - Checks 12 | 13 | private let kWhitespaceCharactersSet = CharacterSet(charactersIn: " \n\r") 14 | 15 | extension String { 16 | 17 | var isNil: Bool { 18 | isEmpty || self == "-" 19 | } 20 | 21 | var firstNonWhitespaceCharacter: Character? { 22 | guard let index = firstIndex(where: { !kWhitespaceCharactersSet._containsUnicodeScalars(of: $0) }) else { return nil } 23 | return self[index] 24 | } 25 | 26 | var secondNonWhitespaceCharacter: Character? { 27 | guard let firstIndex = firstIndex(where: { !kWhitespaceCharactersSet._containsUnicodeScalars(of: $0) }) else { return nil } 28 | 29 | let secondIndex = index(after: firstIndex) 30 | guard secondIndex < endIndex else { return nil } 31 | 32 | return self[secondIndex] 33 | } 34 | 35 | var lastNonWhitespaceCharacter: Character? { 36 | guard let index = lastIndex(where: { !kWhitespaceCharactersSet._containsUnicodeScalars(of: $0) }) else { return nil } 37 | return self[index] 38 | } 39 | 40 | var beforeLastNonWhitespaceCharacter: Character? { 41 | guard let lastIndex = lastIndex(where: { !kWhitespaceCharactersSet._containsUnicodeScalars(of: $0) }) else { return nil } 42 | 43 | let beforeLastIndex = index(before: lastIndex) 44 | guard startIndex <= beforeLastIndex else { return nil } 45 | 46 | return self[beforeLastIndex] 47 | } 48 | } 49 | 50 | 51 | // ******************************* MARK: - Other 52 | 53 | extension String { 54 | 55 | /// Returns fileName without extension 56 | var _fileName: String { 57 | guard let lastPathComponent = components(separatedBy: "/").last else { return "" } 58 | 59 | var components = lastPathComponent.components(separatedBy: ".") 60 | if components.count == 1 { 61 | return lastPathComponent 62 | } else { 63 | components.removeLast() 64 | return components.joined(separator: ".") 65 | } 66 | } 67 | } 68 | 69 | extension CharacterSet { 70 | func _containsUnicodeScalars(of character: Character) -> Bool { 71 | return character.unicodeScalars.allSatisfy(contains(_:)) 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.10 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "ObjectMapperAdditions", 7 | platforms: [ 8 | .iOS(.v13), 9 | .macOS(.v12), 10 | .tvOS(.v12), 11 | .watchOS(.v5), 12 | ], 13 | products: [ 14 | .library( 15 | name: "ObjectMapperAdditions", 16 | targets: ["ObjectMapperAdditions"] 17 | ), 18 | .library( 19 | name: "ObjectMapperAdditionsRealm", 20 | targets: ["ObjectMapperAdditionsRealm"] 21 | ), 22 | ], 23 | dependencies: [ 24 | .package(url: "https://github.com/realm/realm-swift", .upToNextMajor(from: "10.50.0")), 25 | .package(url: "https://github.com/tristanhimmelman/ObjectMapper", .upToNextMajor(from: "4.0.0")), 26 | .package(url: "https://github.com/anton-plebanovich/RoutableLogger", .upToNextMajor(from: "2.0.0")), 27 | ], 28 | targets: [ 29 | .target( 30 | name: "ObjectMapperAdditions", 31 | dependencies: [ 32 | .product(name: "ObjectMapper", package: "ObjectMapper"), 33 | .product(name: "RoutableLogger", package: "RoutableLogger"), 34 | ], 35 | path: "ObjectMapperAdditions", 36 | exclude: [ 37 | "Classes/Realm", 38 | "Privacy/ObjectMapperAdditions.Realm", 39 | ], 40 | sources: ["Classes/Core"], 41 | resources: [ 42 | .process("Privacy/ObjectMapperAdditions.Core/PrivacyInfo.xcprivacy") 43 | ], 44 | swiftSettings: [ 45 | .define("SPM"), 46 | ] 47 | ), 48 | .target( 49 | name: "ObjectMapperAdditionsRealm", 50 | dependencies: [ 51 | "ObjectMapperAdditions", 52 | .product(name: "RealmSwift", package: "realm-swift"), 53 | ], 54 | path: "ObjectMapperAdditions", 55 | exclude: [ 56 | "Classes/Core", 57 | "Privacy/ObjectMapperAdditions.Core", 58 | ], 59 | sources: ["Classes/Realm"], 60 | resources: [ 61 | .process("Privacy/ObjectMapperAdditions.Realm/PrivacyInfo.xcprivacy") 62 | ], 63 | swiftSettings: [ 64 | .define("SPM"), 65 | ] 66 | ), 67 | ], 68 | swiftLanguageVersions: [.v5] 69 | ) 70 | -------------------------------------------------------------------------------- /ObjectMapperAdditions.podspec: -------------------------------------------------------------------------------- 1 | # 2 | # Be sure to run `pod lib lint ObjectMapperAdditions.podspec' to ensure this is a 3 | # valid spec before submitting. 4 | # 5 | # Any lines starting with a # are optional, but their use is encouraged 6 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html 7 | # 8 | 9 | Pod::Spec.new do |s| 10 | s.name = 'ObjectMapperAdditions' 11 | s.version = '14.0.2' 12 | s.summary = 'ObjectMapper Extensions and Transforms' 13 | 14 | # This description is used to generate tags and improve search results. 15 | # * Think: What does it do? Why did you write it? What is the focus? 16 | # * Try to keep it short, snappy and to the point. 17 | # * Write the description between the DESC delimiters below. 18 | # * Finally, don't worry about the indent, CocoaPods strips it! 19 | 20 | s.description = <<-DESC 21 | - Adds simple calls to include NULL values in output JSON. 22 | - Adds ability to simply type cast JSON values to specified type. 23 | DESC 24 | 25 | s.homepage = 'https://github.com/APUtils/ObjectMapperAdditions' 26 | # s.screenshots = 'www.example.com/screenshots_1', 'www.example.com/screenshots_2' 27 | s.license = { :type => 'MIT', :file => 'LICENSE' } 28 | s.author = { 'Anton Plebanovich' => 'anton.plebanovich@gmail.com' } 29 | s.source = { :git => 'https://github.com/APUtils/ObjectMapperAdditions.git', :tag => s.version.to_s } 30 | # s.social_media_url = 'https://twitter.com/' 31 | 32 | s.ios.deployment_target = '13.0' 33 | s.osx.deployment_target = '12.0' 34 | s.tvos.deployment_target = '12.0' 35 | s.watchos.deployment_target = '5.0' 36 | 37 | # 1.12.0: Ensure developers won't hit CocoaPods/CocoaPods#11402 with the resource 38 | # bundle for the privacy manifest. 39 | # 1.13.0: visionOS is recognized as a platform. 40 | s.cocoapods_version = '>= 1.13.0' 41 | 42 | s.default_subspec = 'Core' 43 | s.frameworks = 'Foundation' 44 | s.dependency 'ObjectMapper', '>= 4.4.2' 45 | s.dependency 'RoutableLogger', '>= 12.0' 46 | 47 | s.subspec 'Core' do |core| 48 | core.source_files = 'ObjectMapperAdditions/Classes/Core/**/*' 49 | core.resource_bundle = {"ObjectMapperAdditions.Core.privacy"=>"ObjectMapperAdditions/Privacy/ObjectMapperAdditions.Core/PrivacyInfo.xcprivacy"} 50 | end 51 | 52 | s.subspec 'Realm' do |realm| 53 | realm.source_files = 'ObjectMapperAdditions/Classes/Realm/**/*' 54 | realm.resource_bundle = {"ObjectMapperAdditions.Realm.privacy"=>"ObjectMapperAdditions/Privacy/ObjectMapperAdditions.Realm/PrivacyInfo.xcprivacy"} 55 | realm.dependency 'ObjectMapperAdditions/Core' 56 | realm.dependency 'RealmSwift' 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /ObjectMapperAdditions/Classes/Core/Extensions/Data+Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Data+Extension.swift 3 | // ObjectMapperAdditions 4 | // 5 | // Created by Anton Plebanovich on 17.11.21. 6 | // Copyright © 2021 Anton Plebanovich. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import RoutableLogger 11 | 12 | extension Data { 13 | 14 | // ******************************* MARK: - As 15 | 16 | /// Get HEX string from data. Can be used for sending APNS token to backend. 17 | var hexString: String { 18 | return map { String(format: "%02hhx", $0) }.joined() 19 | } 20 | 21 | /// Try to convert data to ASCII string 22 | var asciiString: String? { 23 | String(data: self, encoding: String.Encoding.ascii) 24 | } 25 | 26 | /// Try to convert data to UTF8 string 27 | var utf8String: String? { 28 | return String(data: self, encoding: String.Encoding.utf8) 29 | } 30 | 31 | /// String representation for data. 32 | /// Try to decode as UTF8 string at first. 33 | /// Try to decode as ASCII string at second. 34 | /// Uses hex representation if data can not be represented as UTF8 or ASCII string. 35 | var asString: String { 36 | utf8String ?? asciiString ?? hexString 37 | } 38 | 39 | /// Try to serialize `self` to JSON object and report error if unable. 40 | func safeSerializeToJSON(file: String = #file, function: String = #function, line: UInt = #line) -> Any? { 41 | do { 42 | return try JSONSerialization.jsonObject(with: self, options: .allowFragments) 43 | } catch { 44 | RoutableLogger.logError("Unable to parse data to JSON", error: error, data: ["data": asString], file: file, function: function, line: line) 45 | return nil 46 | } 47 | } 48 | 49 | // ******************************* MARK: - Checks 50 | 51 | var firstNonWhitespaceByte: UInt8? { 52 | guard let index = firstIndex(where: { !ASCIICodes.whitespaceSet.contains($0) }) else { return nil } 53 | return self[index] 54 | } 55 | var secondNonWhitespaceByte: UInt8? { 56 | guard let index = firstIndex(where: { !ASCIICodes.whitespaceSet.contains($0) }), 57 | index + 1 < count else { return nil } 58 | 59 | return self[index + 1] 60 | } 61 | 62 | var lastNonWhitespaceByte: UInt8? { 63 | guard let index = lastIndex(where: { !ASCIICodes.whitespaceSet.contains($0) }) else { return nil } 64 | return self[index] 65 | } 66 | 67 | var beforeLastNonWhitespaceByte: UInt8? { 68 | guard let index = lastIndex(where: { !ASCIICodes.whitespaceSet.contains($0) }), 69 | index - 1 >= 0 else { return nil } 70 | 71 | return self[index - 1] 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /Example/ObjectMapperAdditions/MyRealmModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MyRealmModel.swift 3 | // ObjectMapperAdditions 4 | // 5 | // Created by mac-246 on 07/27/17. 6 | // Copyright © 2017 mac-246. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import ObjectMapper 11 | import ObjectMapperAdditions 12 | import RealmSwift 13 | 14 | class MyRealmModel: Object, Mappable { 15 | @objc dynamic var id: Int = 0 16 | @objc dynamic var double: Double = 0 17 | let optionalDouble = RealmProperty() 18 | @objc dynamic var string: String? 19 | @objc dynamic var myOtherRealmModel: MyOtherRealmModel? 20 | let myOtherRealmModels = List() 21 | let myRealmMap = RealmSwift.Map() 22 | var strings: List = List() 23 | 24 | override class func primaryKey() -> String? { "id" } 25 | 26 | override init() { 27 | super.init() 28 | } 29 | 30 | required init?(map: ObjectMapper.Map) { 31 | super.init() 32 | 33 | // Primary kay should not be reassigned after object is added to the Realm so we make sure it is assigned during init only 34 | id <- (map["id"], IntTransform.shared) 35 | } 36 | 37 | func mapping(map: ObjectMapper.Map) { 38 | performMapping { 39 | // Read-only primary key 40 | id >>> map["id"] 41 | 42 | // Same as for ordinary model 43 | double <- (map["double"], DoubleTransform.shared) 44 | 45 | // Using ObjectMapperAdditions's RealmPropertyTypeCastTransform 46 | optionalDouble <- map["optionalDouble"] 47 | 48 | // Custom transform support 49 | // optionalDouble <- (map["optionalDouble"], DoubleTransform.shared) 50 | 51 | // You could also use RealmPropertyTransform if you don't like type cast but you need to declare `optionalDouble` as a `var` then 52 | // optionalDouble <- (map["optionalDouble"], RealmPropertyTransform()) 53 | 54 | string <- (map["string"], StringTransform.shared) 55 | myOtherRealmModel <- map["myOtherRealmModel"] 56 | 57 | // Using ObjectMapper+Realm's RealmListTransform to transform custom types 58 | myOtherRealmModels <- map["myOtherRealmModels"] 59 | 60 | myRealmMap <- map["myRealmMap"] 61 | 62 | // Using ObjectMapperAdditions's RealmTypeCastTransform 63 | strings <- map["strings"] 64 | 65 | // // Custom transform support 66 | // strings <- (map["strings"], StringTransform.shared) 67 | 68 | // You could also use RealmTransform if you don't like type cast but you need to declare `optionalDouble` as a `var` then 69 | // strings <- (map["strings"], RealmTransform()) 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /ObjectMapperAdditions/Classes/Core/Transforms/EnumTypeCastTransform.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EnumTypeCastTransform.swift 3 | // ObjectMapperAdditions 4 | // 5 | // Created by Anton Plebanovich on 7/17/17. 6 | // Copyright © 2017 Anton Plebanovich. All rights reserved. 7 | // 8 | 9 | import ObjectMapper 10 | import RoutableLogger 11 | 12 | /// Transforms value of type Any to RawRepresentable enum. Tries to typecast if possible. 13 | public class EnumTypeCastTransform: TransformType { 14 | public typealias Object = T 15 | public typealias JSON = T.RawValue 16 | 17 | public init() {} 18 | 19 | open func transformFromJSON(_ value: Any?) -> T? { 20 | guard let value else { return nil } 21 | 22 | if let value = value as? T.RawValue { 23 | let result = T(rawValue: value) 24 | reportUnknownCaseIfNeeded(result, value: value, rawValue: nil) 25 | return result 26 | 27 | } else if T.RawValue.self == Int.self { 28 | let rawValue = IntTransform.shared.transformFromJSON(value) as? T.RawValue 29 | let result = rawValue.flatMap(T.init(rawValue:)) 30 | reportUnknownCaseIfNeeded(result, value: value, rawValue: rawValue) 31 | return result 32 | 33 | } else if T.RawValue.self == Double.self { 34 | let rawValue = DoubleTransform.shared.transformFromJSON(value) as? T.RawValue 35 | let result = rawValue.flatMap(T.init(rawValue:)) 36 | reportUnknownCaseIfNeeded(result, value: value, rawValue: rawValue) 37 | return result 38 | 39 | } else if T.RawValue.self == Bool.self { 40 | let rawValue = BoolTransform.shared.transformFromJSON(value) as? T.RawValue 41 | let result = rawValue.flatMap(T.init(rawValue:)) 42 | reportUnknownCaseIfNeeded(result, value: value, rawValue: rawValue) 43 | return result 44 | 45 | } else if T.RawValue.self == String.self { 46 | let rawValue = StringTransform.shared.transformFromJSON(value) as? T.RawValue 47 | let result = rawValue.flatMap(T.init(rawValue:)) 48 | reportUnknownCaseIfNeeded(result, value: value, rawValue: rawValue) 49 | return result 50 | 51 | } else { 52 | RoutableLogger.logError("Can not cast value of type \(type(of: value)) to type \(T.RawValue.self)", data: ["value": value]) 53 | return nil 54 | } 55 | } 56 | 57 | open func transformToJSON(_ value: T?) -> T.RawValue? { 58 | if let obj = value { 59 | return obj.rawValue 60 | } 61 | return nil 62 | } 63 | 64 | private func reportUnknownCaseIfNeeded(_ result: T?, value: Any, rawValue: Any?) { 65 | if result == nil { 66 | RoutableLogger.logError("Unknown enum case", data: ["value": value, "rawValue": rawValue]) 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /Example/ObjectMapperAdditions/MyPersistedRealmModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MyPersistedRealmModel.swift 3 | // ObjectMapperAdditions 4 | // 5 | // Created by Anton Plebanovich on 12.04.24. 6 | // Copyright © 2024 Anton Plebanovich. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import ObjectMapper 11 | import ObjectMapperAdditions 12 | import RealmSwift 13 | 14 | class MyPersistedRealmModel: Object, Mappable { 15 | @Persisted var id: Int = 0 16 | @Persisted var double: Double = 0 17 | @Persisted var optionalDouble: Double? 18 | @Persisted var string: String? 19 | @Persisted var myOtherRealmModel: MyOtherRealmModel? 20 | @Persisted var myOtherRealmModels: List 21 | @Persisted var myRealmMap: RealmSwift.Map 22 | @Persisted var strings: List 23 | 24 | override class func primaryKey() -> String? { "id" } 25 | 26 | override init() { 27 | super.init() 28 | } 29 | 30 | required init?(map: ObjectMapper.Map) { 31 | super.init() 32 | 33 | // Primary kay should not be reassigned after object is added to the Realm so we make sure it is assigned during init only 34 | id <- (map["id"], IntTransform.shared) 35 | } 36 | 37 | func mapping(map: ObjectMapper.Map) { 38 | performMapping { 39 | // Read-only primary key 40 | id >>> map["id"] 41 | 42 | // Same as for ordinary model 43 | double <- (map["double"], DoubleTransform.shared) 44 | 45 | // Using ObjectMapperAdditions's RealmPropertyTypeCastTransform 46 | optionalDouble <- (map["optionalDouble"], DoubleTransform.shared) 47 | 48 | // Custom transform support 49 | // optionalDouble <- (map["optionalDouble"], DoubleTransform.shared) 50 | 51 | // You could also use RealmPropertyTransform if you don't like type cast but you need to declare `optionalDouble` as a `var` then 52 | // optionalDouble <- (map["optionalDouble"], RealmPropertyTransform()) 53 | 54 | string <- (map["string"], StringTransform.shared) 55 | myOtherRealmModel <- map["myOtherRealmModel"] 56 | 57 | // Using ObjectMapper+Realm's RealmListTransform to transform custom types 58 | myOtherRealmModels <- map["myOtherRealmModels"] 59 | 60 | // For some reason, Xcode 15.3 can't properly select proper operator so we need a workaround 61 | let myRealmMap = self.myRealmMap 62 | myRealmMap <- map["myRealmMap"] 63 | 64 | // Using ObjectMapperAdditions's RealmTypeCastTransform 65 | strings <- map["strings"] 66 | 67 | // // Custom transform support 68 | // strings <- (map["strings"], StringTransform.shared) 69 | 70 | // You could also use RealmTransform if you don't like type cast but you need to declare `optionalDouble` as a `var` then 71 | // strings <- (map["strings"], RealmTransform()) 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /Example/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - APExtensions (15.0.1): 3 | - APExtensions/Core (= 15.0.1) 4 | - APExtensions/Dispatch (= 15.0.1) 5 | - APExtensions/Occupiable (= 15.0.1) 6 | - APExtensions/OptionalType (= 15.0.1) 7 | - APExtensions/Storyboard (= 15.0.1) 8 | - APExtensions/ViewModel (= 15.0.1) 9 | - APExtensions/Core (15.0.1): 10 | - RoutableLogger 11 | - APExtensions/Dispatch (15.0.1): 12 | - RoutableLogger 13 | - APExtensions/Occupiable (15.0.1) 14 | - APExtensions/OptionalType (15.0.1) 15 | - APExtensions/Storyboard (15.0.1): 16 | - RoutableLogger 17 | - APExtensions/ViewModel (15.0.1) 18 | - CwlCatchException (2.2.1): 19 | - CwlCatchExceptionSupport (~> 2.2.1) 20 | - CwlCatchExceptionSupport (2.2.1) 21 | - CwlMachBadInstructionHandler (2.2.2) 22 | - CwlPosixPreconditionTesting (2.2.2) 23 | - CwlPreconditionTesting (2.2.2): 24 | - CwlCatchException (~> 2.2.1) 25 | - CwlMachBadInstructionHandler (~> 2.2.2) 26 | - CwlPosixPreconditionTesting (~> 2.2.2) 27 | - Nimble (13.7.1): 28 | - CwlPreconditionTesting (~> 2.2.0) 29 | - ObjectMapper (4.4.2) 30 | - ObjectMapperAdditions/Core (14.0.2): 31 | - ObjectMapper (>= 4.4.2) 32 | - RoutableLogger (>= 12.0) 33 | - ObjectMapperAdditions/Realm (14.0.2): 34 | - ObjectMapper (>= 4.4.2) 35 | - ObjectMapperAdditions/Core 36 | - RealmSwift 37 | - RoutableLogger (>= 12.0) 38 | - Quick (7.6.2) 39 | - Realm (20.0.3): 40 | - Realm/Headers (= 20.0.3) 41 | - Realm/Headers (20.0.3) 42 | - RealmSwift (20.0.3): 43 | - Realm (= 20.0.3) 44 | - RoutableLogger (13.0.0) 45 | 46 | DEPENDENCIES: 47 | - APExtensions (from `https://github.com/APUtils/APExtensions`) 48 | - Nimble 49 | - ObjectMapperAdditions/Realm (from `../`) 50 | - Quick 51 | 52 | SPEC REPOS: 53 | https://github.com/CocoaPods/Specs.git: 54 | - CwlCatchException 55 | - CwlCatchExceptionSupport 56 | - CwlMachBadInstructionHandler 57 | - CwlPosixPreconditionTesting 58 | - CwlPreconditionTesting 59 | - Nimble 60 | - ObjectMapper 61 | - Quick 62 | - Realm 63 | - RealmSwift 64 | - RoutableLogger 65 | 66 | EXTERNAL SOURCES: 67 | APExtensions: 68 | :git: https://github.com/APUtils/APExtensions 69 | ObjectMapperAdditions: 70 | :path: "../" 71 | 72 | CHECKOUT OPTIONS: 73 | APExtensions: 74 | :commit: 9293dc8f0bbf9f6b7e1ea96e243f24ddeaafd6de 75 | :git: https://github.com/APUtils/APExtensions 76 | 77 | SPEC CHECKSUMS: 78 | APExtensions: 1c783b402ac7100f54bc7073cf0e1621d3dcbc8d 79 | CwlCatchException: 7acc161b299a6de7f0a46a6ed741eae2c8b4d75a 80 | CwlCatchExceptionSupport: 54ccab8d8c78907b57f99717fb19d4cc3bce02dc 81 | CwlMachBadInstructionHandler: dae4fdd124d45c9910ac240287cc7b898f4502a1 82 | CwlPosixPreconditionTesting: ecd095aa2129e740b44301c34571e8d85906fb88 83 | CwlPreconditionTesting: 67a0047dd4de4382b93442c0e3f25207f984f35a 84 | Nimble: 317d713c30c3336dd8571da1889f7ec3afc626e8 85 | ObjectMapper: e6e4d91ff7f2861df7aecc536c92d8363f4c9677 86 | ObjectMapperAdditions: fff03f0b2841d95a7269c70df90d99107698f78c 87 | Quick: b8bec97cd4b9f21da0472d45580f763b801fc353 88 | Realm: 853e5089d6042dff807bda000277eadfe2da93d2 89 | RealmSwift: f33c19577cefcbf681345d721fcbc7b42be4c949 90 | RoutableLogger: 31ce799b16b4868c169ed68c36e25437dd0ca40e 91 | 92 | PODFILE CHECKSUM: e223c3e3286192f30711607ae0eaf728240324a6 93 | 94 | COCOAPODS: 1.16.2 95 | -------------------------------------------------------------------------------- /Example/ObjectMapperAdditions/Base.lproj/LaunchScreen.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 24 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /Example/Tests/Create_Spec.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Create_Spec.swift 3 | // ObjectMapperAdditions 4 | // 5 | // Created by Anton Plebanovich on 12.01.22. 6 | // Copyright © 2022 Anton Plebanovich. All rights reserved. 7 | // 8 | 9 | import Quick 10 | import Nimble 11 | import ObjectMapper 12 | import ObjectMapperAdditions 13 | import APExtensions 14 | @testable import ObjectMapperAdditions_Example 15 | 16 | class Create_Spec: QuickSpec { 17 | override class func spec() { 18 | describe("Create") { 19 | 20 | let jsonObjectData = try! JSONSerialization.data(withJSONObject: ["string":"string"]) 21 | let jsonObjectString = jsonObjectData.safeUTF8String() 22 | 23 | let jsonArrayData = try! JSONSerialization.data(withJSONObject: [["string":"string"]]) 24 | let jsonArrayString = jsonArrayData.safeUTF8String() 25 | 26 | let jsonArrayOfArraysData = try! JSONSerialization.data(withJSONObject: [[["string":"string"]]]) 27 | let jsonArrayOfArraysString = jsonArrayOfArraysData.safeUTF8String() 28 | 29 | describe("[BaseMappable]") { 30 | it("should map from JSON") { 31 | expect(try [MappableStruct].create(jsonData: jsonArrayData)).toNot(throwError()) 32 | expect([MappableStruct].safeCreate(jsonData: jsonArrayData)).toNot(beNil()) 33 | 34 | expect(try [MappableStruct].create(jsonString: jsonArrayString)).toNot(throwError()) 35 | expect([MappableStruct].safeCreate(jsonString: jsonArrayString)).toNot(beNil()) 36 | } 37 | } 38 | 39 | describe("[BaseMappable?]") { 40 | it("should map from JSON") { 41 | expect(try [MappableStruct?].create(jsonData: jsonArrayData)).toNot(throwError()) 42 | expect([MappableStruct?].safeCreate(jsonData: jsonArrayData)).toNot(beNil()) 43 | } 44 | } 45 | 46 | describe("[[BaseMappable]]") { 47 | it("should map from JSON") { 48 | expect(try [[MappableStruct]].create(jsonData: jsonArrayOfArraysData)).toNot(throwError()) 49 | expect([[MappableStruct]].safeCreate(jsonData: jsonArrayOfArraysData)).toNot(beNil()) 50 | 51 | expect(try [[MappableStruct]].create(jsonString: jsonArrayOfArraysString)).toNot(throwError()) 52 | expect([[MappableStruct]].safeCreate(jsonString: jsonArrayOfArraysString)).toNot(beNil()) 53 | } 54 | } 55 | 56 | describe("Mappable") { 57 | it("should map from JSON object") { 58 | expect(try MappableStruct.create(jsonData: jsonObjectData)).toNot(throwError()) 59 | expect(MappableStruct.safeCreate(jsonData: jsonObjectData)).toNot(beNil()) 60 | 61 | expect(try MappableStruct.create(jsonString: jsonObjectString)).toNot(throwError()) 62 | expect(MappableStruct.safeCreate(jsonString: jsonObjectString)).toNot(beNil()) 63 | } 64 | } 65 | 66 | describe("ImmutableMappable") { 67 | it("should map from JSON object") { 68 | expect(try ImmutableMappableStruct.create(jsonData: jsonObjectData)).toNot(throwError()) 69 | expect(ImmutableMappableStruct.safeCreate(jsonData: jsonObjectData)).toNot(beNil()) 70 | 71 | expect(try ImmutableMappableStruct.create(jsonString: jsonObjectString)).toNot(throwError()) 72 | expect(ImmutableMappableStruct.safeCreate(jsonString: jsonObjectString)).toNot(beNil()) 73 | } 74 | } 75 | } 76 | } 77 | } 78 | 79 | private struct MappableStruct: Mappable { 80 | var string: String? 81 | init?(map: Map) {} 82 | mutating func mapping(map: ObjectMapper.Map) { 83 | string <- map["string"] 84 | } 85 | } 86 | 87 | private struct ImmutableMappableStruct: ImmutableMappable { 88 | let string: String 89 | init(map: Map) throws { 90 | string = try map.value("string") 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /ObjectMapperAdditions/Classes/Realm/ObjectMapper+Realm.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ObjectMapper+Realm.swift 3 | // Pods 4 | // 5 | // Created by Anton Plebanovich on 23.02.23. 6 | // Copyright © 2023 Anton Plebanovich. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import ObjectMapper 11 | import RealmSwift 12 | #if !COCOAPODS 13 | import ObjectMapperAdditions 14 | #endif 15 | 16 | // ******************************* MARK: - List 17 | 18 | public func <- (left: List, right: ObjectMapper.Map) { 19 | if right.mappingType == .toJSON { 20 | Array(left) >>> right 21 | 22 | } else if right.mappingType == .fromJSON { 23 | if right.isKeyPresent { 24 | var objects: [T]? 25 | objects <- right 26 | 27 | if let objects { 28 | if !left.isEmpty { 29 | left.removeAll() 30 | } 31 | left.append(objectsIn: objects) 32 | 33 | } else { 34 | left.removeAll() 35 | } 36 | } 37 | } 38 | } 39 | 40 | public func <- (left: List, right: ObjectMapper.Map) { 41 | if right.mappingType == .toJSON { 42 | Array(left) >>> right 43 | 44 | } else if right.mappingType == .fromJSON { 45 | if right.isKeyPresent { 46 | var objects: [T]? 47 | objects <- (right, TypeCastTransform()) 48 | 49 | if let objects { 50 | if !left.isEmpty { 51 | left.removeAll() 52 | } 53 | left.append(objectsIn: objects) 54 | 55 | } else { 56 | left.removeAll() 57 | } 58 | } 59 | } 60 | } 61 | 62 | public func <- (left: List, right: (ObjectMapper.Map, Transform)) where T == Transform.Object { 63 | let map = right.0 64 | if map.mappingType == .toJSON { 65 | Array(left) >>> right 66 | 67 | } else if map.mappingType == .fromJSON { 68 | if map.isKeyPresent { 69 | var objects: [T]? 70 | objects <- right 71 | 72 | if let objects { 73 | if !left.isEmpty { 74 | left.removeAll() 75 | } 76 | left.append(objectsIn: objects) 77 | 78 | } else { 79 | left.removeAll() 80 | } 81 | } 82 | } 83 | } 84 | 85 | // ******************************* MARK: - Map 86 | 87 | /// Old sytax with `let` 88 | public func <- (left: RealmSwift.Map, right: ObjectMapper.Map) { 89 | if right.mappingType == .toJSON { 90 | let dictionary = left.reduce(into: [Key: Value]()) { dictionary, tuple in 91 | dictionary[tuple.key] = tuple.value 92 | } 93 | 94 | dictionary >>> right 95 | 96 | } else if right.mappingType == .fromJSON { 97 | if right.isKeyPresent { 98 | var keyValues: [Key: Value]? 99 | keyValues <- right 100 | 101 | if let keyValues { 102 | if left.count > 0 { 103 | left.removeAll() 104 | } 105 | for keyValue in keyValues { 106 | left[keyValue.key] = keyValue.value 107 | } 108 | 109 | } else { 110 | left.removeAll() 111 | } 112 | } 113 | } 114 | } 115 | 116 | // ******************************* MARK: - RealmProperty 117 | 118 | public func <- (left: RealmProperty, right: ObjectMapper.Map) { 119 | if right.mappingType == .toJSON { 120 | left.value >>> right 121 | 122 | } else if right.mappingType == .fromJSON { 123 | if right.isKeyPresent { 124 | var value: T? 125 | value <- (right, TypeCastTransform()) 126 | left.value = value 127 | } 128 | } 129 | } 130 | 131 | public func <- (left: RealmProperty, right: (ObjectMapper.Map, Transform)) where T == Transform.Object { 132 | let map = right.0 133 | let transform = right.1 134 | if map.mappingType == .toJSON { 135 | left.value >>> map 136 | 137 | } else if map.mappingType == .fromJSON { 138 | if map.isKeyPresent { 139 | let value = transform.transformFromJSON(map.currentValue) 140 | left.value = value 141 | } 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /ObjectMapperAdditions/Classes/Core/Extensions/ImmutableMappable+Create.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImmutableMappable+Create.swift 3 | // ObjectMapperAdditions 4 | // 5 | // Created by Anton Plebanovich on 16.11.21. 6 | // Copyright © 2021 Anton Plebanovich. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import ObjectMapper 11 | import RoutableLogger 12 | 13 | // ******************************* MARK: - From data 14 | 15 | public extension ImmutableMappable { 16 | 17 | /// Creates model from JSON string. 18 | /// - parameter jsonData: Data in JSON format to use for model creation. 19 | /// - throws: `MappingError.emptyData` if response data is empty. 20 | /// - throws: `MappingError.invalidJSON` if response isn't a valid JSON. 21 | /// - throws: `MappingError.unknownType` if it wasn't possible to create model. 22 | static func create(jsonData: Data?, file: String = #file, function: String = #function, line: UInt = #line) throws -> Self { 23 | guard let jsonData = jsonData, !jsonData.isEmpty else { 24 | throw MappingError.emptyData 25 | } 26 | 27 | 28 | // Start check 29 | guard jsonData.firstNonWhitespaceByte == ASCIICodes.openCurlyBracket else { 30 | throw MappingError.invalidJSON(message: "JSON object should start with the '{' character") 31 | } 32 | 33 | // End check 34 | guard jsonData.lastNonWhitespaceByte == ASCIICodes.closeCurlyBracket else { 35 | throw MappingError.invalidJSON(message: "JSON object should end with the '}' character") 36 | } 37 | 38 | guard let jsonObject = jsonData.safeSerializeToJSON(file: file, function: function, line: line) else { 39 | throw MappingError.invalidJSON(message: "Unable to serialize JSON object from the data") 40 | } 41 | 42 | guard let jsonDictionary = jsonObject as? [String: Any] else { 43 | throw MappingError.unknownType 44 | } 45 | 46 | let model = try Self(JSON: jsonDictionary) 47 | 48 | return model 49 | } 50 | 51 | /// Create model from JSON string. Report error and return nil if unable. 52 | /// - parameter jsonData: Data in JSON format to use for model creation. 53 | static func safeCreate(jsonData: Data?, file: String = #file, function: String = #function, line: UInt = #line) -> Self? { 54 | do { 55 | return try create(jsonData: jsonData) 56 | } catch { 57 | RoutableLogger.logError("Unable to create object from JSON data", error: error, data: ["jsonData": jsonData, "self": self], file: file, function: function, line: line) 58 | return nil 59 | } 60 | } 61 | } 62 | 63 | // ******************************* MARK: - From string 64 | 65 | public extension ImmutableMappable { 66 | 67 | /// Creates model from JSON string. 68 | /// - parameter jsonString: String in JSON format to use for model creation. 69 | /// - throws: `MappingError.emptyData` if response data is empty. 70 | /// - throws: `MappingError.invalidJSON` if response isn't a valid JSON. 71 | /// - throws: Any other error that model may emmit during initialization. 72 | static func create(jsonString: String?, file: String = #file, function: String = #function, line: UInt = #line) throws -> Self { 73 | guard let jsonString = jsonString else { 74 | throw MappingError.emptyData 75 | } 76 | 77 | guard !jsonString.isEmpty else { 78 | throw MappingError.emptyData 79 | } 80 | 81 | // Start check 82 | guard jsonString.firstNonWhitespaceCharacter == "{" else { 83 | throw MappingError.invalidJSON(message: "JSON object should start with the '{' character") 84 | } 85 | 86 | // End check 87 | guard jsonString.lastNonWhitespaceCharacter == "}" else { 88 | throw MappingError.invalidJSON(message: "JSON object should end with the '}' character") 89 | } 90 | 91 | let model = try Self(JSONString: jsonString) 92 | 93 | return model 94 | } 95 | 96 | /// Create model from JSON string. Report error and return nil if unable. 97 | /// - parameter jsonString: String in JSON format to use for model creation. 98 | static func safeCreate(jsonString: String?, file: String = #file, function: String = #function, line: UInt = #line) -> Self? { 99 | do { 100 | return try create(jsonString: jsonString) 101 | } catch { 102 | RoutableLogger.logError("Unable to create object from JSON string", error: error, data: ["jsonString": jsonString, "self": self], file: file, function: function, line: line) 103 | return nil 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /ObjectMapperAdditions/Classes/Core/Extensions/Mappable+Create.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Mappable+Create.swift 3 | // ObjectMapperAdditions 4 | // 5 | // Created by Anton Plebanovich on 16.11.21. 6 | // Copyright © 2021 Anton Plebanovich. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import ObjectMapper 11 | import RoutableLogger 12 | 13 | // ******************************* MARK: - From data 14 | 15 | public extension Mappable { 16 | 17 | /// Creates model from JSON string. 18 | /// - parameter jsonData: Data in JSON format to use for model creation. 19 | /// - throws: `MappingError.emptyData` if response data is empty. 20 | /// - throws: `MappingError.invalidJSON` if response isn't a valid JSON. 21 | /// - throws: `MappingError.unknownType` if it wasn't possible to create model. 22 | static func create(jsonData: Data?, file: String = #file, function: String = #function, line: UInt = #line) throws -> Self { 23 | guard let jsonData = jsonData, !jsonData.isEmpty else { 24 | throw MappingError.emptyData 25 | } 26 | 27 | // Start check 28 | guard jsonData.firstNonWhitespaceByte == ASCIICodes.openCurlyBracket else { 29 | throw MappingError.invalidJSON(message: "JSON object should start with the '{' character") 30 | } 31 | 32 | // End check 33 | guard jsonData.lastNonWhitespaceByte == ASCIICodes.closeCurlyBracket else { 34 | throw MappingError.invalidJSON(message: "JSON object should end with the '}' character") 35 | } 36 | 37 | guard let jsonObject = jsonData.safeSerializeToJSON(file: file, function: function, line: line) else { 38 | throw MappingError.invalidJSON(message: "Unable to serialize JSON array from the data") 39 | } 40 | 41 | guard let jsonDictionary = jsonObject as? [String: Any] else { 42 | throw MappingError.unknownType 43 | } 44 | 45 | guard let model = Self(JSON: jsonDictionary) else { 46 | throw MappingError.unknownType 47 | } 48 | 49 | return model 50 | } 51 | 52 | /// Create model from JSON string. Report error and return nil if unable. 53 | /// - parameter jsonData: Data in JSON format to use for model creation. 54 | static func safeCreate(jsonData: Data?, file: String = #file, function: String = #function, line: UInt = #line) -> Self? { 55 | do { 56 | return try create(jsonData: jsonData) 57 | } catch { 58 | RoutableLogger.logError("Unable to create object from JSON data", error: error, data: ["jsonData": jsonData, "self": self], file: file, function: function, line: line) 59 | return nil 60 | } 61 | } 62 | } 63 | 64 | // ******************************* MARK: - From string 65 | 66 | public extension Mappable { 67 | 68 | /// Creates model from JSON string. 69 | /// - parameter jsonString: String in JSON format to use for model creation. 70 | /// - throws: `MappingError.emptyData` if response data is empty. 71 | /// - throws: `MappingError.invalidJSON` if response isn't a valid JSON. 72 | /// - throws: `MappingError.unknownType` if it wasn't possible to create model. 73 | static func create(jsonString: String?, file: String = #file, function: String = #function, line: UInt = #line) throws -> Self { 74 | guard let jsonString = jsonString else { 75 | throw MappingError.emptyData 76 | } 77 | 78 | guard !jsonString.isEmpty else { 79 | throw MappingError.emptyData 80 | } 81 | 82 | // Start check 83 | guard jsonString.firstNonWhitespaceCharacter == "{" else { 84 | throw MappingError.invalidJSON(message: "JSON object should start with the '{' character") 85 | } 86 | 87 | // End check 88 | guard jsonString.lastNonWhitespaceCharacter == "}" else { 89 | throw MappingError.invalidJSON(message: "JSON object should end with the '}' character") 90 | } 91 | 92 | guard let model = Self(JSONString: jsonString) else { 93 | throw MappingError.unknownType 94 | } 95 | 96 | return model 97 | } 98 | 99 | /// Create model from JSON string. Report error and return nil if unable. 100 | /// - parameter jsonString: String in JSON format to use for model creation. 101 | static func safeCreate(jsonString: String?, file: String = #file, function: String = #function, line: UInt = #line) -> Self? { 102 | do { 103 | return try create(jsonString: jsonString) 104 | } catch { 105 | RoutableLogger.logError("Unable to create object from JSON string", error: error, data: ["jsonString": jsonString, "self": self], file: file, function: function, line: line) 106 | return nil 107 | } 108 | } 109 | } 110 | -------------------------------------------------------------------------------- /Example/ObjectMapperAdditions.xcodeproj/xcshareddata/xcschemes/ObjectMapperAdditions-Example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 44 | 45 | 51 | 52 | 53 | 54 | 57 | 63 | 64 | 65 | 66 | 67 | 77 | 79 | 85 | 86 | 87 | 88 | 94 | 96 | 102 | 103 | 104 | 105 | 107 | 108 | 111 | 112 | 113 | -------------------------------------------------------------------------------- /Example/ObjectMapperAdditions/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // ObjectMapperAdditions 4 | // 5 | // Created by Anton Plebanovich on 07/17/2017. 6 | // Copyright (c) 2017 Anton Plebanovich. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import APExtensions 11 | import ObjectMapperAdditions 12 | import RealmSwift 13 | 14 | 15 | class ViewController: UIViewController { 16 | 17 | //----------------------------------------------------------------------------- 18 | // MARK: - UIViewController Methods 19 | //----------------------------------------------------------------------------- 20 | 21 | override func viewDidLoad() { 22 | super.viewDidLoad() 23 | 24 | let spaces = String(repeating: " ", count: 10_000_000) 25 | let string = " {\(spaces)\(spaces)} " 26 | 27 | let data = string.data(using: .utf8)! 28 | 29 | let date1 = Date() 30 | _ = try! MyModel.create(jsonString: string) 31 | print("From string: ******** %f", Date().timeIntervalSince(date1)) 32 | 33 | let date2 = Date() 34 | _ = try! MyModel.create(jsonData: data) 35 | print("From data: ******** %f", Date().timeIntervalSince(date2)) 36 | 37 | //----------------------------------------------------------------------------- 38 | // MARK: - ISO8601Formatter 39 | //----------------------------------------------------------------------------- 40 | 41 | let date = Date() 42 | let dateToDateString = ISO8601JustDateTransform.shared.transformToJSON(date)! 43 | let dateStringToDate = ISO8601JustDateTransform.shared.transformFromJSON(dateToDateString)! 44 | print("Date - \(date) to ISO8601 date string transform - \(dateToDateString)") 45 | print("ISO8601 date string - \(dateToDateString) to date transform - \(dateStringToDate)") 46 | 47 | //----------------------------------------------------------------------------- 48 | // MARK: - TimestampTransform 49 | //----------------------------------------------------------------------------- 50 | 51 | let dateToTimestamp = TimestampTransform.shared.transformToJSON(date)! 52 | let timestampToDate = TimestampTransform.shared.transformFromJSON(dateToTimestamp)! 53 | print("Date - \(date) to timestamp transform - \(dateToTimestamp)") 54 | print("Timestamp - \(dateToTimestamp) to date transform - \(timestampToDate)") 55 | 56 | //----------------------------------------------------------------------------- 57 | // MARK: - Type Cast Example 58 | //----------------------------------------------------------------------------- 59 | 60 | let typeMatchingJSON: [String: Any] = [ 61 | "string": "123", 62 | "stringsArray": ["123.0", "321.0"], 63 | "double": 1.1 64 | ] 65 | 66 | let typeMismatchingJSON: [String: Any] = [ 67 | "string": 123, 68 | "stringsArray": [123.0, 321.0], 69 | "double": "1.1" 70 | ] 71 | 72 | // Check if type cast was successful 73 | print(MyModel(JSON: typeMatchingJSON) == MyModel(JSON: typeMismatchingJSON) ? "Type cast success" : "Type cast fail") 74 | 75 | //----------------------------------------------------------------------------- 76 | // MARK: - Realm Example 77 | //----------------------------------------------------------------------------- 78 | 79 | let realmJSON: [String: Any] = [ 80 | "double": 1.1, 81 | "string": "123", 82 | "myOtherRealmModel": [String: Any](), 83 | "myOtherRealmModels": [[String: Any](), [String: Any]()], 84 | "myRealmMap": ["key":"value"], 85 | "strings": ["123.0", "321.0"] 86 | ] 87 | 88 | let realm = try? Realm() 89 | // Create model from JSON and store it to realm 90 | if let realmModel = MyRealmModel(JSON: realmJSON) { 91 | try? realm?.write { 92 | realm?.deleteAll() 93 | realm?.add(realmModel) 94 | } 95 | 96 | // Get stored model 97 | let storedModel = realm?.objects(MyRealmModel.self).first 98 | 99 | // Checking that all data still there and same as in JSON 100 | var success = true 101 | success = success && realmModel.double == storedModel?.double && realmModel.double == realmJSON.double(forKey: "double") 102 | success = success && realmModel.string == storedModel?.string && realmModel.string == realmJSON.string(forKey: "string") 103 | success = success && realmModel.myOtherRealmModel == storedModel?.myOtherRealmModel 104 | success = success && Array(realmModel.myOtherRealmModels) == Array(storedModel!.myOtherRealmModels) 105 | success = success && realmModel.myRealmMap["key"] == realmJSON.dictionary(forKey: "myRealmMap")?.string(forKey: "key") 106 | success = success && Array(realmModel.strings) == Array(storedModel!.strings) && Array(realmModel.strings) == (realmJSON["strings"] as! [String]) 107 | 108 | print(success ? "Data is not lost" : "Some data is lost") 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /Example/Tests/Realm_Spec.swift: -------------------------------------------------------------------------------- 1 | // https://github.com/Quick/Quick 2 | 3 | import Quick 4 | import Nimble 5 | import RealmSwift 6 | import ObjectMapper 7 | import ObjectMapperAdditions 8 | import APExtensions 9 | @testable import ObjectMapperAdditions_Example 10 | 11 | 12 | class RealmSpec: QuickSpec { 13 | override class func spec() { 14 | describe("Realm object") { 15 | beforeEach { 16 | let realm = try! Realm(configuration: .init(deleteRealmIfMigrationNeeded: true)) 17 | try! realm.write { 18 | realm.deleteAll() 19 | } 20 | } 21 | 22 | context("when JSON contains proper type params") { 23 | let id = Int.random(in: ClosedRange(uncheckedBounds: (Int.min, Int.max))) 24 | let realmJSON: [String: Any] = [ 25 | "id": id, 26 | "double": 1.1, 27 | "optionalDouble": "2.2", 28 | "string": "123", 29 | "myOtherRealmModel": ["string":"string"], 30 | "myOtherRealmModels": [["string":"string"], ["string":"string"]], 31 | "myRealmMap": ["key":"value"], 32 | "strings": ["123.0", "321.0"] 33 | ] 34 | 35 | it("should map properly from JSON to MyRealmModel") { 36 | let model = MyRealmModel(JSON: realmJSON) 37 | expect(model?.id).to(equal(id)) 38 | expect(model?.double).to(equal(1.1)) 39 | expect(model?.optionalDouble.value).to(equal(2.2)) 40 | expect(model?.string).to(equal("123")) 41 | expect(model?.myOtherRealmModel?.string).to(equal("string")) 42 | expect(model?.myOtherRealmModels.count).to(equal(2)) 43 | expect(model?.myOtherRealmModels[0].string).to(equal("string")) 44 | expect(model?.myOtherRealmModels[1].string).to(equal("string")) 45 | expect(model?.myRealmMap["key"]).to(equal("value")) 46 | expect(Array(model!.strings)).to(equal(["123.0", "321.0"])) 47 | } 48 | 49 | it("should map properly from JSON to MyPersistedRealmModel") { 50 | let model = MyPersistedRealmModel(JSON: realmJSON) 51 | expect(model?.id).to(equal(id)) 52 | expect(model?.double).to(equal(1.1)) 53 | expect(model?.optionalDouble).to(equal(2.2)) 54 | expect(model?.string).to(equal("123")) 55 | expect(model?.myOtherRealmModel?.string).to(equal("string")) 56 | expect(model?.myOtherRealmModels.count).to(equal(2)) 57 | expect(model?.myOtherRealmModels[0].string).to(equal("string")) 58 | expect(model?.myOtherRealmModels[1].string).to(equal("string")) 59 | expect(model?.myRealmMap["key"]).to(equal("value")) 60 | expect(Array(model!.strings)).to(equal(["123.0", "321.0"])) 61 | } 62 | 63 | context("and added to Realm") { 64 | it("should be able to transform to JSON") { 65 | var model = MyRealmModel(JSON: realmJSON)! 66 | let realm = try! Realm() 67 | try! realm.write({ 68 | realm.add(model) 69 | }) 70 | 71 | model = realm.object(ofType: MyRealmModel.self, forPrimaryKey: model.id)! 72 | let json = model.toJSON() 73 | expect(json.count).to(equal(8)) 74 | } 75 | } 76 | } 77 | 78 | context("when JSON contains wrong type params") { 79 | let realmJSON: [String: Any] = [ 80 | "id": "1", 81 | "double": "1.1", 82 | "optionalDouble": 2.2, 83 | "string": 123, 84 | "myOtherRealmModel": ["string":123], 85 | "myOtherRealmModels": [["string":123], ["string":123]], 86 | "strings": [123.0, 321.0] 87 | ] 88 | 89 | it("should map properly from JSON") { 90 | let model = MyRealmModel(JSON: realmJSON) 91 | expect(model?.id).to(equal(1)) 92 | expect(model?.double).to(equal(1.1)) 93 | expect(model?.optionalDouble.value).to(equal(2.2)) 94 | expect(model?.string).to(equal("123")) 95 | expect(model?.myOtherRealmModel?.string).to(equal("123")) 96 | expect(model?.myOtherRealmModels.count).to(equal(2)) 97 | expect(model?.myOtherRealmModels[0].string).to(equal("123")) 98 | expect(model?.myOtherRealmModels[1].string).to(equal("123")) 99 | expect(Array(model!.strings)).to(equal(["123.0", "321.0"])) 100 | } 101 | } 102 | 103 | context("filled with data") { 104 | var realm: Realm! 105 | var model: MyRealmModel! 106 | 107 | beforeEach { 108 | realm = try! Realm() 109 | try! realm.write { 110 | realm.deleteAll() 111 | } 112 | 113 | model = MyRealmModel() 114 | model.double = 1.1 115 | model.optionalDouble.value = 2.2 116 | model.string = "123" 117 | model.myOtherRealmModel = MyOtherRealmModel() 118 | model.myOtherRealmModels.append(objectsIn: [MyOtherRealmModel(), MyOtherRealmModel()]) 119 | model.strings.append(objectsIn: ["123.0", "321.0"]) 120 | } 121 | 122 | it("should be saved and restored successfully") { 123 | try! realm.write { 124 | realm.add(model) 125 | } 126 | 127 | let storedModel = realm.objects(MyRealmModel.self).first! 128 | expect(model.double).to(equal(storedModel.double)) 129 | expect(model.string).to(equal(storedModel.string)) 130 | expect(model.myOtherRealmModel).to(equal(storedModel.myOtherRealmModel)) 131 | expect(Array(model.myOtherRealmModels)).to(equal(Array(storedModel.myOtherRealmModels))) 132 | expect(Array(model.strings)).to(equal(Array(storedModel.strings))) 133 | } 134 | } 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /Example/Tests/TypeCast_Spec.swift: -------------------------------------------------------------------------------- 1 | // https://github.com/Quick/Quick 2 | 3 | import Quick 4 | import Nimble 5 | import ObjectMapper 6 | import ObjectMapperAdditions 7 | import APExtensions 8 | @testable import ObjectMapperAdditions_Example 9 | 10 | 11 | class TypeCastSpec: QuickSpec { 12 | override class func spec() { 13 | describe("Type cast") { 14 | context("when JSON contains proper type params") { 15 | let typeMatchingJSON: [String: Any] = [ 16 | "string": "123", 17 | "stringsArray": ["123.0", "321.0"], 18 | "double": 1.1, 19 | "intEnum": 1, 20 | "stringEnum": "2" 21 | ] 22 | 23 | it("should map it propertly") { 24 | let model = MyModel(JSON: typeMatchingJSON) 25 | expect(model?.string).to(equal("123")) 26 | expect(model?.stringsArray).to(equal(["123.0", "321.0"])) 27 | expect(model?.double).to(equal(1.1)) 28 | expect(model?.intEnum).to(equal(.one)) 29 | expect(model?.stringEnum).to(equal(.two)) 30 | } 31 | } 32 | 33 | context("when JSON contains wrong type params") { 34 | let typeMismatchingJSON: [String: Any] = [ 35 | "string": 123, 36 | "stringsArray": [123.0, 321.0], 37 | "double": "1.1", 38 | "intEnum": "1", 39 | "stringEnum": 2 40 | ] 41 | 42 | it("should map it propertly") { 43 | let model = MyModel(JSON: typeMismatchingJSON) 44 | expect(model?.string).to(equal("123")) 45 | expect(model?.stringsArray).to(equal(["123.0", "321.0"])) 46 | expect(model?.double).to(equal(1.1)) 47 | expect(model?.intEnum).to(equal(.one)) 48 | expect(model?.stringEnum).to(equal(.two)) 49 | } 50 | } 51 | 52 | context("when JSON contains doubles") { 53 | let typeMismatchingJSON: [String: Any] = [ 54 | "double1": 1, 55 | "double2": "1.0", 56 | "double3": "-1.0000", 57 | "double4": "1.0001", 58 | "double5": "-100.001", 59 | "double6": "1000000.0001", 60 | "double7": "-2.66", 61 | "double8": -1000000, 62 | "double9": 1.0, 63 | "double10": 0.1, 64 | "double11": "0.1", 65 | "double12": "0.1000000", 66 | ] 67 | 68 | let doubleModel = MyDoubleModel(JSON: typeMismatchingJSON)! 69 | it("should map it propertly") { 70 | expect(doubleModel.double1).to(equal(1)) 71 | expect(doubleModel.double2).to(equal(1)) 72 | expect(doubleModel.double3).to(equal(-1)) 73 | expect(doubleModel.double4).to(equal(1.0001)) 74 | expect(doubleModel.double5).to(equal(-100.001)) 75 | expect(doubleModel.double6).to(equal(1000000.0001)) 76 | expect(doubleModel.double7).to(equal(-2.66)) 77 | expect(doubleModel.double8).to(equal(-1000000)) 78 | expect(doubleModel.double9).to(equal(1)) 79 | expect(doubleModel.double10).to(equal(0.1)) 80 | expect(doubleModel.double11).to(equal(0.1)) 81 | expect(doubleModel.double12).to(equal(0.1)) 82 | } 83 | 84 | it("should map back to JSON propertly") { 85 | let json = doubleModel.toJSON() 86 | expect(json["double1"] as? Double).to(equal(1)) 87 | expect(json["double2"] as? Double).to(equal(1)) 88 | expect(json["double3"] as? Double).to(equal(-1)) 89 | expect(json["double4"] as? Double).to(equal(1.0001)) 90 | expect(json["double5"] as? Double).to(equal(-100.001)) 91 | expect(json["double6"] as? Double).to(equal(1000000.0001)) 92 | expect(json["double7"] as? Double).to(equal(-2.66)) 93 | expect(json["double8"] as? Double).to(equal(-1000000)) 94 | expect(json["double9"] as? Double).to(equal(1)) 95 | expect(json["double10"] as? Double).to(equal(0.1)) 96 | expect(json["double11"] as? Double).to(equal(0.1)) 97 | expect(json["double12"] as? Double).to(equal(0.1)) 98 | } 99 | 100 | it("should map back to JSON string propertly") { 101 | // { 102 | // "double8": -1000000, 103 | // "double3": -1, 104 | // "double7": -2.6600000000000001, 105 | // "double5": -100.001, 106 | // "double9": 1, 107 | // "double6": 1000000.0000999999, 108 | // "double1": 1, 109 | // "double12": 0.10000000000000001, 110 | // "double4": 1.0001, 111 | // "double10": 0.10000000000000001, 112 | // "double2": 1, 113 | // "double11": 0.10000000000000001 114 | // } 115 | let jsonString = doubleModel.toJSONString()! 116 | print(jsonString) 117 | 118 | // This one fails because of the Apple JSON mapping 119 | // expect(jsonString.contains("10000000000000001")).to(beFalse()) 120 | } 121 | } 122 | } 123 | } 124 | } 125 | 126 | private struct MyDoubleModel: Mappable { 127 | var double1: Double? 128 | var double2: Double? 129 | var double3: Double? 130 | var double4: Double? 131 | var double5: Double? 132 | var double6: Double? 133 | var double7: Double? 134 | var double8: Double? 135 | var double9: Double? 136 | var double10: Double? 137 | var double11: Double? 138 | var double12: Double? 139 | 140 | init?(map: Map) {} 141 | 142 | mutating func mapping(map: Map) { 143 | double1 <- (map["double1"], TypeCastTransform()) 144 | double2 <- (map["double2"], TypeCastTransform()) 145 | double3 <- (map["double3"], TypeCastTransform()) 146 | double4 <- (map["double4"], TypeCastTransform()) 147 | double5 <- (map["double5"], TypeCastTransform()) 148 | double6 <- (map["double6"], TypeCastTransform()) 149 | double7 <- (map["double7"], TypeCastTransform()) 150 | double8 <- (map["double8"], TypeCastTransform()) 151 | double9 <- (map["double9"], TypeCastTransform()) 152 | double10 <- (map["double10"], TypeCastTransform()) 153 | double11 <- (map["double11"], TypeCastTransform()) 154 | double12 <- (map["double12"], TypeCastTransform()) 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | All notable changes to this project will be documented in this file. 3 | `ObjectMapperAdditions` adheres to [Semantic Versioning](http://semver.org/). 4 | 5 | 6 | ## [14.0.3](https://github.com/APUtils/LogsManager/releases/tag/14.0.3) 7 | Released on `2025-08-13` 8 | 9 | #### Changed 10 | - [SPM] Use default `type` instead of `.dynamic` for libraries 11 | 12 | 13 | ## [14.0.2](https://github.com/APUtils/LogsManager/releases/tag/14.0.2) 14 | Released on `2025-08-12` 15 | 16 | #### Changed 17 | - [MongoDateTransform] Better transform logic and tests 18 | - [RealmSwift] No strict limitation on dependency version 19 | 20 | #### Fixed 21 | - [SPM] Build and warning fixes [With [@AndreyzZz](https://github.com/AndreyzZz)] 22 | 23 | 24 | ## [14.0.0](https://github.com/APUtils/LogsManager/releases/tag/14.0.0) 25 | Released on `2025-04-03` 26 | 27 | #### Added 28 | - `jsonDescription` for `BaseMappable` and `[BaseMappable]` 29 | - New transform: `ObjectIdTransform` 30 | - New transform: `MongoDateTransform` 31 | 32 | #### Changed 33 | - Realm podspec limitation `< 20.0` 34 | 35 | 36 | ## [13.2.1](https://github.com/APUtils/LogsManager/releases/tag/13.2.1) 37 | Released on `2024-04-09` 38 | 39 | #### Added 40 | - `RealmSwift.Map` mapping support 41 | 42 | 43 | ## [13.2.0](https://github.com/APUtils/LogsManager/releases/tag/13.2.0) 44 | Released on `2024-03-27` 45 | 46 | #### Added 47 | - `performMapping` safety for frozen and invalidated objects 48 | - Carriage return support as a whitespace character 49 | - [EnumTypeCastTransform] Report unknown cases 50 | - [Map] additions 51 | 52 | #### Changed 53 | - `<-` operator for `List` and `RealmProperty` now does nothing on key absense to mimic the same behavior of `ObjectMapper` 54 | - `<-` operator for `List` now replace existing entries on subsequent mapping 55 | 56 | 57 | ## [13.1.0](https://github.com/APUtils/LogsManager/releases/tag/13.1.0) 58 | Released on `2023-04-25` 59 | 60 | #### Added 61 | - `<-` operator for `List` and `RealmProperty` with custom transform 62 | 63 | #### Changed 64 | - `<-` operator for `List` and `RealmProperty` now does nothing on key absense to mimic the same behavior of `ObjectMapper` 65 | - `<-` operator for `List` now replace existing entries on subsequent mapping 66 | 67 | 68 | ## [13.0.3](https://github.com/APUtils/LogsManager/releases/tag/13.0.3) 69 | Released on `2023-04-25` 70 | 71 | #### Added 72 | - [EmbeddedObject] `performMapping(_:)` 73 | 74 | 75 | ## [13.0.0](https://github.com/APUtils/LogsManager/releases/tag/13.0.0) 76 | Released on `2023-04-25` 77 | 78 | #### Changed 79 | - Examples 80 | - Reverted `12.0.0` version deprecations 81 | - `ObjectMapperAdditions` and `ObjectMapperAdditionsCore` SPM frameworks separation 82 | 83 | #### Deprecated 84 | - Carthage 85 | 86 | #### Fixed 87 | - SPM and Carthage builds 88 | 89 | 90 | ## [12.0.0](https://github.com/APUtils/LogsManager/releases/tag/12.0.0) 91 | Released on `2023-02-23` 92 | 93 | #### Added 94 | - `<-` operator to map and typecast `List` and `RealmProperty` 95 | - [Object] `performMapping(_:)` method to simplify safe mappings 96 | 97 | #### Changed 98 | - Using `cancelWrite` instead of `commitWrite` for Realm models mapping 99 | 100 | #### Deprecated 101 | - `RealmListTransform` in favor of `<-` operator 102 | - `RealmPropertyTransform` 103 | - `RealmPropertyTypeCastTransform` in favor of `<-` operator 104 | - `RealmTransform` 105 | - `RealmTypeCastTransform` in favor of `<-` operator 106 | 107 | #### Removed 108 | - Deprecated `RealmOptionalTransform` 109 | - Deprecated `RealmOptionalTypeCastTransform` 110 | 111 | 112 | ## [11.0.0](https://github.com/APUtils/LogsManager/releases/tag/11.0.0) 113 | Released on `2023-01-29` 114 | 115 | #### Changed 116 | - Access modifier examples update 117 | - Better code reuse 118 | - Better log messages 119 | - Consider empty string as `nil` for casting 120 | - Considering `-` string as `nil` 121 | - Min supported OS versions rised 122 | 123 | #### Fixed 124 | - Error message and param name in data error log 125 | 126 | 127 | ## [10.0.2](https://github.com/APUtils/ObjectMapperAdditions/releases/tag/10.0.2) 128 | Released on `2022-07-28` 129 | 130 | #### Added 131 | - Int to Double rounding warning 132 | 133 | 134 | ## [10.0.1](https://github.com/APUtils/ObjectMapperAdditions/releases/tag/10.0.1) 135 | Released on `2022-07-28` 136 | 137 | #### Added 138 | - More error reports on failure casts 139 | 140 | 141 | ## [10.0.0](https://github.com/APUtils/ObjectMapperAdditions/releases/tag/10.0.0) 142 | Released on `2022-07-07` 143 | 144 | #### Changed 145 | - Some transformers are now singletons 146 | 147 | 148 | ## [9.0.0](https://github.com/APUtils/ObjectMapperAdditions/releases/tag/9.0.0) 149 | Released on `2022-07-07` 150 | 151 | #### Added 152 | - Array toJSONData() 153 | - BaseMappable toJSONString(options:) 154 | - Set toJSONData() 155 | - [BaseMappable] toJSONString(options:) 156 | 157 | #### Changed 158 | - Check for closing brackets before creation 159 | - Do not fail on whitespaces 160 | - Search for non-whitespace characters instead of warning 161 | - `RoutableLogger.logError` instead of `print` for type cast errors 162 | 163 | #### Improved 164 | - Better error report 165 | - Better invalid JSON messages 166 | 167 | #### Tests 168 | - Create specs 169 | 170 | ## [8.2.0](https://github.com/APUtils/ObjectMapperAdditions/releases/tag/8.2.0) 171 | Released on 11/17/2021. 172 | 173 | #### Added 174 | - [BaseMappable?].create(jsonData:) 175 | - [BaseMappable?].safeCreate(jsonData:) 176 | - [BaseMappable].create(jsonData:) 177 | - [BaseMappable].create(jsonString:) 178 | - [BaseMappable].safeCreate(jsonData:) 179 | - [BaseMappable].safeCreate(jsonString:) 180 | - [BaseMappable].toJSONData() 181 | - [ImmutableMappable].create(jsonData:) 182 | - [ImmutableMappable].create(jsonString:) 183 | - [ImmutableMappable].safeCreate(jsonData:) 184 | - [ImmutableMappable].safeCreate(jsonString:) 185 | - [Mappable].create(jsonData:) 186 | - [Mappable].create(jsonString:) 187 | - [Mappable].safeCreate(jsonData:) 188 | - [Mappable].safeCreate(jsonString:) 189 | - [[BaseMappable]].create(jsonData:) 190 | - [[BaseMappable]].create(jsonString:) 191 | - [[BaseMappable]].safeCreate(jsonData:) 192 | - [[BaseMappable]].safeCreate(jsonString:) 193 | 194 | 195 | ## [8.1.0](https://github.com/APUtils/ObjectMapperAdditions/releases/tag/8.1.0) 196 | Released on 11/07/2021. 197 | 198 | #### Added 199 | - RealmPropertyTransform [[@Drusy](https://github.com/Drusy)] 200 | - RealmPropertyTypeCastTransform [[@Drusy](https://github.com/Drusy)] 201 | 202 | #### Changed 203 | - Deprecated RealmOptionalTransform 204 | - Deprecated RealmOptionalTypeCastTransform 205 | 206 | 207 | ## [8.0.0](https://github.com/APUtils/ObjectMapperAdditions/releases/tag/8.0.0) 208 | Released on 04/15/2021. 209 | 210 | #### Changed 211 | - SPM support for the latest Realm and ObjectMapper versions [[@dams229](https://github.com/dams229)] 212 | - iOS 11.0 min 213 | - OSx 10.10 min 214 | 215 | #### Fixed 216 | - Carthage project 217 | 218 | 219 | ## [7.0.0](https://github.com/APUtils/ObjectMapperAdditions/releases/tag/7.0.0) 220 | Released on 05/20/2020. 221 | 222 | #### Changed 223 | - Set minimum iOS deployment target to 10.0 to match ObjectMapper requirements. 224 | 225 | 226 | ## [6.0.2](https://github.com/APUtils/ObjectMapperAdditions/releases/tag/6.0.2) 227 | Released on 07/25/2019. 228 | 229 | #### Added 230 | - MacOS support for Cocoapods and Carthage 231 | 232 | 233 | ## [6.0.1](https://github.com/APUtils/ObjectMapperAdditions/releases/tag/6.0.1) 234 | Released on 05/23/2019. 235 | 236 | #### Fixed 237 | - Swift 5.0 support fix. 238 | 239 | #### Changed 240 | - Removed dependency on `ObjectMapper+Realm` framework. 241 | 242 | 243 | ## [6.0.0](https://github.com/APUtils/ObjectMapperAdditions/releases/tag/6.0.0) 244 | Released on 04/04/2019. 245 | 246 | #### Added 247 | - Swift 5.0 support 248 | - EnumTypeCastTransform 249 | 250 | 251 | ## [5.0.0](https://github.com/APUtils/ObjectMapperAdditions/releases/tag/5.0.0) 252 | Released on 12/30/2018. 253 | 254 | #### Added 255 | - Swift 4.2 256 | 257 | 258 | ## [4.2.1](https://github.com/APUtils/ObjectMapperAdditions/releases/tag/4.2.1) 259 | Released on 10/13/2018. 260 | 261 | #### Added 262 | - Cocoapods support for tvOS 9.0 263 | 264 | 265 | ## [4.2.0](https://github.com/APUtils/ObjectMapperAdditions/releases/tag/4.2.0) 266 | Released on 10/13/2018. 267 | 268 | #### Added 269 | - Cocoapods support for tvOS 270 | 271 | #### Fixed 272 | - Warnings 273 | 274 | 275 | ## [4.1.0](https://github.com/APUtils/ObjectMapperAdditions/releases/tag/4.1.0) 276 | Released on 03/27/2018. 277 | 278 | #### Added 279 | - RealmOptionalTransform and RealmOptionalTypeCastTransform to trannsform to RealmOptional type 280 | 281 | 282 | ## [4.0.0](https://github.com/APUtils/ObjectMapperAdditions/releases/tag/4.0.0) 283 | Released on 02/09/2018. 284 | 285 | #### Added 286 | - Realm 3 support for basic types. 287 | 288 | #### Removed 289 | - RealmAdditions dependency is not required anymore. 290 | 291 | ## [3.0.5](https://github.com/APUtils/ObjectMapperAdditions/releases/tag/3.0.5) 292 | Released on 09/26/2017. 293 | 294 | #### Added 295 | - ISO8601JustDateTransform. Transforms ISO8601 **date** string to/from Date. 296 | 297 | 298 | ## [3.0.4](https://github.com/APUtils/ObjectMapperAdditions/releases/tag/3.0.4) 299 | Released on 09/26/2017. 300 | 301 | #### Added 302 | - TimestampTransform. Transforms UNIX timestamp (aka POSIX timestamp or epoch) to/from Date. 303 | 304 | 305 | ## [3.0.1](https://github.com/APUtils/ObjectMapperAdditions/releases/tag/3.0.1) 306 | Released on 09/21/2017. 307 | 308 | #### Fixed 309 | - Carthage support 310 | 311 | 312 | ## [3.0.0](https://github.com/APUtils/ObjectMapperAdditions/releases/tag/3.0.0) 313 | Released on 09/21/2017. 314 | 315 | Swift 4 migration 316 | 317 | 318 | ## [2.0.4](https://github.com/APUtils/ObjectMapperAdditions/releases/tag/2.0.4) 319 | Released on 08/08/2017. 320 | 321 | #### Added 322 | - Carthage support 323 | 324 | 325 | ## [2.0.0](https://github.com/APUtils/ObjectMapperAdditions/releases/tag/2.0.0) 326 | Released on 07/28/2017. 327 | 328 | #### Changed 329 | - Moved core functionality to Core subspec. 330 | - Realm subspec to convert simple type arrays to realm objects. 331 | 332 | 333 | ## [1.1.1](https://github.com/APUtils/ObjectMapperAdditions/releases/tag/1.1.1) 334 | Released on 07/18/2017. 335 | 336 | #### Fixed 337 | - Added public inits. 338 | 339 | 340 | ## [1.1.0](https://github.com/APUtils/ObjectMapperAdditions/releases/tag/1.1.0) 341 | Released on 07/18/2017. 342 | 343 | #### Added 344 | - ObjectMapper+Additions. 345 | 346 | 347 | #### Fixed 348 | - Public extensions fix. 349 | 350 | 351 | #### Changed 352 | - Cases reordered. 353 | 354 | 355 | ## [1.0.0](https://github.com/APUtils/ObjectMapperAdditions/releases/tag/1.0.0) 356 | Released on 07/17/2017. 357 | 358 | #### Added 359 | - Initial release of ObjectMapperAdditions. 360 | - Added by [Anton Plebanovich](https://github.com/anton-plebanovich). 361 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ObjectMapperAdditions 2 | 3 | [![Swift Package Manager compatible](https://img.shields.io/badge/Swift%20Package%20Manager-compatible-brightgreen.svg)](https://github.com/apple/swift-package-manager) 4 | [![Version](https://img.shields.io/cocoapods/v/ObjectMapperAdditions.svg?style=flat)](http://cocoapods.org/pods/ObjectMapperAdditions) 5 | [![License](https://img.shields.io/cocoapods/l/ObjectMapperAdditions.svg?style=flat)](http://cocoapods.org/pods/ObjectMapperAdditions) 6 | [![Platform](https://img.shields.io/cocoapods/p/ObjectMapperAdditions.svg?style=flat)](http://cocoapods.org/pods/ObjectMapperAdditions) 7 | [![CI Status](http://img.shields.io/travis/APUtils/ObjectMapperAdditions.svg?style=flat)](https://travis-ci.org/APUtils/ObjectMapperAdditions) 8 | 9 | - Adds simple calls to include NULL values in output JSON. 10 | - Adds ability to simply type cast JSON values to specified type. 11 | - Adds ability to map Swift base type arrays into Realm arrays. 12 | - Adds `TimestampTransform` to simply transform to/from UNIX timestamps. 13 | - Adds `ISO8601JustDateTransform` to simplty transform to/from ISO8601 **date** string. Because ObjectMapper's `ISO8601DateTransform` actually is **date and time** transform. 14 | 15 | ## Example 16 | 17 | To run the example project, clone the repo, and run `pod install` from the Example directory first. 18 | 19 | ## Installation 20 | 21 | #### Swift Package Manager 22 | 23 | - In Xcode select `File` > `Add Packages...` 24 | - Copy and paste the following into the search: `https://github.com/APUtils/ObjectMapperAdditions` 25 | - **‼️Make sure `Up to Next Major Version` is selected and put `14.0.0` into the lower bound if needed. There is a bug in 14.2 Xcode, it does not select versions higher than 9.0.0 by default‼️** 26 | - Tap `Add Package` 27 | - Select `ObjectMapperAdditions` to add core functionality 28 | - Optionally, select `ObjectMapperAdditionsRealm` to add `Realm` related functionality 29 | - Tap `Add Package` 30 | 31 | #### CocoaPods 32 | 33 | ObjectMapperAdditions is available through [CocoaPods](http://cocoapods.org). 34 | 35 | To install Core features, simply add the following line to your Podfile: 36 | 37 | ```ruby 38 | pod 'ObjectMapperAdditions/Core', '~> 14.0' 39 | ``` 40 | 41 | To add Realm transform to your project add the following line to your Podfile: 42 | 43 | ```ruby 44 | pod 'ObjectMapperAdditions/Realm', '~> 14.0' 45 | ``` 46 | 47 | ## Usage 48 | 49 | #### Core Features 50 | 51 | It's a common case when app gets Int in JSON instead of String even if backend guy said you it'll be String. Worst of all sometimes it could be String and sometimes something else so it'll look like you released broken app even if you tested it well. 52 | 53 | After several projects I made a rule for myself: `Never trust a backend!`. I always make optional fields and cast values to type I'll use. Right now I'm using a great framework `ObjectMapper` to map my objects but it doesn't have transforms I need so I wrote them as this separate pod. 54 | 55 | Example model: 56 | 57 | ``` swift 58 | import Foundation 59 | import ObjectMapper 60 | import ObjectMapperAdditions 61 | 62 | struct MyModel: Mappable { 63 | var string: String? 64 | var stringsArray: [String]? 65 | var double: Double? 66 | var myOtherModel: MyOtherModel? 67 | var myOtherModelsArray: [MyOtherModel]? 68 | 69 | init?(map: Map) {} 70 | 71 | mutating func mapping(map: Map) { 72 | // You could specify proper type transform directly 73 | string <- (map["string"], StringTransform.shared) 74 | 75 | // Or you could just use TypeCastTransform 76 | string <- (map["string"], TypeCastTransform()) 77 | 78 | // No doubt it also works with Double 79 | double <- (map["double"], TypeCastTransform()) 80 | 81 | // Works with arrays too but for TypeCastTransform you must specify type 82 | stringsArray <- (map["stringsArray"], TypeCastTransform()) 83 | 84 | // Or just use StringTransform directly 85 | stringsArray <- (map["stringsArray"], StringTransform.shared) 86 | 87 | // No need to transform your types. They should specify transforms by themselfs. 88 | myOtherModel <- map["myOtherModel"] 89 | myOtherModelsArray <- map["myOtherModelsArray"] 90 | } 91 | } 92 | ``` 93 | 94 | Right now there are 4 base type transforms you could use: `BoolTransform`, `DoubleTransform`, `IntTransform` and `StringTransform`. But for basic types it's easier to just use `TypeCastTransform` which will type cast to proper type automatically. 95 | 96 | Typecasting for `Bool`, `Double`, `Int` and `String` raw representable enums are also supported with `EnumTypeCastTransform`. 97 | 98 | Moreover this pod has extension to simplify creation of JSON with NULL values included from objects. Just call `.toJSON(shouldIncludeNilValues: true)` on `BaseMappable` object or array/set. 99 | 100 | Date transformers example usage: 101 | ```swift 102 | // If date in timestamp format (1506423767) 103 | date <- (map["date"], TimestampTransform.shared) 104 | 105 | // If date in ISO8601 full-date format (yyyy-MM-dd) 106 | date <- (map["date"], ISO8601JustDateTransform.shared) 107 | ``` 108 | 109 | See example and tests projects for more details. 110 | 111 | #### Realm Features 112 | 113 | This part of ObjectMapperAdditions solves issues that prevent simply using ObjectMapper and Realm in one model. `RealmListTransform` to transform custom types into realm lists was taken from [ObjectMapper-Realm](https://github.com/Jakenberg/ObjectMapper-Realm) but it can't transform simple type arrays nor optional values. 114 | 115 | ``` swift 116 | import Foundation 117 | import ObjectMapper 118 | import ObjectMapperAdditions 119 | import RealmSwift 120 | 121 | class MyRealmModel: Object, Mappable { 122 | @objc dynamic var id: Int = 0 123 | @objc dynamic var double: Double = 0 124 | let optionalDouble = RealmProperty() 125 | @objc dynamic var string: String? 126 | @objc dynamic var myOtherRealmModel: MyOtherRealmModel? 127 | let myOtherRealmModels = List() 128 | let myRealmMap = RealmSwift.Map() 129 | var strings: List = List() 130 | 131 | override class func primaryKey() -> String? { "id" } 132 | 133 | override init() { 134 | super.init() 135 | } 136 | 137 | required init?(map: ObjectMapper.Map) { 138 | super.init() 139 | 140 | // Primary kay should not be reassigned after object is added to the Realm so we make sure it is assigned during init only 141 | id <- (map["id"], IntTransform.shared) 142 | } 143 | 144 | func mapping(map: ObjectMapper.Map) { 145 | performMapping { 146 | // Read-only primary key 147 | id >>> map["id"] 148 | 149 | // Same as for ordinary model 150 | double <- (map["double"], DoubleTransform.shared) 151 | 152 | // Using ObjectMapperAdditions's RealmPropertyTypeCastTransform 153 | optionalDouble <- map["optionalDouble"] 154 | 155 | // Custom transform support 156 | // optionalDouble <- (map["optionalDouble"], DoubleTransform.shared) 157 | 158 | // You could also use RealmPropertyTransform if you don't like type cast but you need to declare `optionalDouble` as a `var` then 159 | // optionalDouble <- (map["optionalDouble"], RealmPropertyTransform()) 160 | 161 | string <- (map["string"], StringTransform.shared) 162 | myOtherRealmModel <- map["myOtherRealmModel"] 163 | 164 | // Using ObjectMapper+Realm's RealmListTransform to transform custom types 165 | myOtherRealmModels <- map["myOtherRealmModels"] 166 | 167 | myRealmMap <- map["myRealmMap"] 168 | 169 | // Using ObjectMapperAdditions's RealmTypeCastTransform 170 | strings <- map["strings"] 171 | 172 | // // Custom transform support 173 | // strings <- (map["strings"], StringTransform.shared) 174 | 175 | // You could also use RealmTransform if you don't like type cast but you need to declare `optionalDouble` as a `var` then 176 | // strings <- (map["strings"], RealmTransform()) 177 | } 178 | } 179 | } 180 | ``` 181 | 182 | Example `@Persisted` Realm model: 183 | 184 | ``` swift 185 | import Foundation 186 | import ObjectMapper 187 | import ObjectMapperAdditions 188 | import RealmSwift 189 | 190 | class MyPersistedRealmModel: Object, Mappable { 191 | @Persisted var id: Int = 0 192 | @Persisted var double: Double = 0 193 | @Persisted var optionalDouble: Double? 194 | @Persisted var string: String? 195 | @Persisted var myOtherRealmModel: MyOtherRealmModel? 196 | @Persisted var myOtherRealmModels: List 197 | @Persisted var myRealmMap: RealmSwift.Map 198 | @Persisted var strings: List 199 | 200 | override class func primaryKey() -> String? { "id" } 201 | 202 | override init() { 203 | super.init() 204 | } 205 | 206 | required init?(map: ObjectMapper.Map) { 207 | super.init() 208 | 209 | // Primary kay should not be reassigned after object is added to the Realm so we make sure it is assigned during init only 210 | id <- (map["id"], IntTransform.shared) 211 | } 212 | 213 | func mapping(map: ObjectMapper.Map) { 214 | performMapping { 215 | // Read-only primary key 216 | id >>> map["id"] 217 | 218 | // Same as for ordinary model 219 | double <- (map["double"], DoubleTransform.shared) 220 | 221 | // Using ObjectMapperAdditions's RealmPropertyTypeCastTransform 222 | optionalDouble <- (map["optionalDouble"], DoubleTransform.shared) 223 | 224 | // Custom transform support 225 | // optionalDouble <- (map["optionalDouble"], DoubleTransform.shared) 226 | 227 | // You could also use RealmPropertyTransform if you don't like type cast but you need to declare `optionalDouble` as a `var` then 228 | // optionalDouble <- (map["optionalDouble"], RealmPropertyTransform()) 229 | 230 | string <- (map["string"], StringTransform.shared) 231 | myOtherRealmModel <- map["myOtherRealmModel"] 232 | 233 | // Using ObjectMapper+Realm's RealmListTransform to transform custom types 234 | myOtherRealmModels <- map["myOtherRealmModels"] 235 | 236 | // For some reason, Xcode 15.3 can't properly select proper operator so we need a workaround 237 | let myRealmMap = self.myRealmMap 238 | myRealmMap <- map["myRealmMap"] 239 | 240 | // Using ObjectMapperAdditions's RealmTypeCastTransform 241 | strings <- map["strings"] 242 | 243 | // // Custom transform support 244 | // strings <- (map["strings"], StringTransform.shared) 245 | 246 | // You could also use RealmTransform if you don't like type cast but you need to declare `optionalDouble` as a `var` then 247 | // strings <- (map["strings"], RealmTransform()) 248 | } 249 | } 250 | } 251 | ``` 252 | 253 | Swift optionals cast to realm optionals this way: `Int?` -> `RealmProperty>`, `Double?` -> `RealmProperty`, `Bool?` -> `RealmProperty`, etc. 254 | 255 | Swift arrays cast to realm arrays this way: `[String]` -> `List`, `[Int]` -> `List`, `[Double]` -> `List`, `[Bool]` -> `List`, etc. 256 | 257 | **Be sure to check that properties of type `RealmProperty` and `List` are not dynamic nor optional. Also despite of they defined as `var` they should be handled as constants if model is added to Realm. Use `.value` to change `RealmOptional` value or use `.removeAll()` and `append(objectsIn:)` methods to change `List` content** 258 | 259 | See example and tests projects for more details. 260 | 261 | ## Contributions 262 | 263 | Any contribution is more than welcome! You can contribute through pull requests and issues on GitHub. 264 | 265 | ## Author 266 | 267 | Anton Plebanovich, anton.plebanovich@gmail.com 268 | 269 | ## License 270 | 271 | ObjectMapperAdditions is available under the MIT license. See the LICENSE file for more info. 272 | -------------------------------------------------------------------------------- /ObjectMapperAdditions/Classes/Core/Extensions/BaseMappable+Create.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BaseMappable+Create.swift 3 | // ObjectMapperAdditions 4 | // 5 | // Created by Anton Plebanovich on 16.11.21. 6 | // Copyright © 2021 Anton Plebanovich. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import ObjectMapper 11 | import RoutableLogger 12 | 13 | // ******************************* MARK: - [BaseMappable] with data 14 | 15 | public extension Array where Element: BaseMappable { 16 | 17 | /// Creates models array from JSON string. 18 | /// - parameter jsonData: Data in JSON format to use for model creation. 19 | /// - throws: `MappingError.emptyData` if response data is empty. 20 | /// - throws: `MappingError.invalidJSON` if response isn't a valid JSON. 21 | /// - throws: `MappingError.unknownType` if it wasn't possible to create model. 22 | static func create(jsonData: Data?, file: String = #file, function: String = #function, line: UInt = #line) throws -> Array { 23 | guard let jsonData = jsonData, !jsonData.isEmpty else { 24 | throw MappingError.emptyData 25 | } 26 | 27 | // Start check 28 | guard jsonData.firstNonWhitespaceByte == ASCIICodes.openSquareBracket else { 29 | throw MappingError.invalidJSON(message: "JSON array should start with the '[' character") 30 | } 31 | 32 | // End check 33 | guard jsonData.lastNonWhitespaceByte == ASCIICodes.closeSquareBracket else { 34 | throw MappingError.invalidJSON(message: "JSON array should end with the ']' character") 35 | } 36 | 37 | guard let jsonObject = jsonData.safeSerializeToJSON(file: file, function: function, line: line) else { 38 | throw MappingError.invalidJSON(message: "Unable to serialize JSON array from the data") 39 | } 40 | 41 | guard let jsonArrayOfDictionaries = jsonObject as? [[String: Any]] else { 42 | throw MappingError.unknownType 43 | } 44 | 45 | let array = Mapper().mapArray(JSONArray: jsonArrayOfDictionaries) 46 | 47 | return array 48 | } 49 | 50 | /// Create models array from JSON string. Report error and return nil if unable. 51 | /// - parameter jsonData: Data in JSON format to use for model creation. 52 | static func safeCreate(jsonData: Data?, file: String = #file, function: String = #function, line: UInt = #line) -> Array? { 53 | do { 54 | return try create(jsonData: jsonData) 55 | } catch { 56 | RoutableLogger.logError("Unable to create array of objects from JSON data", error: error, data: ["jsonData": jsonData?.asString], file: file, function: function, line: line) 57 | return nil 58 | } 59 | } 60 | } 61 | 62 | // ******************************* MARK: - [BaseMappable] with string 63 | 64 | public extension Array where Element: BaseMappable { 65 | 66 | /// Creates models array from JSON string. 67 | /// - parameter jsonString: String in JSON format to use for model creation. 68 | /// - throws: `MappingError.emptyData` if response data is empty. 69 | /// - throws: `MappingError.invalidJSON` if response isn't a valid JSON. 70 | /// - throws: `MappingError.unknownType` if it wasn't possible to create model. 71 | static func create(jsonString: String?, file: String = #file, function: String = #function, line: UInt = #line) throws -> Array { 72 | guard let jsonString = jsonString else { 73 | throw MappingError.emptyData 74 | } 75 | 76 | guard !jsonString.isEmpty else { 77 | throw MappingError.emptyData 78 | } 79 | 80 | // Start check 81 | guard jsonString.firstNonWhitespaceCharacter == "[" else { 82 | throw MappingError.invalidJSON(message: "JSON array should start with the '[' character") 83 | } 84 | 85 | // End check 86 | guard jsonString.lastNonWhitespaceCharacter == "]" else { 87 | throw MappingError.invalidJSON(message: "JSON array should end with the ']' character") 88 | } 89 | 90 | guard let array = Mapper().mapArray(JSONString: jsonString) else { 91 | throw MappingError.unknownType 92 | } 93 | 94 | return array 95 | } 96 | 97 | /// Create models array from JSON string. Report error and return nil if unable. 98 | /// - parameter jsonString: String in JSON format to use for model creation. 99 | static func safeCreate(jsonString: String?, file: String = #file, function: String = #function, line: UInt = #line) -> Array? { 100 | do { 101 | return try create(jsonString: jsonString) 102 | } catch { 103 | RoutableLogger.logError("Unable to create array of objects from JSON string", error: error, data: ["jsonString": jsonString, "self": self], file: file, function: function, line: line) 104 | return nil 105 | } 106 | } 107 | } 108 | 109 | // ******************************* MARK: - [BaseMappable?] with data 110 | 111 | public extension Array where Element: OptionalType, Element.Wrapped: BaseMappable { 112 | 113 | /// Creates models array from JSON string. 114 | /// - parameter jsonData: Data in JSON format to use for model creation. 115 | /// - throws: `MappingError.emptyData` if response data is empty. 116 | /// - throws: `MappingError.invalidJSON` if response isn't a valid JSON. 117 | /// - throws: `MappingError.unknownType` if it wasn't possible to create model. 118 | static func create(jsonData: Data?, file: String = #file, function: String = #function, line: UInt = #line) throws -> [Element] { 119 | guard let jsonData = jsonData, !jsonData.isEmpty else { 120 | throw MappingError.emptyData 121 | } 122 | 123 | // Start check 124 | guard jsonData.firstNonWhitespaceByte == ASCIICodes.openSquareBracket else { 125 | throw MappingError.invalidJSON(message: "JSON array should start with the '[' character") 126 | } 127 | 128 | // End check 129 | guard jsonData.lastNonWhitespaceByte == ASCIICodes.closeSquareBracket else { 130 | throw MappingError.invalidJSON(message: "JSON array should end with the ']' character") 131 | } 132 | 133 | guard let jsonObject = jsonData.safeSerializeToJSON(file: file, function: function, line: line) else { 134 | throw MappingError.invalidJSON(message: "Unable to serialize JSON array from the data") 135 | } 136 | 137 | guard let jsonArrayOfObjects = jsonObject as? [Any] else { 138 | throw MappingError.unknownType 139 | } 140 | 141 | return try jsonArrayOfObjects.map { object in 142 | if object is NSNull { 143 | return Element(nilLiteral: ()) 144 | 145 | } else { 146 | if let _jsonObject = object as? [String: Any], 147 | let jsonObject = Mapper().map(JSON: _jsonObject) as? Element { 148 | 149 | return jsonObject 150 | 151 | } else { 152 | throw MappingError.unknownType 153 | } 154 | } 155 | } 156 | } 157 | 158 | /// Create models array from JSON string. Report error and return nil if unable. 159 | /// - parameter jsonData: Data in JSON format to use for model creation. 160 | static func safeCreate(jsonData: Data?, file: String = #file, function: String = #function, line: UInt = #line) -> Array? { 161 | do { 162 | return try create(jsonData: jsonData) 163 | } catch { 164 | RoutableLogger.logError("Unable to create array of optional objects from JSON data", error: error, data: ["jsonData": jsonData?.asString], file: file, function: function, line: line) 165 | return nil 166 | } 167 | } 168 | } 169 | 170 | // ******************************* MARK: - [[BaseMappable]] 171 | 172 | public extension RandomAccessCollection where Element: RandomAccessCollection, Element.Element: BaseMappable { 173 | 174 | /// Creates models array from JSON string. 175 | /// - parameter jsonString: String in JSON format to use for model creation. 176 | /// - throws: `MappingError.emptyData` if response data is empty. 177 | /// - throws: `MappingError.invalidJSON` if response isn't a valid JSON. 178 | /// - throws: `MappingError.unknownType` if it wasn't possible to create model. 179 | static func create(jsonData: Data?, file: String = #file, function: String = #function, line: UInt = #line) throws -> [[Element.Element]] { 180 | guard let jsonData = jsonData else { 181 | throw MappingError.emptyData 182 | } 183 | 184 | guard !jsonData.isEmpty else { 185 | throw MappingError.emptyData 186 | } 187 | 188 | // Start check 189 | guard jsonData.firstNonWhitespaceByte == ASCIICodes.openSquareBracket else { 190 | throw MappingError.invalidJSON(message: "JSON array of arrays should start with the '[' character") 191 | } 192 | 193 | guard jsonData.secondNonWhitespaceByte == ASCIICodes.openSquareBracket else { 194 | throw MappingError.invalidJSON(message: "JSON array should start with the '[' character") 195 | } 196 | 197 | // End check 198 | guard jsonData.lastNonWhitespaceByte == ASCIICodes.closeSquareBracket else { 199 | throw MappingError.invalidJSON(message: "JSON array of arrays should end with the ']' character") 200 | } 201 | 202 | guard jsonData.beforeLastNonWhitespaceByte == ASCIICodes.closeSquareBracket else { 203 | throw MappingError.invalidJSON(message: "JSON array should end with the ']' character") 204 | } 205 | 206 | guard let json = jsonData.safeSerializeToJSON(file: file, function: function, line: line) else { 207 | throw MappingError.invalidJSON(message: "Unable to serialize JSON array from the data") 208 | } 209 | 210 | guard let arrayOfArraysOfDictionaries = json as? [[[String: Any]]] else { 211 | throw MappingError.unknownType 212 | } 213 | 214 | guard let arrayOfArraysOfObjects = Mapper().mapArrayOfArrays(JSONObject: arrayOfArraysOfDictionaries) else { 215 | throw MappingError.unknownType 216 | } 217 | 218 | return arrayOfArraysOfObjects 219 | } 220 | 221 | /// Create models array from JSON string. Report error and return nil if unable. 222 | /// - parameter jsonString: String in JSON format to use for model creation. 223 | static func safeCreate(jsonData: Data?, file: String = #file, function: String = #function, line: UInt = #line) -> [[Element.Element]]? { 224 | do { 225 | return try create(jsonData: jsonData) 226 | } catch { 227 | RoutableLogger.logError("Unable to create array of objects from JSON string", error: error, data: ["jsonData": jsonData, "self": self], file: file, function: function, line: line) 228 | return nil 229 | } 230 | } 231 | 232 | /// Creates models array from JSON string. 233 | /// - parameter jsonString: String in JSON format to use for model creation. 234 | /// - throws: `MappingError.emptyData` if response data is empty. 235 | /// - throws: `MappingError.invalidJSON` if response isn't a valid JSON. 236 | /// - throws: `MappingError.unknownType` if it wasn't possible to create model. 237 | static func create(jsonString: String?, file: String = #file, function: String = #function, line: UInt = #line) throws -> [[Element.Element]] { 238 | guard let jsonString = jsonString else { 239 | throw MappingError.emptyData 240 | } 241 | 242 | guard !jsonString.isEmpty else { 243 | throw MappingError.emptyData 244 | } 245 | 246 | // Start check 247 | guard jsonString.firstNonWhitespaceCharacter == "[" else { 248 | throw MappingError.invalidJSON(message: "JSON array should start with the '[' character") 249 | } 250 | 251 | guard jsonString.secondNonWhitespaceCharacter == "[" else { 252 | throw MappingError.invalidJSON(message: "JSON array should start with the '[' character") 253 | } 254 | 255 | // End check 256 | guard jsonString.lastNonWhitespaceCharacter == "]" else { 257 | throw MappingError.invalidJSON(message: "JSON array of arrays should end with the ']' character") 258 | } 259 | 260 | guard jsonString.beforeLastNonWhitespaceCharacter == "]" else { 261 | throw MappingError.invalidJSON(message: "JSON array should end with the ']' character") 262 | } 263 | 264 | guard let arrayOfArraysOfDictionaries = Mapper.parseJSONString(JSONString: jsonString) as? [[[String: Any]]] else { 265 | throw MappingError.unknownType 266 | } 267 | 268 | guard let arrayOfArraysOfObjects = Mapper().mapArrayOfArrays(JSONObject: arrayOfArraysOfDictionaries) else { 269 | throw MappingError.unknownType 270 | } 271 | 272 | return arrayOfArraysOfObjects 273 | } 274 | 275 | /// Create models array from JSON string. Report error and return nil if unable. 276 | /// - parameter jsonString: String in JSON format to use for model creation. 277 | static func safeCreate(jsonString: String?, file: String = #file, function: String = #function, line: UInt = #line) -> [[Element.Element]]? { 278 | do { 279 | return try create(jsonString: jsonString) 280 | } catch { 281 | RoutableLogger.logError("Unable to create array of objects from JSON string", error: error, data: ["jsonString": jsonString, "self": self], file: file, function: function, line: line) 282 | return nil 283 | } 284 | } 285 | } 286 | -------------------------------------------------------------------------------- /Example/ObjectMapperAdditions.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 54; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 0DC249BB2737CA0F00623738 /* RealmPropertyTransform_Spec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DC249BA2737CA0F00623738 /* RealmPropertyTransform_Spec.swift */; }; 11 | 0DC249BD2737CAED00623738 /* RealmPropertyTypeCastTransform_Spec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DC249BC2737CAED00623738 /* RealmPropertyTypeCastTransform_Spec.swift */; }; 12 | 0DDE964C278F046D00E346EA /* Create_Spec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DDE964B278F046D00E346EA /* Create_Spec.swift */; }; 13 | 15228CB302B7F8FD56C0BE5D /* Pods_ObjectMapperAdditions_Tests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DDEDDCD09D65355F843EFBC5 /* Pods_ObjectMapperAdditions_Tests.framework */; }; 14 | 607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD51AFB9204008FA782 /* AppDelegate.swift */; }; 15 | 607FACD81AFB9204008FA782 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACD71AFB9204008FA782 /* ViewController.swift */; }; 16 | 607FACDB1AFB9204008FA782 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 607FACD91AFB9204008FA782 /* Main.storyboard */; }; 17 | 607FACDD1AFB9204008FA782 /* Images.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDC1AFB9204008FA782 /* Images.xcassets */; }; 18 | 607FACE01AFB9204008FA782 /* LaunchScreen.xib in Resources */ = {isa = PBXBuildFile; fileRef = 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */; }; 19 | 607FACEC1AFB9204008FA782 /* TypeCast_Spec.swift in Sources */ = {isa = PBXBuildFile; fileRef = 607FACEB1AFB9204008FA782 /* TypeCast_Spec.swift */; }; 20 | 679BB5C7D1BC010448F5314B /* Pods_ObjectMapperAdditions_Example.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 7232033871E2CE8DA1F1FBD7 /* Pods_ObjectMapperAdditions_Example.framework */; }; 21 | B02194911F2BAB3200E228EB /* MyModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0B644791F2A07A100C3739E /* MyModel.swift */; }; 22 | B02194981F2BAF3700E228EB /* Realm_Spec.swift in Sources */ = {isa = PBXBuildFile; fileRef = B02194961F2BAE7B00E228EB /* Realm_Spec.swift */; }; 23 | B0B6447C1F2A07EC00C3739E /* MyOtherModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0B6447B1F2A07EC00C3739E /* MyOtherModel.swift */; }; 24 | B0B644811F2A11FC00C3739E /* MyOtherRealmModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0B6447F1F2A11FC00C3739E /* MyOtherRealmModel.swift */; }; 25 | B0B644821F2A11FC00C3739E /* MyRealmModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0B644801F2A11FC00C3739E /* MyRealmModel.swift */; }; 26 | B0D3A0331F7A54E90062AD2B /* TimestampTransform_Spec.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0D3A0311F7A54BC0062AD2B /* TimestampTransform_Spec.swift */; }; 27 | B0D3A03A1F7A5D740062AD2B /* ISO8601JustDateTransform_Spec.swift in Sources */ = {isa = PBXBuildFile; fileRef = B0D3A0381F7A5D710062AD2B /* ISO8601JustDateTransform_Spec.swift */; }; 28 | E28C98D72255F4AA00C5DA49 /* ExampleEnums.swift in Sources */ = {isa = PBXBuildFile; fileRef = E28C98D62255F4AA00C5DA49 /* ExampleEnums.swift */; }; 29 | E2D5E3942BC9231900E746C8 /* MyPersistedRealmModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2D5E3932BC9231900E746C8 /* MyPersistedRealmModel.swift */; }; 30 | E2EC16302DF5ABB500F22701 /* MongoDateTransform_Spec.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2EC162F2DF5ABB500F22701 /* MongoDateTransform_Spec.swift */; }; 31 | /* End PBXBuildFile section */ 32 | 33 | /* Begin PBXContainerItemProxy section */ 34 | 607FACE61AFB9204008FA782 /* PBXContainerItemProxy */ = { 35 | isa = PBXContainerItemProxy; 36 | containerPortal = 607FACC81AFB9204008FA782 /* Project object */; 37 | proxyType = 1; 38 | remoteGlobalIDString = 607FACCF1AFB9204008FA782; 39 | remoteInfo = ObjectMapperAdditions; 40 | }; 41 | /* End PBXContainerItemProxy section */ 42 | 43 | /* Begin PBXFileReference section */ 44 | 0D2D24CD2171632B006817DE /* DEPLOY PROCESS.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; name = "DEPLOY PROCESS.md"; path = "../DEPLOY PROCESS.md"; sourceTree = ""; }; 45 | 0D2D24CF21716F8F006817DE /* checkBuild.command */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; name = checkBuild.command; path = ../../checkBuild.command; sourceTree = ""; }; 46 | 0D716B7026285E2200B28330 /* Package.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = Package.swift; path = ../Package.swift; sourceTree = ""; }; 47 | 0DC249BA2737CA0F00623738 /* RealmPropertyTransform_Spec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RealmPropertyTransform_Spec.swift; sourceTree = ""; }; 48 | 0DC249BC2737CAED00623738 /* RealmPropertyTypeCastTransform_Spec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RealmPropertyTypeCastTransform_Spec.swift; sourceTree = ""; }; 49 | 0DDE964B278F046D00E346EA /* Create_Spec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Create_Spec.swift; sourceTree = ""; }; 50 | 1E6A5436B00ACF85E4A03951 /* podCheck.command */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; name = podCheck.command; path = ../../podCheck.command; sourceTree = ""; }; 51 | 3306BDD26A7024542AF11D3D /* ObjectMapperAdditions.podspec */ = {isa = PBXFileReference; includeInIndex = 1; indentWidth = 2; lastKnownFileType = text; name = ObjectMapperAdditions.podspec; path = ../ObjectMapperAdditions.podspec; sourceTree = ""; tabWidth = 2; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; 52 | 43260D3A6963AE16EDA55BBC /* Pods-ObjectMapperAdditions_Example.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ObjectMapperAdditions_Example.debug.xcconfig"; path = "Pods/Target Support Files/Pods-ObjectMapperAdditions_Example/Pods-ObjectMapperAdditions_Example.debug.xcconfig"; sourceTree = ""; }; 53 | 5CAD258AE7CB612216344F76 /* .gitignore */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = .gitignore; path = ../.gitignore; sourceTree = ""; }; 54 | 607FACD01AFB9204008FA782 /* ObjectMapperAdditions_Example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ObjectMapperAdditions_Example.app; sourceTree = BUILT_PRODUCTS_DIR; }; 55 | 607FACD41AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 56 | 607FACD51AFB9204008FA782 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 57 | 607FACD71AFB9204008FA782 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 58 | 607FACDA1AFB9204008FA782 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 59 | 607FACDC1AFB9204008FA782 /* Images.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Images.xcassets; sourceTree = ""; }; 60 | 607FACDF1AFB9204008FA782 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/LaunchScreen.xib; sourceTree = ""; }; 61 | 607FACE51AFB9204008FA782 /* ObjectMapperAdditions_Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ObjectMapperAdditions_Tests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 62 | 607FACEA1AFB9204008FA782 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 63 | 607FACEB1AFB9204008FA782 /* TypeCast_Spec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TypeCast_Spec.swift; sourceTree = ""; }; 64 | 6B6D68D9A0872088F633DCDF /* Pods-ObjectMapperAdditions_Tests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ObjectMapperAdditions_Tests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-ObjectMapperAdditions_Tests/Pods-ObjectMapperAdditions_Tests.debug.xcconfig"; sourceTree = ""; }; 65 | 7232033871E2CE8DA1F1FBD7 /* Pods_ObjectMapperAdditions_Example.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ObjectMapperAdditions_Example.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 66 | 7780827ABC350B91C5BFF6F0 /* Pods-ObjectMapperAdditions_Tests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ObjectMapperAdditions_Tests.release.xcconfig"; path = "Pods/Target Support Files/Pods-ObjectMapperAdditions_Tests/Pods-ObjectMapperAdditions_Tests.release.xcconfig"; sourceTree = ""; }; 67 | 9DFE7F9B21F6E89955E35AA5 /* LICENSE */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = LICENSE; path = ../LICENSE; sourceTree = ""; }; 68 | A7D8958764C84B431CB2CCEE /* README.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = README.md; path = ../README.md; sourceTree = ""; }; 69 | B02194961F2BAE7B00E228EB /* Realm_Spec.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Realm_Spec.swift; sourceTree = ""; }; 70 | B0B644791F2A07A100C3739E /* MyModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MyModel.swift; sourceTree = ""; }; 71 | B0B6447B1F2A07EC00C3739E /* MyOtherModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MyOtherModel.swift; sourceTree = ""; }; 72 | B0B6447F1F2A11FC00C3739E /* MyOtherRealmModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MyOtherRealmModel.swift; sourceTree = ""; }; 73 | B0B644801F2A11FC00C3739E /* MyRealmModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MyRealmModel.swift; sourceTree = ""; }; 74 | B0D3A0311F7A54BC0062AD2B /* TimestampTransform_Spec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TimestampTransform_Spec.swift; sourceTree = ""; }; 75 | B0D3A0381F7A5D710062AD2B /* ISO8601JustDateTransform_Spec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ISO8601JustDateTransform_Spec.swift; sourceTree = ""; }; 76 | C4D05D631BC1A7024D3C6249 /* .travis.yml */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = .travis.yml; path = ../.travis.yml; sourceTree = ""; }; 77 | CC7012B6B5FC05C395C0B729 /* Pods-ObjectMapperAdditions_Example.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-ObjectMapperAdditions_Example.release.xcconfig"; path = "Pods/Target Support Files/Pods-ObjectMapperAdditions_Example/Pods-ObjectMapperAdditions_Example.release.xcconfig"; sourceTree = ""; }; 78 | D46646B272B8B5A8181F666B /* .cocoadocs.yml */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; name = .cocoadocs.yml; path = ../.cocoadocs.yml; sourceTree = ""; }; 79 | D99D6198B7EB09C279219FB2 /* podPush.command */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; name = podPush.command; path = ../../podPush.command; sourceTree = ""; }; 80 | DDEDDCD09D65355F843EFBC5 /* Pods_ObjectMapperAdditions_Tests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_ObjectMapperAdditions_Tests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 81 | E28C98D62255F4AA00C5DA49 /* ExampleEnums.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ExampleEnums.swift; sourceTree = ""; }; 82 | E2D5E3922BC5760B00E746C8 /* packageUpdate.command */ = {isa = PBXFileReference; lastKnownFileType = text.script.sh; name = packageUpdate.command; path = ../../packageUpdate.command; sourceTree = ""; }; 83 | E2D5E3932BC9231900E746C8 /* MyPersistedRealmModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyPersistedRealmModel.swift; sourceTree = ""; }; 84 | E2EC162F2DF5ABB500F22701 /* MongoDateTransform_Spec.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MongoDateTransform_Spec.swift; sourceTree = ""; }; 85 | F253760DFAA3796C1818AA2F /* CHANGELOG.md */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = net.daringfireball.markdown; name = CHANGELOG.md; path = ../CHANGELOG.md; sourceTree = ""; }; 86 | /* End PBXFileReference section */ 87 | 88 | /* Begin PBXFrameworksBuildPhase section */ 89 | 607FACCD1AFB9204008FA782 /* Frameworks */ = { 90 | isa = PBXFrameworksBuildPhase; 91 | buildActionMask = 2147483647; 92 | files = ( 93 | 679BB5C7D1BC010448F5314B /* Pods_ObjectMapperAdditions_Example.framework in Frameworks */, 94 | ); 95 | runOnlyForDeploymentPostprocessing = 0; 96 | }; 97 | 607FACE21AFB9204008FA782 /* Frameworks */ = { 98 | isa = PBXFrameworksBuildPhase; 99 | buildActionMask = 2147483647; 100 | files = ( 101 | 15228CB302B7F8FD56C0BE5D /* Pods_ObjectMapperAdditions_Tests.framework in Frameworks */, 102 | ); 103 | runOnlyForDeploymentPostprocessing = 0; 104 | }; 105 | /* End PBXFrameworksBuildPhase section */ 106 | 107 | /* Begin PBXGroup section */ 108 | 607FACC71AFB9204008FA782 = { 109 | isa = PBXGroup; 110 | children = ( 111 | E27540EA2D9E8F3100CCE68F /* _Scripts */, 112 | 607FACF51AFB993E008FA782 /* Podspec Metadata */, 113 | 607FACD21AFB9204008FA782 /* Example for ObjectMapperAdditions */, 114 | 607FACE81AFB9204008FA782 /* Tests */, 115 | 607FACD11AFB9204008FA782 /* Products */, 116 | C656692AA65001FBC5451351 /* Pods */, 117 | EDA27D7F51BD1FF51FF63B95 /* Frameworks */, 118 | ); 119 | sourceTree = ""; 120 | }; 121 | 607FACD11AFB9204008FA782 /* Products */ = { 122 | isa = PBXGroup; 123 | children = ( 124 | 607FACD01AFB9204008FA782 /* ObjectMapperAdditions_Example.app */, 125 | 607FACE51AFB9204008FA782 /* ObjectMapperAdditions_Tests.xctest */, 126 | ); 127 | name = Products; 128 | sourceTree = ""; 129 | }; 130 | 607FACD21AFB9204008FA782 /* Example for ObjectMapperAdditions */ = { 131 | isa = PBXGroup; 132 | children = ( 133 | B06688E51F13EAB800A02878 /* _Gifs */, 134 | B06688E41F13EAB300A02878 /* _Images */, 135 | 607FACD31AFB9204008FA782 /* _Supporting Files */, 136 | 607FACD51AFB9204008FA782 /* AppDelegate.swift */, 137 | E28C98D62255F4AA00C5DA49 /* ExampleEnums.swift */, 138 | 607FACDC1AFB9204008FA782 /* Images.xcassets */, 139 | 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */, 140 | 607FACD91AFB9204008FA782 /* Main.storyboard */, 141 | B0B644791F2A07A100C3739E /* MyModel.swift */, 142 | B0B6447B1F2A07EC00C3739E /* MyOtherModel.swift */, 143 | B0B6447F1F2A11FC00C3739E /* MyOtherRealmModel.swift */, 144 | E2D5E3932BC9231900E746C8 /* MyPersistedRealmModel.swift */, 145 | B0B644801F2A11FC00C3739E /* MyRealmModel.swift */, 146 | 607FACD71AFB9204008FA782 /* ViewController.swift */, 147 | ); 148 | name = "Example for ObjectMapperAdditions"; 149 | path = ObjectMapperAdditions; 150 | sourceTree = ""; 151 | }; 152 | 607FACD31AFB9204008FA782 /* _Supporting Files */ = { 153 | isa = PBXGroup; 154 | children = ( 155 | 607FACD41AFB9204008FA782 /* Info.plist */, 156 | ); 157 | name = "_Supporting Files"; 158 | sourceTree = ""; 159 | }; 160 | 607FACE81AFB9204008FA782 /* Tests */ = { 161 | isa = PBXGroup; 162 | children = ( 163 | 607FACE91AFB9204008FA782 /* _Supporting Files */, 164 | 0DDE964B278F046D00E346EA /* Create_Spec.swift */, 165 | B0D3A0381F7A5D710062AD2B /* ISO8601JustDateTransform_Spec.swift */, 166 | E2EC162F2DF5ABB500F22701 /* MongoDateTransform_Spec.swift */, 167 | B02194961F2BAE7B00E228EB /* Realm_Spec.swift */, 168 | 0DC249BA2737CA0F00623738 /* RealmPropertyTransform_Spec.swift */, 169 | 0DC249BC2737CAED00623738 /* RealmPropertyTypeCastTransform_Spec.swift */, 170 | B0D3A0311F7A54BC0062AD2B /* TimestampTransform_Spec.swift */, 171 | 607FACEB1AFB9204008FA782 /* TypeCast_Spec.swift */, 172 | ); 173 | path = Tests; 174 | sourceTree = ""; 175 | }; 176 | 607FACE91AFB9204008FA782 /* _Supporting Files */ = { 177 | isa = PBXGroup; 178 | children = ( 179 | 607FACEA1AFB9204008FA782 /* Info.plist */, 180 | ); 181 | name = "_Supporting Files"; 182 | sourceTree = ""; 183 | }; 184 | 607FACF51AFB993E008FA782 /* Podspec Metadata */ = { 185 | isa = PBXGroup; 186 | children = ( 187 | D46646B272B8B5A8181F666B /* .cocoadocs.yml */, 188 | 5CAD258AE7CB612216344F76 /* .gitignore */, 189 | C4D05D631BC1A7024D3C6249 /* .travis.yml */, 190 | F253760DFAA3796C1818AA2F /* CHANGELOG.md */, 191 | 0D2D24CD2171632B006817DE /* DEPLOY PROCESS.md */, 192 | 9DFE7F9B21F6E89955E35AA5 /* LICENSE */, 193 | 3306BDD26A7024542AF11D3D /* ObjectMapperAdditions.podspec */, 194 | 0D716B7026285E2200B28330 /* Package.swift */, 195 | A7D8958764C84B431CB2CCEE /* README.md */, 196 | ); 197 | name = "Podspec Metadata"; 198 | sourceTree = ""; 199 | }; 200 | B06688E41F13EAB300A02878 /* _Images */ = { 201 | isa = PBXGroup; 202 | children = ( 203 | ); 204 | name = _Images; 205 | sourceTree = ""; 206 | }; 207 | B06688E51F13EAB800A02878 /* _Gifs */ = { 208 | isa = PBXGroup; 209 | children = ( 210 | ); 211 | name = _Gifs; 212 | sourceTree = ""; 213 | }; 214 | C656692AA65001FBC5451351 /* Pods */ = { 215 | isa = PBXGroup; 216 | children = ( 217 | 43260D3A6963AE16EDA55BBC /* Pods-ObjectMapperAdditions_Example.debug.xcconfig */, 218 | CC7012B6B5FC05C395C0B729 /* Pods-ObjectMapperAdditions_Example.release.xcconfig */, 219 | 6B6D68D9A0872088F633DCDF /* Pods-ObjectMapperAdditions_Tests.debug.xcconfig */, 220 | 7780827ABC350B91C5BFF6F0 /* Pods-ObjectMapperAdditions_Tests.release.xcconfig */, 221 | ); 222 | name = Pods; 223 | sourceTree = ""; 224 | }; 225 | E27540EA2D9E8F3100CCE68F /* _Scripts */ = { 226 | isa = PBXGroup; 227 | children = ( 228 | 0D2D24CF21716F8F006817DE /* checkBuild.command */, 229 | E2D5E3922BC5760B00E746C8 /* packageUpdate.command */, 230 | 1E6A5436B00ACF85E4A03951 /* podCheck.command */, 231 | D99D6198B7EB09C279219FB2 /* podPush.command */, 232 | ); 233 | path = _Scripts; 234 | sourceTree = ""; 235 | }; 236 | EDA27D7F51BD1FF51FF63B95 /* Frameworks */ = { 237 | isa = PBXGroup; 238 | children = ( 239 | 7232033871E2CE8DA1F1FBD7 /* Pods_ObjectMapperAdditions_Example.framework */, 240 | DDEDDCD09D65355F843EFBC5 /* Pods_ObjectMapperAdditions_Tests.framework */, 241 | ); 242 | name = Frameworks; 243 | sourceTree = ""; 244 | }; 245 | /* End PBXGroup section */ 246 | 247 | /* Begin PBXNativeTarget section */ 248 | 607FACCF1AFB9204008FA782 /* ObjectMapperAdditions_Example */ = { 249 | isa = PBXNativeTarget; 250 | buildConfigurationList = 607FACEF1AFB9204008FA782 /* Build configuration list for PBXNativeTarget "ObjectMapperAdditions_Example" */; 251 | buildPhases = ( 252 | 2E558D53848DEC446C9D6B93 /* [CP] Check Pods Manifest.lock */, 253 | 607FACCC1AFB9204008FA782 /* Sources */, 254 | 607FACCD1AFB9204008FA782 /* Frameworks */, 255 | 607FACCE1AFB9204008FA782 /* Resources */, 256 | 30DBA9CF11A05D1EBE3DC1EF /* [CP] Embed Pods Frameworks */, 257 | ); 258 | buildRules = ( 259 | ); 260 | dependencies = ( 261 | ); 262 | name = ObjectMapperAdditions_Example; 263 | productName = ObjectMapperAdditions; 264 | productReference = 607FACD01AFB9204008FA782 /* ObjectMapperAdditions_Example.app */; 265 | productType = "com.apple.product-type.application"; 266 | }; 267 | 607FACE41AFB9204008FA782 /* ObjectMapperAdditions_Tests */ = { 268 | isa = PBXNativeTarget; 269 | buildConfigurationList = 607FACF21AFB9204008FA782 /* Build configuration list for PBXNativeTarget "ObjectMapperAdditions_Tests" */; 270 | buildPhases = ( 271 | B5B2F63AD54CECD6335D733B /* [CP] Check Pods Manifest.lock */, 272 | 607FACE11AFB9204008FA782 /* Sources */, 273 | 607FACE21AFB9204008FA782 /* Frameworks */, 274 | 607FACE31AFB9204008FA782 /* Resources */, 275 | 3A0E6AEB31E9F1C2B6EB8F85 /* [CP] Embed Pods Frameworks */, 276 | ); 277 | buildRules = ( 278 | ); 279 | dependencies = ( 280 | 607FACE71AFB9204008FA782 /* PBXTargetDependency */, 281 | ); 282 | name = ObjectMapperAdditions_Tests; 283 | productName = Tests; 284 | productReference = 607FACE51AFB9204008FA782 /* ObjectMapperAdditions_Tests.xctest */; 285 | productType = "com.apple.product-type.bundle.unit-test"; 286 | }; 287 | /* End PBXNativeTarget section */ 288 | 289 | /* Begin PBXProject section */ 290 | 607FACC81AFB9204008FA782 /* Project object */ = { 291 | isa = PBXProject; 292 | attributes = { 293 | BuildIndependentTargetsInParallel = YES; 294 | LastSwiftUpdateCheck = 0720; 295 | LastUpgradeCheck = 1640; 296 | ORGANIZATIONNAME = CocoaPods; 297 | TargetAttributes = { 298 | 607FACCF1AFB9204008FA782 = { 299 | CreatedOnToolsVersion = 6.3.1; 300 | LastSwiftMigration = 1020; 301 | }; 302 | 607FACE41AFB9204008FA782 = { 303 | CreatedOnToolsVersion = 6.3.1; 304 | LastSwiftMigration = 1020; 305 | TestTargetID = 607FACCF1AFB9204008FA782; 306 | }; 307 | }; 308 | }; 309 | buildConfigurationList = 607FACCB1AFB9204008FA782 /* Build configuration list for PBXProject "ObjectMapperAdditions" */; 310 | compatibilityVersion = "Xcode 3.2"; 311 | developmentRegion = en; 312 | hasScannedForEncodings = 0; 313 | knownRegions = ( 314 | en, 315 | Base, 316 | ); 317 | mainGroup = 607FACC71AFB9204008FA782; 318 | productRefGroup = 607FACD11AFB9204008FA782 /* Products */; 319 | projectDirPath = ""; 320 | projectRoot = ""; 321 | targets = ( 322 | 607FACCF1AFB9204008FA782 /* ObjectMapperAdditions_Example */, 323 | 607FACE41AFB9204008FA782 /* ObjectMapperAdditions_Tests */, 324 | ); 325 | }; 326 | /* End PBXProject section */ 327 | 328 | /* Begin PBXResourcesBuildPhase section */ 329 | 607FACCE1AFB9204008FA782 /* Resources */ = { 330 | isa = PBXResourcesBuildPhase; 331 | buildActionMask = 2147483647; 332 | files = ( 333 | 607FACDB1AFB9204008FA782 /* Main.storyboard in Resources */, 334 | 607FACE01AFB9204008FA782 /* LaunchScreen.xib in Resources */, 335 | 607FACDD1AFB9204008FA782 /* Images.xcassets in Resources */, 336 | ); 337 | runOnlyForDeploymentPostprocessing = 0; 338 | }; 339 | 607FACE31AFB9204008FA782 /* Resources */ = { 340 | isa = PBXResourcesBuildPhase; 341 | buildActionMask = 2147483647; 342 | files = ( 343 | ); 344 | runOnlyForDeploymentPostprocessing = 0; 345 | }; 346 | /* End PBXResourcesBuildPhase section */ 347 | 348 | /* Begin PBXShellScriptBuildPhase section */ 349 | 2E558D53848DEC446C9D6B93 /* [CP] Check Pods Manifest.lock */ = { 350 | isa = PBXShellScriptBuildPhase; 351 | buildActionMask = 2147483647; 352 | files = ( 353 | ); 354 | inputPaths = ( 355 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 356 | "${PODS_ROOT}/Manifest.lock", 357 | ); 358 | name = "[CP] Check Pods Manifest.lock"; 359 | outputPaths = ( 360 | "$(DERIVED_FILE_DIR)/Pods-ObjectMapperAdditions_Example-checkManifestLockResult.txt", 361 | ); 362 | runOnlyForDeploymentPostprocessing = 0; 363 | shellPath = /bin/sh; 364 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 365 | showEnvVarsInLog = 0; 366 | }; 367 | 30DBA9CF11A05D1EBE3DC1EF /* [CP] Embed Pods Frameworks */ = { 368 | isa = PBXShellScriptBuildPhase; 369 | buildActionMask = 2147483647; 370 | files = ( 371 | ); 372 | inputPaths = ( 373 | "${PODS_ROOT}/Target Support Files/Pods-ObjectMapperAdditions_Example/Pods-ObjectMapperAdditions_Example-frameworks.sh", 374 | "${BUILT_PRODUCTS_DIR}/APExtensions/APExtensions.framework", 375 | "${BUILT_PRODUCTS_DIR}/ObjectMapper/ObjectMapper.framework", 376 | "${BUILT_PRODUCTS_DIR}/ObjectMapperAdditions/ObjectMapperAdditions.framework", 377 | "${BUILT_PRODUCTS_DIR}/Realm/Realm.framework", 378 | "${BUILT_PRODUCTS_DIR}/RealmSwift/RealmSwift.framework", 379 | "${BUILT_PRODUCTS_DIR}/RoutableLogger/RoutableLogger.framework", 380 | ); 381 | name = "[CP] Embed Pods Frameworks"; 382 | outputPaths = ( 383 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/APExtensions.framework", 384 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ObjectMapper.framework", 385 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/ObjectMapperAdditions.framework", 386 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Realm.framework", 387 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RealmSwift.framework", 388 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RoutableLogger.framework", 389 | ); 390 | runOnlyForDeploymentPostprocessing = 0; 391 | shellPath = /bin/sh; 392 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ObjectMapperAdditions_Example/Pods-ObjectMapperAdditions_Example-frameworks.sh\"\n"; 393 | showEnvVarsInLog = 0; 394 | }; 395 | 3A0E6AEB31E9F1C2B6EB8F85 /* [CP] Embed Pods Frameworks */ = { 396 | isa = PBXShellScriptBuildPhase; 397 | buildActionMask = 2147483647; 398 | files = ( 399 | ); 400 | inputPaths = ( 401 | "${PODS_ROOT}/Target Support Files/Pods-ObjectMapperAdditions_Tests/Pods-ObjectMapperAdditions_Tests-frameworks.sh", 402 | "${BUILT_PRODUCTS_DIR}/CwlCatchException/CwlCatchException.framework", 403 | "${BUILT_PRODUCTS_DIR}/CwlCatchExceptionSupport/CwlCatchExceptionSupport.framework", 404 | "${BUILT_PRODUCTS_DIR}/CwlMachBadInstructionHandler/CwlMachBadInstructionHandler.framework", 405 | "${BUILT_PRODUCTS_DIR}/CwlPosixPreconditionTesting/CwlPosixPreconditionTesting.framework", 406 | "${BUILT_PRODUCTS_DIR}/CwlPreconditionTesting/CwlPreconditionTesting.framework", 407 | "${BUILT_PRODUCTS_DIR}/Nimble/Nimble.framework", 408 | "${BUILT_PRODUCTS_DIR}/Quick/Quick.framework", 409 | ); 410 | name = "[CP] Embed Pods Frameworks"; 411 | outputPaths = ( 412 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CwlCatchException.framework", 413 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CwlCatchExceptionSupport.framework", 414 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CwlMachBadInstructionHandler.framework", 415 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CwlPosixPreconditionTesting.framework", 416 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CwlPreconditionTesting.framework", 417 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Nimble.framework", 418 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Quick.framework", 419 | ); 420 | runOnlyForDeploymentPostprocessing = 0; 421 | shellPath = /bin/sh; 422 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-ObjectMapperAdditions_Tests/Pods-ObjectMapperAdditions_Tests-frameworks.sh\"\n"; 423 | showEnvVarsInLog = 0; 424 | }; 425 | B5B2F63AD54CECD6335D733B /* [CP] Check Pods Manifest.lock */ = { 426 | isa = PBXShellScriptBuildPhase; 427 | buildActionMask = 2147483647; 428 | files = ( 429 | ); 430 | inputPaths = ( 431 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 432 | "${PODS_ROOT}/Manifest.lock", 433 | ); 434 | name = "[CP] Check Pods Manifest.lock"; 435 | outputPaths = ( 436 | "$(DERIVED_FILE_DIR)/Pods-ObjectMapperAdditions_Tests-checkManifestLockResult.txt", 437 | ); 438 | runOnlyForDeploymentPostprocessing = 0; 439 | shellPath = /bin/sh; 440 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 441 | showEnvVarsInLog = 0; 442 | }; 443 | /* End PBXShellScriptBuildPhase section */ 444 | 445 | /* Begin PBXSourcesBuildPhase section */ 446 | 607FACCC1AFB9204008FA782 /* Sources */ = { 447 | isa = PBXSourcesBuildPhase; 448 | buildActionMask = 2147483647; 449 | files = ( 450 | E28C98D72255F4AA00C5DA49 /* ExampleEnums.swift in Sources */, 451 | 607FACD81AFB9204008FA782 /* ViewController.swift in Sources */, 452 | B02194911F2BAB3200E228EB /* MyModel.swift in Sources */, 453 | E2D5E3942BC9231900E746C8 /* MyPersistedRealmModel.swift in Sources */, 454 | B0B6447C1F2A07EC00C3739E /* MyOtherModel.swift in Sources */, 455 | B0B644821F2A11FC00C3739E /* MyRealmModel.swift in Sources */, 456 | B0B644811F2A11FC00C3739E /* MyOtherRealmModel.swift in Sources */, 457 | 607FACD61AFB9204008FA782 /* AppDelegate.swift in Sources */, 458 | ); 459 | runOnlyForDeploymentPostprocessing = 0; 460 | }; 461 | 607FACE11AFB9204008FA782 /* Sources */ = { 462 | isa = PBXSourcesBuildPhase; 463 | buildActionMask = 2147483647; 464 | files = ( 465 | 0DDE964C278F046D00E346EA /* Create_Spec.swift in Sources */, 466 | B02194981F2BAF3700E228EB /* Realm_Spec.swift in Sources */, 467 | B0D3A0331F7A54E90062AD2B /* TimestampTransform_Spec.swift in Sources */, 468 | B0D3A03A1F7A5D740062AD2B /* ISO8601JustDateTransform_Spec.swift in Sources */, 469 | E2EC16302DF5ABB500F22701 /* MongoDateTransform_Spec.swift in Sources */, 470 | 607FACEC1AFB9204008FA782 /* TypeCast_Spec.swift in Sources */, 471 | 0DC249BB2737CA0F00623738 /* RealmPropertyTransform_Spec.swift in Sources */, 472 | 0DC249BD2737CAED00623738 /* RealmPropertyTypeCastTransform_Spec.swift in Sources */, 473 | ); 474 | runOnlyForDeploymentPostprocessing = 0; 475 | }; 476 | /* End PBXSourcesBuildPhase section */ 477 | 478 | /* Begin PBXTargetDependency section */ 479 | 607FACE71AFB9204008FA782 /* PBXTargetDependency */ = { 480 | isa = PBXTargetDependency; 481 | target = 607FACCF1AFB9204008FA782 /* ObjectMapperAdditions_Example */; 482 | targetProxy = 607FACE61AFB9204008FA782 /* PBXContainerItemProxy */; 483 | }; 484 | /* End PBXTargetDependency section */ 485 | 486 | /* Begin PBXVariantGroup section */ 487 | 607FACD91AFB9204008FA782 /* Main.storyboard */ = { 488 | isa = PBXVariantGroup; 489 | children = ( 490 | 607FACDA1AFB9204008FA782 /* Base */, 491 | ); 492 | name = Main.storyboard; 493 | sourceTree = ""; 494 | }; 495 | 607FACDE1AFB9204008FA782 /* LaunchScreen.xib */ = { 496 | isa = PBXVariantGroup; 497 | children = ( 498 | 607FACDF1AFB9204008FA782 /* Base */, 499 | ); 500 | name = LaunchScreen.xib; 501 | sourceTree = ""; 502 | }; 503 | /* End PBXVariantGroup section */ 504 | 505 | /* Begin XCBuildConfiguration section */ 506 | 607FACED1AFB9204008FA782 /* Debug */ = { 507 | isa = XCBuildConfiguration; 508 | buildSettings = { 509 | ALWAYS_SEARCH_USER_PATHS = NO; 510 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 511 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 512 | CLANG_CXX_LIBRARY = "libc++"; 513 | CLANG_ENABLE_MODULES = YES; 514 | CLANG_ENABLE_OBJC_ARC = YES; 515 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 516 | CLANG_WARN_BOOL_CONVERSION = YES; 517 | CLANG_WARN_COMMA = YES; 518 | CLANG_WARN_CONSTANT_CONVERSION = YES; 519 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 520 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 521 | CLANG_WARN_EMPTY_BODY = YES; 522 | CLANG_WARN_ENUM_CONVERSION = YES; 523 | CLANG_WARN_INFINITE_RECURSION = YES; 524 | CLANG_WARN_INT_CONVERSION = YES; 525 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 526 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 527 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 528 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 529 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 530 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 531 | CLANG_WARN_STRICT_PROTOTYPES = YES; 532 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 533 | CLANG_WARN_UNREACHABLE_CODE = YES; 534 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 535 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 536 | COPY_PHASE_STRIP = NO; 537 | DEBUG_INFORMATION_FORMAT = dwarf; 538 | ENABLE_STRICT_OBJC_MSGSEND = YES; 539 | ENABLE_TESTABILITY = YES; 540 | GCC_C_LANGUAGE_STANDARD = gnu99; 541 | GCC_DYNAMIC_NO_PIC = NO; 542 | GCC_NO_COMMON_BLOCKS = YES; 543 | GCC_OPTIMIZATION_LEVEL = 0; 544 | GCC_PREPROCESSOR_DEFINITIONS = ( 545 | "DEBUG=1", 546 | "$(inherited)", 547 | ); 548 | GCC_SYMBOLS_PRIVATE_EXTERN = NO; 549 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 550 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 551 | GCC_WARN_UNDECLARED_SELECTOR = YES; 552 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 553 | GCC_WARN_UNUSED_FUNCTION = YES; 554 | GCC_WARN_UNUSED_VARIABLE = YES; 555 | IPHONEOS_DEPLOYMENT_TARGET = 15.6; 556 | MACOSX_DEPLOYMENT_TARGET = 11.5; 557 | MTL_ENABLE_DEBUG_INFO = YES; 558 | ONLY_ACTIVE_ARCH = YES; 559 | SDKROOT = iphoneos; 560 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 561 | SWIFT_VERSION = 5.0; 562 | TVOS_DEPLOYMENT_TARGET = 15.6; 563 | WATCHOS_DEPLOYMENT_TARGET = 8.7; 564 | }; 565 | name = Debug; 566 | }; 567 | 607FACEE1AFB9204008FA782 /* Release */ = { 568 | isa = XCBuildConfiguration; 569 | buildSettings = { 570 | ALWAYS_SEARCH_USER_PATHS = NO; 571 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 572 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 573 | CLANG_CXX_LIBRARY = "libc++"; 574 | CLANG_ENABLE_MODULES = YES; 575 | CLANG_ENABLE_OBJC_ARC = YES; 576 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 577 | CLANG_WARN_BOOL_CONVERSION = YES; 578 | CLANG_WARN_COMMA = YES; 579 | CLANG_WARN_CONSTANT_CONVERSION = YES; 580 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 581 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 582 | CLANG_WARN_EMPTY_BODY = YES; 583 | CLANG_WARN_ENUM_CONVERSION = YES; 584 | CLANG_WARN_INFINITE_RECURSION = YES; 585 | CLANG_WARN_INT_CONVERSION = YES; 586 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 587 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 588 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 589 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 590 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 591 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 592 | CLANG_WARN_STRICT_PROTOTYPES = YES; 593 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 594 | CLANG_WARN_UNREACHABLE_CODE = YES; 595 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 596 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 597 | COPY_PHASE_STRIP = NO; 598 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 599 | ENABLE_NS_ASSERTIONS = NO; 600 | ENABLE_STRICT_OBJC_MSGSEND = YES; 601 | GCC_C_LANGUAGE_STANDARD = gnu99; 602 | GCC_NO_COMMON_BLOCKS = YES; 603 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 604 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 605 | GCC_WARN_UNDECLARED_SELECTOR = YES; 606 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 607 | GCC_WARN_UNUSED_FUNCTION = YES; 608 | GCC_WARN_UNUSED_VARIABLE = YES; 609 | IPHONEOS_DEPLOYMENT_TARGET = 15.6; 610 | MACOSX_DEPLOYMENT_TARGET = 11.5; 611 | MTL_ENABLE_DEBUG_INFO = NO; 612 | SDKROOT = iphoneos; 613 | SWIFT_COMPILATION_MODE = wholemodule; 614 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 615 | SWIFT_VERSION = 5.0; 616 | TVOS_DEPLOYMENT_TARGET = 15.6; 617 | VALIDATE_PRODUCT = YES; 618 | WATCHOS_DEPLOYMENT_TARGET = 8.7; 619 | }; 620 | name = Release; 621 | }; 622 | 607FACF01AFB9204008FA782 /* Debug */ = { 623 | isa = XCBuildConfiguration; 624 | baseConfigurationReference = 43260D3A6963AE16EDA55BBC /* Pods-ObjectMapperAdditions_Example.debug.xcconfig */; 625 | buildSettings = { 626 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 627 | INFOPLIST_FILE = ObjectMapperAdditions/Info.plist; 628 | LD_RUNPATH_SEARCH_PATHS = ( 629 | "$(inherited)", 630 | "@executable_path/Frameworks", 631 | ); 632 | MODULE_NAME = ExampleApp; 633 | PRODUCT_BUNDLE_IDENTIFIER = "com.anton-plebanovich.ObjectMapperAdditions-Example${DEVELOPMENT_TEAM}"; 634 | PRODUCT_NAME = "$(TARGET_NAME)"; 635 | }; 636 | name = Debug; 637 | }; 638 | 607FACF11AFB9204008FA782 /* Release */ = { 639 | isa = XCBuildConfiguration; 640 | baseConfigurationReference = CC7012B6B5FC05C395C0B729 /* Pods-ObjectMapperAdditions_Example.release.xcconfig */; 641 | buildSettings = { 642 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 643 | INFOPLIST_FILE = ObjectMapperAdditions/Info.plist; 644 | LD_RUNPATH_SEARCH_PATHS = ( 645 | "$(inherited)", 646 | "@executable_path/Frameworks", 647 | ); 648 | MODULE_NAME = ExampleApp; 649 | PRODUCT_BUNDLE_IDENTIFIER = "com.anton-plebanovich.ObjectMapperAdditions-Example${DEVELOPMENT_TEAM}"; 650 | PRODUCT_NAME = "$(TARGET_NAME)"; 651 | }; 652 | name = Release; 653 | }; 654 | 607FACF31AFB9204008FA782 /* Debug */ = { 655 | isa = XCBuildConfiguration; 656 | baseConfigurationReference = 6B6D68D9A0872088F633DCDF /* Pods-ObjectMapperAdditions_Tests.debug.xcconfig */; 657 | buildSettings = { 658 | BUNDLE_LOADER = "$(TEST_HOST)"; 659 | FRAMEWORK_SEARCH_PATHS = "$(inherited)"; 660 | GCC_PREPROCESSOR_DEFINITIONS = ( 661 | "DEBUG=1", 662 | "$(inherited)", 663 | ); 664 | INFOPLIST_FILE = Tests/Info.plist; 665 | LD_RUNPATH_SEARCH_PATHS = ( 666 | "$(inherited)", 667 | "@executable_path/Frameworks", 668 | "@loader_path/Frameworks", 669 | ); 670 | PRODUCT_BUNDLE_IDENTIFIER = "com.anton-plebanovich.$(PRODUCT_NAME:rfc1034identifier)${DEVELOPMENT_TEAM}"; 671 | PRODUCT_NAME = "$(TARGET_NAME)"; 672 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ObjectMapperAdditions_Example.app/ObjectMapperAdditions_Example"; 673 | }; 674 | name = Debug; 675 | }; 676 | 607FACF41AFB9204008FA782 /* Release */ = { 677 | isa = XCBuildConfiguration; 678 | baseConfigurationReference = 7780827ABC350B91C5BFF6F0 /* Pods-ObjectMapperAdditions_Tests.release.xcconfig */; 679 | buildSettings = { 680 | BUNDLE_LOADER = "$(TEST_HOST)"; 681 | FRAMEWORK_SEARCH_PATHS = "$(inherited)"; 682 | INFOPLIST_FILE = Tests/Info.plist; 683 | LD_RUNPATH_SEARCH_PATHS = ( 684 | "$(inherited)", 685 | "@executable_path/Frameworks", 686 | "@loader_path/Frameworks", 687 | ); 688 | PRODUCT_BUNDLE_IDENTIFIER = "com.anton-plebanovich.$(PRODUCT_NAME:rfc1034identifier)${DEVELOPMENT_TEAM}"; 689 | PRODUCT_NAME = "$(TARGET_NAME)"; 690 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ObjectMapperAdditions_Example.app/ObjectMapperAdditions_Example"; 691 | }; 692 | name = Release; 693 | }; 694 | /* End XCBuildConfiguration section */ 695 | 696 | /* Begin XCConfigurationList section */ 697 | 607FACCB1AFB9204008FA782 /* Build configuration list for PBXProject "ObjectMapperAdditions" */ = { 698 | isa = XCConfigurationList; 699 | buildConfigurations = ( 700 | 607FACED1AFB9204008FA782 /* Debug */, 701 | 607FACEE1AFB9204008FA782 /* Release */, 702 | ); 703 | defaultConfigurationIsVisible = 0; 704 | defaultConfigurationName = Release; 705 | }; 706 | 607FACEF1AFB9204008FA782 /* Build configuration list for PBXNativeTarget "ObjectMapperAdditions_Example" */ = { 707 | isa = XCConfigurationList; 708 | buildConfigurations = ( 709 | 607FACF01AFB9204008FA782 /* Debug */, 710 | 607FACF11AFB9204008FA782 /* Release */, 711 | ); 712 | defaultConfigurationIsVisible = 0; 713 | defaultConfigurationName = Release; 714 | }; 715 | 607FACF21AFB9204008FA782 /* Build configuration list for PBXNativeTarget "ObjectMapperAdditions_Tests" */ = { 716 | isa = XCConfigurationList; 717 | buildConfigurations = ( 718 | 607FACF31AFB9204008FA782 /* Debug */, 719 | 607FACF41AFB9204008FA782 /* Release */, 720 | ); 721 | defaultConfigurationIsVisible = 0; 722 | defaultConfigurationName = Release; 723 | }; 724 | /* End XCConfigurationList section */ 725 | }; 726 | rootObject = 607FACC81AFB9204008FA782 /* Project object */; 727 | } 728 | --------------------------------------------------------------------------------