GithubHelp home page GithubHelp logo

anviking / decodable Goto Github PK

View Code? Open in Web Editor NEW
1.0K 1.0K 73.0 663 KB

[Probably deprecated] Swift 2/3 JSON unmarshalling done (more) right

License: MIT License

Swift 99.35% Objective-C 0.25% Ruby 0.40%
json swift

decodable's People

Contributors

adamdebono avatar anviking avatar bdolman avatar brentleyjones avatar codeofrobin avatar danielgarbien avatar fjbelchi avatar izqui avatar javisoto avatar jordanekay avatar nachosoto avatar nap-sam-dean avatar neonichu avatar nuudles avatar ohayon avatar qata avatar voidref avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

decodable's Issues

catchNull() catches wrong exception

I see that parse() throw MissingKey when a value can't be extracted for a key. However, catchNull() only catches TypeMismatch which doesn't seem appropriate for:

/// Try to decode as T, or throw. Will return nil if the object at the keypath is NSNull.
public func => <T: Decodable>(lhs: AnyObject, rhs: String) throws -> T? {
    return try catchNull { try parse(lhs, path: rhs, decode: T.decode) }
}

Wouldn't this actually throw MissingKey if rhs is missing?

Optional arrays not behaving the way they should

First issue 🎉!!!

So, thank you for this awesome piece of code.

I'm finding that optional arrays are not behaving the way they should: expanding on your sample from the README:

struct Repository {
    let name: String
    let description: String
    let stargazersCount: Int
    let language: String?

    let owner: User
    let defaultBranch: Branch
    let externalContributors: [User]?

    var fullName: String { return "\(owner.login)/\(name)" }
}

If we add the optional array called externalContributors and feed the decode function with a JSON such as:

        let json = [
            "name" : "AwesomeKit",
            "description" : "This is awesome",
            "stargazers_count" : 8,
            "owner" : ["login":"pcifani"],
            "default_branch" : ["branch_name":"master"],
        ]

This should be a valid JSON for the Repository struct, but it's not, right now, it's returning a DecodingError.TypeMismatch with "JSON Array" as the mismatched object.

Let me know if I can help in any way

RawRepresentable decoding

Hi, i have a issue trying to decode an array of enums like this ["admin", "merchant"],
i tried like this let roles = (try j => "roles")?.map { LKUserRole(rawValue: $0)! }
but this dosent work when i dont get any role, any help would be great
u can check it here

Decode nested json array

Hi I love Decodable but recently I encounter this situation with nested JSON.

JSON

{
    "cars": [
        {
            "name": "bmw",
            "passagers": [
                {
                    "name": "john",
                    "age": "23"
                },
                {
                    "name": "lucy",
                    "age": "23"
                }
            ]
        },
        {
            "name": "ford",
            "passagers": [
                {
                    "name": "błażej",
                    "age": "27"
                },
                {
                    "name": "marcy",
                    "age": "27"
                }
            ]
        }
    ]
}

Structs

struct Car:Decodable{
    var name:String
    var passagers:[Passager]
    static func decode(json:AnyObject) throws -> Passager {
        return try DirectionJson(
            name: json => "name",
            passagers: json => [Passager].decode //? error, so how?
        )
    }
}

struct Passager:Decodable{
    var name:String
    var age:Int
    static func decode(json:AnyObject) throws -> Passager {
        return try DirectionJson(
            name: json => "name",
            age: json => "age"
        )
    }
}

I was hoping that whole json would be decoded by single [Car].decode(json). But I'm not sure how I should handle [Passager] in Car.

Does Decodable only work with structs?

I'm trying to use Decodable to create Realm objects, but I'm having issues. Then I realized all the examples were structs — does Decodable not work with classes?

Build failed when try to update via Carthage with Xcode 7.3

I get a build failed when i try to update via carthage

carthage update --platform iOS --no-use-binaries

*** Building scheme "Decodable-iOS" in Decodable.xcodeproj
** BUILD FAILED **


