Skip to content
This repository has been archived by the owner on May 17, 2024. It is now read-only.

Commit

Permalink
Add function to create array of RawRepresentables
Browse files Browse the repository at this point in the history
This enables you to easily take an array in the source data, and get a
strongly typed array of RawRepresentable values. This is heavily
documented to make as obvious as possible the fact that we are
discarding (or replacing) unknown values.
  • Loading branch information
keith committed Jul 13, 2016
1 parent b738e35 commit e3ff899
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 2 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,9 @@
- Add `@noescape` to transformation closures
[Keith Smiley](https://github.com/keith)
[#60](https://github.com/lyft/mapper/pull/60)
- Add `from` for arrays of `RawRepresentable`s
[Keith Smiley](https://github.com/keith)
[#61](https://github.com/lyft/mapper/pull/61)

## Bug Fixes

Expand Down
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ install-tvOS:
true

install-lint:
brew remove swiftlint --force || true
brew install https://raw.githubusercontent.com/Homebrew/homebrew/fffa4b271ba57c7633e8e24cae543a197a9e3e01/Library/Formula/swiftlint.rb

install-carthage:
Expand Down
31 changes: 29 additions & 2 deletions Sources/Mapper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,33 @@ public struct Mapper {
return nil
}

/**
Get an array of RawRepresentable values from a field in the the source data.
- note: If T.init(rawValue:) fails given the T.RawValue from the array of source data, that value will be
replaced by the passed defaultValue, which defaults to nil. The resulting array is flatMapped and
all nils are removed. This means that any unrecognized values will be removed or replaced with a
default. This ensures backwards compatibility if your source data has keys that your mapping
layer doesn't know about yet.
- parameter field: The field to use from the source data
- parameter defaultValue: The value to use if the rawValue initializer fails
- returns: An array of the RawRepresentable value, with all nils removed
*/
@warn_unused_result
public func from<T: RawRepresentable where T.RawValue: Convertible,
T.RawValue == T.RawValue.ConvertedType>(field: String, defaultValue: T? = nil) throws -> [T]
{
let value = try self.JSONFromField(field)
guard let array = value as? [AnyObject] else {
throw MapperError.TypeMismatchError(field: field, value: value, type: [AnyObject].self)
}

let rawValues = try array.map { try T.RawValue.fromMap($0) }
return rawValues.flatMap { T(rawValue: $0) ?? defaultValue }
}

// MARK: - T: Mappable

/**
Expand Down Expand Up @@ -112,7 +139,7 @@ public struct Mapper {
This allows you to transparently have nested arrays of Mappable values
Note: If any value in the array of NSDictionaries is invalid, this method throws
- note: If any value in the array of NSDictionaries is invalid, this method throws
- parameter field: The field to retrieve from the source data, can be an empty string to return the
entire data set
Expand Down Expand Up @@ -153,7 +180,7 @@ public struct Mapper {
This allows you to transparently have nested arrays of Mappable values
Note: If any value in the provided array of NSDictionaries is invalid, this method returns nil
- note: If any value in the provided array of NSDictionaries is invalid, this method returns nil
- parameter field: The field to retrieve from the source data, can be an empty string to return the
entire data set
Expand Down
111 changes: 111 additions & 0 deletions Tests/Mapper/RawRepresentibleValueTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -129,4 +129,115 @@ final class RawRepresentibleValueTests: XCTestCase {
let test = try! Test(map: Mapper(JSON: [:]))
XCTAssertNil(test.value)
}

func testArrayOfValuesWithMissingKey() {
struct Test: Mappable {
let value: [Value]
init(map: Mapper) throws {
self.value = try map.from("a")
}
}

enum Value: String {
case First = "hi"
}

do {
_ = try Test(map: Mapper(JSON: [:]))
XCTFail("Expected initialization to fail")
} catch MapperError.MissingFieldError(let field) {
XCTAssertEqual(field, "a")
} catch let error {
XCTFail("Expected only missing field error, got \(error)")
}
}

func testArrayOfValuesInvalidArray() {
struct Test: Mappable {
let values: [Value]
init(map: Mapper) throws {
self.values = try map.from("a")
}
}

enum Value: String {
case First = "hi"
}

do {
_ = try Test(map: Mapper(JSON: ["a": 1]))
XCTFail("Expected initialization to fail")
} catch MapperError.TypeMismatchError(let field, let value, let type) {
XCTAssertEqual(field, "a")
XCTAssertEqual(value as? Int, 1)
XCTAssert(type == [AnyObject].self)
} catch let error {
XCTFail("Expected only missing field error, got \(error)")
}
}

func testArrayOfValuesFailedConvertible() {
struct Test: Mappable {
let values: [Value]
init(map: Mapper) throws {
self.values = try map.from("a")
}
}

enum Value: String {
case First = "hi"
}

do {
_ = try Test(map: Mapper(JSON: ["a": [1]]))
XCTFail("Expected initialization to fail")
} catch MapperError.ConvertibleError(let value, let type) {
XCTAssertEqual(value as? Int, 1)
XCTAssert(type == String.self)
} catch let error {
XCTFail("Expected only missing field error, got \(error)")
}
}

func testArrayOfValuesFiltersNilsWithoutDefault() {
struct Test: Mappable {
let values: [Value]
init(map: Mapper) throws {
self.values = try map.from("a")
}
}

enum Value: String {
case First = "hi"
}

do {
let test = try Test(map: Mapper(JSON: ["a": ["hi", "invalid"]]))
XCTAssertEqual(test.values.count, 1)
XCTAssert(test.values.contains(.First))
} catch let error {
XCTFail("Expected no errors, got \(error)")
}
}

func testArrayOfValuesInsertsDefault() {
struct Test: Mappable {
let values: [Value]
init(map: Mapper) throws {
self.values = try map.from("a", defaultValue: .First)
}
}

enum Value: String {
case First = "hi"
}

do {
let test = try Test(map: Mapper(JSON: ["a": ["invalid"]]))
XCTAssertEqual(test.values.count, 1)
XCTAssert(test.values.contains(.First))
} catch let error {
XCTFail("Expected no errors, got \(error)")
}
}
}

0 comments on commit e3ff899

Please sign in to comment.