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/DefaultCodegen.java b/modules/swagger-codegen/src/main/java/com/wordnik/swagger/codegen/DefaultCodegen.java index a2b4258532f..cd957722483 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,20 +1,7 @@ package com.wordnik.swagger.codegen; -import java.io.File; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import org.apache.commons.lang.StringUtils; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; +import com.google.common.base.Function; +import com.google.common.collect.Lists; import com.wordnik.swagger.codegen.examples.ExampleGenerator; import com.wordnik.swagger.models.ArrayModel; @@ -53,6 +40,25 @@ import com.wordnik.swagger.models.properties.RefProperty; import com.wordnik.swagger.models.properties.StringProperty; import com.wordnik.swagger.util.Json; +import javax.annotation.Nullable; +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import org.apache.commons.lang.StringUtils; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + + public class DefaultCodegen { Logger LOGGER = LoggerFactory.getLogger(DefaultCodegen.class); @@ -192,6 +198,7 @@ public class DefaultCodegen { } public String toParamName(String name) { + name = removeNonNameElementToCamelCase(name); if(reservedWords.contains(name)) { return escapeReservedWord(name); } @@ -718,6 +725,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()); @@ -1166,6 +1174,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); } 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..e763c51e1ac --- /dev/null +++ b/modules/swagger-codegen/src/main/java/com/wordnik/swagger/codegen/languages/SwiftGenerator.java @@ -0,0 +1,250 @@ +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", "", "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("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 c9dd5111861..73dbeae62ef 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,5 +18,6 @@ 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 com.wordnik.swagger.codegen.languages.AkkaScalaClientCodegen 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..aa39ccfcbdd --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/swift/APIs.mustache @@ -0,0 +1,66 @@ +// APIs.swift +// +// Generated by swagger-codegen +// https://github.com/swagger-api/swagger-codegen +// + +import Foundation +import PromiseKit + +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 RequestBuilder { + var credential: NSURLCredential? + var headers: [String:String] = [:] + let parameters: [String:AnyObject]? + let isBody: Bool + let method: String + let URLString: String + + required init(method: String, URLString: String, 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 = {{projectName}}API.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..244d816332c --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/swift/AlamofireImplementations.mustache @@ -0,0 +1,79 @@ +// 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 + } +} + +// 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 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 { + request.authenticate(usingCredential: credential) + } + + let defer = Promise>.defer() + request.responseJSON(options: .AllowFragments) { (req, res, json, error) in + managerStore.removeValueForKey(managerId) + + 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 () is T { + let response = Response(response: res!, body: () as! T) + defer.fulfill(response) + return + } + if let json: AnyObject = json { + let body = Decoders.decode(clazz: T.self, source: json) + let response = Response(response: res!, body: body) + defer.fulfill(response) + 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..af74617bcf2 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/swift/Cartfile.mustache @@ -0,0 +1,2 @@ +github "Alamofire/Alamofire" >= 1.2 +github "mxcl/PromiseKit" >=1.5.3 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..c937db23fe3 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/swift/Extensions.mustache @@ -0,0 +1,52 @@ +// Extensions.swift +// +// Generated by swagger-codegen +// https://github.com/swagger-api/swagger-codegen +// + +import Alamofire +import PromiseKit + +extension Bool: JSONEncodable { + func encode() -> AnyObject { return self } +} + +extension Float: JSONEncodable { + func encode() -> AnyObject { return self } +} + +extension Int: JSONEncodable { + func encode() -> AnyObject { return self } +} + +extension Double: JSONEncodable { + func encode() -> AnyObject { return self } +} + +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 { + 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 + } +} 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..37c497ddcb9 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/swift/Models.mustache @@ -0,0 +1,124 @@ +// Models.swift +// +// Generated by swagger-codegen +// https://github.com/swagger-api/swagger-codegen +// + +import Foundation + +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) + } +} + +private var once = dispatch_once_t() +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 { + initialize() + 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") + } + } + + 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) + } + } + + static private func initialize() { + dispatch_once(&once) { + 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 + if count(sourceString) == 10 { + return dateFormatter.dateFromString(sourceString)! + } + return dateTimeFormatter.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/api.mustache b/modules/swagger-codegen/src/main/resources/swift/api.mustache new file mode 100644 index 00000000000..e24bdae6773 --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/swift/api.mustache @@ -0,0 +1,51 @@ +{{#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}}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 + {{#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}} + + 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}}) + } + {{/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..a964882c97c --- /dev/null +++ b/modules/swagger-codegen/src/main/resources/swift/model.mustache @@ -0,0 +1,35 @@ +{{#models}}{{#model}}// +// {{classname}}.swift +// +// Generated by swagger-codegen +// https://github.com/swagger-api/swagger-codegen +// + +import Foundation + +{{#description}} + +/** {{description}} */{{/description}} +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: 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