The following build commands failed:
    PhaseScriptExecution Run\ Script /Users/marvinnazari/Library/Developer/Xcode/DerivedData/Decodable-bcceucalbtetbifrlcjcisqcisfe/Build/Intermediates/Decodable.build/Release-iphoneos/Decodable-iOS.build/Script-8F0062441C81F26B007BCF48.sh
(1 failure)
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.3.sdk/usr/include/sys/cdefs.h:707:2: error: Unsupported architecture
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.3.sdk/usr/include/machine/_types.h:34:2: error: architecture not supported
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.3.sdk/usr/include/sys/_types.h:55:9: error: unknown type name '__int64_t'
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.3.sdk/usr/include/sys/_types.h:56:9: error: unknown type name '__int32_t'
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.3.sdk/usr/include/sys/_types.h:57:9: error: unknown type name '__int32_t'
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.3.sdk/usr/include/sys/_types.h:60:9: error: unknown type name '__uint32_t'
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.3.sdk/usr/include/sys/_types.h:61:9: error: unknown type name '__uint32_t'
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.3.sdk/usr/include/sys/_types.h:62:9: error: unknown type name '__uint64_t'; did you mean 'uint64_t'?
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.3.sdk/usr/include/sys/_types.h:68:9: error: unknown type name '__darwin_natural_t'
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.3.sdk/usr/include/sys/_types.h:70:9: error: unknown type name '__uint16_t'
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.3.sdk/usr/include/sys/_types.h:71:9: error: unknown type name '__int64_t'
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.3.sdk/usr/include/sys/_types.h:72:9: error: unknown type name '__int32_t'
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.3.sdk/usr/include/sys/_types.h:73:9: error: unknown type name '__uint32_t'
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.3.sdk/usr/include/sys/_types.h:74:9: error: unknown type name '__int32_t'
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.3.sdk/usr/include/sys/_types.h:75:9: error: unknown type name '__uint32_t'
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.3.sdk/usr/include/sys/_types.h:76:9: error: unknown type name '__uint32_t'
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.3.sdk/usr/include/sys/_types/_intptr_t.h:30:9: error: unknown type name '__darwin_intptr_t'
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.3.sdk/usr/include/_types.h:42:9: error: unknown type name '__uint32_t'
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.3.sdk/usr/include/sys/_types/_size_t.h:30:9: error: unknown type name '__darwin_size_t'; did you mean '__darwin_ino_t'?
<unknown>:0: error: too many errors emitted, stopping now
/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS9.3.sdk/System/Library/Frameworks/CoreFoundation.framework/Headers/CoreFoundation.h:11:10: error: could not build module 'Darwin'
<unknown>:0: error: could not build Objective-C module 'CoreFoundation'
A shell task failed with exit code 65:
** BUILD FAILED **


The following build commands failed:
    PhaseScriptExecution Run\ Script /Users/marvinnazari/Library/Developer/Xcode/DerivedData/Decodable-bcceucalbtetbifrlcjcisqcisfe/Build/Intermediates/Decodable.build/Release-iphoneos/Decodable-iOS.build/Script-8F0062441C81F26B007BCF48.sh
(1 failure)

DecodingError.Metadata and DecodingContext are overlapping

Preferably DecodingError.Metadata could be replaced, but DecodingContext is generic, so we would need something like an AnyContext.

Though AnyContext would basically be a DecodingError.Metadata but with more confusing name, so maybe just improve DecodingError.Metadata and make it be initializable with a DecodingContext

Inverse relationship mapping

How would you map an inverse relationship using decodable?

Think of the following scenario:

A <-->> B

With A being in a to-many relationship with B and B being in a to-one relationship with A.

When trying to decode A (assuming an array of B objects are contained in the payload), the decoding of B will be triggered as well.

I can't think of a way using decodable for populating the B's A reference (inverse relationship).

In theory all will be needed is to pass Self (A) to the mapping of B (in the decode function is the only thing I can think of), then having B recognise that object, maybe as root or parent and assign it to the A's reference it holds.

Nested JSON

whats the best practice for mapping nested JSON?

Currently I am creating a struct for each level - this isn't very clever as sometimes I do not care about level.

