Skip to content

Commit

Permalink
Merge pull request JohnSundell#83 from mikezs/feature/array_with_form…
Browse files Browse the repository at this point in the history
…atter

Feature: Unbox an Array with a formatter and optionally allowing invalid values
  • Loading branch information
JohnSundell authored Jul 1, 2016
2 parents 2b53588 + f813026 commit 90403f7
Show file tree
Hide file tree
Showing 2 changed files with 146 additions and 5 deletions.
39 changes: 39 additions & 0 deletions Sources/Unbox.swift
Original file line number Diff line number Diff line change
Expand Up @@ -692,6 +692,45 @@ public class Unboxer {
})
}

/// Unbox a required Array containing values that can be formatted using a formatter (optionally allowing invalid elements)
public func unbox<T: UnboxableWithFormatter, F: UnboxFormatter where F.UnboxFormattedType == T>(key: String, isKeyPath: Bool = true, formatter: F, allowInvalidElements: Bool = false) -> [T] {
return UnboxValueResolver<[F.UnboxRawValueType]>(self).resolveRequiredValueForKey(key, isKeyPath: isKeyPath, fallbackValue: [], transform: { (array) -> [T]? in
if allowInvalidElements {
return array.flatMap({ formatter.formatUnboxedValue($0) })
} else {
return array.map({ (value) -> T in
if let formattedValue = formatter.formatUnboxedValue(value) {
return formattedValue
}

self.failForInvalidValue(value, forKey: key)
return T.unboxFallbackValue()
})
}
})
}

/// Unbox an optional Array containing values that can be formatted using a formatter (optionally allowing invalid elements)
public func unbox<T: UnboxableWithFormatter, F: UnboxFormatter where F.UnboxFormattedType == T>(key: String, isKeyPath: Bool = true, formatter: F, allowInvalidElements: Bool = false) -> [T]? {
return UnboxValueResolver<[F.UnboxRawValueType]>(self).resolveOptionalValueForKey(key, isKeyPath: isKeyPath, transform: { (array) -> [T]? in
if allowInvalidElements {
return array.flatMap({ formatter.formatUnboxedValue($0) })
} else {
var formattedArray = [T]()

for value in array {
guard let formattedValue = formatter.formatUnboxedValue(value) else {
return nil
}

formattedArray.append(formattedValue)
}

return formattedArray
}
})
}

