Skip to content

Commit b0882c1

Browse files
committed
Add swift generics support
1 parent ed8d3da commit b0882c1

File tree

15 files changed

+267
-199
lines changed

15 files changed

+267
-199
lines changed

.travis.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,8 @@ env:
4242
- SDK=Ruby27
4343
- SDK=Ruby30
4444
- SDK=Ruby31
45-
- SDK=SwiftClient55
46-
- SDK=SwiftServer55
45+
- SDK=AppleSwift55
46+
- SDK=Swift55
4747
- SDK=WebChromium
4848
- SDK=WebNode
4949

example.php

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
use Appwrite\SDK\Language\Deno;
1616
use Appwrite\SDK\Language\HTTP;
1717
use Appwrite\SDK\Language\Swift;
18-
use Appwrite\SDK\Language\SwiftClient;
18+
use Appwrite\SDK\Language\Apple;
1919
use Appwrite\SDK\Language\DotNet;
2020
use Appwrite\SDK\Language\Flutter;
2121
use Appwrite\SDK\Language\Android;
@@ -341,7 +341,7 @@ function getSSLPage($url) {
341341
$sdk->generate(__DIR__ . '/examples/swift-server');
342342

343343
// Swift (Client)
344-
$sdk = new SDK(new SwiftClient(), new Swagger2($spec));
344+
$sdk = new SDK(new Apple(), new Swagger2($spec));
345345

346346
$sdk
347347
->setName('NAME')
@@ -362,7 +362,7 @@ function getSSLPage($url) {
362362
])
363363
;
364364

365-
$sdk->generate(__DIR__ . '/examples/swift-client');
365+
$sdk->generate(__DIR__ . '/examples/apple');
366366

367367
// DotNet
368368
$sdk = new SDK(new DotNet(), new Swagger2($spec));

src/SDK/Language.php

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,4 +83,18 @@ public function getFilters(): array
8383
{
8484
return [];
8585
}
86+
87+
protected function toUpperCase(string $value): string
88+
{
89+
return ucfirst($this->helperCamelCase($value));
90+
}
91+
92+
protected function helperCamelCase($str): string
93+
{
94+
$str = preg_replace('/[^a-z0-9' . implode("", []) . ']+/i', ' ', $str);
95+
$str = trim($str);
96+
$str = ucwords($str);
97+
$str = str_replace(" ", "", $str);
98+
return lcfirst($str);
99+
}
86100
}

src/SDK/Language/SwiftClient.php renamed to src/SDK/Language/Apple.php

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,14 @@
22

33
namespace Appwrite\SDK\Language;
44

5-
class SwiftClient extends Swift {
5+
class Apple extends Swift {
66

77
/**
88
* @return string
99
*/
1010
public function getName()
1111
{
12-
return 'SwiftClient';
12+
return 'Apple';
1313
}
1414

1515
public function getFiles()

src/SDK/Language/Swift.php

Lines changed: 92 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -467,10 +467,100 @@ public function getFilters(): array
467467
new TwigFilter('swiftComment', function ($value) {
468468
$value = explode("\n", $value);
469469
foreach ($value as $key => $line) {
470-
$value[$key] = " /// " . wordwrap($value[$key], 75, "\n /// ");
470+
$value[$key] = " /// " . wordwrap($line, 75, "\n /// ");
471471
}
472472
return implode("\n", $value);
473-
}, ['is_safe' => ['html']])
473+
}, ['is_safe' => ['html']]),
474+
new TwigFilter('returnType', function (array $method, array $spec, string $generic = 'T') {
475+
return $this->getReturnType($method, $spec, $generic);
476+
}),
477+
new TwigFilter('modelType', function (array $property, array $spec, string $generic = 'T : Codable') {
478+
return $this->getModelType($property, $spec, $generic);
479+
}),
480+
new TwigFilter('propertyType', function (array $property, array $spec, string $generic = 'T') {
481+
return $this->getPropertyType($property, $spec, $generic);
482+
}),
483+
new TwigFilter('hasGenericType', function (string $model, array $spec) {
484+
return $this->hasGenericType($model, $spec);
485+
}),
474486
];
475487
}
488+
489+
protected function getReturnType(array $method, array $spec, string $generic): string
490+
{
491+
if ($method['type'] === 'webAuth') {
492+
return 'Bool';
493+
}
494+
if ($method['type'] === 'location') {
495+
return 'ByteBuffer';
496+
}
497+
498+
if (!\array_key_exists('responseModel', $method)
499+
|| empty($method['responseModel'])
500+
|| $method['responseModel'] === 'any') {
501+
return 'Any';
502+
}
503+
504+
$ret = $this->toUpperCase($method['responseModel']);
505+
506+
if ($this->hasGenericType($method['responseModel'], $spec)) {
507+
$ret .= '<' . $generic . '>';
508+
}
509+
510+
return \ucfirst($spec['title']) . 'Models.' . $ret;
511+
}
512+
513+
protected function getModelType(array $definition, array $spec, string $generic): string
514+
{
515+
if ($this->hasGenericType($definition['name'], $spec)) {
516+
return $this->toUpperCase($definition['name']) . '<' . $generic . '>';
517+
}
518+
return $this->toUpperCase($definition['name']);
519+
}
520+
521+
protected function getPropertyType(array $property, array $spec, string $generic): string
522+
{
523+
if (\array_key_exists('sub_schema', $property)) {
524+
$type = $this->toUpperCase($property['sub_schema']);
525+
526+
if ($this->hasGenericType($property['sub_schema'], $spec)) {
527+
$type .= '<' . $generic . '>';
528+
}
529+
530+
if ($property['type'] === 'array') {
531+
$type = '[' . $type . ']';
532+
}
533+
} else {
534+
$type = $this->getTypeName($property);
535+
}
536+
537+
if (!$property['required']) {
538+
$type .= '?';
539+
}
540+
541+
return $type;
542+
}
543+
544+
protected function hasGenericType(?string $model, array $spec): string
545+
{
546+
if (empty($model) || $model === 'any') {
547+
return false;
548+
}
549+
550+
$model = $spec['definitions'][$model];
551+
552+
if ($model['additionalProperties']) {
553+
return true;
554+
}
555+
556+
foreach ($model['properties'] as $property) {
557+
if (!\array_key_exists('sub_schema', $property) || !$property['sub_schema']) {
558+
continue;
559+
}
560+
561+
return $this->hasGenericType($property['sub_schema'], $spec);
562+
}
563+
564+
return false;
565+
}
476566
}