e.g. The only data I care about is before & after

  "paging": {
    "cursors": {
      "before": "MTQ5MDQzODAzNzkxNzQ0MQZDZD",
      "after": "ODE0NDQyNDA1MzIwMDU5"
    }
  }

This is my current implementation

extension Paging: Decodable {

  static func decode(json: AnyObject) throws -> Paging {
    return try Paging(
      cursors: json => "cursors"
    )
  }

}

extension Paging.Cursors: Decodable {

  static func decode(json: AnyObject) throws -> Paging.Cursors {
    return try Paging.Cursors(
      before: json => "before",
      after:  json => "after"
    )
  }

}

I want something like this:

extension Paging: Decodable {

  static func decode(json: AnyObject) throws -> Paging {
    return try Paging(
      before: json => "cursors.before",
      after:  json => "cursors.after"
    )
  }

}

Is it possible with the current implementation or can I look at introducing some key-value coding onto the project?

Decode an array

Hi.

First of all great work with this library, I'd tried quite a few other Swift libraries, and this is the one that worked well with the latest version of RealmSwift for me.

Right now I'm having trouble to parse JSON arrays, if I have the following

coordinates: [-90, 14.4]

How should I specify the decoder that I want to parse the latitude and longitude from the array without having the keys?

Unexpected nil values inside optional dictionary

    func testNestedUnexpectedNSNull() {
        let dict: NSDictionary = ["id": 1, "color": ["name": NSNull()]]
        do {
            let apple = try Apple.decode(dict)
            print(apple)
            XCTFail()
        } catch DecodingError.TypeMismatch(NSNull.self, _, _) {

        } catch let error {
            XCTFail("should not throw this exception: \(error)")
        }
    }

Where the color is optional, but the color's name is not

Nested key tests are failing in Swift 3.0

Nested keys don't work j => "a" => "b" => "c"
Workaround:

json => ["a", "b", "c"]

Have still not investigated closely (option click for these overloads fails for me) but suspect there is some kind of String and AnyObject overload ambiguity.

Combination with Core Data

Is it possible to combine this great object mapping library with Core Data? How should swift class look like? Thanks

Missing typical optional/nullable operator

Hi! Nice library. We're using Argo now and going to try this one out as a possible replacement.

One problem I'm noticing though, is that there doesn't seem to be support for regular nullable functionality. You have 2 nullable (?) operators but neither do what I would expect.

The only nullable operator (for setting an optional var) one would typically need would result in 3 cases:

  1. return the object if decode succeeds
  2. return nil if the key does not exist
  3. return an error if the key exists but decoding fails (ie the JSON object is corrupt/unexpected)

This project does not satisfy case 3 and instead returns nil as well instead of an error(at least according to the documentation).

In addition, there is the array '?' operator which weeds out objects that failed to decode but in general I think it should follow the same pattern as above (with no partial successes). Allowing a partial success should probably be a special case with its own operator if at all, because in general this would be the result of developer error (unless you are depending on an unstable API).

tl:dr Should this library provide a way to decode an array or custom object in a failable/throwable way which satisfies the 3 above conditions?

Just some thoughts, what do you think?

