Skip to content

Commit

Permalink
Fix Float mapping with downcasting (#142)
Browse files Browse the repository at this point in the history
The behavior of `NSNumber as? Float` in Swift is such that some values
that cannot "safely express the value stored" in NSNumber. This means
only using `as?` to cast to `Float` can fail, where doing `.floatValue`
or `Float.init` would succeed. To work around this oddity, this new
`Convertible` conformance calls `.floatValue` on `NSNumber`, leading to
a float, that is possibly less precise than you'd like.
  • Loading branch information
keith committed Jul 31, 2018
1 parent c07df3d commit 4adc773
Show file tree
Hide file tree
Showing 5 changed files with 41 additions and 2 deletions.
4 changes: 3 additions & 1 deletion CHANGELOG.md
Expand Up @@ -6,7 +6,9 @@

## Enhancements

- None
- Fix `Float` behavior for some values
[Keith Smiley](https://github.com/keith)
[#142](https://github.com/lyft/mapper/pull/142)

# 8.0.0

Expand Down
4 changes: 4 additions & 0 deletions Mapper.xcodeproj/project.pbxproj
Expand Up @@ -18,6 +18,7 @@
C207F6EA1D7F286700EBCC74 /* OptionalValueTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C207F6E11D7F286700EBCC74 /* OptionalValueTests.swift */; };
C207F6EB1D7F286700EBCC74 /* RawRepresentibleValueTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C207F6E21D7F286700EBCC74 /* RawRepresentibleValueTests.swift */; };
C207F6EC1D7F286700EBCC74 /* TransformTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C207F6E31D7F286700EBCC74 /* TransformTests.swift */; };
C21F9CCE210FCF5F00761DDC /* Float+Convertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = C21F9CCD210FCF5F00761DDC /* Float+Convertible.swift */; };
C2B2A5761D3DE82700F7E7DE /* NSDictionary+Safety.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2B2A5751D3DE82700F7E7DE /* NSDictionary+Safety.swift */; };
C2BBD0C81DD430F400CB9F4E /* JSONSerializationIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2BBD0C61DD430AC00CB9F4E /* JSONSerializationIntegrationTests.swift */; };
C2C036FA1C2B1A0B003FB853 /* Convertible.swift in Sources */ = {isa = PBXBuildFile; fileRef = C2C036F31C2B1A0B003FB853 /* Convertible.swift */; };
Expand Down Expand Up @@ -52,6 +53,7 @@
C207F6E11D7F286700EBCC74 /* OptionalValueTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = OptionalValueTests.swift; sourceTree = "<group>"; };
C207F6E21D7F286700EBCC74 /* RawRepresentibleValueTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RawRepresentibleValueTests.swift; sourceTree = "<group>"; };
C207F6E31D7F286700EBCC74 /* TransformTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TransformTests.swift; sourceTree = "<group>"; };
C21F9CCD210FCF5F00761DDC /* Float+Convertible.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Float+Convertible.swift"; sourceTree = "<group>"; };
C2B2A5751D3DE82700F7E7DE /* NSDictionary+Safety.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSDictionary+Safety.swift"; sourceTree = "<group>"; };
C2BBD0C61DD430AC00CB9F4E /* JSONSerializationIntegrationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = JSONSerializationIntegrationTests.swift; sourceTree = "<group>"; };
C2C036D11C2B180D003FB853 /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
Expand Down Expand Up @@ -139,6 +141,7 @@
children = (
C2C036F31C2B1A0B003FB853 /* Convertible.swift */,
C20586EB1CDEAD9900658A67 /* DefaultConvertible.swift */,
C21F9CCD210FCF5F00761DDC /* Float+Convertible.swift */,
C2C036F51C2B1A0B003FB853 /* Mappable.swift */,
C2C036F61C2B1A0B003FB853 /* Mapper.swift */,
C2C036F41C2B1A0B003FB853 /* MapperError.swift */,
Expand Down Expand Up @@ -236,6 +239,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
C21F9CCE210FCF5F00761DDC /* Float+Convertible.swift in Sources */,
C2C036FF1C2B1A0B003FB853 /* Transform+Dictionary.swift in Sources */,
C2C036FE1C2B1A0B003FB853 /* URL+Convertible.swift in Sources */,
C2B2A5761D3DE82700F7E7DE /* NSDictionary+Safety.swift in Sources */,
Expand Down
1 change: 0 additions & 1 deletion Sources/DefaultConvertible.swift
Expand Up @@ -35,6 +35,5 @@ extension Int64: DefaultConvertible {}
extension UInt: DefaultConvertible {}
extension UInt32: DefaultConvertible {}
extension UInt64: DefaultConvertible {}
extension Float: DefaultConvertible {}
extension Double: DefaultConvertible {}
extension Bool: DefaultConvertible {}
16 changes: 16 additions & 0 deletions Sources/Float+Convertible.swift
@@ -0,0 +1,16 @@
import Foundation

// Float convertible implementation
extension Float: Convertible {
public static func fromMap(_ value: Any) throws -> Float {
if let value = value as? Float {
return value
}

if let object = value as? NSNumber {
return object.floatValue
}

throw MapperError.convertibleError(value: value, type: Float.self)
}
}
18 changes: 18 additions & 0 deletions Tests/MapperTests/ConvertibleValueTests.swift
Expand Up @@ -237,4 +237,22 @@ final class ConvertibleValueTests: XCTestCase {
let test = Test.from(["foo": "not a dictionary"])
XCTAssertNil(test)
}

func testFloatDirectly() throws {
let float: Float = 1.1
let value = try Float.fromMap(float)
XCTAssertEqual(value, 1.1)
}

func testConvertingFloat() throws {
struct Test: Mappable {
let float: Float
init(map: Mapper) throws {
try self.float = map.from("float")
}
}

let test = try Test(map: Mapper(JSON: ["float": 1.1]))
XCTAssertEqual(test.float, 1.1)
}
}

0 comments on commit 4adc773

Please sign in to comment.