diff --git a/bin/swift3-petstore-objcCompatible.json b/bin/swift3-petstore-objcCompatible.json new file mode 100644 index 00000000000..e56770806b9 --- /dev/null +++ b/bin/swift3-petstore-objcCompatible.json @@ -0,0 +1,7 @@ +{ + "podSummary": "PetstoreClient", + "podHomepage": "https://github.com/swagger-api/swagger-codegen", + "podAuthors": "", + "projectName": "PetstoreClient", + "objcCompatible": true +} \ No newline at end of file diff --git a/bin/swift3-petstore-objcCompatible.sh b/bin/swift3-petstore-objcCompatible.sh new file mode 100755 index 00000000000..d774688fdab --- /dev/null +++ b/bin/swift3-petstore-objcCompatible.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 -v -t modules/swagger-codegen/src/main/resources/swift3 -i modules/swagger-codegen/src/test/resources/2_0/petstore-with-fake-endpoints-models-for-testing.yaml -l swift3 -c ./bin/swift3-petstore-objcCompatible.json -o samples/client/petstore/swift3/objcCompatible $@" + +java $JAVA_OPTS -jar $executable $ags diff --git a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/Swift3Codegen.java b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/Swift3Codegen.java index 8bfd73cd261..22642e00e29 100644 --- a/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/Swift3Codegen.java +++ b/modules/swagger-codegen/src/main/java/io/swagger/codegen/languages/Swift3Codegen.java @@ -27,6 +27,7 @@ public class Swift3Codegen extends DefaultCodegen implements CodegenConfig { public static final String PROJECT_NAME = "projectName"; public static final String RESPONSE_AS = "responseAs"; public static final String UNWRAP_REQUIRED = "unwrapRequired"; + public static final String OBJC_COMPATIBLE = "objcCompatible"; public static final String POD_SOURCE = "podSource"; public static final String POD_AUTHORS = "podAuthors"; public static final String POD_SOCIAL_MEDIA_URL = "podSocialMediaURL"; @@ -45,6 +46,7 @@ public class Swift3Codegen extends DefaultCodegen implements CodegenConfig { protected static final String[] RESPONSE_LIBRARIES = {LIBRARY_PROMISE_KIT, LIBRARY_RX_SWIFT}; protected String projectName = "SwaggerClient"; protected boolean unwrapRequired; + protected boolean objcCompatible = false; protected boolean lenientTypeCast = false; protected boolean swiftUseApiNamespace; protected String[] responseAs = new String[0]; @@ -158,6 +160,7 @@ public class Swift3Codegen extends DefaultCodegen implements CodegenConfig { StringUtils.join(RESPONSE_LIBRARIES, ", ") + " are available.")); cliOptions.add(new CliOption(UNWRAP_REQUIRED, "Treat 'required' properties in response as non-optional " + "(which would crash the app if api returns null as opposed to required option specified in json schema")); + cliOptions.add(new CliOption(OBJC_COMPATIBLE, "Add additional properties and methods for Objective-C compatibility (default: false)")); cliOptions.add(new CliOption(POD_SOURCE, "Source information used for Podspec")); cliOptions.add(new CliOption(CodegenConstants.POD_VERSION, "Version used for Podspec")); cliOptions.add(new CliOption(POD_AUTHORS, "Authors used for Podspec")); @@ -202,6 +205,12 @@ public class Swift3Codegen extends DefaultCodegen implements CodegenConfig { } additionalProperties.put(UNWRAP_REQUIRED, unwrapRequired); + // Setup objcCompatible option, which adds additional properties and methods for Objective-C compatibility + if (additionalProperties.containsKey(OBJC_COMPATIBLE)) { + setObjcCompatible(convertPropertyToBooleanAndWriteBack(OBJC_COMPATIBLE)); + } + additionalProperties.put(OBJC_COMPATIBLE, objcCompatible); + // Setup unwrapRequired option, which makes all the properties with "required" non-optional if (additionalProperties.containsKey(RESPONSE_AS)) { Object responseAsObject = additionalProperties.get(RESPONSE_AS); @@ -250,13 +259,13 @@ public class Swift3Codegen extends DefaultCodegen implements CodegenConfig { } @Override - public String escapeReservedWord(String name) { + public String escapeReservedWord(String name) { if(this.reservedWordsMappings().containsKey(name)) { return this.reservedWordsMappings().get(name); } return "_" + name; // add an underscore to the name } - + @Override public String modelFileFolder() { return outputFolder + File.separator + sourceFolder + modelPackage().replace('.', File.separatorChar); @@ -479,6 +488,10 @@ public class Swift3Codegen extends DefaultCodegen implements CodegenConfig { this.unwrapRequired = unwrapRequired; } + public void setObjcCompatible(boolean objcCompatible) { + this.objcCompatible = objcCompatible; + } + public void setLenientTypeCast(boolean lenientTypeCast) { this.lenientTypeCast = lenientTypeCast; } @@ -579,6 +592,31 @@ public class Swift3Codegen extends DefaultCodegen implements CodegenConfig { return postProcessModelsEnum(objs); } + @Override + public void postProcessModelProperty(CodegenModel model, CodegenProperty property) { + super.postProcessModelProperty(model, property); + + // The default template code has the following logic for assigning a type as Swift Optional: + // + // {{^unwrapRequired}}?{{/unwrapRequired}}{{#unwrapRequired}}{{^required}}?{{/required}}{{/unwrapRequired}} + // + // which means: + // + // boolean isSwiftOptional = !unwrapRequired || (unwrapRequired && !property.required); + // + // We can drop the check for unwrapRequired in (unwrapRequired && !property.required) + // due to short-circuit evaluation of the || operator. + boolean isSwiftOptional = !unwrapRequired || !property.required; + boolean isSwiftScalarType = property.isInteger || property.isLong || property.isFloat || property.isDouble || property.isBoolean; + if (isSwiftOptional && isSwiftScalarType) { + // Optional scalar types like Int?, Int64?, Float?, Double?, and Bool? + // do not translate to Objective-C. So we want to flag those + // properties in case we want to put special code in the templates + // which provide Objective-C compatibility. + property.vendorExtensions.put("x-swift-optional-scalar", true); + } + } + @Override public String escapeQuotationMark(String input) { // remove " to avoid code injection diff --git a/modules/swagger-codegen/src/main/resources/swift3/model.mustache b/modules/swagger-codegen/src/main/resources/swift3/model.mustache index cd6a8cd82d1..5e69c93b692 100644 --- a/modules/swagger-codegen/src/main/resources/swift3/model.mustache +++ b/modules/swagger-codegen/src/main/resources/swift3/model.mustache @@ -44,7 +44,12 @@ open class {{classname}}: {{#parent}}{{{parent}}}{{/parent}}{{^parent}}JSONEncod {{/isEnum}} {{^isEnum}} {{#description}}/** {{description}} */ - {{/description}}public var {{name}}: {{{datatype}}}{{^unwrapRequired}}?{{/unwrapRequired}}{{#unwrapRequired}}{{^required}}?{{/required}}{{/unwrapRequired}}{{#defaultValue}} = {{{defaultValue}}}{{/defaultValue}} + {{/description}}public var {{name}}: {{{datatype}}}{{^unwrapRequired}}?{{/unwrapRequired}}{{#unwrapRequired}}{{^required}}?{{/required}}{{/unwrapRequired}}{{#defaultValue}} = {{{defaultValue}}}{{/defaultValue}}{{#objcCompatible}}{{#vendorExtensions.x-swift-optional-scalar}} + public var {{name}}Num: NSNumber? { + get { + return {{name}}.map({ return NSNumber(value: $0) }) + } + }{{/vendorExtensions.x-swift-optional-scalar}}{{/objcCompatible}} {{/isEnum}} {{/vars}} diff --git a/modules/swagger-codegen/src/test/java/io/swagger/codegen/options/Swift3OptionsProvider.java b/modules/swagger-codegen/src/test/java/io/swagger/codegen/options/Swift3OptionsProvider.java index 0a72783a159..6a4607b588a 100644 --- a/modules/swagger-codegen/src/test/java/io/swagger/codegen/options/Swift3OptionsProvider.java +++ b/modules/swagger-codegen/src/test/java/io/swagger/codegen/options/Swift3OptionsProvider.java @@ -13,6 +13,7 @@ public class Swift3OptionsProvider implements OptionsProvider { public static final String PROJECT_NAME_VALUE = "Swagger"; public static final String RESPONSE_AS_VALUE = "test"; public static final String UNWRAP_REQUIRED_VALUE = "true"; + public static final String OBJC_COMPATIBLE_VALUE = "false"; public static final String LENIENT_TYPE_CAST_VALUE = "false"; public static final String POD_SOURCE_VALUE = "{ :git => 'git@github.com:swagger-api/swagger-mustache.git'," + " :tag => 'v1.0.0-SNAPSHOT' }"; @@ -42,6 +43,7 @@ public class Swift3OptionsProvider implements OptionsProvider { .put(Swift3Codegen.PROJECT_NAME, PROJECT_NAME_VALUE) .put(Swift3Codegen.RESPONSE_AS, RESPONSE_AS_VALUE) .put(Swift3Codegen.UNWRAP_REQUIRED, UNWRAP_REQUIRED_VALUE) + .put(Swift3Codegen.OBJC_COMPATIBLE, OBJC_COMPATIBLE_VALUE) .put(Swift3Codegen.LENIENT_TYPE_CAST, LENIENT_TYPE_CAST_VALUE) .put(Swift3Codegen.POD_SOURCE, POD_SOURCE_VALUE) .put(CodegenConstants.POD_VERSION, POD_VERSION_VALUE) diff --git a/modules/swagger-codegen/src/test/java/io/swagger/codegen/swift3/Swift3OptionsTest.java b/modules/swagger-codegen/src/test/java/io/swagger/codegen/swift3/Swift3OptionsTest.java index 7e0166926ab..5af65532076 100644 --- a/modules/swagger-codegen/src/test/java/io/swagger/codegen/swift3/Swift3OptionsTest.java +++ b/modules/swagger-codegen/src/test/java/io/swagger/codegen/swift3/Swift3OptionsTest.java @@ -33,6 +33,8 @@ public class Swift3OptionsTest extends AbstractOptionsTest { times = 1; clientCodegen.setUnwrapRequired(Boolean.valueOf(Swift3OptionsProvider.UNWRAP_REQUIRED_VALUE)); times = 1; + clientCodegen.setObjcCompatible(Boolean.valueOf(Swift3OptionsProvider.OBJC_COMPATIBLE_VALUE)); + times = 1; clientCodegen.setLenientTypeCast(Boolean.valueOf(Swift3OptionsProvider.LENIENT_TYPE_CAST_VALUE)); times = 1; }};