Strategy for dependency injection (if that's the correct term)

class A {
    var b: B
}

class B {
    weak var a: A
    let urlResponse: String 
}

Injecting parameters/dependencies in sub-objects of the objects being decoded is difficult, and Decodable currently does nothing to help this.

This could be seen as the underlying problem for:

  • #51 Using Decodable with Core Data – injecting a NSManagedObjectContext (I think)
  • #29 Initialising objects from different JSON sources (where passing down a format enum could be a solution)

Depending on the solution this could also solve/affect:

  • #26 (If a "DecodingContext"/wrapping type approach is taken)
  • #94 (If ability to properly chain => has been lost in Swift 3.0)

Here are two potential solutions I have thought of:

Solution 1: DecodingContext<Parameters>

public struct DecodingContext<T> {
    var path: [String]
    var json: AnyObject
    var rootObject: AnyObject
    var parameters: T

    init(json: AnyObject, path: [String] = [], rootObject: AnyObject, parameters: T) {...}

    public func parse(keys: [String]) throws -> DecodingContext {...}
    public func parseAndAcceptMissingKeys(keys: [String]) throws -> DecodingContext? {...}

    func map<U>(closure: (T) -> U) -> DecodingContext<U> {...}
}
public protocol Decodable {
    associatedtype Parameters = Void
    static func decode(_ : DecodingContext<Parameters>) throws -> Self
}
let dict: NSDictionary = ["hello": "world"]
let response = "<this is an urlresponse>"
let code = 200

let a = DecodingContext(json: dict, path: [], rootObject: dict, parameters: (urlResponse: response, code: code))
let params = a.parameters.urlResponse // <this is an urlresponse>
let params = a.parameters.code // 200

let b = a.map {
    (urlResponse: $0.urlResponse, code: $0.code, additionalParamter: true)
}
b.parameters.additionalParamter // true
extension A: Decodable {
    static func decode(_ context: DecodingContext<Void>) {
         let context = 
         return A(b: context => "b")
    }
}

Final design would likely be more refined, for instance with more appropriate initialisers.

Pros

  • DecodingContext could be a good central hub for decoding-methods, and allows for alternatives/replacements for =>
  • Easy to add dependencies for a whole tree of objects in a type safe way
  • Ability to have properties decoded according to inferred type is preserved in every instance
  • DecodingContext can be typealiased, e.g typealias JSON = DecodingContext<NSManagedObjectContext> if wanted.
  • Perhaps makes it more type safe.

Cons

  • Single-element-tuples cannot be created/named. This means that if you have only one parameter, you cannot give it a name, since the parameter becomes context.parameters itself.
  • Support to decode dictionaries might be difficult to pull of / inconvenient if key and value have require different parameters (unlikely though).
  • Adds a wrapping type around every AnyObject which could make it more inconvenient to work with.

Note:

In the above described design each object requires a context: DecodingContext<Self.Parameters>. For dependencies that only travel one level, e.g (the child requiring a reference to the parent) this isn't a problem. But for dependencies on the bottom (grandchildren) that needs "external" dependencies, for example the url response that the json came with, every intermediate object must also have set Parameters that include these.

However, would be very weird if it didn't work that way.

Another note (added in hindsight)

Thinking more academically it would be practical if the decode protocol could require that the parameters are a subset of the Self.Parameter requirement.

static func decode<P: Parameters>(_ context: DecodingContext<P>)  throws -> Self {}

However with current swift version P cannot be constrained to an associatedtype. Thus we need separate contexts for decoding different things:

static func decode(context: DecodingContext<NSManagedObjectContext>) throws -> Self {
     let user: User = try context => "user"
     let tags: [Tags] = try context => "tags"
     // Remove the NSManagedObjectContext from the parameters
     let context = context.map { _ }
     let url = try context => url 
}

But some kind of implicit conversion (with overload) could be added for DecodingContext<T> -> DecodingContext<Void> conversion.

Partial implementation

I have been playing around with this idea on this branch

Solution 2: Currying and no protocols

public static func decode(parameter: String, foo: String) -> (AnyObject) throws -> Self {
        return { json in
            try Self(json: json, parameter: parameter, foo: foo)
        }
    }

This is a method I have been (forced) to using in some places which works by not conforming to Decodable but instead returning a custom decode function. Parents would then be able to inject dependencies explicitly, e.g

try b = B(json: json => "b", a: self)

Dependencies that need to be provided at a top level have to cascade down the initialization-chain.

Pros

  • Requires no changes to Decodable
  • Very explicit and non-magical

Cons

  • Operators doesn't work on types with dependencies =>, which is cumbersome for objects with many children with dependencies.
  • Ability for Decodable to help by decoding complex types like [T?]? is removed.

## Closing notes

I think I lean more and more towards alternative 1. Nonetheless, have no strong incentive to implement this now, so going to let this sit for a while in a review-period-esque way.

So any thoughts/reactions are much appreciated!

Discerning between a custom object and an array is ambiguous because of overloaded operator

It seems like arrays should have a separate operator (perhaps '=>|', piped added to regular operator) because receiving an object when expecting an array or vice versa is a valid error case.

Consider the possibility that you are expecting an object of type 'Hat'. Your API is erroneously returning an array of hats so you get [Hat]. Decodable will unfortunately not catch this for you because it doesn't know that you don't want an array.

Better naming

  • TimeMismatch(type: Any.Type <<---- EXPECTED OR ACTUAL?)

Question about MetaDecodable

Hi @Anviking,

I've been reading the new changes in the code, fantastic job with the errors.

I saw this:

public protocol MetaDecodable {
    typealias MetaType
    var objects: MetaType {get}
    static func decode(json: AnyObject, type: Decodable.Type) throws -> MetaType
}

I'm not sure if MetaDecodable is used, could you explain the idea behind please?

Thanks

Problem overriding Castable's decode()

Hej

Goal

To additionally make Bool, Int and Double decodable from String values.

Best guess

extension Bool {
    public static func decode(j: AnyObject) throws -> Bool {
        switch j {
        case is Bool:
            return j as! Bool
        case is String:
            switch j as! String {
            case "true": return true
            case "false": return false
            default: throw DecodingError.UnexpectedValue(value: j, info: DecodingError.Info(object: j))
            }
        default:
            let info = DecodingError.Info(object: j)
            throw DecodingError.TypeMismatch(type: j.dynamicType, expectedType: self, info: info)
        }
    }
}

Problem

This implementation is not called when doing the usual, e.g.:

struct MyDecodableType : Decodable {
    let myBool: Bool

    public static func decode(j: AnyObject) throws -> MyDecodableType {
        return try MyDecodableType(myBool: j => "myBool")
    }
}

instead, it only works when doing this:

return try MyDecodableType(myBool: try parse(json, path: ["myBool"], decode: Bool.decode))

Using this everywhere there's a Bool, Int or Double isn't an option.

T should resolve to Bool inside of =>, but T.decode always seems to resolve to Castable's implementation...

Dynamic dispatch seems to follow some simple rules when it comes to instances (https://medium.com/ios-os-x-development/swift-protocol-extension-method-dispatch-6a6bf270ba94#.1wkt1dbt7), but what is going on here?

Decodable objects should be encodable back to JSON

It would be really great for updating objects on your REST api if you could automatically encode Decodable objects back into JSON. I may look into implementing this, but wanted to hear thoughts on this, or if you're perhaps already working on it.

decode a Dictionary

Hi i get a error when i try decode a Dictionary

public struct ShowMorePosts {
    public let posts: [STPost]
    public let showMorePosts: [String:[STPost]]
}

extension ShowMorePosts: Decodable {
    public static func decode(j: AnyObject) throws -> ShowMorePosts {
        return try ShowMorePosts(
            posts: j => "posts",
            showMorePosts: j => "show_more_posts"
        )
    }
}

Note: STPost is confirming to Decodable
I get this error Cannot convert value of type 'AnyObject' to expected argument type '[String : [STPost]]'

Error when compiling in tests

Hi,

Sorry that this is more of a question than a bug I am sure, but trying to include tests that use any of the decode functions fails to compile with

Undefined symbols for architecture x86_64:
  "static (extension in Decodable):Swift.Array<A where A: Decodable.Decodable>.(decode (Swift.AnyObject, ignoreInvalidObjects : Swift.Bool) throws -> [A]).(default argument 1)", referenced from:
      CityBikesKitTests.CityBikesTests.testNetworksAreSortedAlphabetically () -> () in CityBikesTests.o
  "static (extension in Decodable):Swift.Array<A where A: Decodable.Decodable>.decode (Swift.AnyObject, ignoreInvalidObjects : Swift.Bool) throws -> [A]", referenced from:
      CityBikesKitTests.CityBikesTests.testNetworksAreSortedAlphabetically () -> () in CityBikesTests.o
ld: symbol(s) not found for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)

Strangely enough if I commented out the problematic line, and run the same code in the console with a breakpoint on that test, everything works normally. Have you seen this before? Any suggestions?

screen shot 2016-05-01 at 00 02 18

You can see the problematic line (actually any that uses decode will do, yet if you see the arrow at the bottom, that code works in the console

Swift 3.0

Would love to see this working with swift 3 to test soon :) Happy to contribute in any way that's useful!

Error Paths

Right now it can be difficult to understand from where the parsing error originates. The following works on the functions-branch, but not sure about the vastly different and somewhat weird implementation.

let dict: NSDictionary = ["object": ["repo": ["owner": ["id" : 1, "login": "anviking"]]]]

do {
    let usernames: String = try dict => "object" => "repo" => "owner" => "lllloogon"
} catch let error {
    print(error)
}

===============================
MissingKey at object.repo.owner: lllloogon in {
    id = 1;
    login = anviking;
}

Other approaches could be making allowing => ["repo", "owner", "login"]
and possibly add func =>(lhs: String, rhs: String) -> [String]

Getting a "ResponseObject"

This library is great, I've been using it with some of my structs and it I dig it. How can I use this to return "JSON Objects", i.e. a raw JSON object?

Missing keys and optionals

Hi,

What is the best practice for missing keys? I find it strange (I'm sure I'm wrong here) that the decoding process fails when it founds a missing key and this property is marked as Optional. Is it normal for API responses to not include all the information in their entities for every single call?

I am trying to consume an "index" action that returns some data about the model and a "show" action that shows all the data, but since the "index" is a trimmed down version of the "show" it always fails.

Decoding dictionary types

Hi, this is a question on how to work with Decodable.

I'm talking to an API that gives me a dictionary of the type Dictionary<String,City> where City is another decodable type. This is the naïve way I tried to start with, but I'm unsure how else to go about this.

struct Metadata: Decodable {
    ...
    let cities: [String: City]

    static func decode(json: AnyObject) throws -> Metadata {
        return try Metadata(
            ...
            cities: json => "cities"
        )
    }
}

TypeMismatch __NSCFDictionary, expected: Dictionary<String, City> in cities object:

Thanks for any tips! :)

