diff --git a/Sources/Unbox.swift b/Sources/Unbox.swift index 1fb2c39..6f0b98d 100644 --- a/Sources/Unbox.swift +++ b/Sources/Unbox.swift @@ -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(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(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) diff --git a/Tests/UnboxTests.swift b/Tests/UnboxTests.swift index 52a3c9c..81c9fa8 100644 --- a/Tests/UnboxTests.swift +++ b/Tests/UnboxTests.swift @@ -205,18 +205,35 @@ 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) @@ -224,16 +241,62 @@ class UnboxTests: XCTestCase { 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 @@ -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)") }