├── 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 | [](https://github.com/apple/swift-package-manager)
4 | [](http://cocoapods.org/pods/ObjectMapperAdditions)
5 | [](http://cocoapods.org/pods/ObjectMapperAdditions)
6 | [](http://cocoapods.org/pods/ObjectMapperAdditions)
7 | [](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 |
--------------------------------------------------------------------------------