Keep Podspec up to date/pushed to CocoaPods

Currently the podspec in the repo supports OS X, but isn't pushed to cocoapods.
Also theres been a lot of good work on master the last couple months, but no new tags, podspec updates/pushes.

I'm very much invested in the growth/up-to-dateness of Decodable, and would be happy to maintain the podspec, if added as a maintainer.

Thanks

Xcode warning 'no calls to throwing functions occur within 'try' expression with classes

When trying to use Decodable with classes, I get a odd Xcode error no calls to throwing functions occur within 'try' expression. It's odd because while I can see the line that causes it, Xcode itself doesn't associate the warning with the specific line. And, I don't actually get the error as the init function does throw.

class Model: NSObject, Decodable {
    var name: String

    required init(json: AnyObject) throws {
        try self.name = json => "name"
    }

    static func decode(j: AnyObject) throws -> Self {
        // Xcode does not like this line
        return try self.init(json: j)
    }
}

image 2016-06-25 at 8 38 53 am

So my questions are:

  1. Is there a better setup I should be doing when using Decodable with classes?
  2. Any thoughts on how to resolve that warning itself?

Thanks so much!

Tests need some love

Some kind of abstraction is needed I think, they're pretty messy now. DecodingError should be equatable or something.

Implementing Decodable for common types