src/SDK/Language/Web.php

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -301,22 +301,6 @@ public function getReturn(array $method, array $spec): string
301301
return "";
302302
}
303303

304-
public function toUpperCase(string $value): string
305-
{
306-
return ucfirst((string)$this->helperCamelCase($value));
307-
}
308-
309-
protected function helperCamelCase($str)
310-
{
311-
$str = preg_replace('/[^a-z0-9' . implode("", []) . ']+/i', ' ', $str);
312-
$str = trim($str);
313-
$str = ucwords($str);
314-
$str = str_replace(" ", "", $str);
315-
$str = lcfirst($str);
316-
317-
return $str;
318-
}
319-
320304
public function getSubSchema(array $property, array $spec): string
321305
{
322306
if (array_key_exists('sub_schema', $property)) {
Lines changed: 34 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -1,79 +1,61 @@
1-
{% macro sub_schema(property) %}{% if property.sub_schema %}{% if property.type == 'array' %}[{{property.sub_schema | caseUcfirst}}]{% else %}{{property.sub_schema | caseUcfirst}}{% endif %}{% else %}{% if property.type == 'object' and property.additionalProperties %}[String : Any]{% else %}{{property | typeName}}{% endif %}{% endif %}{% endmacro %}
1+
import Foundation
22

33
/// {{ definition.description }}
44
{% if definition.properties | length == 0 and not definition.additionalProperties %}
5-
public class {{ definition.name | caseUcfirst }} {}
5+
public class {{ definition | modelType(spec) | raw }} {}
66
{% else %}
7-
public class {{ definition.name | caseUcfirst }} {
7+
public class {{ definition | modelType(spec) | raw }} {
88

9-
{% for property in definition.properties %}
9+
{%~ for property in definition.properties %}
1010
/// {{ property.description }}
11-
public let {{ property.name | escapeKeyword | removeDollarSign }}: {{_self.sub_schema(property)}}{% if not property.required %}?{% endif %}
11+
public let {{ property.name | escapeKeyword | removeDollarSign }}: {{ property | propertyType(spec) | raw }}{% if not property.required %}?{% endif %}
1212

1313

14-
{% endfor %}
15-
{% if definition.additionalProperties %}
16-
let data: [String: Any]
14+
{%~ endfor %}
15+
{%~ if definition.additionalProperties %}
16+
/// Additional properties
17+
let data: T
18+
{%~ endif %}
1719

18-
{% endif %}
1920
init(
20-
{% for property in definition.properties %}
21-
{{ property.name | escapeKeyword | removeDollarSign }}: {{_self.sub_schema(property)}}{% if not property.required %}?{% endif %}{% if not loop.last or (loop.last and definition.additionalProperties) %},{% endif %}
21+
{%~ for property in definition.properties %}
22+
{{ property.name | escapeKeyword | removeDollarSign }}: {{ property | propertyType(spec) | raw }}{% if not property.required %}?{% endif %}{% if not loop.last or (loop.last and definition.additionalProperties) %},{% endif %}
2223

23-
{% endfor %}
24-
{% if definition.additionalProperties %}
25-
data: [String: Any]
26-
{% endif %}
24+
{%~ endfor %}
25+
{%~ if definition.additionalProperties %}
26+
data: T
27+
{%~ endif %}
2728
) {
28-
{% for property in definition.properties %}
29+
{%~ for property in definition.properties %}
2930
self.{{ property.name | escapeKeyword | removeDollarSign }} = {{ property.name | escapeKeyword | removeDollarSign }}
30-
{% endfor %}
31-
{% if definition.additionalProperties %}
31+
{%~ endfor %}
32+
{%~ if definition.additionalProperties %}
3233
self.data = data
33-
{% endif %}
34-
}
35-
36-
public static func from(map: [String: Any]) -> {{ definition.name | caseUcfirst }} {
37-
return {{ definition.name | caseUcfirst }}(
38-
{% for property in definition.properties %}
39-
{{ property.name | escapeKeyword | removeDollarSign }}: {% if property.sub_schema %}{% if property.type == 'array' %}(map["{{property.name }}"] as! [[String: Any]]).map { {{property.sub_schema | caseUcfirst}}.from(map: $0) }{% else %}{{property.sub_schema | caseUcfirst}}.from(map: map["{{property.name }}"] as! [String: Any]){% endif %}{% else %}map["{{property.name }}"] as{% if property.required %}!{% else %}?{% endif %} {{ _self.sub_schema(property) }}{% endif %}{% if not loop.last or (loop.last and definition.additionalProperties) %},{% endif %}
40-
41-
{% endfor %}
42-
{% if definition.additionalProperties %}
43-
data: map
44-
{% endif %}
45-
)
34+
{%~ endif %}
4635
}
4736

4837
public func toMap() -> [String: Any] {
4938
return [
50-
{% for property in definition.properties %}
39+
{%~ for property in definition.properties %}
5140
"{{ property.name | escapeKeyword }}": {% if property.sub_schema %}{% if property.type == 'array' %}{{property.name | escapeKeyword | removeDollarSign}}.map { $0.toMap() }{% else %}{{property.name | escapeKeyword | removeDollarSign}}.toMap(){% endif %}{% else %}{{property.name | escapeKeyword | removeDollarSign}}{% endif %} as Any{% if not loop.last or (loop.last and definition.additionalProperties) %},{% endif %}
5241

53-
{% endfor %}
54-
{% if definition.additionalProperties %}
55-
"data": data
56-
{% endif %}
42+
{%~ endfor %}
43+
{%~ if definition.additionalProperties %}
44+
"data": try! JSONEncoder().encode(data)
45+
{%~ endif %}
5746
]
5847
}
59-
{% if definition.additionalProperties %}
6048

61-
public func convertTo<T>(fromJson: ([String: Any]) -> T) -> T {
62-
return fromJson(data)
63-
}
64-
{% endif %}
65-
{% for property in definition.properties %}
66-
{% if property.sub_schema %}
67-
{% for def in spec.definitions %}
68-
{% if def.name == property.sub_schema and def.additionalProperties and property.type == 'array' %}
49+
public static func from(map: [String: Any] ) -> {{ definition.name | caseUcfirst }} {
50+
return {{ definition.name | caseUcfirst }}(
51+
{%~ for property in definition.properties %}
52+
{{ property.name | escapeKeyword | removeDollarSign }}: {% if property.sub_schema %}{% if property.type == 'array' %}(map["{{property.name }}"] as! [[String: Any]]).map { {{property.sub_schema | caseUcfirst}}.from(map: $0) }{% else %}{{property.sub_schema | caseUcfirst}}.from(map: map["{{property.name }}"] as! [String: Any]){% endif %}{% else %}map["{{property.name }}"] as{% if property.required %}!{% else %}?{% endif %} {{ property | propertyType(spec) | raw }}{% endif %}{% if not loop.last or (loop.last and definition.additionalProperties) %},{% endif %}
6953

70-
public func convertTo<T>(fromJson: ([String: Any]) -> T) -> [T] {
71-
{{property.name | removeDollarSign}}.map { d in d.convertTo(fromJson: fromJson) }
54+
{%~ endfor %}
55+
{%~ if definition.additionalProperties %}
56+
data: try! JSONDecoder().decode(T.self, from: JSONSerialization.data(withJSONObject: map, options: []))
57+
{%~ endif %}
58+
)
7259
}
73-
{% endif %}
74-
{% endfor %}
75-
{% endif %}
76-
{% endfor %}
77-
7860
}
7961
{% endif %}

0 commit comments

Comments
 (0)