From f627c813995a607e87b0e87d6619bf2df8323aaf Mon Sep 17 00:00:00 2001 From: kubo_takaichi Date: Wed, 13 May 2015 22:47:46 +0900 Subject: [PATCH 01/15] Remove some characters from param name and operation id (since swagger-doc emits improper characters) --- .../swagger/codegen/DefaultCodegen.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/modules/swagger-codegen/src/main/java/com/wordnik/swagger/codegen/DefaultCodegen.java b/modules/swagger-codegen/src/main/java/com/wordnik/swagger/codegen/DefaultCodegen.java index 6f51374c536..168b6a37717 100644 --- a/modules/swagger-codegen/src/main/java/com/wordnik/swagger/codegen/DefaultCodegen.java +++ b/modules/swagger-codegen/src/main/java/com/wordnik/swagger/codegen/DefaultCodegen.java @@ -1,5 +1,7 @@ package com.wordnik.swagger.codegen; +import com.google.common.base.Function; +import com.google.common.collect.Lists; import com.wordnik.swagger.codegen.examples.ExampleGenerator; import com.wordnik.swagger.models.*; import com.wordnik.swagger.models.auth.ApiKeyAuthDefinition; @@ -15,6 +17,7 @@ import org.apache.commons.lang.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; +import javax.annotation.Nullable; import java.io.File; import java.util.*; @@ -160,6 +163,7 @@ public class DefaultCodegen { } public String toParamName(String name) { + name = removeNonNameElementToCamelCase(name); if(reservedWords.contains(name)) { return escapeReservedWord(name); } @@ -680,6 +684,7 @@ public class DefaultCodegen { operationId = builder.toString(); LOGGER.warn("generated operationId " + operationId); } + operationId = removeNonNameElementToCamelCase(operationId); op.path = path; op.operationId = toOperationId(operationId); op.summary = escapeText(operation.getSummary()); @@ -1108,6 +1113,26 @@ public class DefaultCodegen { return word; } + /** + * Remove characters not suitable for variable or method name from the input and camelize it + * @param name + * @return + */ + public String removeNonNameElementToCamelCase(String name) { + String nonNameElementPattern = "[-_:;#]"; + name = StringUtils.join(Lists.transform(Lists.newArrayList(name.split(nonNameElementPattern)), new Function() { + @Nullable + @Override + public String apply(String input) { + return StringUtils.capitalize(input); + } + }), ""); + if (name.length() > 0) { + name = name.substring(0, 1).toLowerCase() + name.substring(1); + } + return name; + } + public static String camelize(String word) { return camelize(word, false); } From 23d714f2f1374629bad91f929348b3da030d3911 Mon Sep 17 00:00:00 2001 From: kubo_takaichi Date: Wed, 13 May 2015 22:51:14 +0900 Subject: [PATCH 02/15] Add swift code generator --- bin/swift-petstore.sh | 31 +++ .../codegen/languages/SwiftGenerator.java | 249 ++++++++++++++++++ .../com.wordnik.swagger.codegen.CodegenConfig | 1 + .../main/resources/swift/APIHelper.mustache | 21 ++ .../src/main/resources/swift/APIs.mustache | 51 ++++ .../swift/AlamofireImplementations.mustache | 73 +++++ .../main/resources/swift/Cartfile.mustache | 2 + .../main/resources/swift/Extensions.mustache | 111 ++++++++ .../src/main/resources/swift/Models.mustache | 47 ++++ .../src/main/resources/swift/api.mustache | 50 ++++ .../src/main/resources/swift/model.mustache | 46 ++++ .../petstore/swift/PetstoreClient/Cartfile | 2 + 12 files changed, 684 insertions(+) create mode 100755 bin/swift-petstore.sh create mode 100644 modules/swagger-codegen/src/main/java/com/wordnik/swagger/codegen/languages/SwiftGenerator.java create mode 100644 modules/swagger-codegen/src/main/resources/swift/APIHelper.mustache create mode 100644 modules/swagger-codegen/src/main/resources/swift/APIs.mustache create mode 100644 modules/swagger-codegen/src/main/resources/swift/AlamofireImplementations.mustache create mode 100644 modules/swagger-codegen/src/main/resources/swift/Cartfile.mustache create mode 100644 modules/swagger-codegen/src/main/resources/swift/Extensions.mustache create mode 100644 modules/swagger-codegen/src/main/resources/swift/Models.mustache create mode 100644 modules/swagger-codegen/src/main/resources/swift/api.mustache create mode 100644 modules/swagger-codegen/src/main/resources/swift/model.mustache create mode 100644 samples/client/petstore/swift/PetstoreClient/Cartfile diff --git a/bin/swift-petstore.sh b/bin/swift-petstore.sh new file mode 100755 index 00000000000..ce6a7e702f0 --- /dev/null +++ b/bin/swift-petstore.sh @@ -0,0 +1,31 @@ +#!/bin/sh + +SCRIPT="$0" + +while [ -h "$SCRIPT" ] ; do + ls=`ls -ld "$SCRIPT"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + SCRIPT="$link" + else + SCRIPT=`dirname "$SCRIPT"`/"$link" + fi +done + +if [ ! -d "${APP_DIR}" ]; then + APP_DIR=`dirname "$SCRIPT"`/.. + APP_DIR=`cd "${APP_DIR}"; pwd` +fi + +executable="./modules/swagger-codegen-cli/target/swagger-codegen-cli.jar" + +if [ ! -f "$executable" ] +then + mvn clean package +fi + +# if you've executed sbt assembly previously it will use that instead. +export JAVA_OPTS="${JAVA_OPTS} -XX:MaxPermSize=256M -Xmx1024M -DloggerPath=conf/log4j.properties" +ags="$@ generate -t modules/swagger-codegen/src/main/resources/swift -i modules/swagger-codegen/src/test/resources/2_0/petstore.json -l swift -o samples/client/petstore/swift" + +java -DappName=PetstoreClient $JAVA_OPTS -jar $executable $ags diff --git a/modules/swagger-codegen/src/main/java/com/wordnik/swagger/codegen/languages/SwiftGenerator.java b/modules/swagger-codegen/src/main/java/com/wordnik/swagger/codegen/languages/SwiftGenerator.java new file mode 100644 index 00000000000..39a71449d5e --- /dev/null +++ b/modules/swagger-codegen/src/main/java/com/wordnik/swagger/codegen/languages/SwiftGenerator.java @@ -0,0 +1,249 @@ +package com.wordnik.swagger.codegen.languages; + +import com.google.common.base.Predicate; +import com.google.common.collect.Iterators; +import com.google.common.collect.Lists; +import com.wordnik.swagger.codegen.*; +import com.wordnik.swagger.models.Model; +import com.wordnik.swagger.models.Operation; +import com.wordnik.swagger.models.parameters.HeaderParameter; +import com.wordnik.swagger.models.parameters.Parameter; +import com.wordnik.swagger.models.properties.*; +import org.apache.commons.lang.StringUtils; + +import javax.annotation.Nullable; +import java.util.*; +import java.io.File; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +public class SwiftGenerator extends DefaultCodegen implements CodegenConfig { + private static final Pattern PATH_PARAM_PATTERN = Pattern.compile("\\{[a-zA-Z_]+\\}"); + protected String sourceFolder = "Classes/Swaggers"; + + public CodegenType getTag() { + return CodegenType.CLIENT; + } + + public String getName() { + return "swift"; + } + + public String getHelp() { + return "Generates a swift client library."; + } + + public SwiftGenerator() { + super(); + outputFolder = "generated-code/swift"; + modelTemplateFiles.put("model.mustache", ".swift"); + apiTemplateFiles.put("api.mustache", ".swift"); + templateDir = "swift"; + apiPackage = "/APIs"; + modelPackage = "/Models"; + + // Inject application name + String appName = System.getProperty("appName"); + if (appName == null) { + appName = "SwaggerClient"; + } + additionalProperties.put("projectName", appName); + + // Inject base url override + String basePathOverride = System.getProperty("basePathOverride"); + if (basePathOverride != null) { + additionalProperties.put("basePathOverride", basePathOverride); + } + + sourceFolder = appName + "/" + sourceFolder; + + supportingFiles.add(new SupportingFile("Cartfile.mustache", appName, "Cartfile")); + supportingFiles.add(new SupportingFile("APIHelper.mustache", sourceFolder, "APIHelper.swift")); + supportingFiles.add(new SupportingFile("AlamofireImplementations.mustache", sourceFolder, "AlamofireImplementations.swift")); + supportingFiles.add(new SupportingFile("Extensions.mustache", sourceFolder, "Extensions.swift")); + supportingFiles.add(new SupportingFile("Models.mustache", sourceFolder, "Models.swift")); + supportingFiles.add(new SupportingFile("APIs.mustache", sourceFolder, "APIs.swift")); + + languageSpecificPrimitives = new HashSet( + Arrays.asList( + "Int", + "Float", + "Double", + "Bool", + "Void", + "String", + "Character") + ); + defaultIncludes = new HashSet( + Arrays.asList( + "NSDate", + "Array", + "Dictionary", + "Set", + "Any", + "Empty", + "AnyObject") + ); + reservedWords = new HashSet( + Arrays.asList( + "class", "break", "as", "associativity", "deinit", "case", "dynamicType", "convenience", "enum", "continue", + "false", "dynamic", "extension", "default", "is", "didSet", "func", "do", "nil", "final", "import", "else", + "self", "get", "init", "fallthrough", "Self", "infix", "internal", "for", "super", "inout", "let", "if", + "true", "lazy", "operator", "in", "COLUMN", "left", "private", "return", "FILE", "mutating", "protocol", + "switch", "FUNCTION", "none", "public", "where", "LINE", "nonmutating", "static", "while", "optional", + "struct", "override", "subscript", "postfix", "typealias", "precedence", "var", "prefix", "Protocol", + "required", "right", "set", "Type", "unowned", "weak") + ); + + typeMapping = new HashMap(); + typeMapping.put("array", "Array"); + typeMapping.put("List", "Array"); + typeMapping.put("map", "Dictionary"); + typeMapping.put("Date", "NSDate"); + typeMapping.put("DateTime", "NSDate"); + typeMapping.put("boolean", "Bool"); + typeMapping.put("string", "String"); + typeMapping.put("char", "Character"); + typeMapping.put("short", "Int"); + typeMapping.put("int", "Int"); + typeMapping.put("long", "Int"); + typeMapping.put("integer", "Int"); + typeMapping.put("Integer", "Int"); + typeMapping.put("float", "Float"); + typeMapping.put("number", "Double"); + typeMapping.put("double", "Double"); + typeMapping.put("object", "AnyObject"); + typeMapping.put("file", "NSData"); + + importMapping = new HashMap(); + } + + @Override + public String escapeReservedWord(String name) { + return "_" + name; // add an underscore to the name + } + + @Override + public String modelFileFolder() { + return outputFolder + "/" + sourceFolder + modelPackage().replace('.', File.separatorChar); + } + + @Override + public String apiFileFolder() { + return outputFolder + "/" + sourceFolder + apiPackage().replace('.', File.separatorChar); + } + + @Override + public String getTypeDeclaration(Property p) { + if (p instanceof ArrayProperty) { + ArrayProperty ap = (ArrayProperty) p; + Property inner = ap.getItems(); + return "[" + getTypeDeclaration(inner) + "]"; + } else if (p instanceof MapProperty) { + MapProperty mp = (MapProperty) p; + Property inner = mp.getAdditionalProperties(); + return "[String:" + getTypeDeclaration(inner) + "]"; + } + return super.getTypeDeclaration(p); + } + + @Override + public String getSwaggerType(Property p) { + String swaggerType = super.getSwaggerType(p); + String type = null; + if (typeMapping.containsKey(swaggerType)) { + type = typeMapping.get(swaggerType); + if (languageSpecificPrimitives.contains(type)) + return toModelName(type); + } else + type = swaggerType; + return toModelName(type); + } + + @Override + public String toDefaultValue(Property p) { + // nil + return null; + } + + @Override + public String toInstantiationType(Property p) { + if (p instanceof MapProperty) { + MapProperty ap = (MapProperty) p; + String inner = getSwaggerType(ap.getAdditionalProperties()); + return "[String:" + inner + "]"; + } else if (p instanceof ArrayProperty) { + ArrayProperty ap = (ArrayProperty) p; + String inner = getSwaggerType(ap.getItems()); + return "[" + inner + "]"; + } + return null; + } + + @Override + public CodegenProperty fromProperty(String name, Property p) { + CodegenProperty codegenProperty = super.fromProperty(name, p); + if (codegenProperty.isEnum) { + List> swiftEnums = new ArrayList>(); + List values = (List) codegenProperty.allowableValues.get("values"); + for (String value : values) { + Map map = new HashMap(); + map.put("enum", StringUtils.capitalize(value)); + map.put("raw", value); + swiftEnums.add(map); + } + codegenProperty.allowableValues.put("values", swiftEnums); + codegenProperty.datatypeWithEnum = + StringUtils.left(codegenProperty.datatypeWithEnum, codegenProperty.datatypeWithEnum.length() - "Enum".length()); + } + return codegenProperty; + } + + @Override + public String toApiName(String name) { + if(name.length() == 0) + return "DefaultAPI"; + return initialCaps(name) + "API"; + } + + @Override + public CodegenOperation fromOperation(String path, String httpMethod, Operation operation, Map definitions) { + path = normalizePath(path); + List parameters = operation.getParameters(); + parameters = Lists.newArrayList(Iterators.filter(parameters.iterator(), new Predicate() { + @Override + public boolean apply(@Nullable Parameter parameter) { + return !(parameter instanceof HeaderParameter); + } + })); + operation.setParameters(parameters); + return super.fromOperation(path, httpMethod, operation, definitions); + } + + private static String normalizePath(String path) { + StringBuilder builder = new StringBuilder(); + + int cursor = 0; + Matcher matcher = PATH_PARAM_PATTERN.matcher(path); + boolean found = matcher.find(); + while (found) { + String stringBeforeMatch = path.substring(cursor, matcher.start()); + builder.append(stringBeforeMatch); + + String group = matcher.group().substring(1, matcher.group().length() - 1); + group = camelize(group, true); + builder + .append("{") + .append(group) + .append("}"); + + cursor = matcher.end(); + found = matcher.find(); + } + + String stringAfterMatch = path.substring(cursor); + builder.append(stringAfterMatch); + + return builder.toString(); + } +} \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/META-INF/services/com.wordnik.swagger.codegen.CodegenConfig b/modules/swagger-codegen/src/main/resources/META-INF/services/com.wordnik.swagger.codegen.CodegenConfig index 293079fbc97..facb299b5d1 100644 --- a/modules/swagger-codegen/src/main/resources/META-INF/services/com.wordnik.swagger.codegen.CodegenConfig +++ b/modules/swagger-codegen/src/main/resources/META-INF/services/com.wordnik.swagger.codegen.CodegenConfig @@ -18,4 +18,5 @@ com.wordnik.swagger.codegen.languages.StaticDocCodegen com.wordnik.swagger.codegen.languages.StaticHtmlGenerator com.wordnik.swagger.codegen.languages.SwaggerGenerator com.wordnik.swagger.codegen.languages.SwaggerYamlGenerator +com.wordnik.swagger.codegen.languages.SwiftGenerator com.wordnik.swagger.codegen.languages.TizenClientCodegen diff --git a/modules/swagger-codegen/src/main/resources/swift/APIHelper.mustache b/modules/swagger-codegen/src/main/resources/swift/APIHelper.mustache new file mode 100644 index 00000000000..418f1c8512b --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/swift/APIHelper.mustache @@ -0,0 +1,21 @@ +// APIHelper.swift +// +// Generated by swagger-codegen +// https://github.com/swagger-api/swagger-codegen +// + +class APIHelper { + static func rejectNil(source: [String:AnyObject?]) -> [String:AnyObject]? { + var destination = [String:AnyObject]() + for (key, nillableValue) in source { + if let value: AnyObject = nillableValue { + destination[key] = value + } + } + + if destination.isEmpty { + return nil + } + return destination + } +} diff --git a/modules/swagger-codegen/src/main/resources/swift/APIs.mustache b/modules/swagger-codegen/src/main/resources/swift/APIs.mustache new file mode 100644 index 00000000000..073fbd93be7 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/swift/APIs.mustache @@ -0,0 +1,51 @@ +// APIs.swift +// +// Generated by swagger-codegen +// https://github.com/swagger-api/swagger-codegen +// + +import PromiseKit + +class {{projectName}}API { + static let basePath = "{{^basePathOverride}}{{basePath}}{{/basePathOverride}}{{basePathOverride}}" + static var credential: NSURLCredential? + static var requestBuilderFactory: RequestBuilderFactory = AlamofireRequestBuilderFactory() + + class APIBase { + } +} + +class RequestBuilder { + var credential: NSURLCredential? + var headers: [String:String] = [:] + let parameters: [String:AnyObject]? + let isBody: Bool + let method: String + let URLString: URLStringConvertible + + required init(method: String, URLString: URLStringConvertible, parameters: [String:AnyObject]?, isBody: Bool) { + self.method = method + self.URLString = URLString + self.parameters = parameters + self.isBody = isBody + } + + func execute() -> Promise> { fatalError("Not implemented") } + + func addHeader(#name: String, value: String) -> Self { + if !value.isEmpty { + headers[name] = value + } + return self + } + + func addCredential() -> Self { + self.credential = OneteamAPI.credential + return self + } +} + +protocol RequestBuilderFactory { + func getBuilder() -> RequestBuilder.Type +} + diff --git a/modules/swagger-codegen/src/main/resources/swift/AlamofireImplementations.mustache b/modules/swagger-codegen/src/main/resources/swift/AlamofireImplementations.mustache new file mode 100644 index 00000000000..8b34587717a --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/swift/AlamofireImplementations.mustache @@ -0,0 +1,73 @@ +// AlamofireImplementations.swift +// +// Generated by swagger-codegen +// https://github.com/swagger-api/swagger-codegen +// + +import Alamofire +import PromiseKit + +class AlamofireRequestBuilderFactory: RequestBuilderFactory { + func getBuilder() -> RequestBuilder.Type { + return AlamofireRequestBuilder.self + } +} + +class AlamofireRequestBuilder: RequestBuilder { + required init(method: String, URLString: URLStringConvertible, parameters: [String : AnyObject]?, isBody: Bool) { + super.init(method: method, URLString: URLString, parameters: parameters, isBody: isBody) + } + + override func execute() -> Promise> { + let configuration: NSURLSessionConfiguration = NSURLSessionConfiguration.defaultSessionConfiguration() + configuration.HTTPAdditionalHeaders = buildHeaders() + let manager = Alamofire.Manager(configuration: configuration) + let encoding = isBody ? Alamofire.ParameterEncoding.JSON : Alamofire.ParameterEncoding.URL + let request = manager.request(Alamofire.Method(rawValue: method)!, URLString, parameters: parameters, encoding: encoding) + if let credential = self.credential { + request.authenticate(usingCredential: credential) + } + + let defer = Promise>.defer() + request.responseJSON(options: .AllowFragments) { (req, res, json, error) in + if let error = error { + defer.reject(error) + return + } + if res!.statusCode >= 400 { + //TODO: Add error entity + let error = NSError(domain: res!.URL!.URLString, code: res!.statusCode, userInfo: [:]) + defer.reject(error) + return + } + + if Empty.instance is T { + let response = Response(response: res!, body: Empty.instance as! T) + defer.fulfill(response) + return + } + if let json: AnyObject = json { + if let body = T.decode(json) { + let response = Response(response: res!, body: body) + defer.fulfill(response) + } else { + defer.reject(NSError(domain: "localhost", code: 500, userInfo: ["reason": "cannot convertible: \(json)"])) + } + return + } + + defer.reject(NSError(domain: "localhost", code: 500, userInfo: ["reason": "unreacheable code"])) + } + return defer.promise + } + + private func buildHeaders() -> [String: AnyObject] { + var httpHeaders = Manager.defaultHTTPHeaders + for (key, value) in self.headers { + httpHeaders[key] = value + } + return httpHeaders + } +} + + diff --git a/modules/swagger-codegen/src/main/resources/swift/Cartfile.mustache b/modules/swagger-codegen/src/main/resources/swift/Cartfile.mustache new file mode 100644 index 00000000000..245a690a74b --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/swift/Cartfile.mustache @@ -0,0 +1,2 @@ +github "Alamofire/Alamofire" >= 1.2 +github "mxcl/PromiseKit" \ No newline at end of file diff --git a/modules/swagger-codegen/src/main/resources/swift/Extensions.mustache b/modules/swagger-codegen/src/main/resources/swift/Extensions.mustache new file mode 100644 index 00000000000..d0aed716b7e --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/swift/Extensions.mustache @@ -0,0 +1,111 @@ +// Extensions.swift +// +// Generated by swagger-codegen +// https://github.com/swagger-api/swagger-codegen +// + +import Alamofire +import PromiseKit +import SwiftyJSON + +extension Request { + func responsePromise() -> Promise> { + let defer = Promise>.defer() + responseJSON(options: .AllowFragments) { (req, res, json, error) in + if let error = error { + defer.reject(error) + return + } + if res!.statusCode >= 400 { + let error = NSError(domain: res!.URL!.URLString, code: res!.statusCode, userInfo: [:]) + defer.reject(error) + return + } + + if Empty.instance is T { + let response = Response(response: res!, body: Empty.instance as! T) + defer.fulfill(response) + return + } + if let json: AnyObject = json { + if let body = T.decode(json) { + let response = Response(response: res!, body: body) + defer.fulfill(response) + } else { + defer.reject(NSError(domain: "localhost", code: 500, userInfo: ["reason": "cannot convertible: \(json)"])) + } + return + } + + defer.reject(NSError(domain: "localhost", code: 500, userInfo: ["reason": "unreacheable code"])) + } + return defer.promise + } +} + +extension Array: JSONEncodable { + func encode() -> AnyObject { + if Element.self is JSONEncodable { + return self.map { ($0 as! JSONEncodable).encode() } + } else { + return self.map { ($0 as! AnyObject) } + } + } +} + +extension JSON { + func decode() -> Bool? { + return self.bool + } + func decode() -> Bool { + return self.boolValue + } + func decode() -> Int? { + return self.int + } + func decode() -> Int { + return self.intValue + } + func decode() -> Float? { + return self.float + } + func decode() -> Float { + return self.floatValue + } + func decode() -> Double? { + return self.double + } + func decode() -> Double { + return self.doubleValue + } + func decode() -> String? { + return self.string + } + func decode() -> String { + return self.stringValue + } + func decode() -> T? { + return T.decode(self.object) + } + func decode() -> [T]? { + return self.arrayObject?.map(T.decode).filter({ $0 != nil }).map({ $0! }) + } + func decode() -> [T]? { + return self.array?.map({ $0 as! T }) + } + static let DateFormat: NSDateFormatter = { + let formatter = NSDateFormatter() + formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss'Z'" + return formatter + }() + func decode() -> NSDate? { + return JSON.DateFormat.dateFromString(self.string ?? "") + } + func decode() -> NSData? { + return self.rawData(options: .allZeros, error: nil) + } + func decode() -> NSData { + return self.decode()! + } +} + diff --git a/modules/swagger-codegen/src/main/resources/swift/Models.mustache b/modules/swagger-codegen/src/main/resources/swift/Models.mustache new file mode 100644 index 00000000000..dd02aa12d70 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/swift/Models.mustache @@ -0,0 +1,47 @@ +// Models.swift +// +// Generated by swagger-codegen +// https://github.com/swagger-api/swagger-codegen +// + +import Foundation +import SwiftyJSON + +protocol JSONDecodable { + static func decode(source: AnyObject) -> Self? +} + +protocol JSONEncodable { + func encode() -> AnyObject +} + +class Response { + let statusCode: Int + let header: [String: String] + let body: T + + init(statusCode: Int, header: [String: String], body: T) { + self.statusCode = statusCode + self.header = header + self.body = body + } + + convenience init(response: NSHTTPURLResponse, body: T) { + let rawHeader = response.allHeaderFields + var header = [String:String]() + for (key, value) in rawHeader { + header[key as! String] = value as? String + } + self.init(statusCode: response.statusCode, header: header, body: body) + } +} + +final class Empty: JSONDecodable { + private init() { } + + static let instance = Empty() + + static func decode(source: AnyObject) -> Empty? { + return instance + } +} diff --git a/modules/swagger-codegen/src/main/resources/swift/api.mustache b/modules/swagger-codegen/src/main/resources/swift/api.mustache new file mode 100644 index 00000000000..0277ec8dfa3 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/swift/api.mustache @@ -0,0 +1,50 @@ +{{#operations}}// +// {{classname}}.swift +// +// Generated by swagger-codegen +// https://github.com/swagger-api/swagger-codegen +// + +import Alamofire +import PromiseKit + +extension {{projectName}}API { + {{#description}} + /** {{description}} */{{/description}} + class {{classname}}: APIBase { + {{#operation}} + /** + {{#summary}} + {{{summary}}} + {{/summary}} + - {{httpMethod}} {{path}}{{#notes}} + - {{{notes}}}{{/notes}}{{#subresourceOperation}} + - subresourceOperation: {{subresourceOperation}}{{/subresourceOperation}}{{#defaultResponse}} + - defaultResponse: {{defaultResponse}}{{/defaultResponse}}{{#authMethods}} + - authMethods: {{authMethods}}{{/authMethods}}{{#responseHeaders}} + - responseHeaders: {{responseHeaders}}{{/responseHeaders}}{{#examples}} + - examples: {{{examples}}}{{/examples}}{{#externalDocs}} + - externalDocs: {{externalDocs}}{{/externalDocs}}{{#hasParams}} + {{/hasParams}}{{#allParams}} + :param: {{paramName}} ({{#isFormParam}}form{{/isFormParam}}{{#isQueryParam}}query{{/isQueryParam}}{{#isPathParam}}path{{/isPathParam}}{{#isHeaderParam}}header{{/isHeaderParam}}{{#isBodyParam}}body{{/isBodyParam}}) {{description}}{{/allParams}} + + :returns: Promise> {{description}} + */ + func {{operationId}}({{#allParams}}{{^secondaryParam}}#{{/secondaryParam}}{{paramName}}: {{dataType}}{{^required}}?{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) -> RequestBuilder<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Empty{{/returnType}}> { + {{^pathParams}}let{{/pathParams}}{{#pathParams}}{{^secondaryParam}}var{{/secondaryParam}}{{/pathParams}} path = "{{path}}"{{#pathParams}} + path = path.stringByReplacingOccurrencesOfString("{{=<% %>=}}{<%paramName%>}<%={{ }}=%>", withString: "\({{paramName}})", options: .LiteralSearch, range: nil){{/pathParams}} + let url = {{projectName}}API.basePath + path + {{#bodyParam}} + let parameters = {{paramName}}.encode() as! [String:AnyObject]{{/bodyParam}}{{^bodyParam}} + let nillableParameters: [String:AnyObject?] = {{^queryParams}}[:]{{/queryParams}}{{#queryParams}}{{^secondaryParam}}[{{/secondaryParam}} + "{{paramName}}": {{paramName}}{{#hasMore}},{{/hasMore}}{{^hasMore}} + ]{{/hasMore}}{{/queryParams}} + let parameters = APIHelper.rejectNil(nillableParameters){{/bodyParam}} + + return {{projectName}}API.requestBuilderFactory.getBuilder() + (method: "{{httpMethod}}", URLString: url, parameters: parameters, isBody: {{^queryParams}}true{{/queryParams}}{{#queryParams}}{{^secondaryParam}}false{{/secondaryParam}}{{/queryParams}}) + } + {{/operation}} + } +} +{{/operations}} diff --git a/modules/swagger-codegen/src/main/resources/swift/model.mustache b/modules/swagger-codegen/src/main/resources/swift/model.mustache new file mode 100644 index 00000000000..e9c01fdecc2 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/swift/model.mustache @@ -0,0 +1,46 @@ +{{#models}}{{#model}}// +// {{classname}}.swift +// +// Generated by swagger-codegen +// https://github.com/swagger-api/swagger-codegen +// + +import SwiftyJSON + +extension {{projectName}}API { + {{#description}} + + /** {{description}} */{{/description}} + final class {{classname}}: JSONEncodable, JSONDecodable { + {{#vars}}{{#isEnum}} + enum {{datatypeWithEnum}}: String { {{#allowableValues}}{{#values}} + case {{enum}} = "{{raw}}"{{/values}}{{/allowableValues}} + } + {{/isEnum}}{{/vars}} + {{#vars}}{{#isEnum}}{{#description}}/** {{description}} */ + {{/description}}var {{name}}: {{{datatypeWithEnum}}}{{^required}}?{{/required}}{{#required}}!{{/required}}{{#defaultValue}} = {{{defaultValue}}}{{/defaultValue}}{{/isEnum}}{{^isEnum}}{{#description}}/** {{description}} */ + {{/description}}var {{name}}: {{{datatype}}}{{^required}}?{{/required}}{{#required}}!{{/required}}{{#defaultValue}} = {{{defaultValue}}}{{/defaultValue}}{{/isEnum}} + {{/vars}} + + // MARK: JSONDecodable + static func decode(source: AnyObject) -> {{classname}}? { + let json = JSON(source) + var instance = {{classname}}(){{#vars}}{{#isEnum}} + instance.{{name}} = {{datatypeWithEnum}}(rawValue: json["{{name}}"].string ?? ""){{/isEnum}}{{^isEnum}} + instance.{{name}} = json["{{name}}"].decode(){{/isEnum}}{{/vars}} + return instance + } + + // MARK: JSONEncodable + func encode() -> AnyObject { + var nillableDictionary = [String:AnyObject?](){{#vars}}{{#isNotContainer}}{{#isPrimitiveType}}{{^isEnum}} + nillableDictionary["{{name}}"] = self.{{name}}{{/isEnum}}{{/isPrimitiveType}}{{#isEnum}} + nillableDictionary["{{name}}"] = self.{{name}}{{^required}}?{{/required}}.rawValue{{/isEnum}}{{^isPrimitiveType}} + nillableDictionary["{{name}}"] = self.{{name}}{{^required}}?{{/required}}.encode(){{/isPrimitiveType}}{{/isNotContainer}}{{#isContainer}} + nillableDictionary["{{name}}"] = self.{{name}}{{^required}}?{{/required}}.encode(){{/isContainer}}{{/vars}} + let dictionary: [String:AnyObject] = APIHelper.rejectNil(nillableDictionary) ?? [:] + return dictionary + } + }{{/model}} +} +{{/models}} diff --git a/samples/client/petstore/swift/PetstoreClient/Cartfile b/samples/client/petstore/swift/PetstoreClient/Cartfile new file mode 100644 index 00000000000..245a690a74b --- /dev/null +++ b/samples/client/petstore/swift/PetstoreClient/Cartfile @@ -0,0 +1,2 @@ +github "Alamofire/Alamofire" >= 1.2 +github "mxcl/PromiseKit" \ No newline at end of file From 5dfa753f5cf4328f423ec829b11a945212563e66 Mon Sep 17 00:00:00 2001 From: kubo_takaichi Date: Sun, 17 May 2015 19:26:11 +0900 Subject: [PATCH 03/15] Do refactor and add extension methods --- .../codegen/languages/SwiftGenerator.java | 2 +- .../src/main/resources/swift/APIs.mustache | 6 ++-- .../swift/AlamofireImplementations.mustache | 2 +- .../main/resources/swift/Cartfile.mustache | 3 +- .../main/resources/swift/Extensions.mustache | 33 +++++++++++++++++++ .../src/main/resources/swift/api.mustache | 7 ++-- 6 files changed, 44 insertions(+), 9 deletions(-) diff --git a/modules/swagger-codegen/src/main/java/com/wordnik/swagger/codegen/languages/SwiftGenerator.java b/modules/swagger-codegen/src/main/java/com/wordnik/swagger/codegen/languages/SwiftGenerator.java index 39a71449d5e..53097c35736 100644 --- a/modules/swagger-codegen/src/main/java/com/wordnik/swagger/codegen/languages/SwiftGenerator.java +++ b/modules/swagger-codegen/src/main/java/com/wordnik/swagger/codegen/languages/SwiftGenerator.java @@ -57,7 +57,7 @@ public class SwiftGenerator extends DefaultCodegen implements CodegenConfig { sourceFolder = appName + "/" + sourceFolder; - supportingFiles.add(new SupportingFile("Cartfile.mustache", appName, "Cartfile")); + supportingFiles.add(new SupportingFile("Cartfile.mustache", "", "Cartfile")); supportingFiles.add(new SupportingFile("APIHelper.mustache", sourceFolder, "APIHelper.swift")); supportingFiles.add(new SupportingFile("AlamofireImplementations.mustache", sourceFolder, "AlamofireImplementations.swift")); supportingFiles.add(new SupportingFile("Extensions.mustache", sourceFolder, "Extensions.swift")); diff --git a/modules/swagger-codegen/src/main/resources/swift/APIs.mustache b/modules/swagger-codegen/src/main/resources/swift/APIs.mustache index 073fbd93be7..8e8764514af 100644 --- a/modules/swagger-codegen/src/main/resources/swift/APIs.mustache +++ b/modules/swagger-codegen/src/main/resources/swift/APIs.mustache @@ -21,9 +21,9 @@ class RequestBuilder { let parameters: [String:AnyObject]? let isBody: Bool let method: String - let URLString: URLStringConvertible + let URLString: String - required init(method: String, URLString: URLStringConvertible, parameters: [String:AnyObject]?, isBody: Bool) { + required init(method: String, URLString: String, parameters: [String:AnyObject]?, isBody: Bool) { self.method = method self.URLString = URLString self.parameters = parameters @@ -40,7 +40,7 @@ class RequestBuilder { } func addCredential() -> Self { - self.credential = OneteamAPI.credential + self.credential = {{projectName}}API.credential return self } } diff --git a/modules/swagger-codegen/src/main/resources/swift/AlamofireImplementations.mustache b/modules/swagger-codegen/src/main/resources/swift/AlamofireImplementations.mustache index 8b34587717a..71f47ad67ca 100644 --- a/modules/swagger-codegen/src/main/resources/swift/AlamofireImplementations.mustache +++ b/modules/swagger-codegen/src/main/resources/swift/AlamofireImplementations.mustache @@ -14,7 +14,7 @@ class AlamofireRequestBuilderFactory: RequestBuilderFactory { } class AlamofireRequestBuilder: RequestBuilder { - required init(method: String, URLString: URLStringConvertible, parameters: [String : AnyObject]?, isBody: Bool) { + required init(method: String, URLString: String, parameters: [String : AnyObject]?, isBody: Bool) { super.init(method: method, URLString: URLString, parameters: parameters, isBody: isBody) } diff --git a/modules/swagger-codegen/src/main/resources/swift/Cartfile.mustache b/modules/swagger-codegen/src/main/resources/swift/Cartfile.mustache index 245a690a74b..7259ae3b6d6 100644 --- a/modules/swagger-codegen/src/main/resources/swift/Cartfile.mustache +++ b/modules/swagger-codegen/src/main/resources/swift/Cartfile.mustache @@ -1,2 +1,3 @@ github "Alamofire/Alamofire" >= 1.2 -github "mxcl/PromiseKit" \ No newline at end of file +github "mxcl/PromiseKit" >=1.5.3 +github "SwiftyJSON/SwiftyJSON" >= 2.2 diff --git a/modules/swagger-codegen/src/main/resources/swift/Extensions.mustache b/modules/swagger-codegen/src/main/resources/swift/Extensions.mustache index d0aed716b7e..cdf5b90d216 100644 --- a/modules/swagger-codegen/src/main/resources/swift/Extensions.mustache +++ b/modules/swagger-codegen/src/main/resources/swift/Extensions.mustache @@ -43,6 +43,12 @@ extension Request { } } +extension String: JSONDecodable { + static func decode(source: AnyObject) -> String? { + return source.description + } +} + extension Array: JSONEncodable { func encode() -> AnyObject { if Element.self is JSONEncodable { @@ -53,6 +59,33 @@ extension Array: JSONEncodable { } } +extension Dictionary: JSONEncodable, JSONDecodable { + static func decode(source: AnyObject) -> [Key:Value]? { + return nil + } + + func encode() -> AnyObject { + var dictionary = [NSObject:AnyObject]() + for (key, value) in self { + let keyObject: NSObject + if Key.self is JSONEncodable { + keyObject = (key as! JSONEncodable).encode() as! NSObject + } else { + keyObject = key as! NSObject + } + let valueObject: AnyObject + if Value.self is JSONEncodable { + valueObject = (value as! JSONEncodable).encode() + } else { + valueObject = value as! NSObject + } + + dictionary[keyObject] = valueObject + } + return dictionary + } +} + extension JSON { func decode() -> Bool? { return self.bool diff --git a/modules/swagger-codegen/src/main/resources/swift/api.mustache b/modules/swagger-codegen/src/main/resources/swift/api.mustache index 0277ec8dfa3..28f14853fcd 100644 --- a/modules/swagger-codegen/src/main/resources/swift/api.mustache +++ b/modules/swagger-codegen/src/main/resources/swift/api.mustache @@ -35,14 +35,15 @@ extension {{projectName}}API { path = path.stringByReplacingOccurrencesOfString("{{=<% %>=}}{<%paramName%>}<%={{ }}=%>", withString: "\({{paramName}})", options: .LiteralSearch, range: nil){{/pathParams}} let url = {{projectName}}API.basePath + path {{#bodyParam}} - let parameters = {{paramName}}.encode() as! [String:AnyObject]{{/bodyParam}}{{^bodyParam}} + let parameters = {{paramName}}{{^required}}?{{/required}}.encode() as? [String:AnyObject]{{/bodyParam}}{{^bodyParam}} let nillableParameters: [String:AnyObject?] = {{^queryParams}}[:]{{/queryParams}}{{#queryParams}}{{^secondaryParam}}[{{/secondaryParam}} "{{paramName}}": {{paramName}}{{#hasMore}},{{/hasMore}}{{^hasMore}} ]{{/hasMore}}{{/queryParams}} let parameters = APIHelper.rejectNil(nillableParameters){{/bodyParam}} - return {{projectName}}API.requestBuilderFactory.getBuilder() - (method: "{{httpMethod}}", URLString: url, parameters: parameters, isBody: {{^queryParams}}true{{/queryParams}}{{#queryParams}}{{^secondaryParam}}false{{/secondaryParam}}{{/queryParams}}) + let requestBuilder: RequestBuilder<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Empty{{/returnType}}>.Type = {{projectName}}API.requestBuilderFactory.getBuilder() + + return requestBuilder(method: "{{httpMethod}}", URLString: url, parameters: parameters, isBody: {{^queryParams}}true{{/queryParams}}{{#queryParams}}{{^secondaryParam}}false{{/secondaryParam}}{{/queryParams}}) } {{/operation}} } From 0b7d758077b4da2ad371078aa8fb4af26648dab9 Mon Sep 17 00:00:00 2001 From: kubo_takaichi Date: Sun, 17 May 2015 22:41:54 +0900 Subject: [PATCH 04/15] Add extension methods for primitives --- .../main/resources/swift/Extensions.mustache | 33 +++++++++++++++---- 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/modules/swagger-codegen/src/main/resources/swift/Extensions.mustache b/modules/swagger-codegen/src/main/resources/swift/Extensions.mustache index cdf5b90d216..d617431bd53 100644 --- a/modules/swagger-codegen/src/main/resources/swift/Extensions.mustache +++ b/modules/swagger-codegen/src/main/resources/swift/Extensions.mustache @@ -43,9 +43,34 @@ extension Request { } } + +extension Bool: JSONDecodable { + static func decode(source: AnyObject) -> Bool? { + return source as? Bool + } +} + +extension Float: JSONDecodable { + static func decode(source: AnyObject) -> Float? { + return source as? Float + } +} + +extension Int: JSONDecodable { + static func decode(source: AnyObject) -> Int? { + return source as? Int + } +} + +extension Double: JSONDecodable { + static func decode(source: AnyObject) -> Double? { + return source as? Double + } +} + extension String: JSONDecodable { static func decode(source: AnyObject) -> String? { - return source.description + return source as? String } } @@ -59,11 +84,7 @@ extension Array: JSONEncodable { } } -extension Dictionary: JSONEncodable, JSONDecodable { - static func decode(source: AnyObject) -> [Key:Value]? { - return nil - } - +extension Dictionary: JSONEncodable { func encode() -> AnyObject { var dictionary = [NSObject:AnyObject]() for (key, value) in self { From 063e6d062dbc4336b972c5c6b90b0f8facc2b5a8 Mon Sep 17 00:00:00 2001 From: kubo_takaichi Date: Sun, 17 May 2015 22:42:32 +0900 Subject: [PATCH 05/15] Introduce decodable array and dictionary --- .../codegen/languages/SwiftGenerator.java | 6 ++- .../src/main/resources/swift/APIs.mustache | 1 + .../resources/swift/DecodableArray.mustache | 28 ++++++++++++++ .../swift/DecodableDictionary.mustache | 38 +++++++++++++++++++ .../src/main/resources/swift/api.mustache | 2 +- 5 files changed, 72 insertions(+), 3 deletions(-) create mode 100644 modules/swagger-codegen/src/main/resources/swift/DecodableArray.mustache create mode 100644 modules/swagger-codegen/src/main/resources/swift/DecodableDictionary.mustache diff --git a/modules/swagger-codegen/src/main/java/com/wordnik/swagger/codegen/languages/SwiftGenerator.java b/modules/swagger-codegen/src/main/java/com/wordnik/swagger/codegen/languages/SwiftGenerator.java index 53097c35736..cd27383e70f 100644 --- a/modules/swagger-codegen/src/main/java/com/wordnik/swagger/codegen/languages/SwiftGenerator.java +++ b/modules/swagger-codegen/src/main/java/com/wordnik/swagger/codegen/languages/SwiftGenerator.java @@ -63,6 +63,8 @@ public class SwiftGenerator extends DefaultCodegen implements CodegenConfig { supportingFiles.add(new SupportingFile("Extensions.mustache", sourceFolder, "Extensions.swift")); supportingFiles.add(new SupportingFile("Models.mustache", sourceFolder, "Models.swift")); supportingFiles.add(new SupportingFile("APIs.mustache", sourceFolder, "APIs.swift")); + supportingFiles.add(new SupportingFile("DecodableDictionary.mustache", sourceFolder, "DecodableDictionary.swift")); + supportingFiles.add(new SupportingFile("DecodableArray.mustache", sourceFolder, "DecodableArray.swift")); languageSpecificPrimitives = new HashSet( Arrays.asList( @@ -138,11 +140,11 @@ public class SwiftGenerator extends DefaultCodegen implements CodegenConfig { if (p instanceof ArrayProperty) { ArrayProperty ap = (ArrayProperty) p; Property inner = ap.getItems(); - return "[" + getTypeDeclaration(inner) + "]"; + return "DecodableArray<" + getTypeDeclaration(inner) + ">"; } else if (p instanceof MapProperty) { MapProperty mp = (MapProperty) p; Property inner = mp.getAdditionalProperties(); - return "[String:" + getTypeDeclaration(inner) + "]"; + return "DecodableDictionary"; } return super.getTypeDeclaration(p); } diff --git a/modules/swagger-codegen/src/main/resources/swift/APIs.mustache b/modules/swagger-codegen/src/main/resources/swift/APIs.mustache index 8e8764514af..22c1490423a 100644 --- a/modules/swagger-codegen/src/main/resources/swift/APIs.mustache +++ b/modules/swagger-codegen/src/main/resources/swift/APIs.mustache @@ -4,6 +4,7 @@ // https://github.com/swagger-api/swagger-codegen // +import Foundation import PromiseKit class {{projectName}}API { diff --git a/modules/swagger-codegen/src/main/resources/swift/DecodableArray.mustache b/modules/swagger-codegen/src/main/resources/swift/DecodableArray.mustache new file mode 100644 index 00000000000..6af73c74010 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/swift/DecodableArray.mustache @@ -0,0 +1,28 @@ +// DecodableArray.swift +// +// Generated by swagger-codegen +// https://github.com/swagger-api/swagger-codegen +// + + +final class DecodableArray: JSONEncodable, JSONDecodable, ArrayLiteralConvertible { + let value: [T] + + required init(arrayLiteral elements: T...) { + self.value = elements + } + + init(array elements: [T]) { + self.value = elements + } + + static func decode(source: AnyObject) -> DecodableArray? { + let array = source as! [AnyObject] + let decodedArray = DecodableArray(array: array.map({ T.decode($0)! })) + return decodedArray + } + + func encode() -> AnyObject { + return value.encode() + } +} diff --git a/modules/swagger-codegen/src/main/resources/swift/DecodableDictionary.mustache b/modules/swagger-codegen/src/main/resources/swift/DecodableDictionary.mustache new file mode 100644 index 00000000000..4463b60eec8 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/swift/DecodableDictionary.mustache @@ -0,0 +1,38 @@ +// DecodableDictionary.swift +// +// Generated by swagger-codegen +// https://github.com/swagger-api/swagger-codegen +// + +import Foundation + +final class DecodableDictionary: JSONEncodable, JSONDecodable, DictionaryLiteralConvertible { + let value: [Key:Value] + + init(dictionaryLiteral elements: (Key, Value)...) { + var dic = [Key:Value]() + for (key, value) in elements { + dic[key] = value + } + self.value = dic + } + + init(dictionary elements: [Key:Value]) { + self.value = elements + } + + static func decode(source: AnyObject) -> DecodableDictionary? { + var destination = [Key:Value]() + let dictionary = source as! [NSObject:AnyObject] + for (key, value) in dictionary { + let decodedKey = Key.decode(key)! + let decodedValue = Value.decode(value)! + destination[decodedKey] = decodedValue + } + return DecodableDictionary(dictionary: destination) + } + + func encode() -> AnyObject { + return value.encode() + } +} diff --git a/modules/swagger-codegen/src/main/resources/swift/api.mustache b/modules/swagger-codegen/src/main/resources/swift/api.mustache index 28f14853fcd..e4961e2e835 100644 --- a/modules/swagger-codegen/src/main/resources/swift/api.mustache +++ b/modules/swagger-codegen/src/main/resources/swift/api.mustache @@ -30,7 +30,7 @@ extension {{projectName}}API { :returns: Promise> {{description}} */ - func {{operationId}}({{#allParams}}{{^secondaryParam}}#{{/secondaryParam}}{{paramName}}: {{dataType}}{{^required}}?{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) -> RequestBuilder<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Empty{{/returnType}}> { + func {{operationId}}({{#allParams}}{{^secondaryParam}}#{{/secondaryParam}}{{paramName}}: {{{dataType}}}{{^required}}?{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) -> RequestBuilder<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Empty{{/returnType}}> { {{^pathParams}}let{{/pathParams}}{{#pathParams}}{{^secondaryParam}}var{{/secondaryParam}}{{/pathParams}} path = "{{path}}"{{#pathParams}} path = path.stringByReplacingOccurrencesOfString("{{=<% %>=}}{<%paramName%>}<%={{ }}=%>", withString: "\({{paramName}})", options: .LiteralSearch, range: nil){{/pathParams}} let url = {{projectName}}API.basePath + path From f21d8503d5f514ce03454ba90111f756962ad250 Mon Sep 17 00:00:00 2001 From: kubo_takaichi Date: Sun, 17 May 2015 22:43:16 +0900 Subject: [PATCH 06/15] Add manager storage to prevent request cancellation --- .../resources/swift/AlamofireImplementations.mustache | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/modules/swagger-codegen/src/main/resources/swift/AlamofireImplementations.mustache b/modules/swagger-codegen/src/main/resources/swift/AlamofireImplementations.mustache index 71f47ad67ca..78ab1c8f3cf 100644 --- a/modules/swagger-codegen/src/main/resources/swift/AlamofireImplementations.mustache +++ b/modules/swagger-codegen/src/main/resources/swift/AlamofireImplementations.mustache @@ -13,15 +13,22 @@ class AlamofireRequestBuilderFactory: RequestBuilderFactory { } } +// Store manager to retain its reference +private var managerStore: [String: Alamofire.Manager] = [:] + class AlamofireRequestBuilder: RequestBuilder { required init(method: String, URLString: String, parameters: [String : AnyObject]?, isBody: Bool) { super.init(method: method, URLString: URLString, parameters: parameters, isBody: isBody) } override func execute() -> Promise> { - let configuration: NSURLSessionConfiguration = NSURLSessionConfiguration.defaultSessionConfiguration() + let managerId = NSUUID().UUIDString + // Create a new manager for each request to customize its request header + let configuration = NSURLSessionConfiguration.defaultSessionConfiguration() configuration.HTTPAdditionalHeaders = buildHeaders() let manager = Alamofire.Manager(configuration: configuration) + managerStore[managerId] = manager + let encoding = isBody ? Alamofire.ParameterEncoding.JSON : Alamofire.ParameterEncoding.URL let request = manager.request(Alamofire.Method(rawValue: method)!, URLString, parameters: parameters, encoding: encoding) if let credential = self.credential { @@ -30,6 +37,8 @@ class AlamofireRequestBuilder: RequestBuilder { let defer = Promise>.defer() request.responseJSON(options: .AllowFragments) { (req, res, json, error) in + managerStore.removeValueForKey(managerId) + if let error = error { defer.reject(error) return From be7fccce684b4837456545c1b871c4052deecc70 Mon Sep 17 00:00:00 2001 From: kubo_takaichi Date: Mon, 18 May 2015 10:43:11 +0900 Subject: [PATCH 07/15] Update JSONEncodable implementations --- .../resources/swift/DecodableArray.mustache | 4 +- .../swift/DecodableDictionary.mustache | 10 +++- .../main/resources/swift/Extensions.mustache | 48 ++++--------------- 3 files changed, 20 insertions(+), 42 deletions(-) diff --git a/modules/swagger-codegen/src/main/resources/swift/DecodableArray.mustache b/modules/swagger-codegen/src/main/resources/swift/DecodableArray.mustache index 6af73c74010..4c658f08f9b 100644 --- a/modules/swagger-codegen/src/main/resources/swift/DecodableArray.mustache +++ b/modules/swagger-codegen/src/main/resources/swift/DecodableArray.mustache @@ -5,7 +5,7 @@ // -final class DecodableArray: JSONEncodable, JSONDecodable, ArrayLiteralConvertible { +final class DecodableArray: JSONEncodable, JSONDecodable, ArrayLiteralConvertible { let value: [T] required init(arrayLiteral elements: T...) { @@ -23,6 +23,6 @@ final class DecodableArray: JSONEncodable, JSONDecodable, Arr } func encode() -> AnyObject { - return value.encode() + return value.map { $0.encode() } } } diff --git a/modules/swagger-codegen/src/main/resources/swift/DecodableDictionary.mustache b/modules/swagger-codegen/src/main/resources/swift/DecodableDictionary.mustache index 4463b60eec8..f22115227cd 100644 --- a/modules/swagger-codegen/src/main/resources/swift/DecodableDictionary.mustache +++ b/modules/swagger-codegen/src/main/resources/swift/DecodableDictionary.mustache @@ -6,7 +6,7 @@ import Foundation -final class DecodableDictionary: JSONEncodable, JSONDecodable, DictionaryLiteralConvertible { +final class DecodableDictionary: JSONEncodable, JSONDecodable, DictionaryLiteralConvertible { let value: [Key:Value] init(dictionaryLiteral elements: (Key, Value)...) { @@ -33,6 +33,12 @@ final class DecodableDictionary AnyObject { - return value.encode() + var destination = [String: AnyObject]() + for (key, value) in self.value { + let keyStr = "\(key.encode())" + let valueObj: AnyObject = value.encode() + destination[keyStr] = valueObj + } + return destination } } diff --git a/modules/swagger-codegen/src/main/resources/swift/Extensions.mustache b/modules/swagger-codegen/src/main/resources/swift/Extensions.mustache index d617431bd53..cfbdcdb7e6b 100644 --- a/modules/swagger-codegen/src/main/resources/swift/Extensions.mustache +++ b/modules/swagger-codegen/src/main/resources/swift/Extensions.mustache @@ -44,67 +44,39 @@ extension Request { } -extension Bool: JSONDecodable { +extension Bool: JSONEncodable, JSONDecodable { static func decode(source: AnyObject) -> Bool? { return source as? Bool } + func encode() -> AnyObject { return self } } -extension Float: JSONDecodable { +extension Float: JSONEncodable, JSONDecodable { static func decode(source: AnyObject) -> Float? { return source as? Float } + func encode() -> AnyObject { return self } } -extension Int: JSONDecodable { +extension Int: JSONEncodable, JSONDecodable { static func decode(source: AnyObject) -> Int? { return source as? Int } + func encode() -> AnyObject { return self } } -extension Double: JSONDecodable { +extension Double: JSONEncodable, JSONDecodable { static func decode(source: AnyObject) -> Double? { return source as? Double } + func encode() -> AnyObject { return self } } -extension String: JSONDecodable { +extension String: JSONEncodable, JSONDecodable { static func decode(source: AnyObject) -> String? { return source as? String } -} - -extension Array: JSONEncodable { - func encode() -> AnyObject { - if Element.self is JSONEncodable { - return self.map { ($0 as! JSONEncodable).encode() } - } else { - return self.map { ($0 as! AnyObject) } - } - } -} - -extension Dictionary: JSONEncodable { - func encode() -> AnyObject { - var dictionary = [NSObject:AnyObject]() - for (key, value) in self { - let keyObject: NSObject - if Key.self is JSONEncodable { - keyObject = (key as! JSONEncodable).encode() as! NSObject - } else { - keyObject = key as! NSObject - } - let valueObject: AnyObject - if Value.self is JSONEncodable { - valueObject = (value as! JSONEncodable).encode() - } else { - valueObject = value as! NSObject - } - - dictionary[keyObject] = valueObject - } - return dictionary - } + func encode() -> AnyObject { return self } } extension JSON { From bb3ec04ed15e3d1c301324b5de128d19e2dca78b Mon Sep 17 00:00:00 2001 From: kubo_takaichi Date: Mon, 18 May 2015 17:26:41 +0900 Subject: [PATCH 08/15] Introduce Decoders class --- .../codegen/languages/SwiftGenerator.java | 6 +- .../src/main/resources/swift/APIs.mustache | 17 ++++- .../swift/AlamofireImplementations.mustache | 19 +++--- .../resources/swift/DecodableArray.mustache | 28 --------- .../swift/DecodableDictionary.mustache | 44 ------------- .../main/resources/swift/Extensions.mustache | 63 ++----------------- .../src/main/resources/swift/Models.mustache | 47 ++++++++++---- .../src/main/resources/swift/api.mustache | 6 +- .../src/main/resources/swift/model.mustache | 5 +- 9 files changed, 71 insertions(+), 164 deletions(-) delete mode 100644 modules/swagger-codegen/src/main/resources/swift/DecodableArray.mustache delete mode 100644 modules/swagger-codegen/src/main/resources/swift/DecodableDictionary.mustache diff --git a/modules/swagger-codegen/src/main/java/com/wordnik/swagger/codegen/languages/SwiftGenerator.java b/modules/swagger-codegen/src/main/java/com/wordnik/swagger/codegen/languages/SwiftGenerator.java index cd27383e70f..53097c35736 100644 --- a/modules/swagger-codegen/src/main/java/com/wordnik/swagger/codegen/languages/SwiftGenerator.java +++ b/modules/swagger-codegen/src/main/java/com/wordnik/swagger/codegen/languages/SwiftGenerator.java @@ -63,8 +63,6 @@ public class SwiftGenerator extends DefaultCodegen implements CodegenConfig { supportingFiles.add(new SupportingFile("Extensions.mustache", sourceFolder, "Extensions.swift")); supportingFiles.add(new SupportingFile("Models.mustache", sourceFolder, "Models.swift")); supportingFiles.add(new SupportingFile("APIs.mustache", sourceFolder, "APIs.swift")); - supportingFiles.add(new SupportingFile("DecodableDictionary.mustache", sourceFolder, "DecodableDictionary.swift")); - supportingFiles.add(new SupportingFile("DecodableArray.mustache", sourceFolder, "DecodableArray.swift")); languageSpecificPrimitives = new HashSet( Arrays.asList( @@ -140,11 +138,11 @@ public class SwiftGenerator extends DefaultCodegen implements CodegenConfig { if (p instanceof ArrayProperty) { ArrayProperty ap = (ArrayProperty) p; Property inner = ap.getItems(); - return "DecodableArray<" + getTypeDeclaration(inner) + ">"; + return "[" + getTypeDeclaration(inner) + "]"; } else if (p instanceof MapProperty) { MapProperty mp = (MapProperty) p; Property inner = mp.getAdditionalProperties(); - return "DecodableDictionary"; + return "[String:" + getTypeDeclaration(inner) + "]"; } return super.getTypeDeclaration(p); } diff --git a/modules/swagger-codegen/src/main/resources/swift/APIs.mustache b/modules/swagger-codegen/src/main/resources/swift/APIs.mustache index 22c1490423a..841d6c03888 100644 --- a/modules/swagger-codegen/src/main/resources/swift/APIs.mustache +++ b/modules/swagger-codegen/src/main/resources/swift/APIs.mustache @@ -13,10 +13,23 @@ class {{projectName}}API { static var requestBuilderFactory: RequestBuilderFactory = AlamofireRequestBuilderFactory() class APIBase { + func toParameters(encodable: JSONEncodable?) -> [String: AnyObject]? { + let encoded: AnyObject? = encodable?.encode() + + if encoded! is [AnyObject] { + var dictionary = [String:AnyObject]() + for (index, item) in enumerate(encoded as! [AnyObject]) { + dictionary["\(index)"] = item + } + return dictionary + } else { + return encoded as? [String:AnyObject] + } + } } } -class RequestBuilder { +class RequestBuilder { var credential: NSURLCredential? var headers: [String:String] = [:] let parameters: [String:AnyObject]? @@ -47,6 +60,6 @@ class RequestBuilder { } protocol RequestBuilderFactory { - func getBuilder() -> RequestBuilder.Type + func getBuilder() -> RequestBuilder.Type } diff --git a/modules/swagger-codegen/src/main/resources/swift/AlamofireImplementations.mustache b/modules/swagger-codegen/src/main/resources/swift/AlamofireImplementations.mustache index 78ab1c8f3cf..244d816332c 100644 --- a/modules/swagger-codegen/src/main/resources/swift/AlamofireImplementations.mustache +++ b/modules/swagger-codegen/src/main/resources/swift/AlamofireImplementations.mustache @@ -8,7 +8,7 @@ import Alamofire import PromiseKit class AlamofireRequestBuilderFactory: RequestBuilderFactory { - func getBuilder() -> RequestBuilder.Type { + func getBuilder() -> RequestBuilder.Type { return AlamofireRequestBuilder.self } } @@ -16,7 +16,7 @@ class AlamofireRequestBuilderFactory: RequestBuilderFactory { // Store manager to retain its reference private var managerStore: [String: Alamofire.Manager] = [:] -class AlamofireRequestBuilder: RequestBuilder { +class AlamofireRequestBuilder: RequestBuilder { required init(method: String, URLString: String, parameters: [String : AnyObject]?, isBody: Bool) { super.init(method: method, URLString: URLString, parameters: parameters, isBody: isBody) } @@ -49,19 +49,16 @@ class AlamofireRequestBuilder: RequestBuilder { defer.reject(error) return } - - if Empty.instance is T { - let response = Response(response: res!, body: Empty.instance as! T) + + if () is T { + let response = Response(response: res!, body: () as! T) defer.fulfill(response) return } if let json: AnyObject = json { - if let body = T.decode(json) { - let response = Response(response: res!, body: body) - defer.fulfill(response) - } else { - defer.reject(NSError(domain: "localhost", code: 500, userInfo: ["reason": "cannot convertible: \(json)"])) - } + let body = Decoders.decode(clazz: T.self, source: json) + let response = Response(response: res!, body: body) + defer.fulfill(response) return } diff --git a/modules/swagger-codegen/src/main/resources/swift/DecodableArray.mustache b/modules/swagger-codegen/src/main/resources/swift/DecodableArray.mustache deleted file mode 100644 index 4c658f08f9b..00000000000 --- a/modules/swagger-codegen/src/main/resources/swift/DecodableArray.mustache +++ /dev/null @@ -1,28 +0,0 @@ -// DecodableArray.swift -// -// Generated by swagger-codegen -// https://github.com/swagger-api/swagger-codegen -// - - -final class DecodableArray: JSONEncodable, JSONDecodable, ArrayLiteralConvertible { - let value: [T] - - required init(arrayLiteral elements: T...) { - self.value = elements - } - - init(array elements: [T]) { - self.value = elements - } - - static func decode(source: AnyObject) -> DecodableArray? { - let array = source as! [AnyObject] - let decodedArray = DecodableArray(array: array.map({ T.decode($0)! })) - return decodedArray - } - - func encode() -> AnyObject { - return value.map { $0.encode() } - } -} diff --git a/modules/swagger-codegen/src/main/resources/swift/DecodableDictionary.mustache b/modules/swagger-codegen/src/main/resources/swift/DecodableDictionary.mustache deleted file mode 100644 index f22115227cd..00000000000 --- a/modules/swagger-codegen/src/main/resources/swift/DecodableDictionary.mustache +++ /dev/null @@ -1,44 +0,0 @@ -// DecodableDictionary.swift -// -// Generated by swagger-codegen -// https://github.com/swagger-api/swagger-codegen -// - -import Foundation - -final class DecodableDictionary: JSONEncodable, JSONDecodable, DictionaryLiteralConvertible { - let value: [Key:Value] - - init(dictionaryLiteral elements: (Key, Value)...) { - var dic = [Key:Value]() - for (key, value) in elements { - dic[key] = value - } - self.value = dic - } - - init(dictionary elements: [Key:Value]) { - self.value = elements - } - - static func decode(source: AnyObject) -> DecodableDictionary? { - var destination = [Key:Value]() - let dictionary = source as! [NSObject:AnyObject] - for (key, value) in dictionary { - let decodedKey = Key.decode(key)! - let decodedValue = Value.decode(value)! - destination[decodedKey] = decodedValue - } - return DecodableDictionary(dictionary: destination) - } - - func encode() -> AnyObject { - var destination = [String: AnyObject]() - for (key, value) in self.value { - let keyStr = "\(key.encode())" - let valueObj: AnyObject = value.encode() - destination[keyStr] = valueObj - } - return destination - } -} diff --git a/modules/swagger-codegen/src/main/resources/swift/Extensions.mustache b/modules/swagger-codegen/src/main/resources/swift/Extensions.mustache index cfbdcdb7e6b..62e22dcf7a8 100644 --- a/modules/swagger-codegen/src/main/resources/swift/Extensions.mustache +++ b/modules/swagger-codegen/src/main/resources/swift/Extensions.mustache @@ -9,73 +9,26 @@ import PromiseKit import SwiftyJSON extension Request { - func responsePromise() -> Promise> { - let defer = Promise>.defer() - responseJSON(options: .AllowFragments) { (req, res, json, error) in - if let error = error { - defer.reject(error) - return - } - if res!.statusCode >= 400 { - let error = NSError(domain: res!.URL!.URLString, code: res!.statusCode, userInfo: [:]) - defer.reject(error) - return - } - - if Empty.instance is T { - let response = Response(response: res!, body: Empty.instance as! T) - defer.fulfill(response) - return - } - if let json: AnyObject = json { - if let body = T.decode(json) { - let response = Response(response: res!, body: body) - defer.fulfill(response) - } else { - defer.reject(NSError(domain: "localhost", code: 500, userInfo: ["reason": "cannot convertible: \(json)"])) - } - return - } - - defer.reject(NSError(domain: "localhost", code: 500, userInfo: ["reason": "unreacheable code"])) - } - return defer.promise - } } -extension Bool: JSONEncodable, JSONDecodable { - static func decode(source: AnyObject) -> Bool? { - return source as? Bool - } +extension Bool: JSONEncodable { func encode() -> AnyObject { return self } } -extension Float: JSONEncodable, JSONDecodable { - static func decode(source: AnyObject) -> Float? { - return source as? Float - } +extension Float: JSONEncodable { func encode() -> AnyObject { return self } } -extension Int: JSONEncodable, JSONDecodable { - static func decode(source: AnyObject) -> Int? { - return source as? Int - } +extension Int: JSONEncodable { func encode() -> AnyObject { return self } } -extension Double: JSONEncodable, JSONDecodable { - static func decode(source: AnyObject) -> Double? { - return source as? Double - } +extension Double: JSONEncodable { func encode() -> AnyObject { return self } } -extension String: JSONEncodable, JSONDecodable { - static func decode(source: AnyObject) -> String? { - return source as? String - } +extension String: JSONEncodable { func encode() -> AnyObject { return self } } @@ -110,12 +63,6 @@ extension JSON { func decode() -> String { return self.stringValue } - func decode() -> T? { - return T.decode(self.object) - } - func decode() -> [T]? { - return self.arrayObject?.map(T.decode).filter({ $0 != nil }).map({ $0! }) - } func decode() -> [T]? { return self.array?.map({ $0 as! T }) } diff --git a/modules/swagger-codegen/src/main/resources/swift/Models.mustache b/modules/swagger-codegen/src/main/resources/swift/Models.mustache index dd02aa12d70..f4bbfecc779 100644 --- a/modules/swagger-codegen/src/main/resources/swift/Models.mustache +++ b/modules/swagger-codegen/src/main/resources/swift/Models.mustache @@ -5,17 +5,12 @@ // import Foundation -import SwiftyJSON - -protocol JSONDecodable { - static func decode(source: AnyObject) -> Self? -} protocol JSONEncodable { func encode() -> AnyObject } -class Response { +class Response { let statusCode: Int let header: [String: String] let body: T @@ -36,12 +31,38 @@ class Response { } } -final class Empty: JSONDecodable { - private init() { } - - static let instance = Empty() - - static func decode(source: AnyObject) -> Empty? { - return instance +class Decoders { + static private var decoders = Dictionary AnyObject)>() + + static func addDecoder(#clazz: T.Type, decoder: ((AnyObject) -> T)) { + let key = "\(T.self)" + decoders[key] = { decoder($0) as! AnyObject } + } + + static func decode(#clazz: [T].Type, source: AnyObject) -> [T] { + let array = source as! [AnyObject] + return array.map { Decoders.decode(clazz: T.self, source: $0) } + } + + static func decode(#clazz: [Key:T].Type, source: AnyObject) -> [Key:T] { + let sourceDictinoary = source as! [Key: AnyObject] + var dictionary = [Key:T]() + for (key, value) in sourceDictinoary { + dictionary[key] = Decoders.decode(clazz: T.self, source: value) + } + return dictionary + } + + static func decode(#clazz: T.Type, source: AnyObject) -> T { + if source is T { + return source as! T + } + + let key = "\(T.self)" + if let decoder = decoders[key] { + return decoder(source) as! T + } else { + fatalError("Source \(source) is not convertible to type \(clazz): Maybe swagger file is insufficient") + } } } diff --git a/modules/swagger-codegen/src/main/resources/swift/api.mustache b/modules/swagger-codegen/src/main/resources/swift/api.mustache index e4961e2e835..e24bdae6773 100644 --- a/modules/swagger-codegen/src/main/resources/swift/api.mustache +++ b/modules/swagger-codegen/src/main/resources/swift/api.mustache @@ -28,9 +28,9 @@ extension {{projectName}}API { {{/hasParams}}{{#allParams}} :param: {{paramName}} ({{#isFormParam}}form{{/isFormParam}}{{#isQueryParam}}query{{/isQueryParam}}{{#isPathParam}}path{{/isPathParam}}{{#isHeaderParam}}header{{/isHeaderParam}}{{#isBodyParam}}body{{/isBodyParam}}) {{description}}{{/allParams}} - :returns: Promise> {{description}} + :returns: Promise> {{description}} */ - func {{operationId}}({{#allParams}}{{^secondaryParam}}#{{/secondaryParam}}{{paramName}}: {{{dataType}}}{{^required}}?{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) -> RequestBuilder<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Empty{{/returnType}}> { + func {{operationId}}({{#allParams}}{{^secondaryParam}}#{{/secondaryParam}}{{paramName}}: {{{dataType}}}{{^required}}?{{/required}}{{#hasMore}}, {{/hasMore}}{{/allParams}}) -> RequestBuilder<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Void{{/returnType}}> { {{^pathParams}}let{{/pathParams}}{{#pathParams}}{{^secondaryParam}}var{{/secondaryParam}}{{/pathParams}} path = "{{path}}"{{#pathParams}} path = path.stringByReplacingOccurrencesOfString("{{=<% %>=}}{<%paramName%>}<%={{ }}=%>", withString: "\({{paramName}})", options: .LiteralSearch, range: nil){{/pathParams}} let url = {{projectName}}API.basePath + path @@ -41,7 +41,7 @@ extension {{projectName}}API { ]{{/hasMore}}{{/queryParams}} let parameters = APIHelper.rejectNil(nillableParameters){{/bodyParam}} - let requestBuilder: RequestBuilder<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Empty{{/returnType}}>.Type = {{projectName}}API.requestBuilderFactory.getBuilder() + let requestBuilder: RequestBuilder<{{#returnType}}{{{returnType}}}{{/returnType}}{{^returnType}}Void{{/returnType}}>.Type = {{projectName}}API.requestBuilderFactory.getBuilder() return requestBuilder(method: "{{httpMethod}}", URLString: url, parameters: parameters, isBody: {{^queryParams}}true{{/queryParams}}{{#queryParams}}{{^secondaryParam}}false{{/secondaryParam}}{{/queryParams}}) } diff --git a/modules/swagger-codegen/src/main/resources/swift/model.mustache b/modules/swagger-codegen/src/main/resources/swift/model.mustache index e9c01fdecc2..a47b2909b73 100644 --- a/modules/swagger-codegen/src/main/resources/swift/model.mustache +++ b/modules/swagger-codegen/src/main/resources/swift/model.mustache @@ -7,11 +7,14 @@ import SwiftyJSON +extension JSON { +} + extension {{projectName}}API { {{#description}} /** {{description}} */{{/description}} - final class {{classname}}: JSONEncodable, JSONDecodable { + final class {{classname}}: JSONEncodable { {{#vars}}{{#isEnum}} enum {{datatypeWithEnum}}: String { {{#allowableValues}}{{#values}} case {{enum}} = "{{raw}}"{{/values}}{{/allowableValues}} From dbfc46658f64f209c0a4f7bbeff25dc3f5f547e3 Mon Sep 17 00:00:00 2001 From: kubo_takaichi Date: Mon, 18 May 2015 17:31:57 +0900 Subject: [PATCH 09/15] Add once-removed Array extension method --- .../src/main/resources/swift/Extensions.mustache | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/modules/swagger-codegen/src/main/resources/swift/Extensions.mustache b/modules/swagger-codegen/src/main/resources/swift/Extensions.mustache index 62e22dcf7a8..e21bec5d53e 100644 --- a/modules/swagger-codegen/src/main/resources/swift/Extensions.mustache +++ b/modules/swagger-codegen/src/main/resources/swift/Extensions.mustache @@ -8,10 +8,6 @@ import Alamofire import PromiseKit import SwiftyJSON -extension Request { -} - - extension Bool: JSONEncodable { func encode() -> AnyObject { return self } } @@ -32,6 +28,16 @@ extension String: JSONEncodable { func encode() -> AnyObject { return self } } +extension Array: JSONEncodable { + func encode() -> AnyObject { + if Element.self is JSONEncodable { + return self.map { ($0 as! JSONEncodable).encode() } + } else { + return self.map { ($0 as! AnyObject) } + } + } +} + extension JSON { func decode() -> Bool? { return self.bool From e0c0d2a72a3cfdbfb18f7dbb4760f491c7a5b419 Mon Sep 17 00:00:00 2001 From: kubo_takaichi Date: Mon, 18 May 2015 21:28:41 +0900 Subject: [PATCH 10/15] Remove dependency on SwiftyJSON --- .../src/main/resources/swift/APIs.mustache | 25 ++++--- .../main/resources/swift/Extensions.mustache | 74 +++++-------------- .../src/main/resources/swift/Models.mustache | 42 +++++++++++ .../src/main/resources/swift/model.mustache | 62 ++++++---------- 4 files changed, 97 insertions(+), 106 deletions(-) diff --git a/modules/swagger-codegen/src/main/resources/swift/APIs.mustache b/modules/swagger-codegen/src/main/resources/swift/APIs.mustache index 841d6c03888..aa39ccfcbdd 100644 --- a/modules/swagger-codegen/src/main/resources/swift/APIs.mustache +++ b/modules/swagger-codegen/src/main/resources/swift/APIs.mustache @@ -11,20 +11,20 @@ class {{projectName}}API { static let basePath = "{{^basePathOverride}}{{basePath}}{{/basePathOverride}}{{basePathOverride}}" static var credential: NSURLCredential? static var requestBuilderFactory: RequestBuilderFactory = AlamofireRequestBuilderFactory() +} - class APIBase { - func toParameters(encodable: JSONEncodable?) -> [String: AnyObject]? { - let encoded: AnyObject? = encodable?.encode() - - if encoded! is [AnyObject] { - var dictionary = [String:AnyObject]() - for (index, item) in enumerate(encoded as! [AnyObject]) { - dictionary["\(index)"] = item - } - return dictionary - } else { - return encoded as? [String:AnyObject] +class APIBase { + func toParameters(encodable: JSONEncodable?) -> [String: AnyObject]? { + let encoded: AnyObject? = encodable?.encode() + + if encoded! is [AnyObject] { + var dictionary = [String:AnyObject]() + for (index, item) in enumerate(encoded as! [AnyObject]) { + dictionary["\(index)"] = item } + return dictionary + } else { + return encoded as? [String:AnyObject] } } } @@ -63,3 +63,4 @@ protocol RequestBuilderFactory { func getBuilder() -> RequestBuilder.Type } + diff --git a/modules/swagger-codegen/src/main/resources/swift/Extensions.mustache b/modules/swagger-codegen/src/main/resources/swift/Extensions.mustache index e21bec5d53e..c937db23fe3 100644 --- a/modules/swagger-codegen/src/main/resources/swift/Extensions.mustache +++ b/modules/swagger-codegen/src/main/resources/swift/Extensions.mustache @@ -6,7 +6,6 @@ import Alamofire import PromiseKit -import SwiftyJSON extension Bool: JSONEncodable { func encode() -> AnyObject { return self } @@ -28,63 +27,26 @@ extension String: JSONEncodable { func encode() -> AnyObject { return self } } +private func encodeIfPossible(object: T) -> AnyObject { + if object is JSONEncodable { + return (object as! JSONEncodable).encode() + } else { + return object as! AnyObject + } +} + extension Array: JSONEncodable { func encode() -> AnyObject { - if Element.self is JSONEncodable { - return self.map { ($0 as! JSONEncodable).encode() } - } else { - return self.map { ($0 as! AnyObject) } + return self.map(encodeIfPossible) + } +} + +extension Dictionary: JSONEncodable { + func encode() -> AnyObject { + var dictionary = [NSObject:AnyObject]() + for (key, value) in self { + dictionary[key as! NSObject] = encodeIfPossible(value) } + return dictionary } } - -extension JSON { - func decode() -> Bool? { - return self.bool - } - func decode() -> Bool { - return self.boolValue - } - func decode() -> Int? { - return self.int - } - func decode() -> Int { - return self.intValue - } - func decode() -> Float? { - return self.float - } - func decode() -> Float { - return self.floatValue - } - func decode() -> Double? { - return self.double - } - func decode() -> Double { - return self.doubleValue - } - func decode() -> String? { - return self.string - } - func decode() -> String { - return self.stringValue - } - func decode() -> [T]? { - return self.array?.map({ $0 as! T }) - } - static let DateFormat: NSDateFormatter = { - let formatter = NSDateFormatter() - formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss'Z'" - return formatter - }() - func decode() -> NSDate? { - return JSON.DateFormat.dateFromString(self.string ?? "") - } - func decode() -> NSData? { - return self.rawData(options: .allZeros, error: nil) - } - func decode() -> NSData { - return self.decode()! - } -} - diff --git a/modules/swagger-codegen/src/main/resources/swift/Models.mustache b/modules/swagger-codegen/src/main/resources/swift/Models.mustache index f4bbfecc779..8bdd72577db 100644 --- a/modules/swagger-codegen/src/main/resources/swift/Models.mustache +++ b/modules/swagger-codegen/src/main/resources/swift/Models.mustache @@ -31,6 +31,7 @@ class Response { } } +private var once = dispatch_once_t() class Decoders { static private var decoders = Dictionary AnyObject)>() @@ -54,6 +55,7 @@ class Decoders { } static func decode(#clazz: T.Type, source: AnyObject) -> T { + initialize() if source is T { return source as! T } @@ -65,4 +67,44 @@ class Decoders { fatalError("Source \(source) is not convertible to type \(clazz): Maybe swagger file is insufficient") } } + + static func decodeOptional(#clazz: T.Type, source: AnyObject?) -> T? { + return source.map { (source: AnyObject) -> T in + Decoders.decode(clazz: clazz, source: source) + } + } + + static func decodeOptional(#clazz: [T].Type, source: AnyObject?) -> [T]? { + return source.map { (someSource: AnyObject) -> [T] in + Decoders.decode(clazz: clazz, source: someSource) + } + } + + static func decodeOptional(#clazz: [Key:T].Type, source: AnyObject?) -> [Key:T]? { + return source.map { (someSource: AnyObject) -> [Key:T] in + Decoders.decode(clazz: clazz, source: someSource) + } + } + + static private func initialize() { + dispatch_once(&once) { + let formatter = NSDateFormatter() + formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss'Z'" + // Decoder for NSDate + Decoders.addDecoder(clazz: NSDate.self) { (source: AnyObject) -> NSDate in + let sourceString = source as! String + return formatter.dateFromString(sourceString)! + } {{#models}}{{#model}} + + // Decoder for {{{classname}}} + Decoders.addDecoder(clazz: {{{classname}}}.self) { (source: AnyObject) -> {{{classname}}} in + let sourceDictionary = source as! [NSObject:AnyObject] + var instance = {{classname}}(){{#vars}}{{#isEnum}} + instance.{{name}} = (sourceDictionary["{{name}}"] as? String).map { {{classname}}.{{datatypeWithEnum}}(rawValue: $0)! } {{#required}}!{{/required}} {{/isEnum}}{{^isEnum}} + instance.{{name}} = Decoders.decode{{^required}}Optional{{/required}}(clazz: {{{baseType}}}.self, source: sourceDictionary["{{name}}"]{{#required}}!{{/required}}){{/isEnum}}{{/vars}} + return instance + }{{/model}} + {{/models}} + } + } } diff --git a/modules/swagger-codegen/src/main/resources/swift/model.mustache b/modules/swagger-codegen/src/main/resources/swift/model.mustache index a47b2909b73..1ecd21a065a 100644 --- a/modules/swagger-codegen/src/main/resources/swift/model.mustache +++ b/modules/swagger-codegen/src/main/resources/swift/model.mustache @@ -5,45 +5,31 @@ // https://github.com/swagger-api/swagger-codegen // -import SwiftyJSON +import Foundation -extension JSON { -} +{{#description}} -extension {{projectName}}API { - {{#description}} +/** {{description}} */{{/description}} +final class {{classname}}: JSONEncodable { +{{#vars}}{{#isEnum}} + enum {{datatypeWithEnum}}: String { {{#allowableValues}}{{#values}} + case {{enum}} = "{{raw}}"{{/values}}{{/allowableValues}} + } + {{/isEnum}}{{/vars}} + {{#vars}}{{#isEnum}}{{#description}}/** {{description}} */ + {{/description}}var {{name}}: {{{datatypeWithEnum}}}{{^required}}?{{/required}}{{#required}}!{{/required}}{{#defaultValue}} = {{{defaultValue}}}{{/defaultValue}}{{/isEnum}}{{^isEnum}}{{#description}}/** {{description}} */ + {{/description}}var {{name}}: {{{datatype}}}{{^required}}?{{/required}}{{#required}}!{{/required}}{{#defaultValue}} = {{{defaultValue}}}{{/defaultValue}}{{/isEnum}} + {{/vars}} - /** {{description}} */{{/description}} - final class {{classname}}: JSONEncodable { - {{#vars}}{{#isEnum}} - enum {{datatypeWithEnum}}: String { {{#allowableValues}}{{#values}} - case {{enum}} = "{{raw}}"{{/values}}{{/allowableValues}} - } - {{/isEnum}}{{/vars}} - {{#vars}}{{#isEnum}}{{#description}}/** {{description}} */ - {{/description}}var {{name}}: {{{datatypeWithEnum}}}{{^required}}?{{/required}}{{#required}}!{{/required}}{{#defaultValue}} = {{{defaultValue}}}{{/defaultValue}}{{/isEnum}}{{^isEnum}}{{#description}}/** {{description}} */ - {{/description}}var {{name}}: {{{datatype}}}{{^required}}?{{/required}}{{#required}}!{{/required}}{{#defaultValue}} = {{{defaultValue}}}{{/defaultValue}}{{/isEnum}} - {{/vars}} - - // MARK: JSONDecodable - static func decode(source: AnyObject) -> {{classname}}? { - let json = JSON(source) - var instance = {{classname}}(){{#vars}}{{#isEnum}} - instance.{{name}} = {{datatypeWithEnum}}(rawValue: json["{{name}}"].string ?? ""){{/isEnum}}{{^isEnum}} - instance.{{name}} = json["{{name}}"].decode(){{/isEnum}}{{/vars}} - return instance - } - - // MARK: JSONEncodable - func encode() -> AnyObject { - var nillableDictionary = [String:AnyObject?](){{#vars}}{{#isNotContainer}}{{#isPrimitiveType}}{{^isEnum}} - nillableDictionary["{{name}}"] = self.{{name}}{{/isEnum}}{{/isPrimitiveType}}{{#isEnum}} - nillableDictionary["{{name}}"] = self.{{name}}{{^required}}?{{/required}}.rawValue{{/isEnum}}{{^isPrimitiveType}} - nillableDictionary["{{name}}"] = self.{{name}}{{^required}}?{{/required}}.encode(){{/isPrimitiveType}}{{/isNotContainer}}{{#isContainer}} - nillableDictionary["{{name}}"] = self.{{name}}{{^required}}?{{/required}}.encode(){{/isContainer}}{{/vars}} - let dictionary: [String:AnyObject] = APIHelper.rejectNil(nillableDictionary) ?? [:] - return dictionary - } - }{{/model}} -} + // MARK: JSONEncodable + func encode() -> AnyObject { + var nillableDictionary = [String:AnyObject?](){{#vars}}{{#isNotContainer}}{{#isPrimitiveType}}{{^isEnum}} + nillableDictionary["{{name}}"] = self.{{name}}{{/isEnum}}{{/isPrimitiveType}}{{#isEnum}} + nillableDictionary["{{name}}"] = self.{{name}}{{^required}}?{{/required}}.rawValue{{/isEnum}}{{^isPrimitiveType}} + nillableDictionary["{{name}}"] = self.{{name}}{{^required}}?{{/required}}.encode(){{/isPrimitiveType}}{{/isNotContainer}}{{#isContainer}} + nillableDictionary["{{name}}"] = self.{{name}}{{^required}}?{{/required}}.encode(){{/isContainer}}{{/vars}} + let dictionary: [String:AnyObject] = APIHelper.rejectNil(nillableDictionary) ?? [:] + return dictionary + } +}{{/model}} {{/models}} From 3623a0dd56c5eb1299d2a94f65a6f97ca95d1061 Mon Sep 17 00:00:00 2001 From: kubo_takaichi Date: Mon, 18 May 2015 21:30:37 +0900 Subject: [PATCH 11/15] Modify Cartfile --- .../swagger-codegen/src/main/resources/swift/Cartfile.mustache | 1 - 1 file changed, 1 deletion(-) diff --git a/modules/swagger-codegen/src/main/resources/swift/Cartfile.mustache b/modules/swagger-codegen/src/main/resources/swift/Cartfile.mustache index 7259ae3b6d6..af74617bcf2 100644 --- a/modules/swagger-codegen/src/main/resources/swift/Cartfile.mustache +++ b/modules/swagger-codegen/src/main/resources/swift/Cartfile.mustache @@ -1,3 +1,2 @@ github "Alamofire/Alamofire" >= 1.2 github "mxcl/PromiseKit" >=1.5.3 -github "SwiftyJSON/SwiftyJSON" >= 2.2 From 9cf1835230b82991d2e4651b8895b62f2f0a3d02 Mon Sep 17 00:00:00 2001 From: kubo_takaichi Date: Mon, 18 May 2015 21:59:11 +0900 Subject: [PATCH 12/15] Fix bug --- .../com/wordnik/swagger/codegen/languages/SwiftGenerator.java | 1 + .../swagger-codegen/src/main/resources/swift/Models.mustache | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/modules/swagger-codegen/src/main/java/com/wordnik/swagger/codegen/languages/SwiftGenerator.java b/modules/swagger-codegen/src/main/java/com/wordnik/swagger/codegen/languages/SwiftGenerator.java index 53097c35736..e763c51e1ac 100644 --- a/modules/swagger-codegen/src/main/java/com/wordnik/swagger/codegen/languages/SwiftGenerator.java +++ b/modules/swagger-codegen/src/main/java/com/wordnik/swagger/codegen/languages/SwiftGenerator.java @@ -99,6 +99,7 @@ public class SwiftGenerator 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("DateTime", "NSDate"); typeMapping.put("boolean", "Bool"); diff --git a/modules/swagger-codegen/src/main/resources/swift/Models.mustache b/modules/swagger-codegen/src/main/resources/swift/Models.mustache index 8bdd72577db..100ba1f4c2d 100644 --- a/modules/swagger-codegen/src/main/resources/swift/Models.mustache +++ b/modules/swagger-codegen/src/main/resources/swift/Models.mustache @@ -100,7 +100,7 @@ class Decoders { Decoders.addDecoder(clazz: {{{classname}}}.self) { (source: AnyObject) -> {{{classname}}} in let sourceDictionary = source as! [NSObject:AnyObject] var instance = {{classname}}(){{#vars}}{{#isEnum}} - instance.{{name}} = (sourceDictionary["{{name}}"] as? String).map { {{classname}}.{{datatypeWithEnum}}(rawValue: $0)! } {{#required}}!{{/required}} {{/isEnum}}{{^isEnum}} + instance.{{name}} = (sourceDictionary["{{name}}"] as? String).map { {{classname}}.{{datatypeWithEnum}}(rawValue: $0)! }{{#required}}!{{/required}} {{/isEnum}}{{^isEnum}} instance.{{name}} = Decoders.decode{{^required}}Optional{{/required}}(clazz: {{{baseType}}}.self, source: sourceDictionary["{{name}}"]{{#required}}!{{/required}}){{/isEnum}}{{/vars}} return instance }{{/model}} From 16a4b60526fc224b1067062044a6f3e16b056c0b Mon Sep 17 00:00:00 2001 From: kubo_takaichi Date: Tue, 19 May 2015 10:06:54 +0900 Subject: [PATCH 13/15] Handle NSNull --- .../src/main/resources/swift/Models.mustache | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/modules/swagger-codegen/src/main/resources/swift/Models.mustache b/modules/swagger-codegen/src/main/resources/swift/Models.mustache index 100ba1f4c2d..d3b00df53ae 100644 --- a/modules/swagger-codegen/src/main/resources/swift/Models.mustache +++ b/modules/swagger-codegen/src/main/resources/swift/Models.mustache @@ -69,18 +69,27 @@ class Decoders { } static func decodeOptional(#clazz: T.Type, source: AnyObject?) -> T? { + if source is NSNull { + return nil + } return source.map { (source: AnyObject) -> T in Decoders.decode(clazz: clazz, source: source) } } static func decodeOptional(#clazz: [T].Type, source: AnyObject?) -> [T]? { + if source is NSNull { + return nil + } return source.map { (someSource: AnyObject) -> [T] in Decoders.decode(clazz: clazz, source: someSource) } } static func decodeOptional(#clazz: [Key:T].Type, source: AnyObject?) -> [Key:T]? { + if source is NSNull { + return nil + } return source.map { (someSource: AnyObject) -> [Key:T] in Decoders.decode(clazz: clazz, source: someSource) } From 2f6b975669c045186d6fa0156b7ebbb5f27087cd Mon Sep 17 00:00:00 2001 From: kubo_takaichi Date: Tue, 19 May 2015 10:07:06 +0900 Subject: [PATCH 14/15] Remove final mark --- modules/swagger-codegen/src/main/resources/swift/model.mustache | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/modules/swagger-codegen/src/main/resources/swift/model.mustache b/modules/swagger-codegen/src/main/resources/swift/model.mustache index 1ecd21a065a..a964882c97c 100644 --- a/modules/swagger-codegen/src/main/resources/swift/model.mustache +++ b/modules/swagger-codegen/src/main/resources/swift/model.mustache @@ -10,7 +10,7 @@ import Foundation {{#description}} /** {{description}} */{{/description}} -final class {{classname}}: JSONEncodable { +class {{classname}}: JSONEncodable { {{#vars}}{{#isEnum}} enum {{datatypeWithEnum}}: String { {{#allowableValues}}{{#values}} case {{enum}} = "{{raw}}"{{/values}}{{/allowableValues}} From 72054078184c30a5a2d42afd2b5abe21288def9b Mon Sep 17 00:00:00 2001 From: kubo_takaichi Date: Tue, 19 May 2015 10:07:40 +0900 Subject: [PATCH 15/15] Add another date formatter (which should be externalized though) --- .../src/main/resources/swift/Models.mustache | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/modules/swagger-codegen/src/main/resources/swift/Models.mustache b/modules/swagger-codegen/src/main/resources/swift/Models.mustache index d3b00df53ae..37c497ddcb9 100644 --- a/modules/swagger-codegen/src/main/resources/swift/Models.mustache +++ b/modules/swagger-codegen/src/main/resources/swift/Models.mustache @@ -97,12 +97,17 @@ class Decoders { static private func initialize() { dispatch_once(&once) { - let formatter = NSDateFormatter() - formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss'Z'" + let dateTimeFormatter = NSDateFormatter() + dateTimeFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss'Z'" + let dateFormatter = NSDateFormatter() + dateFormatter.dateFormat = "yyyy-MM-dd" // Decoder for NSDate Decoders.addDecoder(clazz: NSDate.self) { (source: AnyObject) -> NSDate in let sourceString = source as! String - return formatter.dateFromString(sourceString)! + if count(sourceString) == 10 { + return dateFormatter.dateFromString(sourceString)! + } + return dateTimeFormatter.dateFromString(sourceString)! } {{#models}}{{#model}} // Decoder for {{{classname}}}