Hey, I'm trying to allow easy decoding on things like NSURL / NSDate etc… Seems the protocol is complaining when trying to add it to a ObjC class.

Keeps asking 'expected return type to be Self' but if you do that then the return types break.

Have you done this at all?

Refactor operators away

So people that are not emotionally attached to them can use function-overloads instead. If the operators should exist they should be entirely optional. Originally planned it for v0.3 but it never happened. (You have to write twice the number of overloads). Opening this now because of a lot of really valid skepticism towards Decodable's operators on twitter.

Here some of my thoughts;

  • There is no need for overloads, they just make it look nicer. Though before v0.3 custom operators were unavoidable
  • Not only are they not needed, they also make the implementation more difficult.
  • Arguable they make the usage more difficult also. Like what does the =>? do?

So, yeah.

But I do think many people, including myself, want to keep their model-deserializing code short and implicit. So I believe in keeping the operators, just making them optional.

  • Do I really want to maintain one operator and one function for each overload? The operator should call the corresponding function-overload, but still.
  • Think about naming (parse<T>(son: AnyObject, path: String...) probably?)
  • copy and paste overloads

Note: subscripts would of course be nice, but they can't be generic or throwing.

Edit No, I have changed my mind again for the bizillionth time. I really don't know if I should do this, meaning nothing will change for now.

