forked from loafle/openapi-generator-original
Add additional cli option and properties to swift3 for Objective-C compatibility. (#6129)
Currently, in the swift3 language, if you have an optional integer, number, or boolean property in a model, then the generated swift3 model class might look like: class SomeModel { var someInt: Int? var someFloat: Float? var someDouble: Double? var someBool: Bool? } This works fine if you are accessing this model only from Swift code. However, it is very common for iOS codebases to contain both Swift AND Objective-C. If you need to access this model from Objective-C, then those 4 properties are not accessible, since Optional scalars do not translate to Objective-C. Therefore, in the swift3 language, we want to add some code for Objective-C compatibility: 1. We add a "objCompatible" boolean command-line option. If objCompatible=true, then this enables some additional code generation to make these types of properties accessible from Objective-C. If objCompatible=false, then the generated code is exactly as it currently is. The default is objcCopmatible=false. 2. If objCompatible=true, then for these types of Objective-C-inaccessible properties (Optional scalars), then we add a "x-swift-optional-scalar=true" vendor extension in the CodegenProperty. 3. Then, in the model.mustache template, if we see x-swift-optional-scalar=true, then we add an additional computed property which returns an optional NSNumber. So, for example, when objcCompatible=false (the default case), then the generated code for the "declawed" property of the Cat model looks like: open class Cat: Animal { public var declawed: Bool? ... But when objcCompatible=true, then it looks like: open class Cat: Animal { public var declawed: Bool? public var declawedNum: NSNumber? { get { return declawed.map({ return NSNumber(value: $0) }) } } ...
This commit is contained in:
parent
218273e9a8
commit
dd46ba9ef6
7
bin/swift3-petstore-objcCompatible.json
Normal file
7
bin/swift3-petstore-objcCompatible.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"podSummary": "PetstoreClient",
|
||||||
|
"podHomepage": "https://github.com/swagger-api/swagger-codegen",
|
||||||
|
"podAuthors": "",
|
||||||
|
"projectName": "PetstoreClient",
|
||||||
|
"objcCompatible": true
|
||||||
|
}
|
31
bin/swift3-petstore-objcCompatible.sh
Executable file
31
bin/swift3-petstore-objcCompatible.sh
Executable file
@ -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
|
@ -27,6 +27,7 @@ public class Swift3Codegen extends DefaultCodegen implements CodegenConfig {
|
|||||||
public static final String PROJECT_NAME = "projectName";
|
public static final String PROJECT_NAME = "projectName";
|
||||||
public static final String RESPONSE_AS = "responseAs";
|
public static final String RESPONSE_AS = "responseAs";
|
||||||
public static final String UNWRAP_REQUIRED = "unwrapRequired";
|
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_SOURCE = "podSource";
|
||||||
public static final String POD_AUTHORS = "podAuthors";
|
public static final String POD_AUTHORS = "podAuthors";
|
||||||
public static final String POD_SOCIAL_MEDIA_URL = "podSocialMediaURL";
|
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 static final String[] RESPONSE_LIBRARIES = {LIBRARY_PROMISE_KIT, LIBRARY_RX_SWIFT};
|
||||||
protected String projectName = "SwaggerClient";
|
protected String projectName = "SwaggerClient";
|
||||||
protected boolean unwrapRequired;
|
protected boolean unwrapRequired;
|
||||||
|
protected boolean objcCompatible = false;
|
||||||
protected boolean lenientTypeCast = false;
|
protected boolean lenientTypeCast = false;
|
||||||
protected boolean swiftUseApiNamespace;
|
protected boolean swiftUseApiNamespace;
|
||||||
protected String[] responseAs = new String[0];
|
protected String[] responseAs = new String[0];
|
||||||
@ -158,6 +160,7 @@ public class Swift3Codegen extends DefaultCodegen implements CodegenConfig {
|
|||||||
StringUtils.join(RESPONSE_LIBRARIES, ", ") + " are available."));
|
StringUtils.join(RESPONSE_LIBRARIES, ", ") + " are available."));
|
||||||
cliOptions.add(new CliOption(UNWRAP_REQUIRED, "Treat 'required' properties in response as non-optional " +
|
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"));
|
"(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(POD_SOURCE, "Source information used for Podspec"));
|
||||||
cliOptions.add(new CliOption(CodegenConstants.POD_VERSION, "Version used for Podspec"));
|
cliOptions.add(new CliOption(CodegenConstants.POD_VERSION, "Version used for Podspec"));
|
||||||
cliOptions.add(new CliOption(POD_AUTHORS, "Authors 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);
|
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
|
// Setup unwrapRequired option, which makes all the properties with "required" non-optional
|
||||||
if (additionalProperties.containsKey(RESPONSE_AS)) {
|
if (additionalProperties.containsKey(RESPONSE_AS)) {
|
||||||
Object responseAsObject = additionalProperties.get(RESPONSE_AS);
|
Object responseAsObject = additionalProperties.get(RESPONSE_AS);
|
||||||
@ -250,13 +259,13 @@ public class Swift3Codegen extends DefaultCodegen implements CodegenConfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String escapeReservedWord(String name) {
|
public String escapeReservedWord(String name) {
|
||||||
if(this.reservedWordsMappings().containsKey(name)) {
|
if(this.reservedWordsMappings().containsKey(name)) {
|
||||||
return this.reservedWordsMappings().get(name);
|
return this.reservedWordsMappings().get(name);
|
||||||
}
|
}
|
||||||
return "_" + name; // add an underscore to the name
|
return "_" + name; // add an underscore to the name
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String modelFileFolder() {
|
public String modelFileFolder() {
|
||||||
return outputFolder + File.separator + sourceFolder + modelPackage().replace('.', File.separatorChar);
|
return outputFolder + File.separator + sourceFolder + modelPackage().replace('.', File.separatorChar);
|
||||||
@ -479,6 +488,10 @@ public class Swift3Codegen extends DefaultCodegen implements CodegenConfig {
|
|||||||
this.unwrapRequired = unwrapRequired;
|
this.unwrapRequired = unwrapRequired;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setObjcCompatible(boolean objcCompatible) {
|
||||||
|
this.objcCompatible = objcCompatible;
|
||||||
|
}
|
||||||
|
|
||||||
public void setLenientTypeCast(boolean lenientTypeCast) {
|
public void setLenientTypeCast(boolean lenientTypeCast) {
|
||||||
this.lenientTypeCast = lenientTypeCast;
|
this.lenientTypeCast = lenientTypeCast;
|
||||||
}
|
}
|
||||||
@ -579,6 +592,31 @@ public class Swift3Codegen extends DefaultCodegen implements CodegenConfig {
|
|||||||
return postProcessModelsEnum(objs);
|
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
|
@Override
|
||||||
public String escapeQuotationMark(String input) {
|
public String escapeQuotationMark(String input) {
|
||||||
// remove " to avoid code injection
|
// remove " to avoid code injection
|
||||||
|
@ -44,7 +44,12 @@ open class {{classname}}: {{#parent}}{{{parent}}}{{/parent}}{{^parent}}JSONEncod
|
|||||||
{{/isEnum}}
|
{{/isEnum}}
|
||||||
{{^isEnum}}
|
{{^isEnum}}
|
||||||
{{#description}}/** {{description}} */
|
{{#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}}
|
{{/isEnum}}
|
||||||
{{/vars}}
|
{{/vars}}
|
||||||
|
|
||||||
|
@ -13,6 +13,7 @@ public class Swift3OptionsProvider implements OptionsProvider {
|
|||||||
public static final String PROJECT_NAME_VALUE = "Swagger";
|
public static final String PROJECT_NAME_VALUE = "Swagger";
|
||||||
public static final String RESPONSE_AS_VALUE = "test";
|
public static final String RESPONSE_AS_VALUE = "test";
|
||||||
public static final String UNWRAP_REQUIRED_VALUE = "true";
|
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 LENIENT_TYPE_CAST_VALUE = "false";
|
||||||
public static final String POD_SOURCE_VALUE = "{ :git => 'git@github.com:swagger-api/swagger-mustache.git'," +
|
public static final String POD_SOURCE_VALUE = "{ :git => 'git@github.com:swagger-api/swagger-mustache.git'," +
|
||||||
" :tag => 'v1.0.0-SNAPSHOT' }";
|
" :tag => 'v1.0.0-SNAPSHOT' }";
|
||||||
@ -42,6 +43,7 @@ public class Swift3OptionsProvider implements OptionsProvider {
|
|||||||
.put(Swift3Codegen.PROJECT_NAME, PROJECT_NAME_VALUE)
|
.put(Swift3Codegen.PROJECT_NAME, PROJECT_NAME_VALUE)
|
||||||
.put(Swift3Codegen.RESPONSE_AS, RESPONSE_AS_VALUE)
|
.put(Swift3Codegen.RESPONSE_AS, RESPONSE_AS_VALUE)
|
||||||
.put(Swift3Codegen.UNWRAP_REQUIRED, UNWRAP_REQUIRED_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.LENIENT_TYPE_CAST, LENIENT_TYPE_CAST_VALUE)
|
||||||
.put(Swift3Codegen.POD_SOURCE, POD_SOURCE_VALUE)
|
.put(Swift3Codegen.POD_SOURCE, POD_SOURCE_VALUE)
|
||||||
.put(CodegenConstants.POD_VERSION, POD_VERSION_VALUE)
|
.put(CodegenConstants.POD_VERSION, POD_VERSION_VALUE)
|
||||||
|
@ -33,6 +33,8 @@ public class Swift3OptionsTest extends AbstractOptionsTest {
|
|||||||
times = 1;
|
times = 1;
|
||||||
clientCodegen.setUnwrapRequired(Boolean.valueOf(Swift3OptionsProvider.UNWRAP_REQUIRED_VALUE));
|
clientCodegen.setUnwrapRequired(Boolean.valueOf(Swift3OptionsProvider.UNWRAP_REQUIRED_VALUE));
|
||||||
times = 1;
|
times = 1;
|
||||||
|
clientCodegen.setObjcCompatible(Boolean.valueOf(Swift3OptionsProvider.OBJC_COMPATIBLE_VALUE));
|
||||||
|
times = 1;
|
||||||
clientCodegen.setLenientTypeCast(Boolean.valueOf(Swift3OptionsProvider.LENIENT_TYPE_CAST_VALUE));
|
clientCodegen.setLenientTypeCast(Boolean.valueOf(Swift3OptionsProvider.LENIENT_TYPE_CAST_VALUE));
|
||||||
times = 1;
|
times = 1;
|
||||||
}};
|
}};
|
||||||
|
Loading…
x
Reference in New Issue
Block a user