Support object schemas with only additionalProperties. (#6492)

Previously, we had implemented the Codable protocol by simply claiming conformance, and making sure that each of our internal classes also implemented the Codable protocol. So our model classes looked like:

class MyModel: Codable {
   var propInt: Int
   var propString: String
}

class MyOtherModel: Codable {
   var propModel: MyModel
}

Previously, our additionalProperties implementation would have meant an object schema with an additionalProperties of Int type would have looked like:

class MyModelWithAdditionalProperties: Codable {
   var additionalProperties: [String: Int]
}

But the default implementation of Codable would have serialized MyModelWithAdditionalProperties like this:

{
  "additionalProperties": {
    "myInt1": 1,
    "myInt2": 2,
    "myInt3": 3
  }
}

The default implementation would put the additionalProperties in its own dictionary (which would be incorrect), as opposed to the desired serialization of:

{
  "myInt1": 1,
  "myInt2": 2,
  "myInt3": 3
}

So therefore, the only way to support this was to do our own implementation of the Codable protocol. The Codable protocol is actually two protocols: Encodable and Decodable.

So therefore, this change generates implementations of Encodable and Decodable for each generated model class. So the new generated classes look like:

class MyModel: Codable {
   var propInt: Int
   var propString: String

   // Encodable protocol methods

   public func encode(to encoder: Encoder) throws {

        var container = encoder.container(keyedBy: String.self)

        try container.encode(propInt, forKey: "propInt")
        try container.encode(propString, forKey: "propString")
    }

    // Decodable protocol methods

    public required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: String.self)

        propInt = try container.decode(Int.self, forKey: "propInt")
        propString = try container.decode(String.self, forKey: "propString")
    }

}

class MyOtherModel: Codable {
   var propModel: MyModel

   // Encodable protocol methods

   public func encode(to encoder: Encoder) throws {

        var container = encoder.container(keyedBy: String.self)

        try container.encode(propModel, forKey: "propModel")
    }

    // Decodable protocol methods

    public required init(from decoder: Decoder) throws {
        let container = try decoder.container(keyedBy: String.self)

        propModel = try container.decode(MyModel.self, forKey: "propModel")
    }

}
This commit is contained in:
ehyche
2017-09-18 13:04:50 -04:00
committed by wing328
parent 8067612e06
commit b807f6ff96
40 changed files with 814 additions and 247 deletions

View File