Concider changing the optional => operator

The more sane thing is perhaps to have the optional => to throw on a missing key (but accept an explicit null anywhere), and add a completely non-throwing =>? operator

TypeMismatch NSTaggedPointerString when some attributes are not set to String

Hi There! I'm really liking your library. I'm currently attempting to decode responses I get from Alamofire's responseJSON serializer but I'm encountering issues. Here's a print out of the JSON response. It looks as expected:

Optional({
    id = 173722;
    token = 9eea351ff15d43d1609145eb699412cc4297b58d173722;
    user =     {
        "activities_total" = 12780;
        avatar = "https://stagingsite.s3.amazonaws.com/avatars/[email protected]";
        "avatar_lg" = "https://stagingsite.s3.amazonaws.com/avatars/173722orig.jpeg";
        "competitions_on" = 1;
        converter = 1;
        "first_name" = "";
        gender = Male;
        "giveaway_points_on" = 1;
        "goal_enforced" = 0;
        "inception_date" = "2015-07-27 11:44:11";
        "last_name" = "";
        "last_visit" = "2015-10-02 11:26:04";
        level = 2;
        "levels_and_points" = 1;
        logo = "https://stagingsite.s3.amazonaws.com/logos/saZIvy_mountain-2.jpg";
        "mobilehealth_allowed" = 1;
        "number_of_entries" = 22;
        "pedometer_image" = "https://api.stagingsite.com/_views/images/devices/googlefit32.png";
        "pedometer_last_contact" = 1441394474;
        "pedometer_name" = Googlefit;
        "pedometer_type" = "Google Fit";
        points = 270;
        "points_granted" = 0;
        "points_to_grant" = 36;
        "program_end" = "2017-01-31";
        "program_start" = "2013-10-01";
        "step_average" = 4155;
        "step_goal" = 8000;
        "step_goal_met" = 0;
        "step_total" = 78628;
        "stride_length" = 0;
        "teams_on" = 1;
        "this_device" = "app_name/com. appname.app-name (1; os version 9.0 (build 13a340))";
        "total_privacy" = 0;
        units = Miles;
        url = "beta.stagingsite.com";
        username = somerusername;
    };
    username = someusername;
})

Then I have a struct for a UserSession and a User. The issue is that sometimes I'll get an error on attributes that are not set to be a String. For example, several of these attributes should be Bool's or Ints. In some cases this works fine. For example these attributes parse correctly:

struct User {
    ...
    var competitionsOn: Int
    var giveawayPointsOn: Bool
    var mobilehealthAllowed: Int
    ...
}

But every so often one of the attributes throws this error:

TypeMismatch NSTaggedPointerString, expected: Int in user.number_of_entries object: Optional(22)

The correct value was found (22) but it's showing up as an Optional. Are there any additional steps I should take when parsing values to describe or cast their type? I currently am just using the most basic implementation per the examples:

numberOfEntries: j => "number_of_entries",

In any case -- adjusting the attribute type to a String resolves the parsing error but that is not ideal.

Debugging

I found when debugging the NSError thrown does not mention what failed -

I get this error:

Error Domain=Decodable.DecodingError Code=0 "(null)"

It is simple to reproduce,

extension Event.Place: Decodable {

  static func decode(json: AnyObject) throws -> Event.Place {
    return try Event.Place(
      name:      json => "title",
      longitude: json => "location" => "longitude",
      latitude:  json => "location" => "latitude"
    )
  }

}

