[Swift] Use ISOFullDate for date format

This commit is contained in:
Jason Gavris 2016-09-12 13:44:28 -04:00 committed by wing328
parent 40610373f6
commit f639a312e8
41 changed files with 748 additions and 172 deletions

View File

@ -115,8 +115,8 @@ public class SwiftCodegen extends DefaultCodegen implements CodegenConfig {
typeMapping.put("array", "Array");
typeMapping.put("List", "Array");
typeMapping.put("map", "Dictionary");
typeMapping.put("date", "NSDate");
typeMapping.put("Date", "NSDate");
typeMapping.put("date", "ISOFullDate");
typeMapping.put("Date", "ISOFullDate");
typeMapping.put("DateTime", "NSDate");
typeMapping.put("boolean", "Bool");
typeMapping.put("string", "String");

View File

@ -84,6 +84,99 @@ extension NSUUID: JSONEncodable {
}
}
/// Represents an ISO-8601 full-date (RFC-3339).
/// ex: 12-31-1999
/// https://xml2rfc.tools.ietf.org/public/rfc/html/rfc3339.html#anchor14
public final class ISOFullDate: CustomStringConvertible {
public let year: Int
public let month: Int
public let day: Int
public init(year year: Int, month: Int, day: Int) {
self.year = year
self.month = month
self.day = day
}
/**
Converts an NSDate to an ISOFullDate. Only interested in the year, month, day components.
- parameter date: The date to convert.
- returns: An ISOFullDate constructed from the year, month, day of the date.
*/
public static func from(date date: NSDate) -> ISOFullDate? {
guard let calendar = NSCalendar(identifier: NSCalendarIdentifierGregorian) else {
return nil
}
let components = calendar.components(
[
.Year,
.Month,
.Day,
],
fromDate: date
)
return ISOFullDate(
year: components.year,
month: components.month,
day: components.day
)
}
/**
Converts a ISO-8601 full-date string to an ISOFullDate.
- parameter string: The ISO-8601 full-date format string to convert.
- returns: An ISOFullDate constructed from the string.
*/
public static func from(string string: String) -> ISOFullDate? {
let components = string
.characters
.split("-")
.map(String.init)
.flatMap { Int($0) }
guard components.count == 3 else { return nil }
return ISOFullDate(
year: components[0],
month: components[1],
day: components[2]
)
}
/**
Converts the receiver to an NSDate, in the default time zone.
- returns: An NSDate from the components of the receiver, in the default time zone.
*/
public func toDate() -> NSDate? {
let components = NSDateComponents()
components.year = year
components.month = month
components.day = day
components.timeZone = NSTimeZone.defaultTimeZone()
let calendar = NSCalendar(identifier: NSCalendarIdentifierGregorian)
return calendar?.dateFromComponents(components)
}
// MARK: CustomStringConvertible
public var description: String {
return "\(year)-\(month)-\(day)"
}
}
extension ISOFullDate: JSONEncodable {
public func encodeToJSON() -> AnyObject {
return "\(year)-\(month)-\(day)"
}
}
{{#usePromiseKit}}extension RequestBuilder {
public func execute() -> Promise<Response<T>> {
let deferred = Promise<Response<T>>.pendingPromise()

View File

@ -139,7 +139,16 @@ class Decoders {
return NSDate(timeIntervalSince1970: Double(sourceInt / 1000) )
}
fatalError("formatter failed to parse \(source)")
} {{#models}}{{#model}}
}
// Decoder for ISOFullDate
Decoders.addDecoder(clazz: ISOFullDate.self, decoder: { (source: AnyObject) -> ISOFullDate in
if let string = source as? String,
let isoDate = ISOFullDate.from(string: string) {
return isoDate
}
fatalError("formatter failed to parse \(source)")
}) {{#models}}{{#model}}
// Decoder for [{{{classname}}}]
Decoders.addDecoder(clazz: [{{{classname}}}].self) { (source: AnyObject) -> [{{{classname}}}] in

View File

@ -63,6 +63,18 @@ public class SwiftCodegenTest {
Assert.assertTrue(op.responses.get(0).isBinary);
}
@Test(description = "returns ISOFullDate when response format is date")
public void dateTest() {
final Swagger model = new SwaggerParser().read("src/test/resources/2_0/datePropertyTest.json");
final DefaultCodegen codegen = new SwiftCodegen();
final String path = "/tests/dateResponse";
final Operation p = model.getPaths().get(path).getPost();
final CodegenOperation op = codegen.fromOperation(path, "post", p, model.getDefinitions());
Assert.assertEquals(op.returnType, "ISOFullDate");
Assert.assertEquals(op.bodyParam.dataType, "ISOFullDate");
}
@Test
public void testDefaultPodAuthors() throws Exception {
// Given

View File

@ -21,6 +21,7 @@ public class SwiftModelTest {
.property("binary", new BinaryProperty())
.property("byte", new ByteArrayProperty())
.property("uuid", new UUIDProperty())
.property("dateOfBirth", new DateProperty())
.required("id")
.required("name")
.discriminator("test");
@ -30,7 +31,7 @@ public class SwiftModelTest {
Assert.assertEquals(cm.name, "sample");
Assert.assertEquals(cm.classname, "Sample");
Assert.assertEquals(cm.description, "a sample model");
Assert.assertEquals(cm.vars.size(), 6);
Assert.assertEquals(cm.vars.size(), 7);
Assert.assertEquals(cm.discriminator,"test");
final CodegenProperty property1 = cm.vars.get(0);
@ -91,9 +92,19 @@ public class SwiftModelTest {
Assert.assertEquals(property6.name, "uuid");
Assert.assertNull(property6.defaultValue);
Assert.assertEquals(property6.baseType, "NSUUID");
Assert.assertNull(property6.hasMore);
Assert.assertTrue(property6.hasMore);
Assert.assertNull(property6.required);
Assert.assertTrue(property6.isNotContainer);
final CodegenProperty property7 = cm.vars.get(6);
Assert.assertEquals(property7.baseName, "dateOfBirth");
Assert.assertEquals(property7.datatype, "ISOFullDate");
Assert.assertEquals(property7.name, "dateOfBirth");
Assert.assertNull(property7.defaultValue);
Assert.assertEquals(property7.baseType, "ISOFullDate");
Assert.assertNull(property7.hasMore);
Assert.assertNull(property7.required);
Assert.assertTrue(property7.isNotContainer);
}
}

View File

@ -0,0 +1,45 @@
{
"swagger": "2.0",
"info": {
"description": "This is a sample server Petstore server. You can find out more about Swagger at <a href=\"http://swagger.io\">http://swagger.io</a> or on irc.freenode.net, #swagger. For this sample, you can use the api key \"special-key\" to test the authorization filters",
"version": "1.0.0",
"title": "Swagger Petstore",
"termsOfService": "http://helloreverb.com/terms/",
"license": {
"name": "Apache 2.0",
"url": "http://www.apache.org/licenses/LICENSE-2.0.html"
}
},
"basePath": "/v2",
"schemes": [
"http"
],
"paths": {
"/tests/dateResponse": {
"post": {
"summary": "Echo test",
"operationId": "echotest",
"parameters": [
{
"name": "InputDate",
"in": "body",
"required": true,
"schema": {
"type": "string",
"format": "date"
}
}
],
"responses": {
"200": {
"description": "OutputDate",
"schema": {
"type": "string",
"format": "date"
}
}
}
}
}
}
}

View File

@ -833,6 +833,10 @@
"username": {
"type": "string"
},
"dateOfBirth": {
"type": "string",
"format": "date"
},
"firstName": {
"type": "string"
},

View File

@ -179,6 +179,7 @@ public class UserAPI: APIBase {
}}, {contentType=application/xml, example=<User>
<id>123456</id>
<username>string</username>
<dateOfBirth>2000-01-23T04:56:07.000Z</dateOfBirth>
<firstName>string</firstName>
<lastName>string</lastName>
<email>string</email>
@ -198,6 +199,7 @@ public class UserAPI: APIBase {
}}, {contentType=application/xml, example=<User>
<id>123456</id>
<username>string</username>
<dateOfBirth>2000-01-23T04:56:07.000Z</dateOfBirth>
<firstName>string</firstName>
<lastName>string</lastName>
<email>string</email>

View File

@ -83,4 +83,97 @@ extension NSUUID: JSONEncodable {
}
}
/// Represents an ISO-8601 full-date (RFC-3339).
/// ex: 12-31-1999
/// https://xml2rfc.tools.ietf.org/public/rfc/html/rfc3339.html#anchor14
public final class ISOFullDate: CustomStringConvertible {
public let year: Int
public let month: Int
public let day: Int
public init(year year: Int, month: Int, day: Int) {
self.year = year
self.month = month
self.day = day
}
/**
Converts an NSDate to an ISOFullDate. Only interested in the year, month, day components.
- parameter date: The date to convert.
- returns: An ISOFullDate constructed from the year, month, day of the date.
*/
public static func from(date date: NSDate) -> ISOFullDate? {
guard let calendar = NSCalendar(identifier: NSCalendarIdentifierGregorian) else {
return nil
}
let components = calendar.components(
[
.Year,
.Month,
.Day,
],
fromDate: date
)
return ISOFullDate(
year: components.year,
month: components.month,
day: components.day
)
}
/**
Converts a ISO-8601 full-date string to an ISOFullDate.
- parameter string: The ISO-8601 full-date format string to convert.
- returns: An ISOFullDate constructed from the string.
*/
public static func from(string string: String) -> ISOFullDate? {
let components = string
.characters
.split("-")
.map(String.init)
.flatMap { Int($0) }
guard components.count == 3 else { return nil }
return ISOFullDate(
year: components[0],
month: components[1],
day: components[2]
)
}
/**
Converts the receiver to an NSDate, in the default time zone.
- returns: An NSDate from the components of the receiver, in the default time zone.
*/
public func toDate() -> NSDate? {
let components = NSDateComponents()
components.year = year
components.month = month
components.day = day
components.timeZone = NSTimeZone.defaultTimeZone()
let calendar = NSCalendar(identifier: NSCalendarIdentifierGregorian)
return calendar?.dateFromComponents(components)
}
// MARK: CustomStringConvertible
public var description: String {
return "\(year)-\(month)-\(day)"
}
}
extension ISOFullDate: JSONEncodable {
public func encodeToJSON() -> AnyObject {
return "\(year)-\(month)-\(day)"
}
}

View File

@ -141,6 +141,15 @@ class Decoders {
fatalError("formatter failed to parse \(source)")
}
// Decoder for ISOFullDate
Decoders.addDecoder(clazz: ISOFullDate.self, decoder: { (source: AnyObject) -> ISOFullDate in
if let string = source as? String,
let isoDate = ISOFullDate.from(string: string) {
return isoDate
}
fatalError("formatter failed to parse \(source)")
})
// Decoder for [Category]
Decoders.addDecoder(clazz: [Category].self) { (source: AnyObject) -> [Category] in
return Decoders.decode(clazz: [Category].self, source: source)
@ -215,6 +224,7 @@ class Decoders {
let instance = User()
instance.id = Decoders.decodeOptional(clazz: Int64.self, source: sourceDictionary["id"])
instance.username = Decoders.decodeOptional(clazz: String.self, source: sourceDictionary["username"])
instance.dateOfBirth = Decoders.decodeOptional(clazz: ISOFullDate.self, source: sourceDictionary["dateOfBirth"])
instance.firstName = Decoders.decodeOptional(clazz: String.self, source: sourceDictionary["firstName"])
instance.lastName = Decoders.decodeOptional(clazz: String.self, source: sourceDictionary["lastName"])
instance.email = Decoders.decodeOptional(clazz: String.self, source: sourceDictionary["email"])

View File

@ -11,6 +11,7 @@ import Foundation
public class User: JSONEncodable {
public var id: Int64?
public var username: String?
public var dateOfBirth: ISOFullDate?
public var firstName: String?
public var lastName: String?
public var email: String?
@ -26,6 +27,7 @@ public class User: JSONEncodable {
var nillableDictionary = [String:AnyObject?]()
nillableDictionary["id"] = self.id?.encodeToJSON()
nillableDictionary["username"] = self.username
nillableDictionary["dateOfBirth"] = self.dateOfBirth?.encodeToJSON()
nillableDictionary["firstName"] = self.firstName
nillableDictionary["lastName"] = self.lastName
nillableDictionary["email"] = self.email

View File

@ -17,6 +17,7 @@
6D4EFBB71C693BED00B96B06 /* StoreAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D4EFBB61C693BED00B96B06 /* StoreAPITests.swift */; };
6D4EFBB91C693BFC00B96B06 /* UserAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D4EFBB81C693BFC00B96B06 /* UserAPITests.swift */; };
751C65B82F596107A3DC8ED9 /* Pods_SwaggerClient.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8F60AECFF321A25553B6A5B0 /* Pods_SwaggerClient.framework */; };
C6AAAD211D8718D00016A515 /* ISOFullDateTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C6AAAD201D8718D00016A515 /* ISOFullDateTests.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@ -47,6 +48,7 @@
A638467ACFB30852DEA51F7A /* Pods-SwaggerClient.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SwaggerClient.debug.xcconfig"; path = "Pods/Target Support Files/Pods-SwaggerClient/Pods-SwaggerClient.debug.xcconfig"; sourceTree = "<group>"; };
B4B2BEC2ECA535C616F2F3FE /* Pods-SwaggerClientTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SwaggerClientTests.release.xcconfig"; path = "Pods/Target Support Files/Pods-SwaggerClientTests/Pods-SwaggerClientTests.release.xcconfig"; sourceTree = "<group>"; };
C07EC0A94AA0F86D60668B32 /* Pods.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods.framework; sourceTree = BUILT_PRODUCTS_DIR; };
C6AAAD201D8718D00016A515 /* ISOFullDateTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ISOFullDateTests.swift; sourceTree = "<group>"; };
F65B6638217EDDC99D103B16 /* Pods_SwaggerClientTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_SwaggerClientTests.framework; sourceTree = BUILT_PRODUCTS_DIR; };
FC60BDC7328C2AA916F25840 /* Pods-SwaggerClient.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-SwaggerClient.release.xcconfig"; path = "Pods/Target Support Files/Pods-SwaggerClient/Pods-SwaggerClient.release.xcconfig"; sourceTree = "<group>"; };
/* End PBXFileReference section */
@ -129,6 +131,7 @@
isa = PBXGroup;
children = (
6D4EFBAB1C692C6300B96B06 /* Info.plist */,
C6AAAD201D8718D00016A515 /* ISOFullDateTests.swift */,
6D4EFBB41C693BE200B96B06 /* PetAPITests.swift */,
6D4EFBB61C693BED00B96B06 /* StoreAPITests.swift */,
6D4EFBB81C693BFC00B96B06 /* UserAPITests.swift */,
@ -473,6 +476,7 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
C6AAAD211D8718D00016A515 /* ISOFullDateTests.swift in Sources */,
6D4EFBB71C693BED00B96B06 /* StoreAPITests.swift in Sources */,
6D4EFBB91C693BFC00B96B06 /* UserAPITests.swift in Sources */,
6D4EFBB51C693BE200B96B06 /* PetAPITests.swift in Sources */,

View File

@ -0,0 +1,83 @@
/// Copyright 2012-2016 (C) Butterfly Network, Inc.
import PetstoreClient
import XCTest
@testable import SwaggerClient
final class ISOFullDateTests: XCTestCase {
var fullDate = ISOFullDate(year: 1999, month: 12, day: 31)
func testValidDate() {
XCTAssertEqual(fullDate.year, 1999)
XCTAssertEqual(fullDate.month, 12)
XCTAssertEqual(fullDate.day, 31)
}
func testFromDate() {
let date = NSDate()
let fullDate = ISOFullDate.from(date: date)
guard let calendar = NSCalendar(identifier: NSCalendarIdentifierGregorian) else {
XCTFail()
return
}
let components = calendar.components(
[
.Year,
.Month,
.Day,
],
fromDate: date
)
XCTAssertEqual(fullDate?.year, components.year)
XCTAssertEqual(fullDate?.month, components.month)
XCTAssertEqual(fullDate?.day, components.day)
}
func testFromString() {
let string = "1999-12-31"
let fullDate = ISOFullDate.from(string: string)
XCTAssertEqual(fullDate?.year, 1999)
XCTAssertEqual(fullDate?.month, 12)
XCTAssertEqual(fullDate?.day, 31)
}
func testFromInvalidString() {
XCTAssertNil(ISOFullDate.from(string: "1999-12"))
}
func testToDate() {
let fullDate = ISOFullDate(year: 1999, month: 12, day: 31)
guard let date = fullDate.toDate(),
let calendar = NSCalendar(identifier: NSCalendarIdentifierGregorian) else {
XCTFail()
return
}
let components = calendar.components(
[
.Year,
.Month,
.Day,
],
fromDate: date
)
XCTAssertEqual(fullDate.year, components.year)
XCTAssertEqual(fullDate.month, components.month)
XCTAssertEqual(fullDate.day, components.day)
}
func testDescription() {
XCTAssertEqual(fullDate.description, "1999-12-31")
}
func testEncodeToJSON() {
XCTAssertEqual(fullDate.encodeToJSON() as? String, "1999-12-31")
}
}

View File

@ -14,16 +14,6 @@ class UserAPITests: XCTestCase {
let testTimeout = 10.0
override func setUp() {
super.setUp()
// Put setup code here. This method is called before the invocation of each test method in the class.
}
override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
super.tearDown()
}
func testLogin() {
let expectation = self.expectationWithDescription("testLogin")
@ -59,6 +49,7 @@ class UserAPITests: XCTestCase {
let newUser = User()
newUser.email = "test@test.com"
newUser.dateOfBirth = ISOFullDate.from(string: "1999-12-31")
newUser.firstName = "Test"
newUser.lastName = "Tester"
newUser.id = 1000
@ -95,6 +86,7 @@ class UserAPITests: XCTestCase {
XCTAssert(user.lastName == "Tester", "invalid lastName")
XCTAssert(user.password == "test!", "invalid password")
XCTAssert(user.phone == "867-5309", "invalid phone")
XCTAssert(user.dateOfBirth?.description == "1999-12-31", "invalid date of birth")
expectation.fulfill()
}

View File

@ -259,12 +259,14 @@ public class UserAPI: APIBase {
"password" : "aeiou",
"userStatus" : 123,
"phone" : "aeiou",
"dateOfBirth" : "2000-01-23T04:56:07.000+00:00",
"id" : 123456789,
"email" : "aeiou",
"username" : "aeiou"
}}, {contentType=application/xml, example=<User>
<id>123456</id>
<username>string</username>
<dateOfBirth>2000-01-23T04:56:07.000Z</dateOfBirth>
<firstName>string</firstName>
<lastName>string</lastName>
<email>string</email>
@ -278,12 +280,14 @@ public class UserAPI: APIBase {
"password" : "aeiou",
"userStatus" : 123,
"phone" : "aeiou",
"dateOfBirth" : "2000-01-23T04:56:07.000+00:00",
"id" : 123456789,
"email" : "aeiou",
"username" : "aeiou"
}}, {contentType=application/xml, example=<User>
<id>123456</id>
<username>string</username>
<dateOfBirth>2000-01-23T04:56:07.000Z</dateOfBirth>
<firstName>string</firstName>
<lastName>string</lastName>
<email>string</email>

View File

@ -84,6 +84,99 @@ extension NSUUID: JSONEncodable {
}
}
/// Represents an ISO-8601 full-date (RFC-3339).
/// ex: 12-31-1999
/// https://xml2rfc.tools.ietf.org/public/rfc/html/rfc3339.html#anchor14
public final class ISOFullDate: CustomStringConvertible {
public let year: Int
public let month: Int
public let day: Int
public init(year year: Int, month: Int, day: Int) {
self.year = year
self.month = month
self.day = day
}
/**
Converts an NSDate to an ISOFullDate. Only interested in the year, month, day components.
- parameter date: The date to convert.
- returns: An ISOFullDate constructed from the year, month, day of the date.
*/
public static func from(date date: NSDate) -> ISOFullDate? {
guard let calendar = NSCalendar(identifier: NSCalendarIdentifierGregorian) else {
return nil
}
let components = calendar.components(
[
.Year,
.Month,
.Day,
],
fromDate: date
)
return ISOFullDate(
year: components.year,
month: components.month,
day: components.day
)
}
/**
Converts a ISO-8601 full-date string to an ISOFullDate.
- parameter string: The ISO-8601 full-date format string to convert.
- returns: An ISOFullDate constructed from the string.
*/
public static func from(string string: String) -> ISOFullDate? {
let components = string
.characters
.split("-")
.map(String.init)
.flatMap { Int($0) }
guard components.count == 3 else { return nil }
return ISOFullDate(
year: components[0],
month: components[1],
day: components[2]
)
}
/**
Converts the receiver to an NSDate, in the default time zone.
- returns: An NSDate from the components of the receiver, in the default time zone.
*/
public func toDate() -> NSDate? {
let components = NSDateComponents()
components.year = year
components.month = month
components.day = day
components.timeZone = NSTimeZone.defaultTimeZone()
let calendar = NSCalendar(identifier: NSCalendarIdentifierGregorian)
return calendar?.dateFromComponents(components)
}
// MARK: CustomStringConvertible
public var description: String {
return "\(year)-\(month)-\(day)"
}
}
extension ISOFullDate: JSONEncodable {
public func encodeToJSON() -> AnyObject {
return "\(year)-\(month)-\(day)"
}
}
extension RequestBuilder {
public func execute() -> Promise<Response<T>> {
let deferred = Promise<Response<T>>.pendingPromise()

View File

@ -141,6 +141,15 @@ class Decoders {
fatalError("formatter failed to parse \(source)")
}
// Decoder for ISOFullDate
Decoders.addDecoder(clazz: ISOFullDate.self, decoder: { (source: AnyObject) -> ISOFullDate in
if let string = source as? String,
let isoDate = ISOFullDate.from(string: string) {
return isoDate
}
fatalError("formatter failed to parse \(source)")
})
// Decoder for [Category]
Decoders.addDecoder(clazz: [Category].self) { (source: AnyObject) -> [Category] in
return Decoders.decode(clazz: [Category].self, source: source)
@ -215,6 +224,7 @@ class Decoders {
let instance = User()
instance.id = Decoders.decodeOptional(clazz: Int64.self, source: sourceDictionary["id"])
instance.username = Decoders.decodeOptional(clazz: String.self, source: sourceDictionary["username"])
instance.dateOfBirth = Decoders.decodeOptional(clazz: ISOFullDate.self, source: sourceDictionary["dateOfBirth"])
instance.firstName = Decoders.decodeOptional(clazz: String.self, source: sourceDictionary["firstName"])
instance.lastName = Decoders.decodeOptional(clazz: String.self, source: sourceDictionary["lastName"])
instance.email = Decoders.decodeOptional(clazz: String.self, source: sourceDictionary["email"])

View File

@ -11,6 +11,7 @@ import Foundation
public class User: JSONEncodable {
public var id: Int64?
public var username: String?
public var dateOfBirth: ISOFullDate?
public var firstName: String?
public var lastName: String?
public var email: String?
@ -26,6 +27,7 @@ public class User: JSONEncodable {
var nillableDictionary = [String:AnyObject?]()
nillableDictionary["id"] = self.id?.encodeToJSON()
nillableDictionary["username"] = self.username
nillableDictionary["dateOfBirth"] = self.dateOfBirth?.encodeToJSON()
nillableDictionary["firstName"] = self.firstName
nillableDictionary["lastName"] = self.lastName
nillableDictionary["email"] = self.email

View File

@ -718,7 +718,7 @@ Requests can be suspended, resumed, and cancelled:
Before implementing custom response serializers or object serialization methods, it's important to be prepared to handle any errors that may occur. Alamofire recommends handling these through the use of either your own `NSError` creation methods, or a simple `enum` that conforms to `ErrorType`. For example, this `BackendError` type, which will be used in later examples:
```swift
enum BackendError: ErrorType {
public enum BackendError: ErrorType {
case Network(error: NSError)
case DataSerialization(reason: String)
case JSONSerialization(error: NSError)
@ -1288,7 +1288,7 @@ The [ASF](https://github.com/Alamofire/Foundation#members) is looking to raise m
* Potentially fund test servers to make it easier for us to test the edge cases
* Potentially fund developers to work on one of our projects full-time
The community adoption of the ASF libraries has been amazing. We are greatly humbled by your enthusiasm around the projects, and want to continue to do everything we can to move the needle forward. With your continued support, the ASF will be able to improve its reach and also provide better legal safety for the core members. If you use any of our libraries for work, see if your employers would be interested in donating. Our initial goal is to raise $1000 to get all our legal ducks in a row and kickstart this campaign. Any amount you can donate today to help us reach our goal would be greatly appreciated.
The community adoption of the ASF libraries has been amazing. We are greatly humbled by your enthusiam around the projects, and want to continue to do everything we can to move the needle forward. With your continued support, the ASF will be able to improve its reach and also provide better legal safety for the core members. If you use any of our libraries for work, see if your employers would be interested in donating. Our initial goal is to raise $1000 to get all our legal ducks in a row and kickstart this campaign. Any amount you can donate today to help us reach our goal would be greatly appreciated.
<a href='https://pledgie.com/campaigns/31474'><img alt='Click here to lend your support to: Alamofire Software Foundation and make a donation at pledgie.com !' src='https://pledgie.com/campaigns/31474.png?skin_name=chrome' border='0' ></a>

View File

@ -44,27 +44,19 @@ public protocol URLStringConvertible {
}
extension String: URLStringConvertible {
public var URLString: String {
return self
}
public var URLString: String { return self }
}
extension NSURL: URLStringConvertible {
public var URLString: String {
return absoluteString
}
public var URLString: String { return absoluteString }
}
extension NSURLComponents: URLStringConvertible {
public var URLString: String {
return URL!.URLString
}
public var URLString: String { return URL!.URLString }
}
extension NSURLRequest: URLStringConvertible {
public var URLString: String {
return URL!.URLString
}
public var URLString: String { return URL!.URLString }
}
// MARK: - URLRequestConvertible
@ -78,9 +70,7 @@ public protocol URLRequestConvertible {
}
extension NSURLRequest: URLRequestConvertible {
public var URLRequest: NSMutableURLRequest {
return self.mutableCopy() as! NSMutableURLRequest
}
public var URLRequest: NSMutableURLRequest { return self.mutableCopy() as! NSMutableURLRequest }
}
// MARK: - Convenience
@ -91,7 +81,16 @@ func URLRequest(
headers: [String: String]? = nil)
-> NSMutableURLRequest
{
let mutableURLRequest = NSMutableURLRequest(URL: NSURL(string: URLString.URLString)!)
let mutableURLRequest: NSMutableURLRequest
if let request = URLString as? NSMutableURLRequest {
mutableURLRequest = request
} else if let request = URLString as? NSURLRequest {
mutableURLRequest = request.URLRequest
} else {
mutableURLRequest = NSMutableURLRequest(URL: NSURL(string: URLString.URLString)!)
}
mutableURLRequest.HTTPMethod = method.rawValue
if let headers = headers {

View File

@ -60,7 +60,8 @@ public class Manager {
if let info = NSBundle.mainBundle().infoDictionary {
let executable = info[kCFBundleExecutableKey as String] as? String ?? "Unknown"
let bundle = info[kCFBundleIdentifierKey as String] as? String ?? "Unknown"
let version = info[kCFBundleVersionKey as String] as? String ?? "Unknown"
let appVersion = info["CFBundleShortVersionString"] as? String ?? "Unknown"
let appBuild = info[kCFBundleVersionKey as String] as? String ?? "Unknown"
let osNameVersion: String = {
let versionString: String
@ -91,7 +92,7 @@ public class Manager {
return "\(osName) \(versionString)"
}()
return "\(executable)/\(bundle) (\(version); \(osNameVersion))"
return "\(executable)/\(bundle) (\(appVersion)/\(appBuild)); \(osNameVersion))"
}
return "Alamofire"

View File

@ -64,7 +64,7 @@ extension Manager {
- parameter hostName: The hostname of the server to connect to.
- parameter port: The port of the server to connect to.
:returns: The created stream request.
- returns: The created stream request.
*/
public func stream(hostName hostName: String, port: Int) -> Request {
return stream(.Stream(hostName, port))

View File

@ -1,5 +1,5 @@
PODS:
- Alamofire (3.4.1)
- Alamofire (3.4.2)
- OMGHTTPURLRQ (3.1.3):
- OMGHTTPURLRQ/RQ (= 3.1.3)
- OMGHTTPURLRQ/FormURLEncode (3.1.3)
@ -31,7 +31,7 @@ EXTERNAL SOURCES:
:path: "../"
SPEC CHECKSUMS:
Alamofire: 01a82e2f6c0f860ade35534c8dd88be61bdef40c
Alamofire: 6aa33201d20d069e1598891cf928883ff1888c7a
OMGHTTPURLRQ: a547be1b9721ddfbf9d08aab56ab72dc4c1cc417
PetstoreClient: 24135348a992f2cbd76bc324719173b52e864cdc
PromiseKit: 4e8127c22a9b29d1b44958ab2ec762ea6115cbfb

View File

@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>3.4.1</string>
<string>3.4.2</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>

View File

@ -275,6 +275,7 @@ public class UserAPI: APIBase {
}}, {contentType=application/xml, example=<User>
<id>123456</id>
<username>string</username>
<dateOfBirth>2000-01-23T04:56:07.000Z</dateOfBirth>
<firstName>string</firstName>
<lastName>string</lastName>
<email>string</email>
@ -294,6 +295,7 @@ public class UserAPI: APIBase {
}}, {contentType=application/xml, example=<User>
<id>123456</id>
<username>string</username>
<dateOfBirth>2000-01-23T04:56:07.000Z</dateOfBirth>
<firstName>string</firstName>
<lastName>string</lastName>
<email>string</email>

View File

@ -83,4 +83,97 @@ extension NSUUID: JSONEncodable {
}
}
/// Represents an ISO-8601 full-date (RFC-3339).
/// ex: 12-31-1999
/// https://xml2rfc.tools.ietf.org/public/rfc/html/rfc3339.html#anchor14
public final class ISOFullDate: CustomStringConvertible {
public let year: Int
public let month: Int
public let day: Int
public init(year year: Int, month: Int, day: Int) {
self.year = year
self.month = month
self.day = day
}
/**
Converts an NSDate to an ISOFullDate. Only interested in the year, month, day components.
- parameter date: The date to convert.
- returns: An ISOFullDate constructed from the year, month, day of the date.
*/
public static func from(date date: NSDate) -> ISOFullDate? {
guard let calendar = NSCalendar(identifier: NSCalendarIdentifierGregorian) else {
return nil
}
let components = calendar.components(
[
.Year,
.Month,
.Day,
],
fromDate: date
)
return ISOFullDate(
year: components.year,
month: components.month,
day: components.day
)
}
/**
Converts a ISO-8601 full-date string to an ISOFullDate.
- parameter string: The ISO-8601 full-date format string to convert.
- returns: An ISOFullDate constructed from the string.
*/
public static func from(string string: String) -> ISOFullDate? {
let components = string
.characters
.split("-")
.map(String.init)
.flatMap { Int($0) }
guard components.count == 3 else { return nil }
return ISOFullDate(
year: components[0],
month: components[1],
day: components[2]
)
}
/**
Converts the receiver to an NSDate, in the default time zone.
- returns: An NSDate from the components of the receiver, in the default time zone.
*/
public func toDate() -> NSDate? {
let components = NSDateComponents()
components.year = year
components.month = month
components.day = day
components.timeZone = NSTimeZone.defaultTimeZone()
let calendar = NSCalendar(identifier: NSCalendarIdentifierGregorian)
return calendar?.dateFromComponents(components)
}
// MARK: CustomStringConvertible
public var description: String {
return "\(year)-\(month)-\(day)"
}
}
extension ISOFullDate: JSONEncodable {
public func encodeToJSON() -> AnyObject {
return "\(year)-\(month)-\(day)"
}
}

View File

@ -141,6 +141,15 @@ class Decoders {
fatalError("formatter failed to parse \(source)")
}
// Decoder for ISOFullDate
Decoders.addDecoder(clazz: ISOFullDate.self, decoder: { (source: AnyObject) -> ISOFullDate in
if let string = source as? String,
let isoDate = ISOFullDate.from(string: string) {
return isoDate
}
fatalError("formatter failed to parse \(source)")
})
// Decoder for [Category]
Decoders.addDecoder(clazz: [Category].self) { (source: AnyObject) -> [Category] in
return Decoders.decode(clazz: [Category].self, source: source)
@ -215,6 +224,7 @@ class Decoders {
let instance = User()
instance.id = Decoders.decodeOptional(clazz: Int64.self, source: sourceDictionary["id"])
instance.username = Decoders.decodeOptional(clazz: String.self, source: sourceDictionary["username"])
instance.dateOfBirth = Decoders.decodeOptional(clazz: ISOFullDate.self, source: sourceDictionary["dateOfBirth"])
instance.firstName = Decoders.decodeOptional(clazz: String.self, source: sourceDictionary["firstName"])
instance.lastName = Decoders.decodeOptional(clazz: String.self, source: sourceDictionary["lastName"])
instance.email = Decoders.decodeOptional(clazz: String.self, source: sourceDictionary["email"])

View File

@ -11,6 +11,7 @@ import Foundation
public class User: JSONEncodable {
public var id: Int64?
public var username: String?
public var dateOfBirth: ISOFullDate?
public var firstName: String?
public var lastName: String?
public var email: String?
@ -26,6 +27,7 @@ public class User: JSONEncodable {
var nillableDictionary = [String:AnyObject?]()
nillableDictionary["id"] = self.id?.encodeToJSON()
nillableDictionary["username"] = self.username
nillableDictionary["dateOfBirth"] = self.dateOfBirth?.encodeToJSON()
nillableDictionary["firstName"] = self.firstName
nillableDictionary["lastName"] = self.lastName
nillableDictionary["email"] = self.email