Skip to content
This repository has been archived by the owner on Jun 18, 2019. It is now read-only.

Log warnings when invalid elements are found in a collection #189

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Sources/Dictionary+Unbox.swift
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,9 @@ private extension Dictionary {
} catch {
if !allowInvalidElements {
throw error
} else if let unboxError = error as? UnboxError {
let warning = UnboxWarning.invalidElement(error: unboxError)
Unboxer.warningLogger?.log(warning: warning)
}
}
}
Expand Down
12 changes: 11 additions & 1 deletion Sources/Sequence+Unbox.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,17 @@ internal extension Sequence {
}

return self.flatMap {
return try? transform($0)

do {
let unboxed = try transform($0)
return unboxed
} catch {
if let error = error as? UnboxError {
let warning = UnboxWarning.invalidElement(error: error)
Unboxer.warningLogger?.log(warning: warning)
}
return nil
}
}
}
}
18 changes: 18 additions & 0 deletions Sources/UnboxWarning.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
//
// UnboxWarningLogger.swift
// Unbox
//
// Created by Nicolas Jakubowski on 6/6/17.
// Copyright © 2017 John Sundell. All rights reserved.
//

import Foundation

/// Warnings are things that went wrong during the unbox operation
/// These aren't thrown, instead they are sent to a logger where you'll be able to keep record of them and research why you're getting them
public enum UnboxWarning {

/// An invalid element was found in an array
case invalidElement(error: UnboxError)

}
16 changes: 16 additions & 0 deletions Sources/UnboxWarningLogger.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
//
// UnboxWarningLogger.swift
// Unbox
//
// Created by Nicolas Jakubowski on 6/6/17.
// Copyright © 2017 John Sundell. All rights reserved.
//

import Foundation

/// Takes care of dealing with warnings
public protocol UnboxWarningLogger {

/// Called whenever a warning is found when Unboxing
func log(warning: UnboxWarning)
}
6 changes: 6 additions & 0 deletions Sources/Unboxer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ import Foundation
* - and the correct type will be returned. If a required (non-optional) value couldn't be unboxed `UnboxError` will be thrown.
*/
public final class Unboxer {

/// Takes care of logging warnings found during unbox operation.
/// Warnings are not fatal as errors, they just indicate that something is wrong.
/// An example of a warning is when you use the `allowInvalidElements` for parsing a collection. If an element in that collection fails to unbox, you'll receive a warning in this logger.
public static var warningLogger: UnboxWarningLogger?

/// The underlying JSON dictionary that is being unboxed
public let dictionary: UnboxableDictionary

Expand Down
157 changes: 157 additions & 0 deletions Tests/UnboxTests/UnboxTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -1744,6 +1744,154 @@ class UnboxTests: XCTestCase {
XCTFail("\(error)")
}
}

func testWarningsAreLoggedForInvalidElementsInArray() {

let logger = UnboxWarningLoggerMock()
Unboxer.warningLogger = logger

struct Model: Unboxable {
let string: String

init(unboxer: Unboxer) throws {
self.string = try unboxer.unbox(key: "string")
}
}

let dictionaries: [UnboxableDictionary] = [
["string" : "one"],
["invalid" : "element"],
["string" : "two"]
]

do {
let unboxed: [Model] = try unbox(dictionaries: dictionaries, allowInvalidElements: true)
XCTAssertEqual(unboxed.first?.string, "one")
XCTAssertEqual(unboxed.last?.string, "two")
} catch {
XCTFail("\(error)")
}

XCTAssertEqual(logger.receivedWarnings.count, 1, "Expected only one warning call")

guard let warning = logger.receivedWarnings.first else {
XCTFail("Expected to find a warning but there wasn't any")
return
}

switch warning {
case .invalidElement:
break
default:
XCTFail("Expected logged warning to be .invalidElement but instead got \(warning)")
}
}

func testWarningsareLoggedForInvalidElementsInNestedArrayOfDictionaries() {

let logger = UnboxWarningLoggerMock()
Unboxer.warningLogger = logger

struct Model: Unboxable {
let nestedModels: [NestedModel]

init(unboxer: Unboxer) throws {
self.nestedModels = try unboxer.unbox(key: "nested", allowInvalidElements: true)
}
}

struct NestedModel: Unboxable {
let string: String

init(unboxer: Unboxer) throws {
self.string = try unboxer.unbox(key: "string")
}
}

let dictionary: UnboxableDictionary = [
"nested" : [
["string" : "one"],
["invalid" : "element"],
["string" : "two"]
]
]

do {
let unboxed: Model = try unbox(dictionary: dictionary)
XCTAssertEqual(unboxed.nestedModels.first?.string, "one")
XCTAssertEqual(unboxed.nestedModels.last?.string, "two")
} catch {
XCTFail("\(error)")
}

XCTAssertEqual(logger.receivedWarnings.count, 1, "Expected only one warning call")

guard let warning = logger.receivedWarnings.first else {
XCTFail("Expected to find a warning but there wasn't any")
return
}

switch warning {
case .invalidElement:
break
default:
XCTFail("Expected logged warning to be .invalidElement but instead got \(warning)")
}
}