let json = [
  "name" : "Home",
  "location" : [
    "longitude" : 0,
    "latitude" : 0
  ]
]

Notice the mistake in the decode - using "title" instead of "name", it would be nice if we could yield that result into the error

Make path, object and rootObject public in DecodingError info struct

Decodable reside in a base framework in our project to access path, object and rootObjec from other framework is not possible without making them public. Can you please make that change ?

public struct Info {

    public init(object: AnyObject, rootObject: AnyObject? = nil, path: [String] = []) {
        self.object = object
        self.rootObject = rootObject
        self.path = path
    }

    public var path: [String]
    public var object: AnyObject?
    public var rootObject: AnyObject?

    public var formattedPath: String {
        return path.joinWithSeparator(".")
    }
}

Operators that return T? should return nil when the key path is missing

Currently the => operators that return T?, [T]?, etc. will throw a .MissingKey if the key path is missing. For JSON not having a field be available is usually the same as having it explicitly be null. Right now the documentation suggests try? for cases like this, but then you will swallow .TypeMismatch errors.

I recommend that either the => operators that return a nil also do so on .MissingKey, or a new set of operators, such as =>?, be introduced. The type already states that it's valid for it to not exist (the point of an Optional in my mind) and .MissingKey matches that.

Transformable

It may actually be useful now that we have the RawRepresentable extension.

Something like:

public protocol Transformable: Decodable {
    typealias RawDecodableType: Decodable
    static func transform(rawValue: RawDecodableType) throws -> Self?
}

Purpose: Automatically throw an useful error if transform fails. This would replace the error for the RawRepresentable extension.

Note: Tempted to call it Transformable but probably shouldn't

Initialise one object from different JSON sources.

Say we have an object, which should be initialised from a JSON coming over the network and from a different JSON e.g. coming from the disk. This is currently not possible since, the decode function has no way of knowing the "type" of the JSON it is getting.

Adding type information to the Decodable protocol would allow for having different decode functions to work with the same object:

public protocol Decodable {
    typealias DataSourceType
    static func decode(json: AnyObject, ofType type:DataSourceType.Type) throws -> Self
}

// It could look something like this...
struct TheObject {
    let info:String
}

struct ServerJSON {}
extension TheObject : Decodable {
    typealias DataSourceType = ServerJSON
    static func decode(json: AnyObject, ofType type:DataSourceType.Type) throws -> TheObject
    {
        return TheObject(info: "fromServer")
    }
}


struct DiskJSON {}
extension TheObject {
    static func decode(json: AnyObject, ofType type:DiskJSON.Type) throws -> TheObject
    {
        return TheObject(info: "fromDisk")
    }
}


let o = try! TheObject.decode(["Some":"Thing"], ofType: ServerJSON.self)
o.info // -> "fromServer"

let p = try! TheObject.decode(["Some":"Thing"], ofType: DiskJSON.self)
p.info // -> "fromDisk"

working with Swift PM

It seems Swift PM only handles numbers in versions, hence the current version tagging scheme (prepending with v as github suggests) doesn't allow to use this library as a package dependency (it only pick ups the old 0.3.1 version). Changing the tag names to omit the v fixes this.

Does `=>?` actually work as intended?

I can’t figure out for the life of me why the following does not work:

struct Foo: Decodable {
    let bar: String?

    static func decode(object: AnyObject) throws -> Foo {
        return try Foo(bar: object =>? "bar")
    }
}

When parsing the following JSON:

{
  "bar": null
}

I would expect decode to not throw, and instead return Foo(bar: nil). What happens instead, is MissingKeyError(key: "bar").

Rethink errors

DecidingError.Info is really strange, perhaps there is a better way

lazy? (decodeArrayLazily)

public func decodeArrayLazily<T>(elementDecodeClosure: AnyObject throws -> T) -> (json: AnyObject) throws -> [T] {
    return { json in
        return try (NSArray.decode(json) as [AnyObject]).lazy.map { try elementDecodeClosure($0) }
    }
}

Perhaps this could be useful. I should investigate performance sometime.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.