[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("array", "Array");
typeMapping.put("List", "Array"); typeMapping.put("List", "Array");
typeMapping.put("map", "Dictionary"); typeMapping.put("map", "Dictionary");
typeMapping.put("date", "NSDate"); typeMapping.put("date", "ISOFullDate");
typeMapping.put("Date", "NSDate"); typeMapping.put("Date", "ISOFullDate");
typeMapping.put("DateTime", "NSDate"); typeMapping.put("DateTime", "NSDate");
typeMapping.put("boolean", "Bool"); typeMapping.put("boolean", "Bool");
typeMapping.put("string", "String"); 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 { {{#usePromiseKit}}extension RequestBuilder {
public func execute() -> Promise<Response<T>> { public func execute() -> Promise<Response<T>> {
let deferred = Promise<Response<T>>.pendingPromise() let deferred = Promise<Response<T>>.pendingPromise()

View File

@ -139,7 +139,16 @@ class Decoders {
return NSDate(timeIntervalSince1970: Double(sourceInt / 1000) ) return NSDate(timeIntervalSince1970: Double(sourceInt / 1000) )
} }
fatalError("formatter failed to parse \(source)") 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}}}] // Decoder for [{{{classname}}}]
Decoders.addDecoder(clazz: [{{{classname}}}].self) { (source: AnyObject) -> [{{{classname}}}] in 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); 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 @Test
public void testDefaultPodAuthors() throws Exception { public void testDefaultPodAuthors() throws Exception {
// Given // Given

View File

@ -21,6 +21,7 @@ public class SwiftModelTest {
.property("binary", new BinaryProperty()) .property("binary", new BinaryProperty())
.property("byte", new ByteArrayProperty()) .property("byte", new ByteArrayProperty())
.property("uuid", new UUIDProperty()) .property("uuid", new UUIDProperty())
.property("dateOfBirth", new DateProperty())
.required("id") .required("id")
.required("name") .required("name")
.discriminator("test"); .discriminator("test");
@ -30,7 +31,7 @@ public class SwiftModelTest {
Assert.assertEquals(cm.name, "sample"); Assert.assertEquals(cm.name, "sample");
Assert.assertEquals(cm.classname, "Sample"); Assert.assertEquals(cm.classname, "Sample");
Assert.assertEquals(cm.description, "a sample model"); Assert.assertEquals(cm.description, "a sample model");
Assert.assertEquals(cm.vars.size(), 6); Assert.assertEquals(cm.vars.size(), 7);
Assert.assertEquals(cm.discriminator,"test"); Assert.assertEquals(cm.discriminator,"test");
final CodegenProperty property1 = cm.vars.get(0); final CodegenProperty property1 = cm.vars.get(0);
@ -91,9 +92,19 @@ public class SwiftModelTest {
Assert.assertEquals(property6.name, "uuid"); Assert.assertEquals(property6.name, "uuid");
Assert.assertNull(property6.defaultValue); Assert.assertNull(property6.defaultValue);
Assert.assertEquals(property6.baseType, "NSUUID"); Assert.assertEquals(property6.baseType, "NSUUID");
Assert.assertNull(property6.hasMore); Assert.assertTrue(property6.hasMore);
Assert.assertNull(property6.required); Assert.assertNull(property6.required);
Assert.assertTrue(property6.isNotContainer); 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": { "username": {
"type": "string" "type": "string"
}, },
"dateOfBirth": {
"type": "string",
"format": "date"
},
"firstName": { "firstName": {
"type": "string" "type": "string"
}, },

View File

@ -179,6 +179,7 @@ public class UserAPI: APIBase {
}}, {contentType=application/xml, example=<User> }}, {contentType=application/xml, example=<User>
<id>123456</id> <id>123456</id>
<username>string</username> <username>string</username>
<dateOfBirth>2000-01-23T04:56:07.000Z</dateOfBirth>
<firstName>string</firstName> <firstName>string</firstName>
<lastName>string</lastName> <lastName>string</lastName>
<email>string</email> <email>string</email>
@ -198,6 +199,7 @@ public class UserAPI: APIBase {
}}, {contentType=application/xml, example=<User> }}, {contentType=application/xml, example=<User>
<id>123456</id> <id>123456</id>
<username>string</username> <username>string</username>
<dateOfBirth>2000-01-23T04:56:07.000Z</dateOfBirth>
<firstName>string</firstName> <firstName>string</firstName>
<lastName>string</lastName> <lastName>string</lastName>
<email>string</email> <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)") 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] // Decoder for [Category]
Decoders.addDecoder(clazz: [Category].self) { (source: AnyObject) -> [Category] in Decoders.addDecoder(clazz: [Category].self) { (source: AnyObject) -> [Category] in
return Decoders.decode(clazz: [Category].self, source: source) return Decoders.decode(clazz: [Category].self, source: source)
@ -215,6 +224,7 @@ class Decoders {
let instance = User() let instance = User()
instance.id = Decoders.decodeOptional(clazz: Int64.self, source: sourceDictionary["id"]) instance.id = Decoders.decodeOptional(clazz: Int64.self, source: sourceDictionary["id"])
instance.username = Decoders.decodeOptional(clazz: String.self, source: sourceDictionary["username"]) 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.firstName = Decoders.decodeOptional(clazz: String.self, source: sourceDictionary["firstName"])
instance.lastName = Decoders.decodeOptional(clazz: String.self, source: sourceDictionary["lastName"]) instance.lastName = Decoders.decodeOptional(clazz: String.self, source: sourceDictionary["lastName"])
instance.email = Decoders.decodeOptional(clazz: String.self, source: sourceDictionary["email"]) instance.email = Decoders.decodeOptional(clazz: String.self, source: sourceDictionary["email"])

View File

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

View File

@ -17,6 +17,7 @@
6D4EFBB71C693BED00B96B06 /* StoreAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D4EFBB61C693BED00B96B06 /* StoreAPITests.swift */; }; 6D4EFBB71C693BED00B96B06 /* StoreAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D4EFBB61C693BED00B96B06 /* StoreAPITests.swift */; };
6D4EFBB91C693BFC00B96B06 /* UserAPITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6D4EFBB81C693BFC00B96B06 /* UserAPITests.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 */; }; 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 */ /* End PBXBuildFile section */
/* Begin PBXContainerItemProxy 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>"; }; 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>"; }; 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; }; 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; }; 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>"; }; 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 */ /* End PBXFileReference section */
@ -129,6 +131,7 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
6D4EFBAB1C692C6300B96B06 /* Info.plist */, 6D4EFBAB1C692C6300B96B06 /* Info.plist */,
C6AAAD201D8718D00016A515 /* ISOFullDateTests.swift */,
6D4EFBB41C693BE200B96B06 /* PetAPITests.swift */, 6D4EFBB41C693BE200B96B06 /* PetAPITests.swift */,
6D4EFBB61C693BED00B96B06 /* StoreAPITests.swift */, 6D4EFBB61C693BED00B96B06 /* StoreAPITests.swift */,
6D4EFBB81C693BFC00B96B06 /* UserAPITests.swift */, 6D4EFBB81C693BFC00B96B06 /* UserAPITests.swift */,
@ -473,6 +476,7 @@
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
C6AAAD211D8718D00016A515 /* ISOFullDateTests.swift in Sources */,
6D4EFBB71C693BED00B96B06 /* StoreAPITests.swift in Sources */, 6D4EFBB71C693BED00B96B06 /* StoreAPITests.swift in Sources */,
6D4EFBB91C693BFC00B96B06 /* UserAPITests.swift in Sources */, 6D4EFBB91C693BFC00B96B06 /* UserAPITests.swift in Sources */,
6D4EFBB51C693BE200B96B06 /* PetAPITests.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 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() { func testLogin() {
let expectation = self.expectationWithDescription("testLogin") let expectation = self.expectationWithDescription("testLogin")
@ -59,6 +49,7 @@ class UserAPITests: XCTestCase {
let newUser = User() let newUser = User()
newUser.email = "test@test.com" newUser.email = "test@test.com"
newUser.dateOfBirth = ISOFullDate.from(string: "1999-12-31")
newUser.firstName = "Test" newUser.firstName = "Test"
newUser.lastName = "Tester" newUser.lastName = "Tester"
newUser.id = 1000 newUser.id = 1000
@ -95,6 +86,7 @@ class UserAPITests: XCTestCase {
XCTAssert(user.lastName == "Tester", "invalid lastName") XCTAssert(user.lastName == "Tester", "invalid lastName")
XCTAssert(user.password == "test!", "invalid password") XCTAssert(user.password == "test!", "invalid password")
XCTAssert(user.phone == "867-5309", "invalid phone") XCTAssert(user.phone == "867-5309", "invalid phone")
XCTAssert(user.dateOfBirth?.description == "1999-12-31", "invalid date of birth")
expectation.fulfill() expectation.fulfill()
} }

View File

@ -259,12 +259,14 @@ public class UserAPI: APIBase {
"password" : "aeiou", "password" : "aeiou",
"userStatus" : 123, "userStatus" : 123,
"phone" : "aeiou", "phone" : "aeiou",
"dateOfBirth" : "2000-01-23T04:56:07.000+00:00",
"id" : 123456789, "id" : 123456789,
"email" : "aeiou", "email" : "aeiou",
"username" : "aeiou" "username" : "aeiou"
}}, {contentType=application/xml, example=<User> }}, {contentType=application/xml, example=<User>
<id>123456</id> <id>123456</id>
<username>string</username> <username>string</username>
<dateOfBirth>2000-01-23T04:56:07.000Z</dateOfBirth>
<firstName>string</firstName> <firstName>string</firstName>
<lastName>string</lastName> <lastName>string</lastName>
<email>string</email> <email>string</email>
@ -278,12 +280,14 @@ public class UserAPI: APIBase {
"password" : "aeiou", "password" : "aeiou",
"userStatus" : 123, "userStatus" : 123,
"phone" : "aeiou", "phone" : "aeiou",
"dateOfBirth" : "2000-01-23T04:56:07.000+00:00",
"id" : 123456789, "id" : 123456789,
"email" : "aeiou", "email" : "aeiou",
"username" : "aeiou" "username" : "aeiou"
}}, {contentType=application/xml, example=<User> }}, {contentType=application/xml, example=<User>
<id>123456</id> <id>123456</id>
<username>string</username> <username>string</username>
<dateOfBirth>2000-01-23T04:56:07.000Z</dateOfBirth>
<firstName>string</firstName> <firstName>string</firstName>
<lastName>string</lastName> <lastName>string</lastName>
<email>string</email> <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 { extension RequestBuilder {
public func execute() -> Promise<Response<T>> { public func execute() -> Promise<Response<T>> {
let deferred = Promise<Response<T>>.pendingPromise() let deferred = Promise<Response<T>>.pendingPromise()

View File

@ -141,6 +141,15 @@ class Decoders {
fatalError("formatter failed to parse \(source)") 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] // Decoder for [Category]
Decoders.addDecoder(clazz: [Category].self) { (source: AnyObject) -> [Category] in Decoders.addDecoder(clazz: [Category].self) { (source: AnyObject) -> [Category] in
return Decoders.decode(clazz: [Category].self, source: source) return Decoders.decode(clazz: [Category].self, source: source)
@ -215,6 +224,7 @@ class Decoders {
let instance = User() let instance = User()
instance.id = Decoders.decodeOptional(clazz: Int64.self, source: sourceDictionary["id"]) instance.id = Decoders.decodeOptional(clazz: Int64.self, source: sourceDictionary["id"])
instance.username = Decoders.decodeOptional(clazz: String.self, source: sourceDictionary["username"]) 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.firstName = Decoders.decodeOptional(clazz: String.self, source: sourceDictionary["firstName"])
instance.lastName = Decoders.decodeOptional(clazz: String.self, source: sourceDictionary["lastName"]) instance.lastName = Decoders.decodeOptional(clazz: String.self, source: sourceDictionary["lastName"])
instance.email = Decoders.decodeOptional(clazz: String.self, source: sourceDictionary["email"]) instance.email = Decoders.decodeOptional(clazz: String.self, source: sourceDictionary["email"])

View File

@ -11,6 +11,7 @@ import Foundation
public class User: JSONEncodable { public class User: JSONEncodable {
public var id: Int64? public var id: Int64?
public var username: String? public var username: String?
public var dateOfBirth: ISOFullDate?
public var firstName: String? public var firstName: String?
public var lastName: String? public var lastName: String?
public var email: String? public var email: String?
@ -26,6 +27,7 @@ public class User: JSONEncodable {
var nillableDictionary = [String:AnyObject?]() var nillableDictionary = [String:AnyObject?]()
nillableDictionary["id"] = self.id?.encodeToJSON() nillableDictionary["id"] = self.id?.encodeToJSON()
nillableDictionary["username"] = self.username nillableDictionary["username"] = self.username
nillableDictionary["dateOfBirth"] = self.dateOfBirth?.encodeToJSON()
nillableDictionary["firstName"] = self.firstName nillableDictionary["firstName"] = self.firstName
nillableDictionary["lastName"] = self.lastName nillableDictionary["lastName"] = self.lastName
nillableDictionary["email"] = self.email 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: 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 ```swift
enum BackendError: ErrorType { public enum BackendError: ErrorType {
case Network(error: NSError) case Network(error: NSError)
case DataSerialization(reason: String) case DataSerialization(reason: String)
case JSONSerialization(error: NSError) 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 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 * 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> <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 { extension String: URLStringConvertible {
public var URLString: String { public var URLString: String { return self }
return self
}
} }
extension NSURL: URLStringConvertible { extension NSURL: URLStringConvertible {
public var URLString: String { public var URLString: String { return absoluteString }
return absoluteString
}
} }
extension NSURLComponents: URLStringConvertible { extension NSURLComponents: URLStringConvertible {
public var URLString: String { public var URLString: String { return URL!.URLString }
return URL!.URLString
}
} }
extension NSURLRequest: URLStringConvertible { extension NSURLRequest: URLStringConvertible {
public var URLString: String { public var URLString: String { return URL!.URLString }
return URL!.URLString
}
} }
// MARK: - URLRequestConvertible // MARK: - URLRequestConvertible
@ -78,9 +70,7 @@ public protocol URLRequestConvertible {
} }
extension NSURLRequest: URLRequestConvertible { extension NSURLRequest: URLRequestConvertible {
public var URLRequest: NSMutableURLRequest { public var URLRequest: NSMutableURLRequest { return self.mutableCopy() as! NSMutableURLRequest }
return self.mutableCopy() as! NSMutableURLRequest
}
} }
// MARK: - Convenience // MARK: - Convenience
@ -91,7 +81,16 @@ func URLRequest(
headers: [String: String]? = nil) headers: [String: String]? = nil)
-> NSMutableURLRequest -> 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 mutableURLRequest.HTTPMethod = method.rawValue
if let headers = headers { if let headers = headers {

View File

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

View File

@ -64,7 +64,7 @@ extension Manager {
- parameter hostName: The hostname of the server to connect to. - parameter hostName: The hostname of the server to connect to.
- parameter port: The port 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 { public func stream(hostName hostName: String, port: Int) -> Request {
return stream(.Stream(hostName, port)) return stream(.Stream(hostName, port))

View File

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

View File

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

View File

@ -275,6 +275,7 @@ public class UserAPI: APIBase {
}}, {contentType=application/xml, example=<User> }}, {contentType=application/xml, example=<User>
<id>123456</id> <id>123456</id>
<username>string</username> <username>string</username>
<dateOfBirth>2000-01-23T04:56:07.000Z</dateOfBirth>
<firstName>string</firstName> <firstName>string</firstName>
<lastName>string</lastName> <lastName>string</lastName>
<email>string</email> <email>string</email>
@ -294,6 +295,7 @@ public class UserAPI: APIBase {
}}, {contentType=application/xml, example=<User> }}, {contentType=application/xml, example=<User>
<id>123456</id> <id>123456</id>
<username>string</username> <username>string</username>
<dateOfBirth>2000-01-23T04:56:07.000Z</dateOfBirth>
<firstName>string</firstName> <firstName>string</firstName>
<lastName>string</lastName> <lastName>string</lastName>
<email>string</email> <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)") 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] // Decoder for [Category]
Decoders.addDecoder(clazz: [Category].self) { (source: AnyObject) -> [Category] in Decoders.addDecoder(clazz: [Category].self) { (source: AnyObject) -> [Category] in
return Decoders.decode(clazz: [Category].self, source: source) return Decoders.decode(clazz: [Category].self, source: source)
@ -215,6 +224,7 @@ class Decoders {
let instance = User() let instance = User()
instance.id = Decoders.decodeOptional(clazz: Int64.self, source: sourceDictionary["id"]) instance.id = Decoders.decodeOptional(clazz: Int64.self, source: sourceDictionary["id"])
instance.username = Decoders.decodeOptional(clazz: String.self, source: sourceDictionary["username"]) 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.firstName = Decoders.decodeOptional(clazz: String.self, source: sourceDictionary["firstName"])
instance.lastName = Decoders.decodeOptional(clazz: String.self, source: sourceDictionary["lastName"]) instance.lastName = Decoders.decodeOptional(clazz: String.self, source: sourceDictionary["lastName"])
instance.email = Decoders.decodeOptional(clazz: String.self, source: sourceDictionary["email"]) instance.email = Decoders.decodeOptional(clazz: String.self, source: sourceDictionary["email"])

View File

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