func testWarningsAreLoggedForInvalidElementsInNestedDictionary() {

let logger = UnboxWarningLoggerMock()
Unboxer.warningLogger = logger

struct Model: Unboxable {
let nestedModels: [String : NestedModel]

init(unboxer: Unboxer) throws {
self.nestedModels = try unboxer.unbox(key: "nested", allowInvalidElements: true)
}
}

struct NestedModel: Unboxable {
let string: String

init(unboxer: Unboxer) throws {
self.string = try unboxer.unbox(key: "string")
}
}

let dictionary: UnboxableDictionary = [
"nested" : [
"one" : ["string" : "one"],
"two" : ["invalid" : "element"],
"three" : ["string" : "two"]
]
]

do {
let unboxed: Model = try unbox(dictionary: dictionary)
XCTAssertEqual(unboxed.nestedModels.count, 2)
XCTAssertEqual(unboxed.nestedModels["one"]?.string, "one")
XCTAssertEqual(unboxed.nestedModels["three"]?.string, "two")
} catch {
XCTFail("\(error)")
}

XCTAssertEqual(logger.receivedWarnings.count, 1, "Expected only one warning call")

guard let warning = logger.receivedWarnings.first else {
XCTFail("Expected to find a warning but there wasn't any")
return
}

switch warning {
case .invalidElement:
break
default:
XCTFail("Expected logged warning to be .invalidElement but instead got \(warning)")
}
}

}

private func UnboxTestDictionaryWithAllRequiredKeysWithValidValues(nested: Bool) -> UnboxableDictionary {
Expand Down Expand Up @@ -2024,6 +2172,15 @@ private final class UnboxTestContextMock: UnboxableWithContext {
}
}

private final class UnboxWarningLoggerMock: UnboxWarningLogger {

private(set) var receivedWarnings: [UnboxWarning] = []
func log(warning: UnboxWarning) {
receivedWarnings.append(warning)
}

}

private struct UnboxTestSimpleMock: Unboxable, Equatable {
let int: Int

Expand Down
20 changes: 20 additions & 0 deletions Unbox.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,14 @@
52D6D9871BEFF229002C0205 /* Unbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 52D6D97C1BEFF229002C0205 /* Unbox.framework */; };
DD7502881C68FEDE006590AF /* Unbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 52D6DA0F1BF000BD002C0205 /* Unbox.framework */; };
DD7502921C690C7A006590AF /* Unbox.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 52D6D9F01BEFFFBE002C0205 /* Unbox.framework */; };
FE63CED71EE70829000239C9 /* UnboxWarning.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE63CED61EE70829000239C9 /* UnboxWarning.swift */; };
FE63CED91EE709DF000239C9 /* UnboxWarningLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE63CED81EE709DF000239C9 /* UnboxWarningLogger.swift */; };
FE63CEDA1EE709DF000239C9 /* UnboxWarningLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE63CED81EE709DF000239C9 /* UnboxWarningLogger.swift */; };
FE63CEDB1EE709DF000239C9 /* UnboxWarningLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE63CED81EE709DF000239C9 /* UnboxWarningLogger.swift */; };
FE63CEDC1EE709DF000239C9 /* UnboxWarningLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE63CED81EE709DF000239C9 /* UnboxWarningLogger.swift */; };
FE63CEDD1EE709F0000239C9 /* UnboxWarning.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE63CED61EE70829000239C9 /* UnboxWarning.swift */; };
FE63CEDE1EE709F0000239C9 /* UnboxWarning.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE63CED61EE70829000239C9 /* UnboxWarning.swift */; };
FE63CEDF1EE709F1000239C9 /* UnboxWarning.swift in Sources */ = {isa = PBXBuildFile; fileRef = FE63CED61EE70829000239C9 /* UnboxWarning.swift */; };
/* End PBXBuildFile section */

/* Begin PBXContainerItemProxy section */
Expand Down Expand Up @@ -260,6 +268,8 @@
AD2FAA281CD0B6E100659CF4 /* UnboxTests.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = UnboxTests.plist; sourceTree = "<group>"; };
DD75027A1C68FCFC006590AF /* Unbox-macOS Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Unbox-macOS Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
DD75028D1C690C7A006590AF /* Unbox-tvOS Tests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Unbox-tvOS Tests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
FE63CED61EE70829000239C9 /* UnboxWarning.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UnboxWarning.swift; sourceTree = "<group>"; };
FE63CED81EE709DF000239C9 /* UnboxWarningLogger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UnboxWarningLogger.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */

/* Begin PBXFrameworksBuildPhase section */
Expand Down Expand Up @@ -363,6 +373,8 @@
524F1E041E89B0A3000AC6FE /* UnboxPathError.swift */,
524F1E9F1E89BF6C000AC6FE /* UnboxPathNode.swift */,
524F1E901E89BDFC000AC6FE /* URL+Unbox.swift */,
FE63CED61EE70829000239C9 /* UnboxWarning.swift */,
FE63CED81EE709DF000239C9 /* UnboxWarningLogger.swift */,
);
path = Sources;
sourceTree = "<group>";
Expand Down Expand Up @@ -736,7 +748,9 @@
524F1E5F1E89BA9E000AC6FE /* UInt64+Unbox.swift in Sources */,
524F1EBE1E89C1C6000AC6FE /* Data+Unbox.swift in Sources */,
524F1E371E89B42E000AC6FE /* UnboxFormatter.swift in Sources */,
FE63CED71EE70829000239C9 /* UnboxWarning.swift in Sources */,
524F1E501E89BA26000AC6FE /* Int32+Unbox.swift in Sources */,
FE63CED91EE709DF000239C9 /* UnboxWarningLogger.swift in Sources */,
524F1ECD1E89C466000AC6FE /* UnboxArrayContainer.swift in Sources */,
524F1EB91E89C16B000AC6FE /* JSONSerialization+Unbox.swift in Sources */,
524F1EAA1E89C01B000AC6FE /* NSArray+Unbox.swift in Sources */,
Expand Down Expand Up @@ -766,12 +780,14 @@
524F1E891E89BD1C000AC6FE /* CGFloat+Unbox.swift in Sources */,
524F1E341E89B409000AC6FE /* UnboxableByTransform.swift in Sources */,
524F1E5C1E89BA78000AC6FE /* UInt32+Unbox.swift in Sources */,
FE63CEDB1EE709DF000239C9 /* UnboxWarningLogger.swift in Sources */,
524F1ECA1E89C41C000AC6FE /* UnboxContainer.swift in Sources */,
524F1E841E89BC99000AC6FE /* Dictionary+Unbox.swift in Sources */,
524F1E201E89B345000AC6FE /* UnboxableCollection.swift in Sources */,
524F1E931E89BDFC000AC6FE /* URL+Unbox.swift in Sources */,
524F1E6B1E89BAE7000AC6FE /* Float+Unbox.swift in Sources */,
524F1E3E1E89B884000AC6FE /* Optional+Unbox.swift in Sources */,
FE63CEDE1EE709F0000239C9 /* UnboxWarning.swift in Sources */,
524F1E1B1E89B2A9000AC6FE /* UnboxableRawType.swift in Sources */,
524F1E111E89B168000AC6FE /* UnboxableWithContext.swift in Sources */,
524F1E251E89B381000AC6FE /* UnboxCollectionElementTransformer.swift in Sources */,
Expand Down Expand Up @@ -815,12 +831,14 @@
524F1E8A1E89BD1C000AC6FE /* CGFloat+Unbox.swift in Sources */,
524F1E351E89B409000AC6FE /* UnboxableByTransform.swift in Sources */,
524F1E5D1E89BA78000AC6FE /* UInt32+Unbox.swift in Sources */,
FE63CEDC1EE709DF000239C9 /* UnboxWarningLogger.swift in Sources */,
524F1ECB1E89C41C000AC6FE /* UnboxContainer.swift in Sources */,
524F1E851E89BC99000AC6FE /* Dictionary+Unbox.swift in Sources */,
524F1E211E89B345000AC6FE /* UnboxableCollection.swift in Sources */,
524F1E941E89BDFC000AC6FE /* URL+Unbox.swift in Sources */,
524F1E6C1E89BAE7000AC6FE /* Float+Unbox.swift in Sources */,
524F1E3F1E89B884000AC6FE /* Optional+Unbox.swift in Sources */,
FE63CEDF1EE709F1000239C9 /* UnboxWarning.swift in Sources */,
524F1E1C1E89B2A9000AC6FE /* UnboxableRawType.swift in Sources */,
524F1E121E89B168000AC6FE /* UnboxableWithContext.swift in Sources */,
524F1E261E89B381000AC6FE /* UnboxCollectionElementTransformer.swift in Sources */,
Expand Down Expand Up @@ -864,12 +882,14 @@
524F1E881E89BD1C000AC6FE /* CGFloat+Unbox.swift in Sources */,
524F1E331E89B409000AC6FE /* UnboxableByTransform.swift in Sources */,
524F1E5B1E89BA78000AC6FE /* UInt32+Unbox.swift in Sources */,
FE63CEDA1EE709DF000239C9 /* UnboxWarningLogger.swift in Sources */,
524F1EC91E89C41C000AC6FE /* UnboxContainer.swift in Sources */,
524F1E831E89BC99000AC6FE /* Dictionary+Unbox.swift in Sources */,
524F1E1F1E89B345000AC6FE /* UnboxableCollection.swift in Sources */,
524F1E921E89BDFC000AC6FE /* URL+Unbox.swift in Sources */,
524F1E6A1E89BAE7000AC6FE /* Float+Unbox.swift in Sources */,
524F1E3D1E89B884000AC6FE /* Optional+Unbox.swift in Sources */,
FE63CEDD1EE709F0000239C9 /* UnboxWarning.swift in Sources */,
524F1E1A1E89B2A9000AC6FE /* UnboxableRawType.swift in Sources */,
524F1E101E89B168000AC6FE /* UnboxableWithContext.swift in Sources */,
524F1E241E89B381000AC6FE /* UnboxCollectionElementTransformer.swift in Sources */,
Expand Down