/// Make this Unboxer fail for a certain key. This will cause the `Unbox()` function that triggered this Unboxer to return `nil`.
public func failForKey(key: String) {
self.failForInvalidValue(nil, forKey: key)
Expand Down
112 changes: 107 additions & 5 deletions Tests/UnboxTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -205,35 +205,98 @@ class UnboxTests: XCTestCase {
func testRequiredDateFormatting() {
struct Model: Unboxable {
let date: NSDate
let dateArray: [NSDate]

init(unboxer: Unboxer) {
let formatter = NSDateFormatter()
formatter.dateFormat = "YYYY-MM-dd"
self.date = unboxer.unbox("date", formatter: formatter)
self.dateArray = unboxer.unbox("dateArray", formatter: formatter)
}
}

struct AllowInvalidElementsModel: Unboxable {
let date: NSDate
let dateArray: [NSDate]

init(unboxer: Unboxer) {
let formatter = NSDateFormatter()
formatter.dateFormat = "YYYY-MM-dd"
self.date = unboxer.unbox("date", formatter: formatter)
self.dateArray = unboxer.unbox("dateArray", formatter: formatter, allowInvalidElements: true)
}
}

let dictionary: UnboxableDictionary = [
"date" : "2015-12-15"
"date" : "2015-12-15",
"dateArray" : ["2015-12-15"]
]

// Valid tests:

do {
let unboxed: Model = try Unbox(dictionary)

let calendar = NSCalendar.currentCalendar()
XCTAssertEqual(calendar.component(.Year, fromDate: unboxed.date), 2015)
XCTAssertEqual(calendar.component(.Month, fromDate: unboxed.date), 12)
XCTAssertEqual(calendar.component(.Day, fromDate: unboxed.date), 15)

if let firstDate = unboxed.dateArray.first {
XCTAssertEqual(calendar.component(.Year, fromDate: firstDate), 2015)
XCTAssertEqual(calendar.component(.Month, fromDate: firstDate), 12)
XCTAssertEqual(calendar.component(.Day, fromDate: firstDate), 15)
} else {
XCTFail("Array empty")
}

} catch {
XCTFail("\(error)")
}

do {
let invalidDictionary: UnboxableDictionary = [
"date" : "2015-12-tuesday"
let invalidValueDateArrayDictionary: UnboxableDictionary = [
"date" : "2015-12-15",
"dateArray" : ["2015-12-tuesday", "2015-12-15"]
]

let unboxed: AllowInvalidElementsModel = try Unbox(invalidValueDateArrayDictionary)

XCTAssertEqual(unboxed.dateArray.count, 1)

if let firstDate = unboxed.dateArray.first {
let calendar = NSCalendar.currentCalendar()
XCTAssertEqual(calendar.component(.Year, fromDate: firstDate), 2015)
XCTAssertEqual(calendar.component(.Month, fromDate: firstDate), 12)
XCTAssertEqual(calendar.component(.Day, fromDate: firstDate), 15)
} else {
XCTFail("Array empty")
}
} catch {
XCTFail("\(error)")
}

// Invalid tests:

do {
let invalidDateDictionary: UnboxableDictionary = [
"date" : "2015-12-tuesday",
"dateArray" : ["2015-12-15"]
]

try Unbox(invalidDateDictionary) as Model
XCTFail("Should have thrown")
} catch {
// Test passed
}

do {
let invalidDateArrayDictionary: UnboxableDictionary = [
"date" : "2015-12-15",
"dateArray" : ["2015-12-tuesday"]
]

try Unbox(invalidDictionary) as Model
try Unbox(invalidDateArrayDictionary) as Model
XCTFail("Should have thrown")
} catch {
// Test passed
Expand All @@ -243,21 +306,60 @@ class UnboxTests: XCTestCase {
func testOptionalDateFormattingFailureNotThrowing() {
struct Model: Unboxable {
let date: NSDate?
let dateArray: [NSDate]?

init(unboxer: Unboxer) {
let formatter = NSDateFormatter()
formatter.dateFormat = "YYYY-MM-dd"
self.date = unboxer.unbox("date", formatter: formatter)
self.dateArray = unboxer.unbox("dateArray", formatter: formatter)
}
}

struct AllowInvalidElementsModel: Unboxable {
let date: NSDate?
let dateArray: [NSDate]?

init(unboxer: Unboxer) {
let formatter = NSDateFormatter()
formatter.dateFormat = "YYYY-MM-dd"
self.date = unboxer.unbox("date", formatter: formatter)
self.dateArray = unboxer.unbox("dateArray", formatter: formatter, allowInvalidElements: true)
}
}

do {
let invalidDictionary: UnboxableDictionary = [
"date" : "2015-12-tuesday"
"date" : "2015-12-tuesday",
"dateArray" : ["2015-12-tuesday"]
]

let unboxed: Model = try Unbox(invalidDictionary)
XCTAssertNil(unboxed.date)
XCTAssertNil(unboxed.dateArray)
} catch {
XCTFail("\(error)")
}

do {
let invalidDictionary: UnboxableDictionary = [
"date" : "2015-12-tuesday",
"dateArray" : ["2015-12-15", "2015-12-tuesday"]
]

let unboxed: AllowInvalidElementsModel = try Unbox(invalidDictionary)
XCTAssertNil(unboxed.date)
XCTAssertEqual(unboxed.dateArray?.count, 1)

let calendar = NSCalendar.currentCalendar()
if let firstDate = unboxed.dateArray?.first {
XCTAssertEqual(calendar.component(.Year, fromDate: firstDate), 2015)
XCTAssertEqual(calendar.component(.Month, fromDate: firstDate), 12)
XCTAssertEqual(calendar.component(.Day, fromDate: firstDate), 15)
} else {
XCTFail("Array empty")
}

} catch {
XCTFail("\(error)")
}
Expand Down

0 comments on commit 90403f7

Please sign in to comment.