@@ -1,4 +1,4 @@
Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/)
Copyright (c) 2014-2017 Alamofire Software Foundation (http://alamofire.org/)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

View File

@@ -1,9 +1,9 @@
![Alamofire: Elegant Networking in Swift](https://raw.githubusercontent.com/Alamofire/Alamofire/assets/alamofire.png)
![Alamofire: Elegant Networking in Swift](https://raw.githubusercontent.com/Alamofire/Alamofire/master/alamofire.png)
[![Build Status](https://travis-ci.org/Alamofire/Alamofire.svg?branch=master)](https://travis-ci.org/Alamofire/Alamofire)
[![CocoaPods Compatible](https://img.shields.io/cocoapods/v/Alamofire.svg)](https://img.shields.io/cocoapods/v/Alamofire.svg)
[![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)
[![Platform](https://img.shields.io/cocoapods/p/Alamofire.svg?style=flat)](http://cocoadocs.org/docsets/Alamofire)
[![Platform](https://img.shields.io/cocoapods/p/Alamofire.svg?style=flat)](https://alamofire.github.io/Alamofire)
[![Twitter](https://img.shields.io/badge/twitter-@AlamofireSF-blue.svg?style=flat)](http://twitter.com/AlamofireSF)
[![Gitter](https://badges.gitter.im/Alamofire/Alamofire.svg)](https://gitter.im/Alamofire/Alamofire?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge)
@@ -45,7 +45,7 @@ Alamofire is an HTTP networking library written in Swift.
- [x] TLS Certificate and Public Key Pinning
- [x] Network Reachability
- [x] Comprehensive Unit and Integration Test Coverage
- [x] [Complete Documentation](http://cocoadocs.org/docsets/Alamofire)
- [x] [Complete Documentation](https://alamofire.github.io/Alamofire)
## Component Libraries
@@ -57,8 +57,8 @@ In order to keep Alamofire focused specifically on core networking implementatio
## Requirements
- iOS 8.0+ / macOS 10.10+ / tvOS 9.0+ / watchOS 2.0+
- Xcode 8.1, 8.2, 8.3, and 9.0
- Swift 3.0, 3.1, 3.2, and 4.0
- Xcode 8.3+
- Swift 3.1+
## Migration Guides
@@ -84,7 +84,7 @@ In order to keep Alamofire focused specifically on core networking implementatio
$ gem install cocoapods
```
> CocoaPods 1.1.0+ is required to build Alamofire 4.0.0+.
> CocoaPods 1.1+ is required to build Alamofire 4.0+.
To integrate Alamofire into your Xcode project using CocoaPods, specify it in your `Podfile`:
@@ -94,7 +94,7 @@ platform :ios, '10.0'
use_frameworks!
target '<Your Target Name>' do
pod 'Alamofire', '~> 4.4'
pod 'Alamofire', '~> 4.5'
end
```
@@ -118,7 +118,7 @@ $ brew install carthage
To integrate Alamofire into your Xcode project using Carthage, specify it in your `Cartfile`:
```ogdl
github "Alamofire/Alamofire" ~> 4.4
github "Alamofire/Alamofire" ~> 4.5
```
Run `carthage update` to build the framework and drag the built `Alamofire.framework` into your Xcode project.
@@ -736,7 +736,7 @@ When sending relatively small amounts of data to a server using JSON or URL enco
#### Uploading Data
```swift
let imageData = UIPNGRepresentation(image)!
let imageData = UIImagePNGRepresentation(image)!
Alamofire.upload(imageData, to: "https://httpbin.org/post").responseJSON { response in
debugPrint(response)
@@ -1812,10 +1812,15 @@ There are some important things to remember when using network reachability to d
The following radars have some effect on the current implementation of Alamofire.
- [`rdar://21349340`](http://www.openradar.me/radar?id=5517037090635776) - Compiler throwing warning due to toll-free bridging issue in test case
- [`rdar://26761490`](http://www.openradar.me/radar?id=5010235949318144) - Swift string interpolation causing memory leak with common usage
- `rdar://26870455` - Background URL Session Configurations do not work in the simulator
- `rdar://26849668` - Some URLProtocol APIs do not properly handle `URLRequest`
## Resolved Radars
The following radars have been resolved over time after being filed against the Alamofire project.
- [`rdar://26761490`](http://www.openradar.me/radar?id=5010235949318144) - Swift string interpolation causing memory leak with common usage (Resolved on 9/1/17 in Xcode 9 beta 6).
## FAQ
### What's the origin of the name Alamofire?

View File

@@ -1,7 +1,7 @@
//
// AFError.swift
//
// Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/)
// Copyright (c) 2014-2017 Alamofire Software Foundation (http://alamofire.org/)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal

View File

@@ -1,7 +1,7 @@
//
// Alamofire.swift
//
// Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/)
// Copyright (c) 2014-2017 Alamofire Software Foundation (http://alamofire.org/)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal

View File

@@ -1,7 +1,7 @@
//
// DispatchQueue+Alamofire.swift
//
// Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/)
// Copyright (c) 2014-2017 Alamofire Software Foundation (http://alamofire.org/)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal

View File

@@ -1,7 +1,7 @@
//
// MultipartFormData.swift
//
// Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/)
// Copyright (c) 2014-2017 Alamofire Software Foundation (http://alamofire.org/)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal

View File

@@ -1,7 +1,7 @@
//
// NetworkReachabilityManager.swift
//
// Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/)
// Copyright (c) 2014-2017 Alamofire Software Foundation (http://alamofire.org/)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal

View File

@@ -1,7 +1,7 @@
//
// Notifications.swift
//
// Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/)
// Copyright (c) 2014-2017 Alamofire Software Foundation (http://alamofire.org/)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal

View File

@@ -1,7 +1,7 @@
//
// ParameterEncoding.swift
//
// Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/)
// Copyright (c) 2014-2017 Alamofire Software Foundation (http://alamofire.org/)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -223,9 +223,9 @@ public struct URLEncoding: ParameterEncoding {
let endIndex = string.index(index, offsetBy: batchSize, limitedBy: string.endIndex) ?? string.endIndex
let range = startIndex..<endIndex
let substring = string.substring(with: range)
let substring = string[range]
escaped += substring.addingPercentEncoding(withAllowedCharacters: allowedCharacterSet) ?? substring
escaped += substring.addingPercentEncoding(withAllowedCharacters: allowedCharacterSet) ?? String(substring)
index = endIndex
}
@@ -241,11 +241,7 @@ public struct URLEncoding: ParameterEncoding {
let value = parameters[key]!
components += queryComponents(fromKey: key, value: value)
}
#if swift(>=4.0)
return components.map { "\($0.0)=\($0.1)" }.joined(separator: "&")
#else
return components.map { "\($0)=\($1)" }.joined(separator: "&")
#endif
}
private func encodesParametersInURL(with method: HTTPMethod) -> Bool {

View File

@@ -1,7 +1,7 @@
//
// Request.swift
//
// Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/)
// Copyright (c) 2014-2017 Alamofire Software Foundation (http://alamofire.org/)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -293,11 +293,12 @@ extension Request: CustomDebugStringConvertible {
if let credentials = credentialStorage.credentials(for: protectionSpace)?.values {
for credential in credentials {
components.append("-u \(credential.user!):\(credential.password!)")
guard let user = credential.user, let password = credential.password else { continue }
components.append("-u \(user):\(password)")
}
} else {
if let credential = delegate.credential {
components.append("-u \(credential.user!):\(credential.password!)")
if let credential = delegate.credential, let user = credential.user, let password = credential.password {
components.append("-u \(user):\(password)")
}
}
}
@@ -308,7 +309,12 @@ extension Request: CustomDebugStringConvertible {
let cookies = cookieStorage.cookies(for: url), !cookies.isEmpty
{
let string = cookies.reduce("") { $0 + "\($1.name)=\($1.value);" }
#if swift(>=3.2)
components.append("-b \"\(string[..<string.index(before: string.endIndex)])\"")
#else
components.append("-b \"\(string.substring(to: string.characters.index(before: string.endIndex)))\"")
#endif
}
}

View File

@@ -1,7 +1,7 @@
//
// Response.swift
//
// Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/)
// Copyright (c) 2014-2017 Alamofire Software Foundation (http://alamofire.org/)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal

View File

@@ -1,7 +1,7 @@
//
// ResponseSerialization.swift
//
// Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/)
// Copyright (c) 2014-2017 Alamofire Software Foundation (http://alamofire.org/)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal

View File

@@ -1,7 +1,7 @@
//
// Result.swift
//
// Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/)
// Copyright (c) 2014-2017 Alamofire Software Foundation (http://alamofire.org/)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal

View File

@@ -1,7 +1,7 @@
//
// ServerTrustPolicy.swift
//
// Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/)
// Copyright (c) 2014-2017 Alamofire Software Foundation (http://alamofire.org/)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal

View File

@@ -1,7 +1,7 @@
//
// SessionDelegate.swift
//
// Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/)
// Copyright (c) 2014-2017 Alamofire Software Foundation (http://alamofire.org/)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal

View File

@@ -1,7 +1,7 @@
//
// SessionManager.swift
//
// Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/)
// Copyright (c) 2014-2017 Alamofire Software Foundation (http://alamofire.org/)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -58,18 +58,10 @@ open class SessionManager {
let acceptEncoding: String = "gzip;q=1.0, compress;q=0.5"
// Accept-Language HTTP Header; see https://tools.ietf.org/html/rfc7231#section-5.3.5
#if swift(>=4.0)
let acceptLanguage = Locale.preferredLanguages.prefix(6).enumerated().map { enumeratedLanguage in
let (index, languageCode) = enumeratedLanguage
let quality = 1.0 - (Double(index) * 0.1)
return "\(languageCode);q=\(quality)"
}.joined(separator: ", ")
#else
let acceptLanguage = Locale.preferredLanguages.prefix(6).enumerated().map { index, languageCode in
let quality = 1.0 - (Double(index) * 0.1)
return "\(languageCode);q=\(quality)"
}.joined(separator: ", ")
#endif
// User-Agent Header; see https://tools.ietf.org/html/rfc7231#section-5.5.3
// Example: `iOS Example/1.0 (org.alamofire.iOS-Example; build:1; iOS 10.0.0) Alamofire/4.0.0`

View File

@@ -1,7 +1,7 @@
//
// TaskDelegate.swift
//
// Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/)
// Copyright (c) 2014-2017 Alamofire Software Foundation (http://alamofire.org/)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -40,17 +40,30 @@ open class TaskDelegate: NSObject {
public var error: Error?
var task: URLSessionTask? {
didSet { reset() }
set {
taskLock.lock(); defer { taskLock.unlock() }
_task = newValue
}
get {
taskLock.lock(); defer { taskLock.unlock() }
return _task
}
}
var initialResponseTime: CFAbsoluteTime?
var credential: URLCredential?
var metrics: AnyObject? // URLSessionTaskMetrics
private var _task: URLSessionTask? {
didSet { reset() }
}
private let taskLock = NSLock()
// MARK: Lifecycle
init(task: URLSessionTask?) {
self.task = task
_task = task
self.queue = {
let operationQueue = OperationQueue()

View File

@@ -1,7 +1,7 @@
//
// Timeline.swift
//
// Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/)
// Copyright (c) 2014-2017 Alamofire Software Foundation (http://alamofire.org/)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal

View File

@@ -1,7 +1,7 @@
//
// Validation.swift
//
// Copyright (c) 2014-2016 Alamofire Software Foundation (http://alamofire.org/)
// Copyright (c) 2014-2017 Alamofire Software Foundation (http://alamofire.org/)
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -48,7 +48,13 @@ extension Request {
init?(_ string: String) {
let components: [String] = {
let stripped = string.trimmingCharacters(in: .whitespacesAndNewlines)
#if swift(>=3.2)
let split = stripped[..<(stripped.range(of: ";")?.lowerBound ?? stripped.endIndex)]
#else
let split = stripped.substring(to: stripped.range(of: ";")?.lowerBound ?? stripped.endIndex)
#endif
return split.components(separatedBy: "/")
}()