moved from previous repo
This commit is contained in:
parent
8fbf7c5105
commit
a182726352
|
@ -0,0 +1,42 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
|
<plist version="1.0">
|
||||||
|
<dict>
|
||||||
|
<key>BuildMachineOSBuild</key>
|
||||||
|
<string></string>
|
||||||
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
|
<string>en</string>
|
||||||
|
<key>CFBundleExecutable</key>
|
||||||
|
<string>SwiftGen_SwiftGenCLI</string>
|
||||||
|
<key>CFBundleIdentifier</key>
|
||||||
|
<string>SwiftGen.SwiftGenCLI.resources</string>
|
||||||
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
|
<string>6.0</string>
|
||||||
|
<key>CFBundleName</key>
|
||||||
|
<string>SwiftGen_SwiftGenCLI</string>
|
||||||
|
<key>CFBundlePackageType</key>
|
||||||
|
<string>BNDL</string>
|
||||||
|
<key>CFBundleSupportedPlatforms</key>
|
||||||
|
<array>
|
||||||
|
<string>MacOSX</string>
|
||||||
|
</array>
|
||||||
|
<key>DTCompiler</key>
|
||||||
|
<string>com.apple.compilers.llvm.clang.1_0</string>
|
||||||
|
<key>DTPlatformBuild</key>
|
||||||
|
<string>13A233</string>
|
||||||
|
<key>DTPlatformName</key>
|
||||||
|
<string>macosx</string>
|
||||||
|
<key>DTPlatformVersion</key>
|
||||||
|
<string>11.3</string>
|
||||||
|
<key>DTSDKBuild</key>
|
||||||
|
<string>20E214</string>
|
||||||
|
<key>DTSDKName</key>
|
||||||
|
<string>macosx11.3</string>
|
||||||
|
<key>DTXcode</key>
|
||||||
|
<string>1300</string>
|
||||||
|
<key>DTXcodeBuild</key>
|
||||||
|
<string>13A233</string>
|
||||||
|
<key>LSMinimumSystemVersion</key>
|
||||||
|
<string>10.11</string>
|
||||||
|
</dict>
|
||||||
|
</plist>
|
|
@ -0,0 +1,43 @@
|
||||||
|
// swiftlint:disable all
|
||||||
|
// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen
|
||||||
|
|
||||||
|
{% if palettes %}
|
||||||
|
{% set enumName %}{{param.enumName|default:"ColorName"}}{% endset %}
|
||||||
|
{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %}
|
||||||
|
#if os(macOS)
|
||||||
|
import AppKit
|
||||||
|
{% if enumName != 'NSColor' %}{{accessModifier}} enum {{enumName}} { }{% endif %}
|
||||||
|
#elseif os(iOS) || os(tvOS) || os(watchOS)
|
||||||
|
import UIKit
|
||||||
|
{% if enumName != 'UIColor' %}{{accessModifier}} enum {{enumName}} { }{% endif %}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// swiftlint:disable superfluous_disable_command
|
||||||
|
// swiftlint:disable file_length
|
||||||
|
|
||||||
|
// MARK: - Colors
|
||||||
|
|
||||||
|
// swiftlint:disable identifier_name line_length type_body_length
|
||||||
|
{{accessModifier}} extension {{enumName}} {
|
||||||
|
{% macro h2f hex %}{{hex|hexToInt|int255toFloat}}{% endmacro %}
|
||||||
|
{% macro enumBlock colors accessPrefix %}
|
||||||
|
{% for color in colors %}
|
||||||
|
/// 0x{{color.red}}{{color.green}}{{color.blue}}{{color.alpha}} (r: {{color.red|hexToInt}}, g: {{color.green|hexToInt}}, b: {{color.blue|hexToInt}}, a: {{color.alpha|hexToInt}})
|
||||||
|
{{accessPrefix}}static let {{color.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}} = #colorLiteral(red: {% call h2f color.red %}, green: {% call h2f color.green %}, blue: {% call h2f color.blue %}, alpha: {% call h2f color.alpha %})
|
||||||
|
{% endfor %}
|
||||||
|
{% endmacro %}
|
||||||
|
{% if palettes.count > 1 or param.forceFileNameEnum %}
|
||||||
|
{% set accessPrefix %}{{accessModifier}} {% endset %}
|
||||||
|
{% for palette in palettes %}
|
||||||
|
enum {{palette.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} {
|
||||||
|
{% filter indent:2 %}{% call enumBlock palette.colors accessPrefix %}{% endfilter %}
|
||||||
|
}
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
{% call enumBlock palettes.first.colors "" %}
|
||||||
|
{% endif %}
|
||||||
|
}
|
||||||
|
// swiftlint:enable identifier_name line_length type_body_length
|
||||||
|
{% else %}
|
||||||
|
// No color found
|
||||||
|
{% endif %}
|
|
@ -0,0 +1,43 @@
|
||||||
|
// swiftlint:disable all
|
||||||
|
// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen
|
||||||
|
|
||||||
|
{% if palettes %}
|
||||||
|
{% set enumName %}{{param.enumName|default:"ColorName"}}{% endset %}
|
||||||
|
{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %}
|
||||||
|
#if os(macOS)
|
||||||
|
import AppKit
|
||||||
|
{% if enumName != 'NSColor' %}{{accessModifier}} enum {{enumName}} { }{% endif %}
|
||||||
|
#elseif os(iOS) || os(tvOS) || os(watchOS)
|
||||||
|
import UIKit
|
||||||
|
{% if enumName != 'UIColor' %}{{accessModifier}} enum {{enumName}} { }{% endif %}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// swiftlint:disable superfluous_disable_command
|
||||||
|
// swiftlint:disable file_length
|
||||||
|
|
||||||
|
// MARK: - Colors
|
||||||
|
|
||||||
|
// swiftlint:disable identifier_name line_length type_body_length
|
||||||
|
{{accessModifier}} extension {{enumName}} {
|
||||||
|
{% macro h2f hex %}{{hex|hexToInt|int255toFloat}}{% endmacro %}
|
||||||
|
{% macro enumBlock colors accessPrefix %}
|
||||||
|
{% for color in colors %}
|
||||||
|
/// 0x{{color.red}}{{color.green}}{{color.blue}}{{color.alpha}} (r: {{color.red|hexToInt}}, g: {{color.green|hexToInt}}, b: {{color.blue|hexToInt}}, a: {{color.alpha|hexToInt}})
|
||||||
|
{{accessPrefix}}static let {{color.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}} = #colorLiteral(red: {% call h2f color.red %}, green: {% call h2f color.green %}, blue: {% call h2f color.blue %}, alpha: {% call h2f color.alpha %})
|
||||||
|
{% endfor %}
|
||||||
|
{% endmacro %}
|
||||||
|
{% if palettes.count > 1 or param.forceFileNameEnum %}
|
||||||
|
{% set accessPrefix %}{{accessModifier}} {% endset %}
|
||||||
|
{% for palette in palettes %}
|
||||||
|
enum {{palette.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} {
|
||||||
|
{% filter indent:2 %}{% call enumBlock palette.colors accessPrefix %}{% endfilter %}
|
||||||
|
}
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
{% call enumBlock palettes.first.colors "" %}
|
||||||
|
{% endif %}
|
||||||
|
}
|
||||||
|
// swiftlint:enable identifier_name line_length type_body_length
|
||||||
|
{% else %}
|
||||||
|
// No color found
|
||||||
|
{% endif %}
|
|
@ -0,0 +1,84 @@
|
||||||
|
// swiftlint:disable all
|
||||||
|
// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen
|
||||||
|
|
||||||
|
{% if palettes %}
|
||||||
|
{% set colorAlias %}{{param.colorAliasName|default:"Color"}}{% endset %}
|
||||||
|
{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %}
|
||||||
|
#if os(macOS)
|
||||||
|
import AppKit.NSColor
|
||||||
|
{{accessModifier}} typealias {{colorAlias}} = NSColor
|
||||||
|
#elseif os(iOS) || os(tvOS) || os(watchOS)
|
||||||
|
import UIKit.UIColor
|
||||||
|
{{accessModifier}} typealias {{colorAlias}} = UIColor
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// swiftlint:disable superfluous_disable_command file_length implicit_return
|
||||||
|
|
||||||
|
// MARK: - Colors
|
||||||
|
|
||||||
|
// swiftlint:disable identifier_name line_length type_body_length
|
||||||
|
{% set enumName %}{{param.enumName|default:"ColorName"}}{% endset %}
|
||||||
|
{{accessModifier}} struct {{enumName}} {
|
||||||
|
{{accessModifier}} let rgbaValue: UInt32
|
||||||
|
{{accessModifier}} var color: {{colorAlias}} { return {{colorAlias}}(named: self) }
|
||||||
|
|
||||||
|
{% macro rgbaValue color %}0x{{color.red}}{{color.green}}{{color.blue}}{{color.alpha}}{% endmacro %}
|
||||||
|
{% macro enumBlock colors %}
|
||||||
|
{% for color in colors %}
|
||||||
|
/// <span style="display:block;width:3em;height:2em;border:1px solid black;background:#{{color.red}}{{color.green}}{{color.blue}}"></span>
|
||||||
|
/// Alpha: {{color.alpha|hexToInt|int255toFloat|percent}} <br/> (0x{{color.red}}{{color.green}}{{color.blue}}{{color.alpha}})
|
||||||
|
{{accessModifier}} static let {{color.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}} = {{enumName}}(rgbaValue: {% call rgbaValue color %})
|
||||||
|
{% endfor %}
|
||||||
|
{% endmacro %}
|
||||||
|
{% if palettes.count > 1 or param.forceFileNameEnum %}
|
||||||
|
{% for palette in palettes %}
|
||||||
|
{{accessModifier}} enum {{palette.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} {
|
||||||
|
{% filter indent:2 %}{% call enumBlock palette.colors %}{% endfilter %}
|
||||||
|
}
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
{% call enumBlock palettes.first.colors %}
|
||||||
|
{% endif %}
|
||||||
|
}
|
||||||
|
// swiftlint:enable identifier_name line_length type_body_length
|
||||||
|
|
||||||
|
// MARK: - Implementation Details
|
||||||
|
|
||||||
|
internal extension {{colorAlias}} {
|
||||||
|
convenience init(rgbaValue: UInt32) {
|
||||||
|
let components = RGBAComponents(rgbaValue: rgbaValue).normalized
|
||||||
|
self.init(red: components[0], green: components[1], blue: components[2], alpha: components[3])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct RGBAComponents {
|
||||||
|
let rgbaValue: UInt32
|
||||||
|
|
||||||
|
private var shifts: [UInt32] {
|
||||||
|
[
|
||||||
|
rgbaValue >> 24, // red
|
||||||
|
rgbaValue >> 16, // green
|
||||||
|
rgbaValue >> 8, // blue
|
||||||
|
rgbaValue // alpha
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
private var components: [CGFloat] {
|
||||||
|
shifts.map {
|
||||||
|
CGFloat($0 & 0xff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var normalized: [CGFloat] {
|
||||||
|
components.map { $0 / 255.0 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{{accessModifier}} extension {{colorAlias}} {
|
||||||
|
convenience init(named color: {{enumName}}) {
|
||||||
|
self.init(rgbaValue: color.rgbaValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{% else %}
|
||||||
|
// No color found
|
||||||
|
{% endif %}
|
|
@ -0,0 +1,84 @@
|
||||||
|
// swiftlint:disable all
|
||||||
|
// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen
|
||||||
|
|
||||||
|
{% if palettes %}
|
||||||
|
{% set colorAlias %}{{param.colorAliasName|default:"Color"}}{% endset %}
|
||||||
|
{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %}
|
||||||
|
#if os(macOS)
|
||||||
|
import AppKit.NSColor
|
||||||
|
{{accessModifier}} typealias {{colorAlias}} = NSColor
|
||||||
|
#elseif os(iOS) || os(tvOS) || os(watchOS)
|
||||||
|
import UIKit.UIColor
|
||||||
|
{{accessModifier}} typealias {{colorAlias}} = UIColor
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// swiftlint:disable superfluous_disable_command file_length implicit_return
|
||||||
|
|
||||||
|
// MARK: - Colors
|
||||||
|
|
||||||
|
// swiftlint:disable identifier_name line_length type_body_length
|
||||||
|
{% set enumName %}{{param.enumName|default:"ColorName"}}{% endset %}
|
||||||
|
{{accessModifier}} struct {{enumName}} {
|
||||||
|
{{accessModifier}} let rgbaValue: UInt32
|
||||||
|
{{accessModifier}} var color: {{colorAlias}} { return {{colorAlias}}(named: self) }
|
||||||
|
|
||||||
|
{% macro rgbaValue color %}0x{{color.red}}{{color.green}}{{color.blue}}{{color.alpha}}{% endmacro %}
|
||||||
|
{% macro enumBlock colors %}
|
||||||
|
{% for color in colors %}
|
||||||
|
/// <span style="display:block;width:3em;height:2em;border:1px solid black;background:#{{color.red}}{{color.green}}{{color.blue}}"></span>
|
||||||
|
/// Alpha: {{color.alpha|hexToInt|int255toFloat|percent}} <br/> (0x{{color.red}}{{color.green}}{{color.blue}}{{color.alpha}})
|
||||||
|
{{accessModifier}} static let {{color.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}} = {{enumName}}(rgbaValue: {% call rgbaValue color %})
|
||||||
|
{% endfor %}
|
||||||
|
{% endmacro %}
|
||||||
|
{% if palettes.count > 1 or param.forceFileNameEnum %}
|
||||||
|
{% for palette in palettes %}
|
||||||
|
{{accessModifier}} enum {{palette.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} {
|
||||||
|
{% filter indent:2 %}{% call enumBlock palette.colors %}{% endfilter %}
|
||||||
|
}
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
{% call enumBlock palettes.first.colors %}
|
||||||
|
{% endif %}
|
||||||
|
}
|
||||||
|
// swiftlint:enable identifier_name line_length type_body_length
|
||||||
|
|
||||||
|
// MARK: - Implementation Details
|
||||||
|
|
||||||
|
internal extension {{colorAlias}} {
|
||||||
|
convenience init(rgbaValue: UInt32) {
|
||||||
|
let components = RGBAComponents(rgbaValue: rgbaValue).normalized
|
||||||
|
self.init(red: components[0], green: components[1], blue: components[2], alpha: components[3])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct RGBAComponents {
|
||||||
|
let rgbaValue: UInt32
|
||||||
|
|
||||||
|
private var shifts: [UInt32] {
|
||||||
|
[
|
||||||
|
rgbaValue >> 24, // red
|
||||||
|
rgbaValue >> 16, // green
|
||||||
|
rgbaValue >> 8, // blue
|
||||||
|
rgbaValue // alpha
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
|
private var components: [CGFloat] {
|
||||||
|
shifts.map {
|
||||||
|
CGFloat($0 & 0xff)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var normalized: [CGFloat] {
|
||||||
|
components.map { $0 / 255.0 }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{{accessModifier}} extension {{colorAlias}} {
|
||||||
|
convenience init(named color: {{enumName}}) {
|
||||||
|
self.init(rgbaValue: color.rgbaValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{% else %}
|
||||||
|
// No color found
|
||||||
|
{% endif %}
|
|
@ -0,0 +1,211 @@
|
||||||
|
// swiftlint:disable all
|
||||||
|
// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen
|
||||||
|
|
||||||
|
// swiftlint:disable superfluous_disable_command implicit_return
|
||||||
|
// swiftlint:disable sorted_imports
|
||||||
|
import CoreData
|
||||||
|
import Foundation
|
||||||
|
{% for import in param.extraImports %}
|
||||||
|
import {{ import }}
|
||||||
|
{% empty %}
|
||||||
|
{# If extraImports is a single String instead of an array, `for` considers it empty but we still have to check if there's a single String value #}
|
||||||
|
{% if param.extraImports %}import {{ param.extraImports }}{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
// swiftlint:disable attributes file_length vertical_whitespace_closing_braces
|
||||||
|
// swiftlint:disable identifier_name line_length type_body_length
|
||||||
|
{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %}
|
||||||
|
|
||||||
|
{% for model in models %}
|
||||||
|
{% for name, entity in model.entities %}
|
||||||
|
{% set superclass %}{{ model.entities[entity.superEntity].className|default:"NSManagedObject" }}{% endset %}
|
||||||
|
{% set entityClassName %}{{ entity.className|default:"NSManagedObject" }}{% endset %}
|
||||||
|
// MARK: - {{ entity.name }}
|
||||||
|
|
||||||
|
{% if not entity.shouldGenerateCode %}
|
||||||
|
// Note: '{{ entity.name }}' has codegen enabled for Xcode, skipping code generation.
|
||||||
|
|
||||||
|
{% elif entityClassName|contains:"." %}
|
||||||
|
// Warning: '{{ entityClassName }}' cannot be a valid type name, skipping code generation.
|
||||||
|
|
||||||
|
{% else %}
|
||||||
|
{% if param.generateObjcName %}
|
||||||
|
@objc({{ entityClassName }})
|
||||||
|
{% endif %}
|
||||||
|
{{ accessModifier }} class {{ entityClassName }}: {{ superclass }} {
|
||||||
|
{% set override %}{% if superclass != "NSManagedObject" %}override {% endif %}{% endset %}
|
||||||
|
{{ override }}{{ accessModifier }} class var entityName: String {
|
||||||
|
return "{{ entity.name }}"
|
||||||
|
}
|
||||||
|
|
||||||
|
{{ override }}{{ accessModifier }} class func entity(in managedObjectContext: NSManagedObjectContext) -> NSEntityDescription? {
|
||||||
|
return NSEntityDescription.entity(forEntityName: entityName, in: managedObjectContext)
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(*, deprecated, renamed: "makeFetchRequest", message: "To avoid collisions with the less concrete method in `NSManagedObject`, please use `makeFetchRequest()` instead.")
|
||||||
|
@nonobjc {{ accessModifier }} class func fetchRequest() -> NSFetchRequest<{{ entityClassName }}> {
|
||||||
|
return NSFetchRequest<{{ entityClassName }}>(entityName: entityName)
|
||||||
|
}
|
||||||
|
|
||||||
|
@nonobjc {{ accessModifier }} class func makeFetchRequest() -> NSFetchRequest<{{ entityClassName }}> {
|
||||||
|
return NSFetchRequest<{{ entityClassName }}>(entityName: entityName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// swiftlint:disable discouraged_optional_boolean discouraged_optional_collection
|
||||||
|
{% for attribute in entity.attributes %}
|
||||||
|
{% if attribute.userInfo.RawType %}
|
||||||
|
{% set rawType attribute.userInfo.RawType %}
|
||||||
|
{% set unwrapOptional attribute.userInfo.unwrapOptional %}
|
||||||
|
{{ accessModifier }} var {{ attribute.name }}: {{ rawType }}{% if not unwrapOptional %}?{% endif %} {
|
||||||
|
get {
|
||||||
|
let key = "{{ attribute.name }}"
|
||||||
|
willAccessValue(forKey: key)
|
||||||
|
defer { didAccessValue(forKey: key) }
|
||||||
|
|
||||||
|
{% if unwrapOptional %}
|
||||||
|
guard let value = primitiveValue(forKey: key) as? {{ rawType }}.RawValue,
|
||||||
|
let result = {{ rawType }}(rawValue: value) else {
|
||||||
|
fatalError("Could not convert value for key '\(key)' to type '{{ rawType }}'")
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
{% else %}
|
||||||
|
guard let value = primitiveValue(forKey: key) as? {{ rawType }}.RawValue else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return {{ rawType }}(rawValue: value)
|
||||||
|
{% endif %}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
let key = "{{ attribute.name }}"
|
||||||
|
willChangeValue(forKey: key)
|
||||||
|
defer { didChangeValue(forKey: key) }
|
||||||
|
|
||||||
|
setPrimitiveValue(newValue{% if not unwrapOptional %}?{% endif %}.rawValue, forKey: key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{% elif attribute.usesScalarValueType and attribute.isOptional %}
|
||||||
|
{{ accessModifier }} var {{ attribute.name }}: {{ attribute.typeName }}? {
|
||||||
|
get {
|
||||||
|
let key = "{{ attribute.name }}"
|
||||||
|
willAccessValue(forKey: key)
|
||||||
|
defer { didAccessValue(forKey: key) }
|
||||||
|
|
||||||
|
return primitiveValue(forKey: key) as? {{ attribute.typeName }}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
let key = "{{ attribute.name }}"
|
||||||
|
willChangeValue(forKey: key)
|
||||||
|
defer { didChangeValue(forKey: key) }
|
||||||
|
|
||||||
|
setPrimitiveValue(newValue, forKey: key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{% else %}
|
||||||
|
@NSManaged {{ accessModifier }} var {{ attribute.name }}: {{ attribute.typeName }}{% if attribute.isOptional %}?{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% for relationship in entity.relationships %}
|
||||||
|
{% if relationship.isToMany %}
|
||||||
|
@NSManaged {{ accessModifier }} var {{ relationship.name }}: {% if relationship.isOrdered %}NSOrderedSet{% else %}Set<{{ model.entities[relationship.destinationEntity].className|default:"NSManagedObject" }}>{% endif %}{% if relationship.isOptional %}?{% endif %}
|
||||||
|
{% else %}
|
||||||
|
@NSManaged {{ accessModifier }} var {{ relationship.name }}: {{ model.entities[relationship.destinationEntity].className|default:"NSManagedObject" }}{% if relationship.isOptional %}?{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% for fetchedProperty in entity.fetchedProperties %}
|
||||||
|
@NSManaged {{ accessModifier }} var {{ fetchedProperty.name }}: [{{ fetchedProperty.fetchRequest.entity }}]
|
||||||
|
{% endfor %}
|
||||||
|
// swiftlint:enable discouraged_optional_boolean discouraged_optional_collection
|
||||||
|
}
|
||||||
|
|
||||||
|
{% for relationship in entity.relationships where relationship.isToMany %}
|
||||||
|
{% set destinationEntityClassName %}{{ model.entities[relationship.destinationEntity].className|default:"NSManagedObject" }}{% endset %}
|
||||||
|
{% set collectionClassName %}{% if relationship.isOrdered %}NSOrderedSet{% else %}Set<{{ destinationEntityClassName }}>{% endif %}{% endset %}
|
||||||
|
{% set relationshipName %}{{ relationship.name | upperFirstLetter }}{% endset %}
|
||||||
|
// MARK: Relationship {{ relationshipName }}
|
||||||
|
|
||||||
|
extension {{ entityClassName }} {
|
||||||
|
{% if relationship.isOrdered %}
|
||||||
|
@objc(insertObject:in{{ relationshipName }}AtIndex:)
|
||||||
|
@NSManaged public func insertInto{{ relationshipName }}(_ value: {{ destinationEntityClassName }}, at idx: Int)
|
||||||
|
|
||||||
|
@objc(removeObjectFrom{{ relationshipName }}AtIndex:)
|
||||||
|
@NSManaged public func removeFrom{{ relationshipName }}(at idx: Int)
|
||||||
|
|
||||||
|
@objc(insert{{ relationshipName }}:atIndexes:)
|
||||||
|
@NSManaged public func insertInto{{ relationshipName }}(_ values: [{{ destinationEntityClassName }}], at indexes: NSIndexSet)
|
||||||
|
|
||||||
|
@objc(remove{{ relationshipName }}AtIndexes:)
|
||||||
|
@NSManaged public func removeFrom{{ relationshipName }}(at indexes: NSIndexSet)
|
||||||
|
|
||||||
|
@objc(replaceObjectIn{{ relationshipName }}AtIndex:withObject:)
|
||||||
|
@NSManaged public func replace{{ relationshipName }}(at idx: Int, with value: {{ destinationEntityClassName }})
|
||||||
|
|
||||||
|
@objc(replace{{ relationshipName }}AtIndexes:with{{ relationshipName }}:)
|
||||||
|
@NSManaged public func replace{{ relationshipName }}(at indexes: NSIndexSet, with values: [{{ destinationEntityClassName }}])
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
@objc(add{{ relationshipName }}Object:)
|
||||||
|
@NSManaged public func addTo{{ relationshipName }}(_ value: {{ destinationEntityClassName }})
|
||||||
|
|
||||||
|
@objc(remove{{ relationshipName }}Object:)
|
||||||
|
@NSManaged public func removeFrom{{ relationshipName }}(_ value: {{ destinationEntityClassName }})
|
||||||
|
|
||||||
|
@objc(add{{ relationshipName }}:)
|
||||||
|
@NSManaged public func addTo{{ relationshipName }}(_ values: {{ collectionClassName }})
|
||||||
|
|
||||||
|
@objc(remove{{ relationshipName }}:)
|
||||||
|
@NSManaged public func removeFrom{{ relationshipName }}(_ values: {{ collectionClassName }})
|
||||||
|
}
|
||||||
|
|
||||||
|
{% endfor %}
|
||||||
|
{% if model.fetchRequests[entity.name].count > 0 %}
|
||||||
|
// MARK: Fetch Requests
|
||||||
|
|
||||||
|
extension {{ entityClassName }} {
|
||||||
|
{% for fetchRequest in model.fetchRequests[entity.name] %}
|
||||||
|
{% set resultTypeName %}{% filter removeNewlines:"leading" %}
|
||||||
|
{% if fetchRequest.resultType == "Object" %}
|
||||||
|
{{ entityClassName }}
|
||||||
|
{% elif fetchRequest.resultType == "Object ID" %}
|
||||||
|
NSManagedObjectID
|
||||||
|
{% elif fetchRequest.resultType == "Dictionary" %}
|
||||||
|
[String: Any]
|
||||||
|
{% endif %}
|
||||||
|
{% endfilter %}{% endset %}
|
||||||
|
class func fetch{{ fetchRequest.name | upperFirstLetter }}({% filter removeNewlines:"leading" %}
|
||||||
|
managedObjectContext: NSManagedObjectContext
|
||||||
|
{% for variableName, variableType in fetchRequest.substitutionVariables %}
|
||||||
|
, {{ variableName | lowerFirstWord }}: {{ variableType }}
|
||||||
|
{% endfor %}
|
||||||
|
{% endfilter %}) throws -> [{{ resultTypeName }}] {
|
||||||
|
guard let persistentStoreCoordinator = managedObjectContext.persistentStoreCoordinator else {
|
||||||
|
fatalError("Managed object context has no persistent store coordinator for getting fetch request templates")
|
||||||
|
}
|
||||||
|
let model = persistentStoreCoordinator.managedObjectModel
|
||||||
|
let substitutionVariables: [String: Any] = [
|
||||||
|
{% for variableName, variableType in fetchRequest.substitutionVariables %}
|
||||||
|
"{{ variableName }}": {{ variableName | lowerFirstWord }}{{ "," if not forloop.last }}
|
||||||
|
{% empty %}
|
||||||
|
:
|
||||||
|
{% endfor %}
|
||||||
|
]
|
||||||
|
|
||||||
|
guard let fetchRequest = model.fetchRequestFromTemplate(withName: "{{ fetchRequest.name }}", substitutionVariables: substitutionVariables) else {
|
||||||
|
fatalError("No fetch request template named '{{ fetchRequest.name }}' found.")
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let result = try managedObjectContext.fetch(fetchRequest) as? [{{ resultTypeName }}] else {
|
||||||
|
fatalError("Unable to cast fetch result to correct result type.")
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
{% endfor %}
|
||||||
|
}
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endfor %}
|
||||||
|
// swiftlint:enable identifier_name line_length type_body_length
|
|
@ -0,0 +1,211 @@
|
||||||
|
// swiftlint:disable all
|
||||||
|
// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen
|
||||||
|
|
||||||
|
// swiftlint:disable superfluous_disable_command implicit_return
|
||||||
|
// swiftlint:disable sorted_imports
|
||||||
|
import CoreData
|
||||||
|
import Foundation
|
||||||
|
{% for import in param.extraImports %}
|
||||||
|
import {{ import }}
|
||||||
|
{% empty %}
|
||||||
|
{# If extraImports is a single String instead of an array, `for` considers it empty but we still have to check if there's a single String value #}
|
||||||
|
{% if param.extraImports %}import {{ param.extraImports }}{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
// swiftlint:disable attributes file_length vertical_whitespace_closing_braces
|
||||||
|
// swiftlint:disable identifier_name line_length type_body_length
|
||||||
|
{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %}
|
||||||
|
|
||||||
|
{% for model in models %}
|
||||||
|
{% for name, entity in model.entities %}
|
||||||
|
{% set superclass %}{{ model.entities[entity.superEntity].className|default:"NSManagedObject" }}{% endset %}
|
||||||
|
{% set entityClassName %}{{ entity.className|default:"NSManagedObject" }}{% endset %}
|
||||||
|
// MARK: - {{ entity.name }}
|
||||||
|
|
||||||
|
{% if not entity.shouldGenerateCode %}
|
||||||
|
// Note: '{{ entity.name }}' has codegen enabled for Xcode, skipping code generation.
|
||||||
|
|
||||||
|
{% elif entityClassName|contains:"." %}
|
||||||
|
// Warning: '{{ entityClassName }}' cannot be a valid type name, skipping code generation.
|
||||||
|
|
||||||
|
{% else %}
|
||||||
|
{% if param.generateObjcName %}
|
||||||
|
@objc({{ entityClassName }})
|
||||||
|
{% endif %}
|
||||||
|
{{ accessModifier }} class {{ entityClassName }}: {{ superclass }} {
|
||||||
|
{% set override %}{% if superclass != "NSManagedObject" %}override {% endif %}{% endset %}
|
||||||
|
{{ override }}{{ accessModifier }} class var entityName: String {
|
||||||
|
return "{{ entity.name }}"
|
||||||
|
}
|
||||||
|
|
||||||
|
{{ override }}{{ accessModifier }} class func entity(in managedObjectContext: NSManagedObjectContext) -> NSEntityDescription? {
|
||||||
|
return NSEntityDescription.entity(forEntityName: entityName, in: managedObjectContext)
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(*, deprecated, renamed: "makeFetchRequest", message: "To avoid collisions with the less concrete method in `NSManagedObject`, please use `makeFetchRequest()` instead.")
|
||||||
|
@nonobjc {{ accessModifier }} class func fetchRequest() -> NSFetchRequest<{{ entityClassName }}> {
|
||||||
|
return NSFetchRequest<{{ entityClassName }}>(entityName: entityName)
|
||||||
|
}
|
||||||
|
|
||||||
|
@nonobjc {{ accessModifier }} class func makeFetchRequest() -> NSFetchRequest<{{ entityClassName }}> {
|
||||||
|
return NSFetchRequest<{{ entityClassName }}>(entityName: entityName)
|
||||||
|
}
|
||||||
|
|
||||||
|
// swiftlint:disable discouraged_optional_boolean discouraged_optional_collection
|
||||||
|
{% for attribute in entity.attributes %}
|
||||||
|
{% if attribute.userInfo.RawType %}
|
||||||
|
{% set rawType attribute.userInfo.RawType %}
|
||||||
|
{% set unwrapOptional attribute.userInfo.unwrapOptional %}
|
||||||
|
{{ accessModifier }} var {{ attribute.name }}: {{ rawType }}{% if not unwrapOptional %}?{% endif %} {
|
||||||
|
get {
|
||||||
|
let key = "{{ attribute.name }}"
|
||||||
|
willAccessValue(forKey: key)
|
||||||
|
defer { didAccessValue(forKey: key) }
|
||||||
|
|
||||||
|
{% if unwrapOptional %}
|
||||||
|
guard let value = primitiveValue(forKey: key) as? {{ rawType }}.RawValue,
|
||||||
|
let result = {{ rawType }}(rawValue: value) else {
|
||||||
|
fatalError("Could not convert value for key '\(key)' to type '{{ rawType }}'")
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
{% else %}
|
||||||
|
guard let value = primitiveValue(forKey: key) as? {{ rawType }}.RawValue else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return {{ rawType }}(rawValue: value)
|
||||||
|
{% endif %}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
let key = "{{ attribute.name }}"
|
||||||
|
willChangeValue(forKey: key)
|
||||||
|
defer { didChangeValue(forKey: key) }
|
||||||
|
|
||||||
|
setPrimitiveValue(newValue{% if not unwrapOptional %}?{% endif %}.rawValue, forKey: key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{% elif attribute.usesScalarValueType and attribute.isOptional %}
|
||||||
|
{{ accessModifier }} var {{ attribute.name }}: {{ attribute.typeName }}? {
|
||||||
|
get {
|
||||||
|
let key = "{{ attribute.name }}"
|
||||||
|
willAccessValue(forKey: key)
|
||||||
|
defer { didAccessValue(forKey: key) }
|
||||||
|
|
||||||
|
return primitiveValue(forKey: key) as? {{ attribute.typeName }}
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
let key = "{{ attribute.name }}"
|
||||||
|
willChangeValue(forKey: key)
|
||||||
|
defer { didChangeValue(forKey: key) }
|
||||||
|
|
||||||
|
setPrimitiveValue(newValue, forKey: key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{% else %}
|
||||||
|
@NSManaged {{ accessModifier }} var {{ attribute.name }}: {{ attribute.typeName }}{% if attribute.isOptional %}?{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% for relationship in entity.relationships %}
|
||||||
|
{% if relationship.isToMany %}
|
||||||
|
@NSManaged {{ accessModifier }} var {{ relationship.name }}: {% if relationship.isOrdered %}NSOrderedSet{% else %}Set<{{ model.entities[relationship.destinationEntity].className|default:"NSManagedObject" }}>{% endif %}{% if relationship.isOptional %}?{% endif %}
|
||||||
|
{% else %}
|
||||||
|
@NSManaged {{ accessModifier }} var {{ relationship.name }}: {{ model.entities[relationship.destinationEntity].className|default:"NSManagedObject" }}{% if relationship.isOptional %}?{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% for fetchedProperty in entity.fetchedProperties %}
|
||||||
|
@NSManaged {{ accessModifier }} var {{ fetchedProperty.name }}: [{{ fetchedProperty.fetchRequest.entity }}]
|
||||||
|
{% endfor %}
|
||||||
|
// swiftlint:enable discouraged_optional_boolean discouraged_optional_collection
|
||||||
|
}
|
||||||
|
|
||||||
|
{% for relationship in entity.relationships where relationship.isToMany %}
|
||||||
|
{% set destinationEntityClassName %}{{ model.entities[relationship.destinationEntity].className|default:"NSManagedObject" }}{% endset %}
|
||||||
|
{% set collectionClassName %}{% if relationship.isOrdered %}NSOrderedSet{% else %}Set<{{ destinationEntityClassName }}>{% endif %}{% endset %}
|
||||||
|
{% set relationshipName %}{{ relationship.name | upperFirstLetter }}{% endset %}
|
||||||
|
// MARK: Relationship {{ relationshipName }}
|
||||||
|
|
||||||
|
extension {{ entityClassName }} {
|
||||||
|
{% if relationship.isOrdered %}
|
||||||
|
@objc(insertObject:in{{ relationshipName }}AtIndex:)
|
||||||
|
@NSManaged public func insertInto{{ relationshipName }}(_ value: {{ destinationEntityClassName }}, at idx: Int)
|
||||||
|
|
||||||
|
@objc(removeObjectFrom{{ relationshipName }}AtIndex:)
|
||||||
|
@NSManaged public func removeFrom{{ relationshipName }}(at idx: Int)
|
||||||
|
|
||||||
|
@objc(insert{{ relationshipName }}:atIndexes:)
|
||||||
|
@NSManaged public func insertInto{{ relationshipName }}(_ values: [{{ destinationEntityClassName }}], at indexes: NSIndexSet)
|
||||||
|
|
||||||
|
@objc(remove{{ relationshipName }}AtIndexes:)
|
||||||
|
@NSManaged public func removeFrom{{ relationshipName }}(at indexes: NSIndexSet)
|
||||||
|
|
||||||
|
@objc(replaceObjectIn{{ relationshipName }}AtIndex:withObject:)
|
||||||
|
@NSManaged public func replace{{ relationshipName }}(at idx: Int, with value: {{ destinationEntityClassName }})
|
||||||
|
|
||||||
|
@objc(replace{{ relationshipName }}AtIndexes:with{{ relationshipName }}:)
|
||||||
|
@NSManaged public func replace{{ relationshipName }}(at indexes: NSIndexSet, with values: [{{ destinationEntityClassName }}])
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
@objc(add{{ relationshipName }}Object:)
|
||||||
|
@NSManaged public func addTo{{ relationshipName }}(_ value: {{ destinationEntityClassName }})
|
||||||
|
|
||||||
|
@objc(remove{{ relationshipName }}Object:)
|
||||||
|
@NSManaged public func removeFrom{{ relationshipName }}(_ value: {{ destinationEntityClassName }})
|
||||||
|
|
||||||
|
@objc(add{{ relationshipName }}:)
|
||||||
|
@NSManaged public func addTo{{ relationshipName }}(_ values: {{ collectionClassName }})
|
||||||
|
|
||||||
|
@objc(remove{{ relationshipName }}:)
|
||||||
|
@NSManaged public func removeFrom{{ relationshipName }}(_ values: {{ collectionClassName }})
|
||||||
|
}
|
||||||
|
|
||||||
|
{% endfor %}
|
||||||
|
{% if model.fetchRequests[entity.name].count > 0 %}
|
||||||
|
// MARK: Fetch Requests
|
||||||
|
|
||||||
|
extension {{ entityClassName }} {
|
||||||
|
{% for fetchRequest in model.fetchRequests[entity.name] %}
|
||||||
|
{% set resultTypeName %}{% filter removeNewlines:"leading" %}
|
||||||
|
{% if fetchRequest.resultType == "Object" %}
|
||||||
|
{{ entityClassName }}
|
||||||
|
{% elif fetchRequest.resultType == "Object ID" %}
|
||||||
|
NSManagedObjectID
|
||||||
|
{% elif fetchRequest.resultType == "Dictionary" %}
|
||||||
|
[String: Any]
|
||||||
|
{% endif %}
|
||||||
|
{% endfilter %}{% endset %}
|
||||||
|
class func fetch{{ fetchRequest.name | upperFirstLetter }}({% filter removeNewlines:"leading" %}
|
||||||
|
managedObjectContext: NSManagedObjectContext
|
||||||
|
{% for variableName, variableType in fetchRequest.substitutionVariables %}
|
||||||
|
, {{ variableName | lowerFirstWord }}: {{ variableType }}
|
||||||
|
{% endfor %}
|
||||||
|
{% endfilter %}) throws -> [{{ resultTypeName }}] {
|
||||||
|
guard let persistentStoreCoordinator = managedObjectContext.persistentStoreCoordinator else {
|
||||||
|
fatalError("Managed object context has no persistent store coordinator for getting fetch request templates")
|
||||||
|
}
|
||||||
|
let model = persistentStoreCoordinator.managedObjectModel
|
||||||
|
let substitutionVariables: [String: Any] = [
|
||||||
|
{% for variableName, variableType in fetchRequest.substitutionVariables %}
|
||||||
|
"{{ variableName }}": {{ variableName | lowerFirstWord }}{{ "," if not forloop.last }}
|
||||||
|
{% empty %}
|
||||||
|
:
|
||||||
|
{% endfor %}
|
||||||
|
]
|
||||||
|
|
||||||
|
guard let fetchRequest = model.fetchRequestFromTemplate(withName: "{{ fetchRequest.name }}", substitutionVariables: substitutionVariables) else {
|
||||||
|
fatalError("No fetch request template named '{{ fetchRequest.name }}' found.")
|
||||||
|
}
|
||||||
|
|
||||||
|
guard let result = try managedObjectContext.fetch(fetchRequest) as? [{{ resultTypeName }}] else {
|
||||||
|
fatalError("Unable to cast fetch result to correct result type.")
|
||||||
|
}
|
||||||
|
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
{% endfor %}
|
||||||
|
}
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endfor %}
|
||||||
|
// swiftlint:enable identifier_name line_length type_body_length
|
|
@ -0,0 +1,103 @@
|
||||||
|
// swiftlint:disable all
|
||||||
|
// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen
|
||||||
|
|
||||||
|
{% if groups.count > 0 %}
|
||||||
|
{% set enumName %}{{param.enumName|default:"Files"}}{% endset %}
|
||||||
|
{% set useExt %}{% if param.useExtension|default:"true" %}true{% endif %}{% endset %}
|
||||||
|
{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %}
|
||||||
|
{% set resourceType %}{{param.resourceTypeName|default:"File"}}{% endset %}
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
// swiftlint:disable superfluous_disable_command file_length line_length implicit_return
|
||||||
|
|
||||||
|
// MARK: - Files
|
||||||
|
|
||||||
|
{% macro groupBlock group %}
|
||||||
|
{% for file in group.files %}
|
||||||
|
{% call fileBlock file %}
|
||||||
|
{% endfor %}
|
||||||
|
{% for dir in group.directories %}
|
||||||
|
{% call dirBlock dir %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endmacro %}
|
||||||
|
{% macro fileBlock file %}
|
||||||
|
/// {% if file.path and param.preservePath %}{{file.path}}/{% endif %}{{file.name}}{% if file.ext %}.{{file.ext}}{% endif %}
|
||||||
|
{% set identifier %}{{ file.name }}{% if useExt %}.{{ file.ext }}{% endif %}{% endset %}
|
||||||
|
{{accessModifier}} static let {{identifier|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}} = {{resourceType}}(name: "{{file.name}}", ext: {% if file.ext %}"{{file.ext}}"{% else %}nil{% endif %}, relativePath: "{{file.path if param.preservePath}}", mimeType: "{{file.mimeType}}")
|
||||||
|
{% endmacro %}
|
||||||
|
{% macro dirBlock directory %}
|
||||||
|
{% for file in directory.files %}
|
||||||
|
{% call fileBlock file %}
|
||||||
|
{% endfor %}
|
||||||
|
{% for dir in directory.directories %}
|
||||||
|
{% call dirBlock dir %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endmacro %}
|
||||||
|
// swiftlint:disable explicit_type_interface identifier_name
|
||||||
|
// swiftlint:disable nesting type_body_length type_name vertical_whitespace_opening_braces
|
||||||
|
{{accessModifier}} enum {{enumName}} {
|
||||||
|
{% if groups.count > 1 or param.forceFileNameEnum %}
|
||||||
|
{% for group in groups %}
|
||||||
|
{{accessModifier}} enum {{group.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} {
|
||||||
|
{% filter indent:2 %}{% call groupBlock group %}{% endfilter %}
|
||||||
|
}
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
{% call groupBlock groups.first %}
|
||||||
|
{% endif %}
|
||||||
|
}
|
||||||
|
// swiftlint:enable explicit_type_interface identifier_name
|
||||||
|
// swiftlint:enable nesting type_body_length type_name vertical_whitespace_opening_braces
|
||||||
|
|
||||||
|
// MARK: - Implementation Details
|
||||||
|
|
||||||
|
{{accessModifier}} struct {{resourceType}} {
|
||||||
|
{{accessModifier}} let name: String
|
||||||
|
{{accessModifier}} let ext: String?
|
||||||
|
{{accessModifier}} let relativePath: String
|
||||||
|
{{accessModifier}} let mimeType: String
|
||||||
|
|
||||||
|
{{accessModifier}} var url: URL {
|
||||||
|
return url(locale: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
{{accessModifier}} func url(locale: Locale?) -> URL {
|
||||||
|
let bundle = {{param.bundle|default:"BundleToken.bundle"}}
|
||||||
|
let url = bundle.url(
|
||||||
|
forResource: name,
|
||||||
|
withExtension: ext,
|
||||||
|
subdirectory: relativePath,
|
||||||
|
localization: locale?.identifier
|
||||||
|
)
|
||||||
|
guard let result = url else {
|
||||||
|
let file = name + (ext.flatMap { ".\($0)" } ?? "")
|
||||||
|
fatalError("Could not locate file named \(file)")
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
{{accessModifier}} var path: String {
|
||||||
|
return path(locale: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
{{accessModifier}} func path(locale: Locale?) -> String {
|
||||||
|
return url(locale: locale).path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{% if not param.bundle %}
|
||||||
|
|
||||||
|
// swiftlint:disable convenience_type explicit_type_interface
|
||||||
|
private final class BundleToken {
|
||||||
|
static let bundle: Bundle = {
|
||||||
|
#if SWIFT_PACKAGE
|
||||||
|
return Bundle.module
|
||||||
|
#else
|
||||||
|
return Bundle(for: BundleToken.self)
|
||||||
|
#endif
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
// swiftlint:enable convenience_type explicit_type_interface
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
// No files found
|
||||||
|
{% endif %}
|
|
@ -0,0 +1,103 @@
|
||||||
|
// swiftlint:disable all
|
||||||
|
// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen
|
||||||
|
|
||||||
|
{% if groups.count > 0 %}
|
||||||
|
{% set enumName %}{{param.enumName|default:"Files"}}{% endset %}
|
||||||
|
{% set useExt %}{% if param.useExtension|default:"true" %}true{% endif %}{% endset %}
|
||||||
|
{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %}
|
||||||
|
{% set resourceType %}{{param.resourceTypeName|default:"File"}}{% endset %}
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
// swiftlint:disable superfluous_disable_command file_length line_length implicit_return
|
||||||
|
|
||||||
|
// MARK: - Files
|
||||||
|
|
||||||
|
{% macro groupBlock group %}
|
||||||
|
{% for file in group.files %}
|
||||||
|
{% call fileBlock file %}
|
||||||
|
{% endfor %}
|
||||||
|
{% for dir in group.directories %}
|
||||||
|
{% call dirBlock dir %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endmacro %}
|
||||||
|
{% macro fileBlock file %}
|
||||||
|
/// {% if file.path and param.preservePath %}{{file.path}}/{% endif %}{{file.name}}{% if file.ext %}.{{file.ext}}{% endif %}
|
||||||
|
{% set identifier %}{{ file.name }}{% if useExt %}.{{ file.ext }}{% endif %}{% endset %}
|
||||||
|
{{accessModifier}} static let {{identifier|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}} = {{resourceType}}(name: "{{file.name}}", ext: {% if file.ext %}"{{file.ext}}"{% else %}nil{% endif %}, relativePath: "{{file.path if param.preservePath}}", mimeType: "{{file.mimeType}}")
|
||||||
|
{% endmacro %}
|
||||||
|
{% macro dirBlock directory %}
|
||||||
|
{% for file in directory.files %}
|
||||||
|
{% call fileBlock file %}
|
||||||
|
{% endfor %}
|
||||||
|
{% for dir in directory.directories %}
|
||||||
|
{% call dirBlock dir %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endmacro %}
|
||||||
|
// swiftlint:disable explicit_type_interface identifier_name
|
||||||
|
// swiftlint:disable nesting type_body_length type_name vertical_whitespace_opening_braces
|
||||||
|
{{accessModifier}} enum {{enumName}} {
|
||||||
|
{% if groups.count > 1 or param.forceFileNameEnum %}
|
||||||
|
{% for group in groups %}
|
||||||
|
{{accessModifier}} enum {{group.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} {
|
||||||
|
{% filter indent:2 %}{% call groupBlock group %}{% endfilter %}
|
||||||
|
}
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
{% call groupBlock groups.first %}
|
||||||
|
{% endif %}
|
||||||
|
}
|
||||||
|
// swiftlint:enable explicit_type_interface identifier_name
|
||||||
|
// swiftlint:enable nesting type_body_length type_name vertical_whitespace_opening_braces
|
||||||
|
|
||||||
|
// MARK: - Implementation Details
|
||||||
|
|
||||||
|
{{accessModifier}} struct {{resourceType}} {
|
||||||
|
{{accessModifier}} let name: String
|
||||||
|
{{accessModifier}} let ext: String?
|
||||||
|
{{accessModifier}} let relativePath: String
|
||||||
|
{{accessModifier}} let mimeType: String
|
||||||
|
|
||||||
|
{{accessModifier}} var url: URL {
|
||||||
|
return url(locale: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
{{accessModifier}} func url(locale: Locale?) -> URL {
|
||||||
|
let bundle = {{param.bundle|default:"BundleToken.bundle"}}
|
||||||
|
let url = bundle.url(
|
||||||
|
forResource: name,
|
||||||
|
withExtension: ext,
|
||||||
|
subdirectory: relativePath,
|
||||||
|
localization: locale?.identifier
|
||||||
|
)
|
||||||
|
guard let result = url else {
|
||||||
|
let file = name + (ext.flatMap { ".\($0)" } ?? "")
|
||||||
|
fatalError("Could not locate file named \(file)")
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
{{accessModifier}} var path: String {
|
||||||
|
return path(locale: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
{{accessModifier}} func path(locale: Locale?) -> String {
|
||||||
|
return url(locale: locale).path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{% if not param.bundle %}
|
||||||
|
|
||||||
|
// swiftlint:disable convenience_type explicit_type_interface
|
||||||
|
private final class BundleToken {
|
||||||
|
static let bundle: Bundle = {
|
||||||
|
#if SWIFT_PACKAGE
|
||||||
|
return Bundle.module
|
||||||
|
#else
|
||||||
|
return Bundle(for: BundleToken.self)
|
||||||
|
#endif
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
// swiftlint:enable convenience_type explicit_type_interface
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
// No files found
|
||||||
|
{% endif %}
|
|
@ -0,0 +1,107 @@
|
||||||
|
// swiftlint:disable all
|
||||||
|
// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen
|
||||||
|
|
||||||
|
{% if groups.count > 0 %}
|
||||||
|
{% set enumName %}{{param.enumName|default:"Files"}}{% endset %}
|
||||||
|
{% set useExt %}{% if param.useExtension|default:"true" %}true{% endif %}{% endset %}
|
||||||
|
{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %}
|
||||||
|
{% set resourceType %}{{param.resourceTypeName|default:"File"}}{% endset %}
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
// swiftlint:disable superfluous_disable_command file_length line_length implicit_return
|
||||||
|
|
||||||
|
// MARK: - Files
|
||||||
|
|
||||||
|
{% macro groupBlock group %}
|
||||||
|
{% for file in group.files %}
|
||||||
|
{% call fileBlock file %}
|
||||||
|
{% endfor %}
|
||||||
|
{% for dir in group.directories %}
|
||||||
|
{% call dirBlock dir "" %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endmacro %}
|
||||||
|
{% macro fileBlock file %}
|
||||||
|
/// {% if file.path and param.preservePath %}{{file.path}}/{% endif %}{{file.name}}{% if file.ext %}.{{file.ext}}{% endif %}
|
||||||
|
{% set identifier %}{{ file.name }}{% if useExt %}.{{ file.ext }}{% endif %}{% endset %}
|
||||||
|
{{accessModifier}} static let {{identifier|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}} = {{resourceType}}(name: "{{file.name}}", ext: {% if file.ext %}"{{file.ext}}"{% else %}nil{% endif %}, relativePath: "{{file.path if param.preservePath}}", mimeType: "{{file.mimeType}}")
|
||||||
|
{% endmacro %}
|
||||||
|
{% macro dirBlock directory parent %}
|
||||||
|
{% set fullDir %}{{parent}}{{directory.name}}/{% endset %}
|
||||||
|
/// {{ fullDir }}
|
||||||
|
{{accessModifier}} enum {{directory.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} {
|
||||||
|
{% for file in directory.files %}
|
||||||
|
{% filter indent:2 %}{% call fileBlock file %}{% endfilter %}
|
||||||
|
{% endfor %}
|
||||||
|
{% for dir in directory.directories %}
|
||||||
|
{% filter indent:2 %}{% call dirBlock dir fullDir %}{% endfilter %}
|
||||||
|
{% endfor %}
|
||||||
|
}
|
||||||
|
{% endmacro %}
|
||||||
|
// swiftlint:disable explicit_type_interface identifier_name
|
||||||
|
// swiftlint:disable nesting type_body_length type_name vertical_whitespace_opening_braces
|
||||||
|
{{accessModifier}} enum {{enumName}} {
|
||||||
|
{% if groups.count > 1 or param.forceFileNameEnum %}
|
||||||
|
{% for group in groups %}
|
||||||
|
{{accessModifier}} enum {{group.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} {
|
||||||
|
{% filter indent:2 %}{% call groupBlock group %}{% endfilter %}
|
||||||
|
}
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
{% call groupBlock groups.first %}
|
||||||
|
{% endif %}
|
||||||
|
}
|
||||||
|
// swiftlint:enable explicit_type_interface identifier_name
|
||||||
|
// swiftlint:enable nesting type_body_length type_name vertical_whitespace_opening_braces
|
||||||
|
|
||||||
|
// MARK: - Implementation Details
|
||||||
|
|
||||||
|
{{accessModifier}} struct {{resourceType}} {
|
||||||
|
{{accessModifier}} let name: String
|
||||||
|
{{accessModifier}} let ext: String?
|
||||||
|
{{accessModifier}} let relativePath: String
|
||||||
|
{{accessModifier}} let mimeType: String
|
||||||
|
|
||||||
|
{{accessModifier}} var url: URL {
|
||||||
|
return url(locale: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
{{accessModifier}} func url(locale: Locale?) -> URL {
|
||||||
|
let bundle = {{param.bundle|default:"BundleToken.bundle"}}
|
||||||
|
let url = bundle.url(
|
||||||
|
forResource: name,
|
||||||
|
withExtension: ext,
|
||||||
|
subdirectory: relativePath,
|
||||||
|
localization: locale?.identifier
|
||||||
|
)
|
||||||
|
guard let result = url else {
|
||||||
|
let file = name + (ext.flatMap { ".\($0)" } ?? "")
|
||||||
|
fatalError("Could not locate file named \(file)")
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
{{accessModifier}} var path: String {
|
||||||
|
return path(locale: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
{{accessModifier}} func path(locale: Locale?) -> String {
|
||||||
|
return url(locale: locale).path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{% if not param.bundle %}
|
||||||
|
|
||||||
|
// swiftlint:disable convenience_type explicit_type_interface
|
||||||
|
private final class BundleToken {
|
||||||
|
static let bundle: Bundle = {
|
||||||
|
#if SWIFT_PACKAGE
|
||||||
|
return Bundle.module
|
||||||
|
#else
|
||||||
|
return Bundle(for: BundleToken.self)
|
||||||
|
#endif
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
// swiftlint:enable convenience_type explicit_type_interface
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
// No files found
|
||||||
|
{% endif %}
|
|
@ -0,0 +1,107 @@
|
||||||
|
// swiftlint:disable all
|
||||||
|
// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen
|
||||||
|
|
||||||
|
{% if groups.count > 0 %}
|
||||||
|
{% set enumName %}{{param.enumName|default:"Files"}}{% endset %}
|
||||||
|
{% set useExt %}{% if param.useExtension|default:"true" %}true{% endif %}{% endset %}
|
||||||
|
{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %}
|
||||||
|
{% set resourceType %}{{param.resourceTypeName|default:"File"}}{% endset %}
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
// swiftlint:disable superfluous_disable_command file_length line_length implicit_return
|
||||||
|
|
||||||
|
// MARK: - Files
|
||||||
|
|
||||||
|
{% macro groupBlock group %}
|
||||||
|
{% for file in group.files %}
|
||||||
|
{% call fileBlock file %}
|
||||||
|
{% endfor %}
|
||||||
|
{% for dir in group.directories %}
|
||||||
|
{% call dirBlock dir "" %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endmacro %}
|
||||||
|
{% macro fileBlock file %}
|
||||||
|
/// {% if file.path and param.preservePath %}{{file.path}}/{% endif %}{{file.name}}{% if file.ext %}.{{file.ext}}{% endif %}
|
||||||
|
{% set identifier %}{{ file.name }}{% if useExt %}.{{ file.ext }}{% endif %}{% endset %}
|
||||||
|
{{accessModifier}} static let {{identifier|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}} = {{resourceType}}(name: "{{file.name}}", ext: {% if file.ext %}"{{file.ext}}"{% else %}nil{% endif %}, relativePath: "{{file.path if param.preservePath}}", mimeType: "{{file.mimeType}}")
|
||||||
|
{% endmacro %}
|
||||||
|
{% macro dirBlock directory parent %}
|
||||||
|
{% set fullDir %}{{parent}}{{directory.name}}/{% endset %}
|
||||||
|
/// {{ fullDir }}
|
||||||
|
{{accessModifier}} enum {{directory.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} {
|
||||||
|
{% for file in directory.files %}
|
||||||
|
{% filter indent:2 %}{% call fileBlock file %}{% endfilter %}
|
||||||
|
{% endfor %}
|
||||||
|
{% for dir in directory.directories %}
|
||||||
|
{% filter indent:2 %}{% call dirBlock dir fullDir %}{% endfilter %}
|
||||||
|
{% endfor %}
|
||||||
|
}
|
||||||
|
{% endmacro %}
|
||||||
|
// swiftlint:disable explicit_type_interface identifier_name
|
||||||
|
// swiftlint:disable nesting type_body_length type_name vertical_whitespace_opening_braces
|
||||||
|
{{accessModifier}} enum {{enumName}} {
|
||||||
|
{% if groups.count > 1 or param.forceFileNameEnum %}
|
||||||
|
{% for group in groups %}
|
||||||
|
{{accessModifier}} enum {{group.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} {
|
||||||
|
{% filter indent:2 %}{% call groupBlock group %}{% endfilter %}
|
||||||
|
}
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
{% call groupBlock groups.first %}
|
||||||
|
{% endif %}
|
||||||
|
}
|
||||||
|
// swiftlint:enable explicit_type_interface identifier_name
|
||||||
|
// swiftlint:enable nesting type_body_length type_name vertical_whitespace_opening_braces
|
||||||
|
|
||||||
|
// MARK: - Implementation Details
|
||||||
|
|
||||||
|
{{accessModifier}} struct {{resourceType}} {
|
||||||
|
{{accessModifier}} let name: String
|
||||||
|
{{accessModifier}} let ext: String?
|
||||||
|
{{accessModifier}} let relativePath: String
|
||||||
|
{{accessModifier}} let mimeType: String
|
||||||
|
|
||||||
|
{{accessModifier}} var url: URL {
|
||||||
|
return url(locale: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
{{accessModifier}} func url(locale: Locale?) -> URL {
|
||||||
|
let bundle = {{param.bundle|default:"BundleToken.bundle"}}
|
||||||
|
let url = bundle.url(
|
||||||
|
forResource: name,
|
||||||
|
withExtension: ext,
|
||||||
|
subdirectory: relativePath,
|
||||||
|
localization: locale?.identifier
|
||||||
|
)
|
||||||
|
guard let result = url else {
|
||||||
|
let file = name + (ext.flatMap { ".\($0)" } ?? "")
|
||||||
|
fatalError("Could not locate file named \(file)")
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
{{accessModifier}} var path: String {
|
||||||
|
return path(locale: nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
{{accessModifier}} func path(locale: Locale?) -> String {
|
||||||
|
return url(locale: locale).path
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{% if not param.bundle %}
|
||||||
|
|
||||||
|
// swiftlint:disable convenience_type explicit_type_interface
|
||||||
|
private final class BundleToken {
|
||||||
|
static let bundle: Bundle = {
|
||||||
|
#if SWIFT_PACKAGE
|
||||||
|
return Bundle.module
|
||||||
|
#else
|
||||||
|
return Bundle(for: BundleToken.self)
|
||||||
|
#endif
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
// swiftlint:enable convenience_type explicit_type_interface
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
// No files found
|
||||||
|
{% endif %}
|
|
@ -0,0 +1,110 @@
|
||||||
|
// swiftlint:disable all
|
||||||
|
// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen
|
||||||
|
|
||||||
|
{% if families %}
|
||||||
|
{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %}
|
||||||
|
{% set fontType %}{{param.fontTypeName|default:"FontConvertible"}}{% endset %}
|
||||||
|
#if os(macOS)
|
||||||
|
import AppKit.NSFont
|
||||||
|
#elseif os(iOS) || os(tvOS) || os(watchOS)
|
||||||
|
import UIKit.UIFont
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Deprecated typealiases
|
||||||
|
@available(*, deprecated, renamed: "{{fontType}}.Font", message: "This typealias will be removed in SwiftGen 7.0")
|
||||||
|
{{accessModifier}} typealias {{param.fontAliasName|default:"Font"}} = {{fontType}}.Font
|
||||||
|
|
||||||
|
// swiftlint:disable superfluous_disable_command
|
||||||
|
// swiftlint:disable file_length
|
||||||
|
// swiftlint:disable implicit_return
|
||||||
|
|
||||||
|
// MARK: - Fonts
|
||||||
|
|
||||||
|
// swiftlint:disable identifier_name line_length type_body_length
|
||||||
|
{% macro transformPath path %}{% filter removeNewlines %}
|
||||||
|
{% if param.preservePath %}
|
||||||
|
{{path}}
|
||||||
|
{% else %}
|
||||||
|
{{path|basename}}
|
||||||
|
{% endif %}
|
||||||
|
{% endfilter %}{% endmacro %}
|
||||||
|
{{accessModifier}} enum {{param.enumName|default:"FontFamily"}} {
|
||||||
|
{% for family in families %}
|
||||||
|
{{accessModifier}} enum {{family.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} {
|
||||||
|
{% for font in family.fonts %}
|
||||||
|
{{accessModifier}} static let {{font.style|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}} = {{fontType}}(name: "{{font.name}}", family: "{{family.name}}", path: "{% call transformPath font.path %}")
|
||||||
|
{% endfor %}
|
||||||
|
{{accessModifier}} static let all: [{{fontType}}] = [{% for font in family.fonts %}{{font.style|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}}{{ ", " if not forloop.last }}{% endfor %}]
|
||||||
|
}
|
||||||
|
{% endfor %}
|
||||||
|
{{accessModifier}} static let allCustomFonts: [{{fontType}}] = [{% for family in families %}{{family.name|swiftIdentifier:"pretty"|escapeReservedKeywords}}.all{{ ", " if not forloop.last }}{% endfor %}].flatMap { $0 }
|
||||||
|
{{accessModifier}} static func registerAllCustomFonts() {
|
||||||
|
allCustomFonts.forEach { $0.register() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// swiftlint:enable identifier_name line_length type_body_length
|
||||||
|
|
||||||
|
// MARK: - Implementation Details
|
||||||
|
|
||||||
|
{{accessModifier}} struct {{fontType}} {
|
||||||
|
{{accessModifier}} let name: String
|
||||||
|
{{accessModifier}} let family: String
|
||||||
|
{{accessModifier}} let path: String
|
||||||
|
|
||||||
|
#if os(macOS)
|
||||||
|
{{accessModifier}} typealias Font = NSFont
|
||||||
|
#elseif os(iOS) || os(tvOS) || os(watchOS)
|
||||||
|
{{accessModifier}} typealias Font = UIFont
|
||||||
|
#endif
|
||||||
|
|
||||||
|
{{accessModifier}} func font(size: CGFloat) -> Font! {
|
||||||
|
return Font(font: self, size: size)
|
||||||
|
}
|
||||||
|
|
||||||
|
{{accessModifier}} func register() {
|
||||||
|
// swiftlint:disable:next conditional_returns_on_newline
|
||||||
|
guard let url = url else { return }
|
||||||
|
CTFontManagerRegisterFontsForURL(url as CFURL, .process, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate var url: URL? {
|
||||||
|
{% if param.lookupFunction %}
|
||||||
|
return {{param.lookupFunction}}(name, family, path)
|
||||||
|
{% else %}
|
||||||
|
return {{param.bundle|default:"BundleToken.bundle"}}.url(forResource: path, withExtension: nil)
|
||||||
|
{% endif %}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{{accessModifier}} extension {{fontType}}.Font {
|
||||||
|
convenience init?(font: {{fontType}}, size: CGFloat) {
|
||||||
|
#if os(iOS) || os(tvOS) || os(watchOS)
|
||||||
|
if !UIFont.fontNames(forFamilyName: font.family).contains(font.name) {
|
||||||
|
font.register()
|
||||||
|
}
|
||||||
|
#elseif os(macOS)
|
||||||
|
if let url = font.url, CTFontManagerGetScopeForURL(url as CFURL) == .none {
|
||||||
|
font.register()
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
self.init(name: font.name, size: size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{% if not param.bundle and not param.lookupFunction %}
|
||||||
|
|
||||||
|
// swiftlint:disable convenience_type
|
||||||
|
private final class BundleToken {
|
||||||
|
static let bundle: Bundle = {
|
||||||
|
#if SWIFT_PACKAGE
|
||||||
|
return Bundle.module
|
||||||
|
#else
|
||||||
|
return Bundle(for: BundleToken.self)
|
||||||
|
#endif
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
// swiftlint:enable convenience_type
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
// No fonts found
|
||||||
|
{% endif %}
|
|
@ -0,0 +1,113 @@
|
||||||
|
// swiftlint:disable all
|
||||||
|
// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen
|
||||||
|
|
||||||
|
{% if families %}
|
||||||
|
{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %}
|
||||||
|
{% set fontType %}{{param.fontTypeName|default:"FontConvertible"}}{% endset %}
|
||||||
|
#if os(macOS)
|
||||||
|
import AppKit.NSFont
|
||||||
|
#elseif os(iOS) || os(tvOS) || os(watchOS)
|
||||||
|
import UIKit.UIFont
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Deprecated typealiases
|
||||||
|
@available(*, deprecated, renamed: "{{fontType}}.Font", message: "This typealias will be removed in SwiftGen 7.0")
|
||||||
|
{{accessModifier}} typealias {{param.fontAliasName|default:"Font"}} = {{fontType}}.Font
|
||||||
|
|
||||||
|
// swiftlint:disable superfluous_disable_command
|
||||||
|
// swiftlint:disable file_length
|
||||||
|
|
||||||
|
// MARK: - Fonts
|
||||||
|
|
||||||
|
// swiftlint:disable identifier_name line_length type_body_length
|
||||||
|
{% macro transformPath path %}{% filter removeNewlines %}
|
||||||
|
{% if param.preservePath %}
|
||||||
|
{{path}}
|
||||||
|
{% else %}
|
||||||
|
{{path|basename}}
|
||||||
|
{% endif %}
|
||||||
|
{% endfilter %}{% endmacro %}
|
||||||
|
{{accessModifier}} enum {{param.enumName|default:"FontFamily"}} {
|
||||||
|
{% for family in families %}
|
||||||
|
{{accessModifier}} enum {{family.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} {
|
||||||
|
{% for font in family.fonts %}
|
||||||
|
{{accessModifier}} static let {{font.style|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}} = {{fontType}}(name: "{{font.name}}", family: "{{family.name}}", path: "{% call transformPath font.path %}")
|
||||||
|
{% endfor %}
|
||||||
|
{{accessModifier}} static let all: [{{fontType}}] = [{% for font in family.fonts %}{{font.style|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}}{{ ", " if not forloop.last }}{% endfor %}]
|
||||||
|
}
|
||||||
|
{% endfor %}
|
||||||
|
{{accessModifier}} static let allCustomFonts: [{{fontType}}] = [{% for family in families %}{{family.name|swiftIdentifier:"pretty"|escapeReservedKeywords}}.all{{ ", " if not forloop.last }}{% endfor %}].flatMap { $0 }
|
||||||
|
{{accessModifier}} static func registerAllCustomFonts() {
|
||||||
|
allCustomFonts.forEach { $0.register() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// swiftlint:enable identifier_name line_length type_body_length
|
||||||
|
|
||||||
|
// MARK: - Implementation Details
|
||||||
|
|
||||||
|
{{accessModifier}} struct {{fontType}} {
|
||||||
|
{{accessModifier}} let name: String
|
||||||
|
{{accessModifier}} let family: String
|
||||||
|
{{accessModifier}} let path: String
|
||||||
|
|
||||||
|
#if os(macOS)
|
||||||
|
{{accessModifier}} typealias Font = NSFont
|
||||||
|
#elseif os(iOS) || os(tvOS) || os(watchOS)
|
||||||
|
{{accessModifier}} typealias Font = UIFont
|
||||||
|
#endif
|
||||||
|
|
||||||
|
{{accessModifier}} func font(size: CGFloat) -> Font {
|
||||||
|
guard let font = Font(font: self, size: size) else {
|
||||||
|
fatalError("Unable to initialize font '\(name)' (\(family))")
|
||||||
|
}
|
||||||
|
return font
|
||||||
|
}
|
||||||
|
|
||||||
|
{{accessModifier}} func register() {
|
||||||
|
// swiftlint:disable:next conditional_returns_on_newline
|
||||||
|
guard let url = url else { return }
|
||||||
|
CTFontManagerRegisterFontsForURL(url as CFURL, .process, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate var url: URL? {
|
||||||
|
// swiftlint:disable:next implicit_return
|
||||||
|
{% if param.lookupFunction %}
|
||||||
|
return {{param.lookupFunction}}(name, family, path)
|
||||||
|
{% else %}
|
||||||
|
return {{param.bundle|default:"BundleToken.bundle"}}.url(forResource: path, withExtension: nil)
|
||||||
|
{% endif %}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{{accessModifier}} extension {{fontType}}.Font {
|
||||||
|
convenience init?(font: {{fontType}}, size: CGFloat) {
|
||||||
|
#if os(iOS) || os(tvOS) || os(watchOS)
|
||||||
|
if !UIFont.fontNames(forFamilyName: font.family).contains(font.name) {
|
||||||
|
font.register()
|
||||||
|
}
|
||||||
|
#elseif os(macOS)
|
||||||
|
if let url = font.url, CTFontManagerGetScopeForURL(url as CFURL) == .none {
|
||||||
|
font.register()
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
self.init(name: font.name, size: size)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{% if not param.bundle and not param.lookupFunction %}
|
||||||
|
|
||||||
|
// swiftlint:disable convenience_type
|
||||||
|
private final class BundleToken {
|
||||||
|
static let bundle: Bundle = {
|
||||||
|
#if SWIFT_PACKAGE
|
||||||
|
return Bundle.module
|
||||||
|
#else
|
||||||
|
return Bundle(for: BundleToken.self)
|
||||||
|
#endif
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
// swiftlint:enable convenience_type
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
// No fonts found
|
||||||
|
{% endif %}
|
|
@ -0,0 +1,157 @@
|
||||||
|
// swiftlint:disable all
|
||||||
|
// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen
|
||||||
|
|
||||||
|
{% if platform and storyboards %}
|
||||||
|
{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %}
|
||||||
|
{% set isAppKit %}{% if platform == "macOS" %}true{% endif %}{% endset %}
|
||||||
|
{% set prefix %}{% if isAppKit %}NS{% else %}UI{% endif %}{% endset %}
|
||||||
|
{% set controller %}{% if isAppKit %}Controller{% else %}ViewController{% endif %}{% endset %}
|
||||||
|
// swiftlint:disable sorted_imports
|
||||||
|
import Foundation
|
||||||
|
{% for module in modules where module != env.PRODUCT_MODULE_NAME and module != param.module %}
|
||||||
|
import {{module}}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
// swiftlint:disable superfluous_disable_command
|
||||||
|
// swiftlint:disable file_length implicit_return
|
||||||
|
|
||||||
|
// MARK: - Storyboard Scenes
|
||||||
|
|
||||||
|
// swiftlint:disable explicit_type_interface identifier_name line_length type_body_length type_name
|
||||||
|
{% macro moduleName item %}{% filter removeNewlines %}
|
||||||
|
{% if item.moduleIsPlaceholder %}
|
||||||
|
{{ env.PRODUCT_MODULE_NAME|default:param.module }}
|
||||||
|
{% else %}
|
||||||
|
{{ item.module }}
|
||||||
|
{% endif %}
|
||||||
|
{% endfilter %}{% endmacro %}
|
||||||
|
{% macro className item %}{% filter removeNewlines %}
|
||||||
|
{% set module %}{% call moduleName item %}{% endset %}
|
||||||
|
{% if module and ( not param.ignoreTargetModule or module != env.PRODUCT_MODULE_NAME and module != param.module ) %}
|
||||||
|
{{module}}.
|
||||||
|
{% endif %}
|
||||||
|
{{item.type}}
|
||||||
|
{% endfilter %}{% endmacro %}
|
||||||
|
{{accessModifier}} enum {{param.enumName|default:"StoryboardScene"}} {
|
||||||
|
{% for storyboard in storyboards %}
|
||||||
|
{% set storyboardName %}{{storyboard.name|swiftIdentifier:"pretty"|escapeReservedKeywords}}{% endset %}
|
||||||
|
{{accessModifier}} enum {{storyboardName}}: StoryboardType {
|
||||||
|
{{accessModifier}} static let storyboardName = "{{storyboard.name}}"
|
||||||
|
{% if storyboard.initialScene %}
|
||||||
|
|
||||||
|
{% set sceneClass %}{% call className storyboard.initialScene %}{% endset %}
|
||||||
|
{{accessModifier}} static let initialScene = InitialSceneType<{{sceneClass}}>(storyboard: {{storyboardName}}.self)
|
||||||
|
{% endif %}
|
||||||
|
{% for scene in storyboard.scenes %}
|
||||||
|
|
||||||
|
{% set sceneID %}{{scene.identifier|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}}{% endset %}
|
||||||
|
{% set sceneClass %}{% call className scene %}{% endset %}
|
||||||
|
{{accessModifier}} static let {{sceneID}} = SceneType<{{sceneClass}}>(storyboard: {{storyboardName}}.self, identifier: "{{scene.identifier}}")
|
||||||
|
{% endfor %}
|
||||||
|
}
|
||||||
|
{% endfor %}
|
||||||
|
}
|
||||||
|
// swiftlint:enable explicit_type_interface identifier_name line_length type_body_length type_name
|
||||||
|
|
||||||
|
// MARK: - Implementation Details
|
||||||
|
|
||||||
|
{{accessModifier}} protocol StoryboardType {
|
||||||
|
static var storyboardName: String { get }
|
||||||
|
}
|
||||||
|
|
||||||
|
{{accessModifier}} extension StoryboardType {
|
||||||
|
static var storyboard: {{prefix}}Storyboard {
|
||||||
|
let name = {% if isAppKit %}NSStoryboard.Name({% endif %}self.storyboardName{% if isAppKit %}){% endif %}
|
||||||
|
{% if param.lookupFunction %}
|
||||||
|
return {{param.lookupFunction}}(name)
|
||||||
|
{% else %}
|
||||||
|
return {{prefix}}Storyboard(name: name, bundle: {{param.bundle|default:"BundleToken.bundle"}})
|
||||||
|
{% endif %}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{{accessModifier}} struct SceneType<T{% if not isAppKit %}: UIViewController{% endif %}> {
|
||||||
|
{{accessModifier}} let storyboard: StoryboardType.Type
|
||||||
|
{{accessModifier}} let identifier: String
|
||||||
|
|
||||||
|
{{accessModifier}} func instantiate() -> T {
|
||||||
|
let identifier = {% if isAppKit %}NSStoryboard.SceneIdentifier({% endif %}self.identifier{% if isAppKit %}){% endif %}
|
||||||
|
guard let controller = storyboard.storyboard.instantiate{{controller}}(withIdentifier: identifier) as? T else {
|
||||||
|
fatalError("{{controller}} '\(identifier)' is not of the expected class \(T.self).")
|
||||||
|
}
|
||||||
|
return controller
|
||||||
|
}
|
||||||
|
|
||||||
|
{% if isAppKit %}
|
||||||
|
@available(macOS 10.15, *)
|
||||||
|
{{accessModifier}} func instantiate(creator block: @escaping (NSCoder) -> T?) -> T where T: NSViewController {
|
||||||
|
return storyboard.storyboard.instantiate{{controller}}(identifier: identifier, creator: block)
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(macOS 10.15, *)
|
||||||
|
{{accessModifier}} func instantiate(creator block: @escaping (NSCoder) -> T?) -> T where T: NSWindowController {
|
||||||
|
return storyboard.storyboard.instantiate{{controller}}(identifier: identifier, creator: block)
|
||||||
|
}
|
||||||
|
{% else %}
|
||||||
|
@available(iOS 13.0, tvOS 13.0, *)
|
||||||
|
{{accessModifier}} func instantiate(creator block: @escaping (NSCoder) -> T?) -> T {
|
||||||
|
return storyboard.storyboard.instantiate{{controller}}(identifier: identifier, creator: block)
|
||||||
|
}
|
||||||
|
{% endif %}
|
||||||
|
}
|
||||||
|
|
||||||
|
{{accessModifier}} struct InitialSceneType<T{% if not isAppKit %}: UIViewController{% endif %}> {
|
||||||
|
{{accessModifier}} let storyboard: StoryboardType.Type
|
||||||
|
|
||||||
|
{{accessModifier}} func instantiate() -> T {
|
||||||
|
guard let controller = storyboard.storyboard.instantiateInitial{{controller}}() as? T else {
|
||||||
|
fatalError("{{controller}} is not of the expected class \(T.self).")
|
||||||
|
}
|
||||||
|
return controller
|
||||||
|
}
|
||||||
|
|
||||||
|
{% if isAppKit %}
|
||||||
|
@available(macOS 10.15, *)
|
||||||
|
{{accessModifier}} func instantiate(creator block: @escaping (NSCoder) -> T?) -> T where T: NSViewController {
|
||||||
|
guard let controller = storyboard.storyboard.instantiateInitial{{controller}}(creator: block) else {
|
||||||
|
fatalError("Storyboard \(storyboard.storyboardName) does not have an initial scene.")
|
||||||
|
}
|
||||||
|
return controller
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(macOS 10.15, *)
|
||||||
|
{{accessModifier}} func instantiate(creator block: @escaping (NSCoder) -> T?) -> T where T: NSWindowController {
|
||||||
|
guard let controller = storyboard.storyboard.instantiateInitial{{controller}}(creator: block) else {
|
||||||
|
fatalError("Storyboard \(storyboard.storyboardName) does not have an initial scene.")
|
||||||
|
}
|
||||||
|
return controller
|
||||||
|
}
|
||||||
|
{% else %}
|
||||||
|
@available(iOS 13.0, tvOS 13.0, *)
|
||||||
|
{{accessModifier}} func instantiate(creator block: @escaping (NSCoder) -> T?) -> T {
|
||||||
|
guard let controller = storyboard.storyboard.instantiateInitial{{controller}}(creator: block) else {
|
||||||
|
fatalError("Storyboard \(storyboard.storyboardName) does not have an initial scene.")
|
||||||
|
}
|
||||||
|
return controller
|
||||||
|
}
|
||||||
|
{% endif %}
|
||||||
|
}
|
||||||
|
{% if not param.bundle and not param.lookupFunction %}
|
||||||
|
|
||||||
|
// swiftlint:disable convenience_type
|
||||||
|
private final class BundleToken {
|
||||||
|
static let bundle: Bundle = {
|
||||||
|
#if SWIFT_PACKAGE
|
||||||
|
return Bundle.module
|
||||||
|
#else
|
||||||
|
return Bundle(for: BundleToken.self)
|
||||||
|
#endif
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
// swiftlint:enable convenience_type
|
||||||
|
{% endif %}
|
||||||
|
{% elif storyboards %}
|
||||||
|
// Mixed AppKit and UIKit storyboard files found, please invoke swiftgen with these separately
|
||||||
|
{% else %}
|
||||||
|
// No storyboard found
|
||||||
|
{% endif %}
|
|
@ -0,0 +1,159 @@
|
||||||
|
// swiftlint:disable all
|
||||||
|
// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen
|
||||||
|
|
||||||
|
{% if platform and storyboards %}
|
||||||
|
{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %}
|
||||||
|
{% set isAppKit %}{% if platform == "macOS" %}true{% endif %}{% endset %}
|
||||||
|
{% set prefix %}{% if isAppKit %}NS{% else %}UI{% endif %}{% endset %}
|
||||||
|
{% set controller %}{% if isAppKit %}Controller{% else %}ViewController{% endif %}{% endset %}
|
||||||
|
// swiftlint:disable sorted_imports
|
||||||
|
import Foundation
|
||||||
|
{% for module in modules where module != env.PRODUCT_MODULE_NAME and module != param.module %}
|
||||||
|
import {{module}}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
// swiftlint:disable superfluous_disable_command
|
||||||
|
// swiftlint:disable file_length implicit_return
|
||||||
|
|
||||||
|
// MARK: - Storyboard Scenes
|
||||||
|
|
||||||
|
// swiftlint:disable explicit_type_interface identifier_name line_length type_body_length type_name
|
||||||
|
{% macro moduleName item %}{% filter removeNewlines %}
|
||||||
|
{% if item.moduleIsPlaceholder %}
|
||||||
|
{{ env.PRODUCT_MODULE_NAME|default:param.module }}
|
||||||
|
{% else %}
|
||||||
|
{{ item.module }}
|
||||||
|
{% endif %}
|
||||||
|
{% endfilter %}{% endmacro %}
|
||||||
|
{% macro className item %}{% filter removeNewlines %}
|
||||||
|
{% set module %}{% call moduleName item %}{% endset %}
|
||||||
|
{% if module and ( not param.ignoreTargetModule or module != env.PRODUCT_MODULE_NAME and module != param.module ) %}
|
||||||
|
{{module}}.
|
||||||
|
{% endif %}
|
||||||
|
{{item.type}}
|
||||||
|
{% endfilter %}{% endmacro %}
|
||||||
|
{{accessModifier}} enum {{param.enumName|default:"StoryboardScene"}} {
|
||||||
|
{% for storyboard in storyboards %}
|
||||||
|
{% set storyboardName %}{{storyboard.name|swiftIdentifier:"pretty"|escapeReservedKeywords}}{% endset %}
|
||||||
|
{{accessModifier}} enum {{storyboardName}}: StoryboardType {
|
||||||
|
{{accessModifier}} static let storyboardName = "{{storyboard.name}}"
|
||||||
|
{% if storyboard.initialScene %}
|
||||||
|
|
||||||
|
{% set sceneClass %}{% call className storyboard.initialScene %}{% endset %}
|
||||||
|
{{accessModifier}} static let initialScene = InitialSceneType<{{sceneClass}}>(storyboard: {{storyboardName}}.self)
|
||||||
|
{% endif %}
|
||||||
|
{% for scene in storyboard.scenes %}
|
||||||
|
|
||||||
|
{% set sceneID %}{{scene.identifier|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}}{% endset %}
|
||||||
|
{% set sceneClass %}{% call className scene %}{% endset %}
|
||||||
|
{{accessModifier}} static let {{sceneID}} = SceneType<{{sceneClass}}>(storyboard: {{storyboardName}}.self, identifier: "{{scene.identifier}}")
|
||||||
|
{% endfor %}
|
||||||
|
}
|
||||||
|
{% endfor %}
|
||||||
|
}
|
||||||
|
// swiftlint:enable explicit_type_interface identifier_name line_length type_body_length type_name
|
||||||
|
|
||||||
|
// MARK: - Implementation Details
|
||||||
|
|
||||||
|
{{accessModifier}} protocol StoryboardType {
|
||||||
|
static var storyboardName: String { get }
|
||||||
|
}
|
||||||
|
|
||||||
|
{{accessModifier}} extension StoryboardType {
|
||||||
|
static var storyboard: {{prefix}}Storyboard {
|
||||||
|
let name = {% if isAppKit %}NSStoryboard.Name({% endif %}self.storyboardName{% if isAppKit %}){% endif %}
|
||||||
|
{% if param.lookupFunction %}
|
||||||
|
return {{param.lookupFunction}}(name)
|
||||||
|
{% else %}
|
||||||
|
return {{prefix}}Storyboard(name: name, bundle: {{param.bundle|default:"BundleToken.bundle"}})
|
||||||
|
{% endif %}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{{accessModifier}} struct SceneType<T{% if not isAppKit %}: UIViewController{% endif %}> {
|
||||||
|
{{accessModifier}} let storyboard: StoryboardType.Type
|
||||||
|
{{accessModifier}} let identifier: String
|
||||||
|
|
||||||
|
{{accessModifier}} func instantiate() -> T {
|
||||||
|
let identifier = {% if isAppKit %}NSStoryboard.SceneIdentifier({% endif %}self.identifier{% if isAppKit %}){% endif %}
|
||||||
|
guard let controller = storyboard.storyboard.instantiate{{controller}}(withIdentifier: identifier) as? T else {
|
||||||
|
fatalError("{{controller}} '\(identifier)' is not of the expected class \(T.self).")
|
||||||
|
}
|
||||||
|
return controller
|
||||||
|
}
|
||||||
|
|
||||||
|
{% if isAppKit %}
|
||||||
|
@available(macOS 10.15, *)
|
||||||
|
{{accessModifier}} func instantiate(creator block: @escaping (NSCoder) -> T?) -> T where T: NSViewController {
|
||||||
|
let identifier = NSStoryboard.SceneIdentifier(self.identifier)
|
||||||
|
return storyboard.storyboard.instantiate{{controller}}(identifier: identifier, creator: block)
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(macOS 10.15, *)
|
||||||
|
{{accessModifier}} func instantiate(creator block: @escaping (NSCoder) -> T?) -> T where T: NSWindowController {
|
||||||
|
let identifier = NSStoryboard.SceneIdentifier(self.identifier)
|
||||||
|
return storyboard.storyboard.instantiate{{controller}}(identifier: identifier, creator: block)
|
||||||
|
}
|
||||||
|
{% else %}
|
||||||
|
@available(iOS 13.0, tvOS 13.0, *)
|
||||||
|
{{accessModifier}} func instantiate(creator block: @escaping (NSCoder) -> T?) -> T {
|
||||||
|
return storyboard.storyboard.instantiate{{controller}}(identifier: identifier, creator: block)
|
||||||
|
}
|
||||||
|
{% endif %}
|
||||||
|
}
|
||||||
|
|
||||||
|
{{accessModifier}} struct InitialSceneType<T{% if not isAppKit %}: UIViewController{% endif %}> {
|
||||||
|
{{accessModifier}} let storyboard: StoryboardType.Type
|
||||||
|
|
||||||
|
{{accessModifier}} func instantiate() -> T {
|
||||||
|
guard let controller = storyboard.storyboard.instantiateInitial{{controller}}() as? T else {
|
||||||
|
fatalError("{{controller}} is not of the expected class \(T.self).")
|
||||||
|
}
|
||||||
|
return controller
|
||||||
|
}
|
||||||
|
|
||||||
|
{% if isAppKit %}
|
||||||
|
@available(macOS 10.15, *)
|
||||||
|
{{accessModifier}} func instantiate(creator block: @escaping (NSCoder) -> T?) -> T where T: NSViewController {
|
||||||
|
guard let controller = storyboard.storyboard.instantiateInitial{{controller}}(creator: block) else {
|
||||||
|
fatalError("Storyboard \(storyboard.storyboardName) does not have an initial scene.")
|
||||||
|
}
|
||||||
|
return controller
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(macOS 10.15, *)
|
||||||
|
{{accessModifier}} func instantiate(creator block: @escaping (NSCoder) -> T?) -> T where T: NSWindowController {
|
||||||
|
guard let controller = storyboard.storyboard.instantiateInitial{{controller}}(creator: block) else {
|
||||||
|
fatalError("Storyboard \(storyboard.storyboardName) does not have an initial scene.")
|
||||||
|
}
|
||||||
|
return controller
|
||||||
|
}
|
||||||
|
{% else %}
|
||||||
|
@available(iOS 13.0, tvOS 13.0, *)
|
||||||
|
{{accessModifier}} func instantiate(creator block: @escaping (NSCoder) -> T?) -> T {
|
||||||
|
guard let controller = storyboard.storyboard.instantiateInitial{{controller}}(creator: block) else {
|
||||||
|
fatalError("Storyboard \(storyboard.storyboardName) does not have an initial scene.")
|
||||||
|
}
|
||||||
|
return controller
|
||||||
|
}
|
||||||
|
{% endif %}
|
||||||
|
}
|
||||||
|
{% if not param.bundle and not param.lookupFunction %}
|
||||||
|
|
||||||
|
// swiftlint:disable convenience_type
|
||||||
|
private final class BundleToken {
|
||||||
|
static let bundle: Bundle = {
|
||||||
|
#if SWIFT_PACKAGE
|
||||||
|
return Bundle.module
|
||||||
|
#else
|
||||||
|
return Bundle(for: BundleToken.self)
|
||||||
|
#endif
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
// swiftlint:enable convenience_type
|
||||||
|
{% endif %}
|
||||||
|
{% elif storyboards %}
|
||||||
|
// Mixed AppKit and UIKit storyboard files found, please invoke swiftgen with these separately
|
||||||
|
{% else %}
|
||||||
|
// No storyboard found
|
||||||
|
{% endif %}
|
|
@ -0,0 +1,60 @@
|
||||||
|
// swiftlint:disable all
|
||||||
|
// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen
|
||||||
|
|
||||||
|
{% if platform and storyboards %}
|
||||||
|
{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %}
|
||||||
|
{% set isAppKit %}{% if platform == "macOS" %}true{% endif %}{% endset %}
|
||||||
|
// swiftlint:disable sorted_imports
|
||||||
|
import Foundation
|
||||||
|
{% for module in modules where module != env.PRODUCT_MODULE_NAME and module != param.module %}
|
||||||
|
import {{module}}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
// swiftlint:disable superfluous_disable_command
|
||||||
|
// swiftlint:disable file_length
|
||||||
|
|
||||||
|
// MARK: - Storyboard Segues
|
||||||
|
|
||||||
|
// swiftlint:disable explicit_type_interface identifier_name line_length type_body_length type_name
|
||||||
|
{{accessModifier}} enum {{param.enumName|default:"StoryboardSegue"}} {
|
||||||
|
{% for storyboard in storyboards where storyboard.segues %}
|
||||||
|
{{accessModifier}} enum {{storyboard.name|swiftIdentifier:"pretty"|escapeReservedKeywords}}: String, SegueType {
|
||||||
|
{% for segue in storyboard.segues %}
|
||||||
|
{% set segueID %}{{segue.identifier|swiftIdentifier:"pretty"|lowerFirstWord}}{% endset %}
|
||||||
|
case {{segueID|escapeReservedKeywords}}{% if segueID != segue.identifier %} = "{{segue.identifier}}"{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
}
|
||||||
|
{% endfor %}
|
||||||
|
}
|
||||||
|
// swiftlint:enable explicit_type_interface identifier_name line_length type_body_length type_name
|
||||||
|
|
||||||
|
// MARK: - Implementation Details
|
||||||
|
|
||||||
|
{{accessModifier}} protocol SegueType: RawRepresentable {}
|
||||||
|
|
||||||
|
{{accessModifier}} extension {% if isAppKit %}NSSeguePerforming{% else %}UIViewController{% endif %} {
|
||||||
|
func perform<S: SegueType>(segue: S, sender: Any? = nil) where S.RawValue == String {
|
||||||
|
let identifier = {% if isAppKit %}NSStoryboardSegue.Identifier({% endif %}segue.rawValue{% if isAppKit %}){% endif %}
|
||||||
|
performSegue{% if isAppKit %}?{% endif %}(withIdentifier: identifier, sender: sender)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{{accessModifier}} extension SegueType where RawValue == String {
|
||||||
|
init?(_ segue: {% if isAppKit %}NS{% else %}UI{% endif %}StoryboardSegue) {
|
||||||
|
{% if isAppKit %}
|
||||||
|
#if swift(>=4.2)
|
||||||
|
guard let identifier = segue.identifier else { return nil }
|
||||||
|
#else
|
||||||
|
guard let identifier = segue.identifier?.rawValue else { return nil }
|
||||||
|
#endif
|
||||||
|
{% else %}
|
||||||
|
guard let identifier = segue.identifier else { return nil }
|
||||||
|
{% endif %}
|
||||||
|
self.init(rawValue: identifier)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{% elif storyboards %}
|
||||||
|
// Mixed AppKit and UIKit storyboard files found, please invoke swiftgen with these separately
|
||||||
|
{% else %}
|
||||||
|
// No storyboard found
|
||||||
|
{% endif %}
|
|
@ -0,0 +1,60 @@
|
||||||
|
// swiftlint:disable all
|
||||||
|
// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen
|
||||||
|
|
||||||
|
{% if platform and storyboards %}
|
||||||
|
{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %}
|
||||||
|
{% set isAppKit %}{% if platform == "macOS" %}true{% endif %}{% endset %}
|
||||||
|
// swiftlint:disable sorted_imports
|
||||||
|
import Foundation
|
||||||
|
{% for module in modules where module != env.PRODUCT_MODULE_NAME and module != param.module %}
|
||||||
|
import {{module}}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
// swiftlint:disable superfluous_disable_command
|
||||||
|
// swiftlint:disable file_length
|
||||||
|
|
||||||
|
// MARK: - Storyboard Segues
|
||||||
|
|
||||||
|
// swiftlint:disable explicit_type_interface identifier_name line_length type_body_length type_name
|
||||||
|
{{accessModifier}} enum {{param.enumName|default:"StoryboardSegue"}} {
|
||||||
|
{% for storyboard in storyboards where storyboard.segues %}
|
||||||
|
{{accessModifier}} enum {{storyboard.name|swiftIdentifier:"pretty"|escapeReservedKeywords}}: String, SegueType {
|
||||||
|
{% for segue in storyboard.segues %}
|
||||||
|
{% set segueID %}{{segue.identifier|swiftIdentifier:"pretty"|lowerFirstWord}}{% endset %}
|
||||||
|
case {{segueID|escapeReservedKeywords}}{% if segueID != segue.identifier %} = "{{segue.identifier}}"{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
}
|
||||||
|
{% endfor %}
|
||||||
|
}
|
||||||
|
// swiftlint:enable explicit_type_interface identifier_name line_length type_body_length type_name
|
||||||
|
|
||||||
|
// MARK: - Implementation Details
|
||||||
|
|
||||||
|
{{accessModifier}} protocol SegueType: RawRepresentable {}
|
||||||
|
|
||||||
|
{{accessModifier}} extension {% if isAppKit %}NSSeguePerforming{% else %}UIViewController{% endif %} {
|
||||||
|
func perform<S: SegueType>(segue: S, sender: Any? = nil) where S.RawValue == String {
|
||||||
|
let identifier = {% if isAppKit %}NSStoryboardSegue.Identifier({% endif %}segue.rawValue{% if isAppKit %}){% endif %}
|
||||||
|
performSegue{% if isAppKit %}?{% endif %}(withIdentifier: identifier, sender: sender)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{{accessModifier}} extension SegueType where RawValue == String {
|
||||||
|
init?(_ segue: {% if isAppKit %}NS{% else %}UI{% endif %}StoryboardSegue) {
|
||||||
|
{% if isAppKit %}
|
||||||
|
#if swift(>=4.2)
|
||||||
|
guard let identifier = segue.identifier else { return nil }
|
||||||
|
#else
|
||||||
|
guard let identifier = segue.identifier?.rawValue else { return nil }
|
||||||
|
#endif
|
||||||
|
{% else %}
|
||||||
|
guard let identifier = segue.identifier else { return nil }
|
||||||
|
{% endif %}
|
||||||
|
self.init(rawValue: identifier)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{% elif storyboards %}
|
||||||
|
// Mixed AppKit and UIKit storyboard files found, please invoke swiftgen with these separately
|
||||||
|
{% else %}
|
||||||
|
// No storyboard found
|
||||||
|
{% endif %}
|
|
@ -0,0 +1,82 @@
|
||||||
|
// swiftlint:disable all
|
||||||
|
// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen
|
||||||
|
|
||||||
|
{% if files %}
|
||||||
|
{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %}
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
// swiftlint:disable superfluous_disable_command
|
||||||
|
// swiftlint:disable file_length
|
||||||
|
|
||||||
|
// MARK: - JSON Files
|
||||||
|
{% macro fileBlock file %}
|
||||||
|
{% call documentBlock file file.document %}
|
||||||
|
{% endmacro %}
|
||||||
|
{% macro documentBlock file document %}
|
||||||
|
{% set rootType %}{% call typeBlock document.metadata %}{% endset %}
|
||||||
|
{% if document.metadata.type == "Array" %}
|
||||||
|
{{accessModifier}} static let items: {{rootType}} = {% call valueBlock document.data document.metadata %}
|
||||||
|
{% elif document.metadata.type == "Dictionary" %}
|
||||||
|
{% for key,value in document.metadata.properties %}
|
||||||
|
{{accessModifier}} {% call propertyBlock key value document.data %}
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
{{accessModifier}} static let value: {{rootType}} = {% call valueBlock document.data document.metadata %}
|
||||||
|
{% endif %}
|
||||||
|
{% endmacro %}
|
||||||
|
{% macro typeBlock metadata %}{% filter removeNewlines:"leading" %}
|
||||||
|
{% if metadata.type == "Array" %}
|
||||||
|
[{% call typeBlock metadata.element %}]
|
||||||
|
{% elif metadata.type == "Dictionary" %}
|
||||||
|
[String: Any]
|
||||||
|
{% elif metadata.type == "Optional" %}
|
||||||
|
Any?
|
||||||
|
{% else %}
|
||||||
|
{{metadata.type}}
|
||||||
|
{% endif %}
|
||||||
|
{% endfilter %}{% endmacro %}
|
||||||
|
{% macro propertyBlock key metadata data %}{% filter removeNewlines:"leading" %}
|
||||||
|
{% set propertyName %}{{key|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}}{% endset %}
|
||||||
|
{% set propertyType %}{% call typeBlock metadata %}{% endset %}
|
||||||
|
static let {{propertyName}}: {{propertyType}} = {% call valueBlock data[key] metadata %}
|
||||||
|
{% endfilter %}{% endmacro %}
|
||||||
|
{% macro valueBlock value metadata %}{% filter removeNewlines:"leading" %}
|
||||||
|
{% if metadata.type == "String" %}
|
||||||
|
"{{ value }}"
|
||||||
|
{% elif metadata.type == "Optional" %}
|
||||||
|
nil
|
||||||
|
{% elif metadata.type == "Array" and value %}
|
||||||
|
[{% for value in value %}
|
||||||
|
{% call valueBlock value metadata.element.items[forloop.counter0]|default:metadata.element %}
|
||||||
|
{{ ", " if not forloop.last }}
|
||||||
|
{% endfor %}]
|
||||||
|
{% elif metadata.type == "Dictionary" %}
|
||||||
|
[{% for key,value in value %}
|
||||||
|
"{{key}}": {% call valueBlock value metadata.properties[key] %}
|
||||||
|
{{ ", " if not forloop.last }}
|
||||||
|
{% empty %}
|
||||||
|
:
|
||||||
|
{% endfor %}]
|
||||||
|
{% elif metadata.type == "Bool" %}
|
||||||
|
{% if value %}true{% else %}false{% endif %}
|
||||||
|
{% else %}
|
||||||
|
{{ value }}
|
||||||
|
{% endif %}
|
||||||
|
{% endfilter %}{% endmacro %}
|
||||||
|
|
||||||
|
// swiftlint:disable identifier_name line_length number_separator type_body_length
|
||||||
|
{{accessModifier}} enum {{param.enumName|default:"JSONFiles"}} {
|
||||||
|
{% if files.count > 1 or param.forceFileNameEnum %}
|
||||||
|
{% for file in files %}
|
||||||
|
{{accessModifier}} enum {{file.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} {
|
||||||
|
{% filter indent:2 %}{% call fileBlock file %}{% endfilter %}
|
||||||
|
}
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
{% call fileBlock files.first %}
|
||||||
|
{% endif %}
|
||||||
|
}
|
||||||
|
// swiftlint:enable identifier_name line_length number_separator type_body_length
|
||||||
|
{% else %}
|
||||||
|
// No files found
|
||||||
|
{% endif %}
|
|
@ -0,0 +1,82 @@
|
||||||
|
// swiftlint:disable all
|
||||||
|
// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen
|
||||||
|
|
||||||
|
{% if files %}
|
||||||
|
{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %}
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
// swiftlint:disable superfluous_disable_command
|
||||||
|
// swiftlint:disable file_length
|
||||||
|
|
||||||
|
// MARK: - JSON Files
|
||||||
|
{% macro fileBlock file %}
|
||||||
|
{% call documentBlock file file.document %}
|
||||||
|
{% endmacro %}
|
||||||
|
{% macro documentBlock file document %}
|
||||||
|
{% set rootType %}{% call typeBlock document.metadata %}{% endset %}
|
||||||
|
{% if document.metadata.type == "Array" %}
|
||||||
|
{{accessModifier}} static let items: {{rootType}} = {% call valueBlock document.data document.metadata %}
|
||||||
|
{% elif document.metadata.type == "Dictionary" %}
|
||||||
|
{% for key,value in document.metadata.properties %}
|
||||||
|
{{accessModifier}} {% call propertyBlock key value document.data %}
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
{{accessModifier}} static let value: {{rootType}} = {% call valueBlock document.data document.metadata %}
|
||||||
|
{% endif %}
|
||||||
|
{% endmacro %}
|
||||||
|
{% macro typeBlock metadata %}{% filter removeNewlines:"leading" %}
|
||||||
|
{% if metadata.type == "Array" %}
|
||||||
|
[{% call typeBlock metadata.element %}]
|
||||||
|
{% elif metadata.type == "Dictionary" %}
|
||||||
|
[String: Any]
|
||||||
|
{% elif metadata.type == "Optional" %}
|
||||||
|
Any?
|
||||||
|
{% else %}
|
||||||
|
{{metadata.type}}
|
||||||
|
{% endif %}
|
||||||
|
{% endfilter %}{% endmacro %}
|
||||||
|
{% macro propertyBlock key metadata data %}{% filter removeNewlines:"leading" %}
|
||||||
|
{% set propertyName %}{{key|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}}{% endset %}
|
||||||
|
{% set propertyType %}{% call typeBlock metadata %}{% endset %}
|
||||||
|
static let {{propertyName}}: {{propertyType}} = {% call valueBlock data[key] metadata %}
|
||||||
|
{% endfilter %}{% endmacro %}
|
||||||
|
{% macro valueBlock value metadata %}{% filter removeNewlines:"leading" %}
|
||||||
|
{% if metadata.type == "String" %}
|
||||||
|
"{{ value }}"
|
||||||
|
{% elif metadata.type == "Optional" %}
|
||||||
|
nil
|
||||||
|
{% elif metadata.type == "Array" and value %}
|
||||||
|
[{% for value in value %}
|
||||||
|
{% call valueBlock value metadata.element.items[forloop.counter0]|default:metadata.element %}
|
||||||
|
{{ ", " if not forloop.last }}
|
||||||
|
{% endfor %}]
|
||||||
|
{% elif metadata.type == "Dictionary" %}
|
||||||
|
[{% for key,value in value %}
|
||||||
|
"{{key}}": {% call valueBlock value metadata.properties[key] %}
|
||||||
|
{{ ", " if not forloop.last }}
|
||||||
|
{% empty %}
|
||||||
|
:
|
||||||
|
{% endfor %}]
|
||||||
|
{% elif metadata.type == "Bool" %}
|
||||||
|
{% if value %}true{% else %}false{% endif %}
|
||||||
|
{% else %}
|
||||||
|
{{ value }}
|
||||||
|
{% endif %}
|
||||||
|
{% endfilter %}{% endmacro %}
|
||||||
|
|
||||||
|
// swiftlint:disable identifier_name line_length number_separator type_body_length
|
||||||
|
{{accessModifier}} enum {{param.enumName|default:"JSONFiles"}} {
|
||||||
|
{% if files.count > 1 or param.forceFileNameEnum %}
|
||||||
|
{% for file in files %}
|
||||||
|
{{accessModifier}} enum {{file.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} {
|
||||||
|
{% filter indent:2 %}{% call fileBlock file %}{% endfilter %}
|
||||||
|
}
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
{% call fileBlock files.first %}
|
||||||
|
{% endif %}
|
||||||
|
}
|
||||||
|
// swiftlint:enable identifier_name line_length number_separator type_body_length
|
||||||
|
{% else %}
|
||||||
|
// No files found
|
||||||
|
{% endif %}
|
|
@ -0,0 +1,112 @@
|
||||||
|
// swiftlint:disable all
|
||||||
|
// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen
|
||||||
|
|
||||||
|
{% if files %}
|
||||||
|
{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %}
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
// swiftlint:disable superfluous_disable_command
|
||||||
|
// swiftlint:disable file_length
|
||||||
|
|
||||||
|
// MARK: - JSON Files
|
||||||
|
{% macro fileBlock file %}
|
||||||
|
{% call documentBlock file file.document %}
|
||||||
|
{% endmacro %}
|
||||||
|
{% macro documentBlock file document %}
|
||||||
|
{% set rootType %}{% call typeBlock document.metadata %}{% endset %}
|
||||||
|
{% if document.metadata.type == "Array" %}
|
||||||
|
{{accessModifier}} static let items: {{rootType}} = objectFromJSON(at: "{% call transformPath file.path %}")
|
||||||
|
{% elif document.metadata.type == "Dictionary" %}
|
||||||
|
private static let _document = JSONDocument(path: "{% call transformPath file.path %}")
|
||||||
|
|
||||||
|
{% for key,value in document.metadata.properties %}
|
||||||
|
{{accessModifier}} {% call propertyBlock key value %}
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
{{accessModifier}} static let value: {{rootType}} = objectFromJSON(at: "{% call transformPath file.path %}")
|
||||||
|
{% endif %}
|
||||||
|
{% endmacro %}
|
||||||
|
{% macro typeBlock metadata %}{% filter removeNewlines:"leading" %}
|
||||||
|
{% if metadata.type == "Array" %}
|
||||||
|
[{% call typeBlock metadata.element %}]
|
||||||
|
{% elif metadata.type == "Dictionary" %}
|
||||||
|
[String: Any]
|
||||||
|
{% elif metadata.type == "Optional" %}
|
||||||
|
Any?
|
||||||
|
{% else %}
|
||||||
|
{{metadata.type}}
|
||||||
|
{% endif %}
|
||||||
|
{% endfilter %}{% endmacro %}
|
||||||
|
{% macro propertyBlock key metadata %}{% filter removeNewlines:"leading" %}
|
||||||
|
{% set propertyName %}{{key|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}}{% endset %}
|
||||||
|
{% set propertyType %}{% call typeBlock metadata %}{% endset %}
|
||||||
|
static let {{propertyName}}: {{propertyType}} = _document["{{key}}"]
|
||||||
|
{% endfilter %}{% endmacro %}
|
||||||
|
{% macro transformPath path %}{% filter removeNewlines %}
|
||||||
|
{% if param.preservePath %}
|
||||||
|
{{path}}
|
||||||
|
{% else %}
|
||||||
|
{{path|basename}}
|
||||||
|
{% endif %}
|
||||||
|
{% endfilter %}{% endmacro %}
|
||||||
|
|
||||||
|
// swiftlint:disable identifier_name line_length type_body_length
|
||||||
|
{{accessModifier}} enum {{param.enumName|default:"JSONFiles"}} {
|
||||||
|
{% if files.count > 1 or param.forceFileNameEnum %}
|
||||||
|
{% for file in files %}
|
||||||
|
{{accessModifier}} enum {{file.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} {
|
||||||
|
{% filter indent:2 %}{% call fileBlock file %}{% endfilter %}
|
||||||
|
}
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
{% call fileBlock files.first %}
|
||||||
|
{% endif %}
|
||||||
|
}
|
||||||
|
// swiftlint:enable identifier_name line_length type_body_length
|
||||||
|
|
||||||
|
// MARK: - Implementation Details
|
||||||
|
|
||||||
|
private func objectFromJSON<T>(at path: String) -> T {
|
||||||
|
{% if param.lookupFunction %}
|
||||||
|
guard let url = {{param.lookupFunction}}(path),
|
||||||
|
{% else %}
|
||||||
|
guard let url = {{param.bundle|default:"BundleToken.bundle"}}.url(forResource: path, withExtension: nil),
|
||||||
|
{% endif %}
|
||||||
|
let json = try? JSONSerialization.jsonObject(with: Data(contentsOf: url), options: []),
|
||||||
|
let result = json as? T else {
|
||||||
|
fatalError("Unable to load JSON at path: \(path)")
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct JSONDocument {
|
||||||
|
let data: [String: Any]
|
||||||
|
|
||||||
|
init(path: String) {
|
||||||
|
self.data = objectFromJSON(at: path)
|
||||||
|
}
|
||||||
|
|
||||||
|
subscript<T>(key: String) -> T {
|
||||||
|
guard let result = data[key] as? T else {
|
||||||
|
fatalError("Property '\(key)' is not of type \(T.self)")
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{% if not param.bundle and not param.lookupFunction %}
|
||||||
|
|
||||||
|
// swiftlint:disable convenience_type
|
||||||
|
private final class BundleToken {
|
||||||
|
static let bundle: Bundle = {
|
||||||
|
#if SWIFT_PACKAGE
|
||||||
|
return Bundle.module
|
||||||
|
#else
|
||||||
|
return Bundle(for: BundleToken.self)
|
||||||
|
#endif
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
// swiftlint:enable convenience_type
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
// No files found
|
||||||
|
{% endif %}
|
|
@ -0,0 +1,112 @@
|
||||||
|
// swiftlint:disable all
|
||||||
|
// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen
|
||||||
|
|
||||||
|
{% if files %}
|
||||||
|
{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %}
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
// swiftlint:disable superfluous_disable_command
|
||||||
|
// swiftlint:disable file_length
|
||||||
|
|
||||||
|
// MARK: - JSON Files
|
||||||
|
{% macro fileBlock file %}
|
||||||
|
{% call documentBlock file file.document %}
|
||||||
|
{% endmacro %}
|
||||||
|
{% macro documentBlock file document %}
|
||||||
|
{% set rootType %}{% call typeBlock document.metadata %}{% endset %}
|
||||||
|
{% if document.metadata.type == "Array" %}
|
||||||
|
{{accessModifier}} static let items: {{rootType}} = objectFromJSON(at: "{% call transformPath file.path %}")
|
||||||
|
{% elif document.metadata.type == "Dictionary" %}
|
||||||
|
private static let _document = JSONDocument(path: "{% call transformPath file.path %}")
|
||||||
|
|
||||||
|
{% for key,value in document.metadata.properties %}
|
||||||
|
{{accessModifier}} {% call propertyBlock key value %}
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
{{accessModifier}} static let value: {{rootType}} = objectFromJSON(at: "{% call transformPath file.path %}")
|
||||||
|
{% endif %}
|
||||||
|
{% endmacro %}
|
||||||
|
{% macro typeBlock metadata %}{% filter removeNewlines:"leading" %}
|
||||||
|
{% if metadata.type == "Array" %}
|
||||||
|
[{% call typeBlock metadata.element %}]
|
||||||
|
{% elif metadata.type == "Dictionary" %}
|
||||||
|
[String: Any]
|
||||||
|
{% elif metadata.type == "Optional" %}
|
||||||
|
Any?
|
||||||
|
{% else %}
|
||||||
|
{{metadata.type}}
|
||||||
|
{% endif %}
|
||||||
|
{% endfilter %}{% endmacro %}
|
||||||
|
{% macro propertyBlock key metadata %}{% filter removeNewlines:"leading" %}
|
||||||
|
{% set propertyName %}{{key|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}}{% endset %}
|
||||||
|
{% set propertyType %}{% call typeBlock metadata %}{% endset %}
|
||||||
|
static let {{propertyName}}: {{propertyType}} = _document["{{key}}"]
|
||||||
|
{% endfilter %}{% endmacro %}
|
||||||
|
{% macro transformPath path %}{% filter removeNewlines %}
|
||||||
|
{% if param.preservePath %}
|
||||||
|
{{path}}
|
||||||
|
{% else %}
|
||||||
|
{{path|basename}}
|
||||||
|
{% endif %}
|
||||||
|
{% endfilter %}{% endmacro %}
|
||||||
|
|
||||||
|
// swiftlint:disable identifier_name line_length type_body_length
|
||||||
|
{{accessModifier}} enum {{param.enumName|default:"JSONFiles"}} {
|
||||||
|
{% if files.count > 1 or param.forceFileNameEnum %}
|
||||||
|
{% for file in files %}
|
||||||
|
{{accessModifier}} enum {{file.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} {
|
||||||
|
{% filter indent:2 %}{% call fileBlock file %}{% endfilter %}
|
||||||
|
}
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
{% call fileBlock files.first %}
|
||||||
|
{% endif %}
|
||||||
|
}
|
||||||
|
// swiftlint:enable identifier_name line_length type_body_length
|
||||||
|
|
||||||
|
// MARK: - Implementation Details
|
||||||
|
|
||||||
|
private func objectFromJSON<T>(at path: String) -> T {
|
||||||
|
{% if param.lookupFunction %}
|
||||||
|
guard let url = {{param.lookupFunction}}(path),
|
||||||
|
{% else %}
|
||||||
|
guard let url = {{param.bundle|default:"BundleToken.bundle"}}.url(forResource: path, withExtension: nil),
|
||||||
|
{% endif %}
|
||||||
|
let json = try? JSONSerialization.jsonObject(with: Data(contentsOf: url), options: []),
|
||||||
|
let result = json as? T else {
|
||||||
|
fatalError("Unable to load JSON at path: \(path)")
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct JSONDocument {
|
||||||
|
let data: [String: Any]
|
||||||
|
|
||||||
|
init(path: String) {
|
||||||
|
self.data = objectFromJSON(at: path)
|
||||||
|
}
|
||||||
|
|
||||||
|
subscript<T>(key: String) -> T {
|
||||||
|
guard let result = data[key] as? T else {
|
||||||
|
fatalError("Property '\(key)' is not of type \(T.self)")
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{% if not param.bundle and not param.lookupFunction %}
|
||||||
|
|
||||||
|
// swiftlint:disable convenience_type
|
||||||
|
private final class BundleToken {
|
||||||
|
static let bundle: Bundle = {
|
||||||
|
#if SWIFT_PACKAGE
|
||||||
|
return Bundle.module
|
||||||
|
#else
|
||||||
|
return Bundle(for: BundleToken.self)
|
||||||
|
#endif
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
// swiftlint:enable convenience_type
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
// No files found
|
||||||
|
{% endif %}
|
|
@ -0,0 +1,82 @@
|
||||||
|
// swiftlint:disable all
|
||||||
|
// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen
|
||||||
|
|
||||||
|
{% if files %}
|
||||||
|
{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %}
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
// swiftlint:disable superfluous_disable_command
|
||||||
|
// swiftlint:disable file_length
|
||||||
|
|
||||||
|
// MARK: - Plist Files
|
||||||
|
{% macro fileBlock file %}
|
||||||
|
{% call documentBlock file file.document %}
|
||||||
|
{% endmacro %}
|
||||||
|
{% macro documentBlock file document %}
|
||||||
|
{% set rootType %}{% call typeBlock document.metadata %}{% endset %}
|
||||||
|
{% if document.metadata.type == "Array" %}
|
||||||
|
{{accessModifier}} static let items: {{rootType}} = {% call valueBlock document.data document.metadata %}
|
||||||
|
{% elif document.metadata.type == "Dictionary" %}
|
||||||
|
{% for key,value in document.metadata.properties %}
|
||||||
|
{{accessModifier}} {% call propertyBlock key value document.data %}
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
{{accessModifier}} static let value: {{rootType}} = {% call valueBlock document.data document.metadata %}
|
||||||
|
{% endif %}
|
||||||
|
{% endmacro %}
|
||||||
|
{% macro typeBlock metadata %}{% filter removeNewlines:"leading" %}
|
||||||
|
{% if metadata.type == "Array" %}
|
||||||
|
[{% call typeBlock metadata.element %}]
|
||||||
|
{% elif metadata.type == "Dictionary" %}
|
||||||
|
[String: Any]
|
||||||
|
{% else %}
|
||||||
|
{{metadata.type}}
|
||||||
|
{% endif %}
|
||||||
|
{% endfilter %}{% endmacro %}
|
||||||
|
{% macro propertyBlock key metadata data %}{% filter removeNewlines:"leading" %}
|
||||||
|
{% set propertyName %}{{key|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}}{% endset %}
|
||||||
|
{% set propertyType %}{% call typeBlock metadata %}{% endset %}
|
||||||
|
static let {{propertyName}}: {{propertyType}} = {% call valueBlock data[key] metadata %}
|
||||||
|
{% endfilter %}{% endmacro %}
|
||||||
|
{% macro valueBlock value metadata %}{% filter removeNewlines:"leading" %}
|
||||||
|
{% if metadata.type == "String" %}
|
||||||
|
"{{ value }}"
|
||||||
|
{% elif metadata.type == "Date" %}
|
||||||
|
Date(timeIntervalSinceReferenceDate: {{ value.timeIntervalSinceReferenceDate }})
|
||||||
|
{% elif metadata.type == "Optional" %}
|
||||||
|
nil
|
||||||
|
{% elif metadata.type == "Array" and value %}
|
||||||
|
[{% for value in value %}
|
||||||
|
{% call valueBlock value metadata.element.items[forloop.counter0]|default:metadata.element %}
|
||||||
|
{{ ", " if not forloop.last }}
|
||||||
|
{% endfor %}]
|
||||||
|
{% elif metadata.type == "Dictionary" %}
|
||||||
|
[{% for key,value in value %}
|
||||||
|
"{{key}}": {% call valueBlock value metadata.properties[key] %}
|
||||||
|
{{ ", " if not forloop.last }}
|
||||||
|
{% empty %}
|
||||||
|
:
|
||||||
|
{% endfor %}]
|
||||||
|
{% elif metadata.type == "Bool" %}
|
||||||
|
{% if value %}true{% else %}false{% endif %}
|
||||||
|
{% else %}
|
||||||
|
{{ value }}
|
||||||
|
{% endif %}
|
||||||
|
{% endfilter %}{% endmacro %}
|
||||||
|
|
||||||
|
// swiftlint:disable identifier_name line_length number_separator type_body_length
|
||||||
|
{{accessModifier}} enum {{param.enumName|default:"PlistFiles"}} {
|
||||||
|
{% if files.count > 1 or param.forceFileNameEnum %}
|
||||||
|
{% for file in files %}
|
||||||
|
{{accessModifier}} enum {{file.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} {
|
||||||
|
{% filter indent:2 %}{% call fileBlock file %}{% endfilter %}
|
||||||
|
}
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
{% call fileBlock files.first %}
|
||||||
|
{% endif %}
|
||||||
|
}
|
||||||
|
// swiftlint:enable identifier_name line_length number_separator type_body_length
|
||||||
|
{% else %}
|
||||||
|
// No files found
|
||||||
|
{% endif %}
|
|
@ -0,0 +1,82 @@
|
||||||
|
// swiftlint:disable all
|
||||||
|
// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen
|
||||||
|
|
||||||
|
{% if files %}
|
||||||
|
{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %}
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
// swiftlint:disable superfluous_disable_command
|
||||||
|
// swiftlint:disable file_length
|
||||||
|
|
||||||
|
// MARK: - Plist Files
|
||||||
|
{% macro fileBlock file %}
|
||||||
|
{% call documentBlock file file.document %}
|
||||||
|
{% endmacro %}
|
||||||
|
{% macro documentBlock file document %}
|
||||||
|
{% set rootType %}{% call typeBlock document.metadata %}{% endset %}
|
||||||
|
{% if document.metadata.type == "Array" %}
|
||||||
|
{{accessModifier}} static let items: {{rootType}} = {% call valueBlock document.data document.metadata %}
|
||||||
|
{% elif document.metadata.type == "Dictionary" %}
|
||||||
|
{% for key,value in document.metadata.properties %}
|
||||||
|
{{accessModifier}} {% call propertyBlock key value document.data %}
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
{{accessModifier}} static let value: {{rootType}} = {% call valueBlock document.data document.metadata %}
|
||||||
|
{% endif %}
|
||||||
|
{% endmacro %}
|
||||||
|
{% macro typeBlock metadata %}{% filter removeNewlines:"leading" %}
|
||||||
|
{% if metadata.type == "Array" %}
|
||||||
|
[{% call typeBlock metadata.element %}]
|
||||||
|
{% elif metadata.type == "Dictionary" %}
|
||||||
|
[String: Any]
|
||||||
|
{% else %}
|
||||||
|
{{metadata.type}}
|
||||||
|
{% endif %}
|
||||||
|
{% endfilter %}{% endmacro %}
|
||||||
|
{% macro propertyBlock key metadata data %}{% filter removeNewlines:"leading" %}
|
||||||
|
{% set propertyName %}{{key|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}}{% endset %}
|
||||||
|
{% set propertyType %}{% call typeBlock metadata %}{% endset %}
|
||||||
|
static let {{propertyName}}: {{propertyType}} = {% call valueBlock data[key] metadata %}
|
||||||
|
{% endfilter %}{% endmacro %}
|
||||||
|
{% macro valueBlock value metadata %}{% filter removeNewlines:"leading" %}
|
||||||
|
{% if metadata.type == "String" %}
|
||||||
|
"{{ value }}"
|
||||||
|
{% elif metadata.type == "Date" %}
|
||||||
|
Date(timeIntervalSinceReferenceDate: {{ value.timeIntervalSinceReferenceDate }})
|
||||||
|
{% elif metadata.type == "Optional" %}
|
||||||
|
nil
|
||||||
|
{% elif metadata.type == "Array" and value %}
|
||||||
|
[{% for value in value %}
|
||||||
|
{% call valueBlock value metadata.element.items[forloop.counter0]|default:metadata.element %}
|
||||||
|
{{ ", " if not forloop.last }}
|
||||||
|
{% endfor %}]
|
||||||
|
{% elif metadata.type == "Dictionary" %}
|
||||||
|
[{% for key,value in value %}
|
||||||
|
"{{key}}": {% call valueBlock value metadata.properties[key] %}
|
||||||
|
{{ ", " if not forloop.last }}
|
||||||
|
{% empty %}
|
||||||
|
:
|
||||||
|
{% endfor %}]
|
||||||
|
{% elif metadata.type == "Bool" %}
|
||||||
|
{% if value %}true{% else %}false{% endif %}
|
||||||
|
{% else %}
|
||||||
|
{{ value }}
|
||||||
|
{% endif %}
|
||||||
|
{% endfilter %}{% endmacro %}
|
||||||
|
|
||||||
|
// swiftlint:disable identifier_name line_length number_separator type_body_length
|
||||||
|
{{accessModifier}} enum {{param.enumName|default:"PlistFiles"}} {
|
||||||
|
{% if files.count > 1 or param.forceFileNameEnum %}
|
||||||
|
{% for file in files %}
|
||||||
|
{{accessModifier}} enum {{file.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} {
|
||||||
|
{% filter indent:2 %}{% call fileBlock file %}{% endfilter %}
|
||||||
|
}
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
{% call fileBlock files.first %}
|
||||||
|
{% endif %}
|
||||||
|
}
|
||||||
|
// swiftlint:enable identifier_name line_length number_separator type_body_length
|
||||||
|
{% else %}
|
||||||
|
// No files found
|
||||||
|
{% endif %}
|
|
@ -0,0 +1,117 @@
|
||||||
|
// swiftlint:disable all
|
||||||
|
// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen
|
||||||
|
|
||||||
|
{% if files %}
|
||||||
|
{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %}
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
// swiftlint:disable superfluous_disable_command
|
||||||
|
// swiftlint:disable file_length
|
||||||
|
|
||||||
|
// MARK: - Plist Files
|
||||||
|
{% macro fileBlock file %}
|
||||||
|
{% call documentBlock file file.document %}
|
||||||
|
{% endmacro %}
|
||||||
|
{% macro documentBlock file document %}
|
||||||
|
{% set rootType %}{% call typeBlock document.metadata %}{% endset %}
|
||||||
|
{% if document.metadata.type == "Array" %}
|
||||||
|
{{accessModifier}} static let items: {{rootType}} = arrayFromPlist(at: "{% call transformPath file.path %}")
|
||||||
|
{% elif document.metadata.type == "Dictionary" %}
|
||||||
|
private static let _document = PlistDocument(path: "{% call transformPath file.path %}")
|
||||||
|
|
||||||
|
{% for key,value in document.metadata.properties %}
|
||||||
|
{{accessModifier}} {% call propertyBlock key value %}
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
// Unsupported root type `{{rootType}}`
|
||||||
|
{% endif %}
|
||||||
|
{% endmacro %}
|
||||||
|
{% macro typeBlock metadata %}{% filter removeNewlines:"leading" %}
|
||||||
|
{% if metadata.type == "Array" %}
|
||||||
|
[{% call typeBlock metadata.element %}]
|
||||||
|
{% elif metadata.type == "Dictionary" %}
|
||||||
|
[String: Any]
|
||||||
|
{% else %}
|
||||||
|
{{metadata.type}}
|
||||||
|
{% endif %}
|
||||||
|
{% endfilter %}{% endmacro %}
|
||||||
|
{% macro propertyBlock key metadata %}{% filter removeNewlines:"leading" %}
|
||||||
|
{% set propertyName %}{{key|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}}{% endset %}
|
||||||
|
{% set propertyType %}{% call typeBlock metadata %}{% endset %}
|
||||||
|
static let {{propertyName}}: {{propertyType}} = _document["{{key}}"]
|
||||||
|
{% endfilter %}{% endmacro %}
|
||||||
|
{% macro transformPath path %}{% filter removeNewlines %}
|
||||||
|
{% if param.preservePath %}
|
||||||
|
{{path}}
|
||||||
|
{% else %}
|
||||||
|
{{path|basename}}
|
||||||
|
{% endif %}
|
||||||
|
{% endfilter %}{% endmacro %}
|
||||||
|
|
||||||
|
// swiftlint:disable identifier_name line_length type_body_length
|
||||||
|
{{accessModifier}} enum {{param.enumName|default:"PlistFiles"}} {
|
||||||
|
{% if files.count > 1 or param.forceFileNameEnum %}
|
||||||
|
{% for file in files %}
|
||||||
|
{{accessModifier}} enum {{file.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} {
|
||||||
|
{% filter indent:2 %}{% call fileBlock file %}{% endfilter %}
|
||||||
|
}
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
{% call fileBlock files.first %}
|
||||||
|
{% endif %}
|
||||||
|
}
|
||||||
|
// swiftlint:enable identifier_name line_length type_body_length
|
||||||
|
|
||||||
|
// MARK: - Implementation Details
|
||||||
|
|
||||||
|
private func arrayFromPlist<T>(at path: String) -> [T] {
|
||||||
|
{% if param.lookupFunction %}
|
||||||
|
guard let url = {{param.lookupFunction}}(path),
|
||||||
|
{% else %}
|
||||||
|
guard let url = {{param.bundle|default:"BundleToken.bundle"}}.url(forResource: path, withExtension: nil),
|
||||||
|
{% endif %}
|
||||||
|
let data = NSArray(contentsOf: url) as? [T] else {
|
||||||
|
fatalError("Unable to load PLIST at path: \(path)")
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct PlistDocument {
|
||||||
|
let data: [String: Any]
|
||||||
|
|
||||||
|
init(path: String) {
|
||||||
|
{% if param.lookupFunction %}
|
||||||
|
guard let url = {{param.lookupFunction}}(path),
|
||||||
|
{% else %}
|
||||||
|
guard let url = {{param.bundle|default:"BundleToken.bundle"}}.url(forResource: path, withExtension: nil),
|
||||||
|
{% endif %}
|
||||||
|
let data = NSDictionary(contentsOf: url) as? [String: Any] else {
|
||||||
|
fatalError("Unable to load PLIST at path: \(path)")
|
||||||
|
}
|
||||||
|
self.data = data
|
||||||
|
}
|
||||||
|
|
||||||
|
subscript<T>(key: String) -> T {
|
||||||
|
guard let result = data[key] as? T else {
|
||||||
|
fatalError("Property '\(key)' is not of type \(T.self)")
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{% if not param.bundle and not param.lookupFunction %}
|
||||||
|
|
||||||
|
// swiftlint:disable convenience_type
|
||||||
|
private final class BundleToken {
|
||||||
|
static let bundle: Bundle = {
|
||||||
|
#if SWIFT_PACKAGE
|
||||||
|
return Bundle.module
|
||||||
|
#else
|
||||||
|
return Bundle(for: BundleToken.self)
|
||||||
|
#endif
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
// swiftlint:enable convenience_type
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
// No files found
|
||||||
|
{% endif %}
|
|
@ -0,0 +1,117 @@
|
||||||
|
// swiftlint:disable all
|
||||||
|
// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen
|
||||||
|
|
||||||
|
{% if files %}
|
||||||
|
{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %}
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
// swiftlint:disable superfluous_disable_command
|
||||||
|
// swiftlint:disable file_length
|
||||||
|
|
||||||
|
// MARK: - Plist Files
|
||||||
|
{% macro fileBlock file %}
|
||||||
|
{% call documentBlock file file.document %}
|
||||||
|
{% endmacro %}
|
||||||
|
{% macro documentBlock file document %}
|
||||||
|
{% set rootType %}{% call typeBlock document.metadata %}{% endset %}
|
||||||
|
{% if document.metadata.type == "Array" %}
|
||||||
|
{{accessModifier}} static let items: {{rootType}} = arrayFromPlist(at: "{% call transformPath file.path %}")
|
||||||
|
{% elif document.metadata.type == "Dictionary" %}
|
||||||
|
private static let _document = PlistDocument(path: "{% call transformPath file.path %}")
|
||||||
|
|
||||||
|
{% for key,value in document.metadata.properties %}
|
||||||
|
{{accessModifier}} {% call propertyBlock key value %}
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
// Unsupported root type `{{rootType}}`
|
||||||
|
{% endif %}
|
||||||
|
{% endmacro %}
|
||||||
|
{% macro typeBlock metadata %}{% filter removeNewlines:"leading" %}
|
||||||
|
{% if metadata.type == "Array" %}
|
||||||
|
[{% call typeBlock metadata.element %}]
|
||||||
|
{% elif metadata.type == "Dictionary" %}
|
||||||
|
[String: Any]
|
||||||
|
{% else %}
|
||||||
|
{{metadata.type}}
|
||||||
|
{% endif %}
|
||||||
|
{% endfilter %}{% endmacro %}
|
||||||
|
{% macro propertyBlock key metadata %}{% filter removeNewlines:"leading" %}
|
||||||
|
{% set propertyName %}{{key|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}}{% endset %}
|
||||||
|
{% set propertyType %}{% call typeBlock metadata %}{% endset %}
|
||||||
|
static let {{propertyName}}: {{propertyType}} = _document["{{key}}"]
|
||||||
|
{% endfilter %}{% endmacro %}
|
||||||
|
{% macro transformPath path %}{% filter removeNewlines %}
|
||||||
|
{% if param.preservePath %}
|
||||||
|
{{path}}
|
||||||
|
{% else %}
|
||||||
|
{{path|basename}}
|
||||||
|
{% endif %}
|
||||||
|
{% endfilter %}{% endmacro %}
|
||||||
|
|
||||||
|
// swiftlint:disable identifier_name line_length type_body_length
|
||||||
|
{{accessModifier}} enum {{param.enumName|default:"PlistFiles"}} {
|
||||||
|
{% if files.count > 1 or param.forceFileNameEnum %}
|
||||||
|
{% for file in files %}
|
||||||
|
{{accessModifier}} enum {{file.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} {
|
||||||
|
{% filter indent:2 %}{% call fileBlock file %}{% endfilter %}
|
||||||
|
}
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
{% call fileBlock files.first %}
|
||||||
|
{% endif %}
|
||||||
|
}
|
||||||
|
// swiftlint:enable identifier_name line_length type_body_length
|
||||||
|
|
||||||
|
// MARK: - Implementation Details
|
||||||
|
|
||||||
|
private func arrayFromPlist<T>(at path: String) -> [T] {
|
||||||
|
{% if param.lookupFunction %}
|
||||||
|
guard let url = {{param.lookupFunction}}(path),
|
||||||
|
{% else %}
|
||||||
|
guard let url = {{param.bundle|default:"BundleToken.bundle"}}.url(forResource: path, withExtension: nil),
|
||||||
|
{% endif %}
|
||||||
|
let data = NSArray(contentsOf: url) as? [T] else {
|
||||||
|
fatalError("Unable to load PLIST at path: \(path)")
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct PlistDocument {
|
||||||
|
let data: [String: Any]
|
||||||
|
|
||||||
|
init(path: String) {
|
||||||
|
{% if param.lookupFunction %}
|
||||||
|
guard let url = {{param.lookupFunction}}(path),
|
||||||
|
{% else %}
|
||||||
|
guard let url = {{param.bundle|default:"BundleToken.bundle"}}.url(forResource: path, withExtension: nil),
|
||||||
|
{% endif %}
|
||||||
|
let data = NSDictionary(contentsOf: url) as? [String: Any] else {
|
||||||
|
fatalError("Unable to load PLIST at path: \(path)")
|
||||||
|
}
|
||||||
|
self.data = data
|
||||||
|
}
|
||||||
|
|
||||||
|
subscript<T>(key: String) -> T {
|
||||||
|
guard let result = data[key] as? T else {
|
||||||
|
fatalError("Property '\(key)' is not of type \(T.self)")
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{% if not param.bundle and not param.lookupFunction %}
|
||||||
|
|
||||||
|
// swiftlint:disable convenience_type
|
||||||
|
private final class BundleToken {
|
||||||
|
static let bundle: Bundle = {
|
||||||
|
#if SWIFT_PACKAGE
|
||||||
|
return Bundle.module
|
||||||
|
#else
|
||||||
|
return Bundle(for: BundleToken.self)
|
||||||
|
#endif
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
// swiftlint:enable convenience_type
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
// No files found
|
||||||
|
{% endif %}
|
|
@ -0,0 +1,99 @@
|
||||||
|
// swiftlint:disable all
|
||||||
|
// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen
|
||||||
|
|
||||||
|
{% if tables.count > 0 %}
|
||||||
|
{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %}
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
// swiftlint:disable superfluous_disable_command file_length implicit_return
|
||||||
|
|
||||||
|
// MARK: - Strings
|
||||||
|
|
||||||
|
{% macro parametersBlock types %}{% filter removeNewlines:"leading" %}
|
||||||
|
{% for type in types %}
|
||||||
|
{% if type == "String" %}
|
||||||
|
_ p{{forloop.counter}}: Any
|
||||||
|
{% else %}
|
||||||
|
_ p{{forloop.counter}}: {{type}}
|
||||||
|
{% endif %}
|
||||||
|
{{ ", " if not forloop.last }}
|
||||||
|
{% endfor %}
|
||||||
|
{% endfilter %}{% endmacro %}
|
||||||
|
{% macro argumentsBlock types %}{% filter removeNewlines:"leading" %}
|
||||||
|
{% for type in types %}
|
||||||
|
{% if type == "String" %}
|
||||||
|
String(describing: p{{forloop.counter}})
|
||||||
|
{% elif type == "UnsafeRawPointer" %}
|
||||||
|
Int(bitPattern: p{{forloop.counter}})
|
||||||
|
{% else %}
|
||||||
|
p{{forloop.counter}}
|
||||||
|
{% endif %}
|
||||||
|
{{ ", " if not forloop.last }}
|
||||||
|
{% endfor %}
|
||||||
|
{% endfilter %}{% endmacro %}
|
||||||
|
{% macro recursiveBlock table item %}
|
||||||
|
{% for string in item.strings %}
|
||||||
|
{% if not param.noComments %}
|
||||||
|
{% for line in string.translation|split:"\n" %}
|
||||||
|
/// {{line}}
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
{% if string.types %}
|
||||||
|
{{accessModifier}} static func {{string.key|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}}({% call parametersBlock string.types %}) -> String {
|
||||||
|
return {{enumName}}.tr("{{table}}", "{{string.key}}", {% call argumentsBlock string.types %})
|
||||||
|
}
|
||||||
|
{% elif param.lookupFunction %}
|
||||||
|
{# custom localization function is mostly used for in-app lang selection, so we want the loc to be recomputed at each call for those (hence the computed var) #}
|
||||||
|
{{accessModifier}} static var {{string.key|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}}: String { return {{enumName}}.tr("{{table}}", "{{string.key}}") }
|
||||||
|
{% else %}
|
||||||
|
{{accessModifier}} static let {{string.key|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}} = {{enumName}}.tr("{{table}}", "{{string.key}}")
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% for child in item.children %}
|
||||||
|
{% call recursiveBlock table child %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endmacro %}
|
||||||
|
// swiftlint:disable function_parameter_count identifier_name line_length type_body_length
|
||||||
|
{% set enumName %}{{param.enumName|default:"L10n"}}{% endset %}
|
||||||
|
{{accessModifier}} enum {{enumName}} {
|
||||||
|
{% if tables.count > 1 or param.forceFileNameEnum %}
|
||||||
|
{% for table in tables %}
|
||||||
|
{{accessModifier}} enum {{table.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} {
|
||||||
|
{% filter indent:2 %}{% call recursiveBlock table.name table.levels %}{% endfilter %}
|
||||||
|
}
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
{% call recursiveBlock tables.first.name tables.first.levels %}
|
||||||
|
{% endif %}
|
||||||
|
}
|
||||||
|
// swiftlint:enable function_parameter_count identifier_name line_length type_body_length
|
||||||
|
|
||||||
|
// MARK: - Implementation Details
|
||||||
|
|
||||||
|
extension {{enumName}} {
|
||||||
|
private static func tr(_ table: String, _ key: String, _ args: CVarArg...) -> String {
|
||||||
|
{% if param.lookupFunction %}
|
||||||
|
let format = {{ param.lookupFunction }}(key, table)
|
||||||
|
{% else %}
|
||||||
|
let format = {{param.bundle|default:"BundleToken.bundle"}}.localizedString(forKey: key, value: nil, table: table)
|
||||||
|
{% endif %}
|
||||||
|
return String(format: format, locale: Locale.current, arguments: args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{% if not param.bundle and not param.lookupFunction %}
|
||||||
|
|
||||||
|
// swiftlint:disable convenience_type
|
||||||
|
private final class BundleToken {
|
||||||
|
static let bundle: Bundle = {
|
||||||
|
#if SWIFT_PACKAGE
|
||||||
|
return Bundle.module
|
||||||
|
#else
|
||||||
|
return Bundle(for: BundleToken.self)
|
||||||
|
#endif
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
// swiftlint:enable convenience_type
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
// No string found
|
||||||
|
{% endif %}
|
|
@ -0,0 +1,99 @@
|
||||||
|
// swiftlint:disable all
|
||||||
|
// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen
|
||||||
|
|
||||||
|
{% if tables.count > 0 %}
|
||||||
|
{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %}
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
// swiftlint:disable superfluous_disable_command file_length implicit_return
|
||||||
|
|
||||||
|
// MARK: - Strings
|
||||||
|
|
||||||
|
{% macro parametersBlock types %}{% filter removeNewlines:"leading" %}
|
||||||
|
{% for type in types %}
|
||||||
|
{% if type == "String" %}
|
||||||
|
_ p{{forloop.counter}}: Any
|
||||||
|
{% else %}
|
||||||
|
_ p{{forloop.counter}}: {{type}}
|
||||||
|
{% endif %}
|
||||||
|
{{ ", " if not forloop.last }}
|
||||||
|
{% endfor %}
|
||||||
|
{% endfilter %}{% endmacro %}
|
||||||
|
{% macro argumentsBlock types %}{% filter removeNewlines:"leading" %}
|
||||||
|
{% for type in types %}
|
||||||
|
{% if type == "String" %}
|
||||||
|
String(describing: p{{forloop.counter}})
|
||||||
|
{% elif type == "UnsafeRawPointer" %}
|
||||||
|
Int(bitPattern: p{{forloop.counter}})
|
||||||
|
{% else %}
|
||||||
|
p{{forloop.counter}}
|
||||||
|
{% endif %}
|
||||||
|
{{ ", " if not forloop.last }}
|
||||||
|
{% endfor %}
|
||||||
|
{% endfilter %}{% endmacro %}
|
||||||
|
{% macro recursiveBlock table item %}
|
||||||
|
{% for string in item.strings %}
|
||||||
|
{% if not param.noComments %}
|
||||||
|
{% for line in string.translation|split:"\n" %}
|
||||||
|
/// {{line}}
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
{% if string.types %}
|
||||||
|
{{accessModifier}} static func {{string.key|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}}({% call parametersBlock string.types %}) -> String {
|
||||||
|
return {{enumName}}.tr("{{table}}", "{{string.key}}", {% call argumentsBlock string.types %})
|
||||||
|
}
|
||||||
|
{% elif param.lookupFunction %}
|
||||||
|
{# custom localization function is mostly used for in-app lang selection, so we want the loc to be recomputed at each call for those (hence the computed var) #}
|
||||||
|
{{accessModifier}} static var {{string.key|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}}: String { return {{enumName}}.tr("{{table}}", "{{string.key}}") }
|
||||||
|
{% else %}
|
||||||
|
{{accessModifier}} static let {{string.key|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}} = {{enumName}}.tr("{{table}}", "{{string.key}}")
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% for child in item.children %}
|
||||||
|
{% call recursiveBlock table child %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endmacro %}
|
||||||
|
// swiftlint:disable function_parameter_count identifier_name line_length type_body_length
|
||||||
|
{% set enumName %}{{param.enumName|default:"L10n"}}{% endset %}
|
||||||
|
{{accessModifier}} enum {{enumName}} {
|
||||||
|
{% if tables.count > 1 or param.forceFileNameEnum %}
|
||||||
|
{% for table in tables %}
|
||||||
|
{{accessModifier}} enum {{table.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} {
|
||||||
|
{% filter indent:2 %}{% call recursiveBlock table.name table.levels %}{% endfilter %}
|
||||||
|
}
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
{% call recursiveBlock tables.first.name tables.first.levels %}
|
||||||
|
{% endif %}
|
||||||
|
}
|
||||||
|
// swiftlint:enable function_parameter_count identifier_name line_length type_body_length
|
||||||
|
|
||||||
|
// MARK: - Implementation Details
|
||||||
|
|
||||||
|
extension {{enumName}} {
|
||||||
|
private static func tr(_ table: String, _ key: String, _ args: CVarArg...) -> String {
|
||||||
|
{% if param.lookupFunction %}
|
||||||
|
let format = {{ param.lookupFunction }}(key, table)
|
||||||
|
{% else %}
|
||||||
|
let format = {{param.bundle|default:"BundleToken.bundle"}}.localizedString(forKey: key, value: nil, table: table)
|
||||||
|
{% endif %}
|
||||||
|
return String(format: format, locale: Locale.current, arguments: args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{% if not param.bundle and not param.lookupFunction %}
|
||||||
|
|
||||||
|
// swiftlint:disable convenience_type
|
||||||
|
private final class BundleToken {
|
||||||
|
static let bundle: Bundle = {
|
||||||
|
#if SWIFT_PACKAGE
|
||||||
|
return Bundle.module
|
||||||
|
#else
|
||||||
|
return Bundle(for: BundleToken.self)
|
||||||
|
#endif
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
// swiftlint:enable convenience_type
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
// No string found
|
||||||
|
{% endif %}
|
|
@ -0,0 +1,68 @@
|
||||||
|
// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen
|
||||||
|
|
||||||
|
{% if tables.count > 0 %}
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_BEGIN
|
||||||
|
|
||||||
|
{% macro parametersBlock types %}{% filter removeNewlines:"leading" %}
|
||||||
|
{% for type in types %}
|
||||||
|
({% call paramTranslate type %})p{{ forloop.counter }}{{ " :" if not forloop.last }}
|
||||||
|
{% endfor %}
|
||||||
|
{% endfilter %}{% endmacro %}
|
||||||
|
{% macro argumentsBlock types %}{% filter removeNewlines:"leading" %}
|
||||||
|
{% for type in types %}
|
||||||
|
p{{forloop.counter}}{{ ", " if not forloop.last }}
|
||||||
|
{% endfor %}
|
||||||
|
{% endfilter %}{% endmacro %}
|
||||||
|
{% macro paramTranslate swiftType %}
|
||||||
|
{% if swiftType == "Any" %}
|
||||||
|
id
|
||||||
|
{% elif swiftType == "CChar" %}
|
||||||
|
char
|
||||||
|
{% elif swiftType == "Float" %}
|
||||||
|
float
|
||||||
|
{% elif swiftType == "Int" %}
|
||||||
|
NSInteger
|
||||||
|
{% elif swiftType == "String" %}
|
||||||
|
id
|
||||||
|
{% elif swiftType == "UnsafePointer<CChar>" %}
|
||||||
|
char*
|
||||||
|
{% elif swiftType == "UnsafeRawPointer" %}
|
||||||
|
void*
|
||||||
|
{% else %}
|
||||||
|
objc-h.stencil is missing '{{swiftType}}'
|
||||||
|
{% endif %}
|
||||||
|
{% endmacro %}
|
||||||
|
{% macro emitOneMethod table item %}
|
||||||
|
{% for string in item.strings %}
|
||||||
|
{% if not param.noComments %}
|
||||||
|
{% for line in string.translation|split:"\n" %}
|
||||||
|
/// {{line}}
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
{% if string.types %}
|
||||||
|
{% if string.types.count == 1 %}
|
||||||
|
+ (NSString*){{string.key|swiftIdentifier:"pretty"|lowerFirstWord}}WithValue:{% call parametersBlock string.types %};
|
||||||
|
{% else %}
|
||||||
|
+ (NSString*){{string.key|swiftIdentifier:"pretty"|lowerFirstWord}}WithValues:{% call parametersBlock string.types %};
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
+ (NSString*){{string.key|swiftIdentifier:"pretty"|lowerFirstWord}};
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% for child in item.children %}
|
||||||
|
{% call emitOneMethod table child %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endmacro %}
|
||||||
|
{% for table in tables %}
|
||||||
|
@interface {{ table.name }} : NSObject
|
||||||
|
{% call emitOneMethod table.name table.levels %}
|
||||||
|
@end
|
||||||
|
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
NS_ASSUME_NONNULL_END
|
||||||
|
{% else %}
|
||||||
|
// No strings found
|
||||||
|
{% endif %}
|
|
@ -0,0 +1,90 @@
|
||||||
|
// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen
|
||||||
|
|
||||||
|
{% if tables.count > 0 %}
|
||||||
|
#import "{{ param.headerName|default:"Localizable.h" }}"
|
||||||
|
{% if not param.bundle %}
|
||||||
|
|
||||||
|
@interface BundleToken : NSObject
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation BundleToken
|
||||||
|
@end
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wformat-security"
|
||||||
|
|
||||||
|
static NSString* tr(NSString *tableName, NSString *key, ...) {
|
||||||
|
NSBundle *bundle = {{param.bundle|default:"[NSBundle bundleForClass:BundleToken.class]"}};
|
||||||
|
NSString *format = [bundle localizedStringForKey:key value:nil table:tableName];
|
||||||
|
NSLocale *locale = [NSLocale currentLocale];
|
||||||
|
|
||||||
|
va_list args;
|
||||||
|
va_start(args, key);
|
||||||
|
NSString *result = [[NSString alloc] initWithFormat:format locale:locale arguments:args];
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
return result;
|
||||||
|
};
|
||||||
|
#pragma clang diagnostic pop
|
||||||
|
|
||||||
|
{% macro parametersBlock types %}{% filter removeNewlines:"leading" %}
|
||||||
|
{% for type in types %}
|
||||||
|
({% call paramTranslate type %})p{{ forloop.counter }}{{ " :" if not forloop.last }}
|
||||||
|
{% endfor %}
|
||||||
|
{% endfilter %}{% endmacro %}
|
||||||
|
{% macro argumentsBlock types %}{% filter removeNewlines:"leading" %}
|
||||||
|
{% for type in types %}
|
||||||
|
p{{forloop.counter}}{{ ", " if not forloop.last }}
|
||||||
|
{% endfor %}
|
||||||
|
{% endfilter %}{% endmacro %}
|
||||||
|
{% macro paramTranslate swiftType %}
|
||||||
|
{% if swiftType == "Any" %}
|
||||||
|
id
|
||||||
|
{% elif swiftType == "CChar" %}
|
||||||
|
char
|
||||||
|
{% elif swiftType == "Float" %}
|
||||||
|
float
|
||||||
|
{% elif swiftType == "Int" %}
|
||||||
|
NSInteger
|
||||||
|
{% elif swiftType == "String" %}
|
||||||
|
id
|
||||||
|
{% elif swiftType == "UnsafePointer<CChar>" %}
|
||||||
|
char*
|
||||||
|
{% elif swiftType == "UnsafeRawPointer" %}
|
||||||
|
void*
|
||||||
|
{% else %}
|
||||||
|
objc-m.stencil is missing '{{swiftType}}'
|
||||||
|
{% endif %}
|
||||||
|
{% endmacro %}
|
||||||
|
{% macro tableContents table item %}
|
||||||
|
{% for string in item.strings %}
|
||||||
|
{% if string.types %}
|
||||||
|
{% if string.types.count == 1 %}
|
||||||
|
+ (NSString*){{string.key|swiftIdentifier:"pretty"|lowerFirstWord}}WithValue:{% call parametersBlock string.types %}
|
||||||
|
{% else %}
|
||||||
|
+ (NSString*){{string.key|swiftIdentifier:"pretty"|lowerFirstWord}}WithValues:{% call parametersBlock string.types %}
|
||||||
|
{% endif %}
|
||||||
|
{
|
||||||
|
return tr(@"{{table}}", @"{{string.key}}", {% call argumentsBlock string.types %});
|
||||||
|
}
|
||||||
|
{% else %}
|
||||||
|
+ (NSString*){{string.key|swiftIdentifier:"pretty"|lowerFirstWord}} {
|
||||||
|
return tr(@"{{table}}", @"{{string.key}}");
|
||||||
|
}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% for child in item.children %}
|
||||||
|
{% call tableContents table child %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endmacro %}
|
||||||
|
{% for table in tables %}
|
||||||
|
{% set tableName %}{{table.name|default:"Localized"}}{% endset %}
|
||||||
|
@implementation {{ tableName }} : NSObject
|
||||||
|
{% call tableContents table.name table.levels %}
|
||||||
|
@end
|
||||||
|
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
// No strings found
|
||||||
|
{% endif %}
|
|
@ -0,0 +1,104 @@
|
||||||
|
// swiftlint:disable all
|
||||||
|
// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen
|
||||||
|
|
||||||
|
{% if tables.count > 0 %}
|
||||||
|
{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %}
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
// swiftlint:disable superfluous_disable_command file_length implicit_return
|
||||||
|
|
||||||
|
// MARK: - Strings
|
||||||
|
|
||||||
|
{% macro parametersBlock types %}{% filter removeNewlines:"leading" %}
|
||||||
|
{% for type in types %}
|
||||||
|
{% if type == "String" %}
|
||||||
|
_ p{{forloop.counter}}: Any
|
||||||
|
{% else %}
|
||||||
|
_ p{{forloop.counter}}: {{type}}
|
||||||
|
{% endif %}
|
||||||
|
{{ ", " if not forloop.last }}
|
||||||
|
{% endfor %}
|
||||||
|
{% endfilter %}{% endmacro %}
|
||||||
|
{% macro argumentsBlock types %}{% filter removeNewlines:"leading" %}
|
||||||
|
{% for type in types %}
|
||||||
|
{% if type == "String" %}
|
||||||
|
String(describing: p{{forloop.counter}})
|
||||||
|
{% elif type == "UnsafeRawPointer" %}
|
||||||
|
Int(bitPattern: p{{forloop.counter}})
|
||||||
|
{% else %}
|
||||||
|
p{{forloop.counter}}
|
||||||
|
{% endif %}
|
||||||
|
{{ ", " if not forloop.last }}
|
||||||
|
{% endfor %}
|
||||||
|
{% endfilter %}{% endmacro %}
|
||||||
|
{% macro recursiveBlock table item %}
|
||||||
|
{% for string in item.strings %}
|
||||||
|
{% if not param.noComments %}
|
||||||
|
{% for line in string.translation|split:"\n" %}
|
||||||
|
/// {{line}}
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
{% if string.types %}
|
||||||
|
{{accessModifier}} static func {{string.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}}({% call parametersBlock string.types %}) -> String {
|
||||||
|
return {{enumName}}.tr("{{table}}", "{{string.key}}", {% call argumentsBlock string.types %})
|
||||||
|
}
|
||||||
|
{% elif param.lookupFunction %}
|
||||||
|
{# custom localization function is mostly used for in-app lang selection, so we want the loc to be recomputed at each call for those (hence the computed var) #}
|
||||||
|
{{accessModifier}} static var {{string.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}}: String { return {{enumName}}.tr("{{table}}", "{{string.key}}") }
|
||||||
|
{% else %}
|
||||||
|
{{accessModifier}} static let {{string.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}} = {{enumName}}.tr("{{table}}", "{{string.key}}")
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% for child in item.children %}
|
||||||
|
|
||||||
|
{{accessModifier}} enum {{child.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} {
|
||||||
|
{% filter indent:2 %}{% call recursiveBlock table child %}{% endfilter %}
|
||||||
|
}
|
||||||
|
{% endfor %}
|
||||||
|
{% endmacro %}
|
||||||
|
// swiftlint:disable explicit_type_interface function_parameter_count identifier_name line_length
|
||||||
|
// swiftlint:disable nesting type_body_length type_name vertical_whitespace_opening_braces
|
||||||
|
{% set enumName %}{{param.enumName|default:"L10n"}}{% endset %}
|
||||||
|
{{accessModifier}} enum {{enumName}} {
|
||||||
|
{% if tables.count > 1 or param.forceFileNameEnum %}
|
||||||
|
{% for table in tables %}
|
||||||
|
{{accessModifier}} enum {{table.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} {
|
||||||
|
{% filter indent:2 %}{% call recursiveBlock table.name table.levels %}{% endfilter %}
|
||||||
|
}
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
{% call recursiveBlock tables.first.name tables.first.levels %}
|
||||||
|
{% endif %}
|
||||||
|
}
|
||||||
|
// swiftlint:enable explicit_type_interface function_parameter_count identifier_name line_length
|
||||||
|
// swiftlint:enable nesting type_body_length type_name vertical_whitespace_opening_braces
|
||||||
|
|
||||||
|
// MARK: - Implementation Details
|
||||||
|
|
||||||
|
extension {{enumName}} {
|
||||||
|
private static func tr(_ table: String, _ key: String, _ args: CVarArg...) -> String {
|
||||||
|
{% if param.lookupFunction %}
|
||||||
|
let format = {{ param.lookupFunction }}(key, table)
|
||||||
|
{% else %}
|
||||||
|
let format = {{param.bundle|default:"BundleToken.bundle"}}.localizedString(forKey: key, value: nil, table: table)
|
||||||
|
{% endif %}
|
||||||
|
return String(format: format, locale: Locale.current, arguments: args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{% if not param.bundle and not param.lookupFunction %}
|
||||||
|
|
||||||
|
// swiftlint:disable convenience_type
|
||||||
|
private final class BundleToken {
|
||||||
|
static let bundle: Bundle = {
|
||||||
|
#if SWIFT_PACKAGE
|
||||||
|
return Bundle.module
|
||||||
|
#else
|
||||||
|
return Bundle(for: BundleToken.self)
|
||||||
|
#endif
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
// swiftlint:enable convenience_type
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
// No string found
|
||||||
|
{% endif %}
|
|
@ -0,0 +1,104 @@
|
||||||
|
// swiftlint:disable all
|
||||||
|
// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen
|
||||||
|
|
||||||
|
{% if tables.count > 0 %}
|
||||||
|
{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %}
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
// swiftlint:disable superfluous_disable_command file_length implicit_return
|
||||||
|
|
||||||
|
// MARK: - Strings
|
||||||
|
|
||||||
|
{% macro parametersBlock types %}{% filter removeNewlines:"leading" %}
|
||||||
|
{% for type in types %}
|
||||||
|
{% if type == "String" %}
|
||||||
|
_ p{{forloop.counter}}: Any
|
||||||
|
{% else %}
|
||||||
|
_ p{{forloop.counter}}: {{type}}
|
||||||
|
{% endif %}
|
||||||
|
{{ ", " if not forloop.last }}
|
||||||
|
{% endfor %}
|
||||||
|
{% endfilter %}{% endmacro %}
|
||||||
|
{% macro argumentsBlock types %}{% filter removeNewlines:"leading" %}
|
||||||
|
{% for type in types %}
|
||||||
|
{% if type == "String" %}
|
||||||
|
String(describing: p{{forloop.counter}})
|
||||||
|
{% elif type == "UnsafeRawPointer" %}
|
||||||
|
Int(bitPattern: p{{forloop.counter}})
|
||||||
|
{% else %}
|
||||||
|
p{{forloop.counter}}
|
||||||
|
{% endif %}
|
||||||
|
{{ ", " if not forloop.last }}
|
||||||
|
{% endfor %}
|
||||||
|
{% endfilter %}{% endmacro %}
|
||||||
|
{% macro recursiveBlock table item %}
|
||||||
|
{% for string in item.strings %}
|
||||||
|
{% if not param.noComments %}
|
||||||
|
{% for line in string.translation|split:"\n" %}
|
||||||
|
/// {{line}}
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
{% if string.types %}
|
||||||
|
{{accessModifier}} static func {{string.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}}({% call parametersBlock string.types %}) -> String {
|
||||||
|
return {{enumName}}.tr("{{table}}", "{{string.key}}", {% call argumentsBlock string.types %})
|
||||||
|
}
|
||||||
|
{% elif param.lookupFunction %}
|
||||||
|
{# custom localization function is mostly used for in-app lang selection, so we want the loc to be recomputed at each call for those (hence the computed var) #}
|
||||||
|
{{accessModifier}} static var {{string.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}}: String { return {{enumName}}.tr("{{table}}", "{{string.key}}") }
|
||||||
|
{% else %}
|
||||||
|
{{accessModifier}} static let {{string.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}} = {{enumName}}.tr("{{table}}", "{{string.key}}")
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% for child in item.children %}
|
||||||
|
|
||||||
|
{{accessModifier}} enum {{child.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} {
|
||||||
|
{% filter indent:2 %}{% call recursiveBlock table child %}{% endfilter %}
|
||||||
|
}
|
||||||
|
{% endfor %}
|
||||||
|
{% endmacro %}
|
||||||
|
// swiftlint:disable explicit_type_interface function_parameter_count identifier_name line_length
|
||||||
|
// swiftlint:disable nesting type_body_length type_name vertical_whitespace_opening_braces
|
||||||
|
{% set enumName %}{{param.enumName|default:"L10n"}}{% endset %}
|
||||||
|
{{accessModifier}} enum {{enumName}} {
|
||||||
|
{% if tables.count > 1 or param.forceFileNameEnum %}
|
||||||
|
{% for table in tables %}
|
||||||
|
{{accessModifier}} enum {{table.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} {
|
||||||
|
{% filter indent:2 %}{% call recursiveBlock table.name table.levels %}{% endfilter %}
|
||||||
|
}
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
{% call recursiveBlock tables.first.name tables.first.levels %}
|
||||||
|
{% endif %}
|
||||||
|
}
|
||||||
|
// swiftlint:enable explicit_type_interface function_parameter_count identifier_name line_length
|
||||||
|
// swiftlint:enable nesting type_body_length type_name vertical_whitespace_opening_braces
|
||||||
|
|
||||||
|
// MARK: - Implementation Details
|
||||||
|
|
||||||
|
extension {{enumName}} {
|
||||||
|
private static func tr(_ table: String, _ key: String, _ args: CVarArg...) -> String {
|
||||||
|
{% if param.lookupFunction %}
|
||||||
|
let format = {{ param.lookupFunction }}(key, table)
|
||||||
|
{% else %}
|
||||||
|
let format = {{param.bundle|default:"BundleToken.bundle"}}.localizedString(forKey: key, value: nil, table: table)
|
||||||
|
{% endif %}
|
||||||
|
return String(format: format, locale: Locale.current, arguments: args)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{% if not param.bundle and not param.lookupFunction %}
|
||||||
|
|
||||||
|
// swiftlint:disable convenience_type
|
||||||
|
private final class BundleToken {
|
||||||
|
static let bundle: Bundle = {
|
||||||
|
#if SWIFT_PACKAGE
|
||||||
|
return Bundle.module
|
||||||
|
#else
|
||||||
|
return Bundle(for: BundleToken.self)
|
||||||
|
#endif
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
// swiftlint:enable convenience_type
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
// No string found
|
||||||
|
{% endif %}
|
|
@ -0,0 +1,329 @@
|
||||||
|
// swiftlint:disable all
|
||||||
|
// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen
|
||||||
|
|
||||||
|
{% if catalogs %}
|
||||||
|
{% set enumName %}{{param.enumName|default:"Asset"}}{% endset %}
|
||||||
|
{% set arResourceGroupType %}{{param.arResourceGroupTypeName|default:"ARResourceGroupAsset"}}{% endset %}
|
||||||
|
{% set colorType %}{{param.colorTypeName|default:"ColorAsset"}}{% endset %}
|
||||||
|
{% set dataType %}{{param.dataTypeName|default:"DataAsset"}}{% endset %}
|
||||||
|
{% set imageType %}{{param.imageTypeName|default:"ImageAsset"}}{% endset %}
|
||||||
|
{% set symbolType %}{{param.symbolTypeName|default:"SymbolAsset"}}{% endset %}
|
||||||
|
{% set forceNamespaces %}{{param.forceProvidesNamespaces|default:"false"}}{% endset %}
|
||||||
|
{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %}
|
||||||
|
#if os(macOS)
|
||||||
|
import AppKit
|
||||||
|
#elseif os(iOS)
|
||||||
|
{% if resourceCount.arresourcegroup > 0 %}
|
||||||
|
import ARKit
|
||||||
|
{% endif %}
|
||||||
|
import UIKit
|
||||||
|
#elseif os(tvOS) || os(watchOS)
|
||||||
|
import UIKit
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Deprecated typealiases
|
||||||
|
{% if resourceCount.color > 0 %}
|
||||||
|
@available(*, deprecated, renamed: "{{colorType}}.Color", message: "This typealias will be removed in SwiftGen 7.0")
|
||||||
|
{{accessModifier}} typealias {{param.colorAliasName|default:"AssetColorTypeAlias"}} = {{colorType}}.Color
|
||||||
|
{% endif %}
|
||||||
|
{% if resourceCount.image > 0 %}
|
||||||
|
@available(*, deprecated, renamed: "{{imageType}}.Image", message: "This typealias will be removed in SwiftGen 7.0")
|
||||||
|
{{accessModifier}} typealias {{param.imageAliasName|default:"AssetImageTypeAlias"}} = {{imageType}}.Image
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
// swiftlint:disable superfluous_disable_command file_length implicit_return
|
||||||
|
|
||||||
|
// MARK: - Asset Catalogs
|
||||||
|
|
||||||
|
{% macro enumBlock assets %}
|
||||||
|
{% call casesBlock assets %}
|
||||||
|
{% if param.allValues %}
|
||||||
|
|
||||||
|
// swiftlint:disable trailing_comma
|
||||||
|
{% if resourceCount.arresourcegroup > 0 %}
|
||||||
|
{{accessModifier}} static let allResourceGroups: [{{arResourceGroupType}}] = [
|
||||||
|
{% filter indent:2 %}{% call allValuesBlock assets "arresourcegroup" "" %}{% endfilter %}
|
||||||
|
]
|
||||||
|
{% endif %}
|
||||||
|
{% if resourceCount.color > 0 %}
|
||||||
|
{{accessModifier}} static let allColors: [{{colorType}}] = [
|
||||||
|
{% filter indent:2 %}{% call allValuesBlock assets "color" "" %}{% endfilter %}
|
||||||
|
]
|
||||||
|
{% endif %}
|
||||||
|
{% if resourceCount.data > 0 %}
|
||||||
|
{{accessModifier}} static let allDataAssets: [{{dataType}}] = [
|
||||||
|
{% filter indent:2 %}{% call allValuesBlock assets "data" "" %}{% endfilter %}
|
||||||
|
]
|
||||||
|
{% endif %}
|
||||||
|
{% if resourceCount.image > 0 %}
|
||||||
|
{{accessModifier}} static let allImages: [{{imageType}}] = [
|
||||||
|
{% filter indent:2 %}{% call allValuesBlock assets "image" "" %}{% endfilter %}
|
||||||
|
]
|
||||||
|
{% endif %}
|
||||||
|
{% if resourceCount.symbol > 0 %}
|
||||||
|
{{accessModifier}} static let allSymbols: [{{symbolType}}] = [
|
||||||
|
{% filter indent:2 %}{% call allValuesBlock assets "symbol" "" %}{% endfilter %}
|
||||||
|
]
|
||||||
|
{% endif %}
|
||||||
|
// swiftlint:enable trailing_comma
|
||||||
|
{% endif %}
|
||||||
|
{% endmacro %}
|
||||||
|
{% macro casesBlock assets %}
|
||||||
|
{% for asset in assets %}
|
||||||
|
{% if asset.type == "arresourcegroup" %}
|
||||||
|
{{accessModifier}} static let {{asset.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}} = {{arResourceGroupType}}(name: "{{asset.value}}")
|
||||||
|
{% elif asset.type == "color" %}
|
||||||
|
{{accessModifier}} static let {{asset.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}} = {{colorType}}(name: "{{asset.value}}")
|
||||||
|
{% elif asset.type == "data" %}
|
||||||
|
{{accessModifier}} static let {{asset.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}} = {{dataType}}(name: "{{asset.value}}")
|
||||||
|
{% elif asset.type == "image" %}
|
||||||
|
{{accessModifier}} static let {{asset.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}} = {{imageType}}(name: "{{asset.value}}")
|
||||||
|
{% elif asset.type == "symbol" %}
|
||||||
|
{{accessModifier}} static let {{asset.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}} = {{symbolType}}(name: "{{asset.value}}")
|
||||||
|
{% elif asset.items and ( forceNamespaces == "true" or asset.isNamespaced == "true" ) %}
|
||||||
|
{{accessModifier}} enum {{asset.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} {
|
||||||
|
{% filter indent:2 %}{% call casesBlock asset.items %}{% endfilter %}
|
||||||
|
}
|
||||||
|
{% elif asset.items %}
|
||||||
|
{% call casesBlock asset.items %}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endmacro %}
|
||||||
|
{% macro allValuesBlock assets filter prefix %}
|
||||||
|
{% for asset in assets %}
|
||||||
|
{% if asset.type == filter %}
|
||||||
|
{{prefix}}{{asset.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}},
|
||||||
|
{% elif asset.items and ( forceNamespaces == "true" or asset.isNamespaced == "true" ) %}
|
||||||
|
{% set prefix2 %}{{prefix}}{{asset.name|swiftIdentifier:"pretty"|escapeReservedKeywords}}.{% endset %}
|
||||||
|
{% call allValuesBlock asset.items filter prefix2 %}
|
||||||
|
{% elif asset.items %}
|
||||||
|
{% call allValuesBlock asset.items filter prefix %}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endmacro %}
|
||||||
|
// swiftlint:disable identifier_name line_length nesting type_body_length type_name
|
||||||
|
{{accessModifier}} enum {{enumName}} {
|
||||||
|
{% if catalogs.count > 1 or param.forceFileNameEnum %}
|
||||||
|
{% for catalog in catalogs %}
|
||||||
|
{{accessModifier}} enum {{catalog.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} {
|
||||||
|
{% filter indent:2 %}{% call enumBlock catalog.assets %}{% endfilter %}
|
||||||
|
}
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
{% call enumBlock catalogs.first.assets %}
|
||||||
|
{% endif %}
|
||||||
|
}
|
||||||
|
// swiftlint:enable identifier_name line_length nesting type_body_length type_name
|
||||||
|
|
||||||
|
// MARK: - Implementation Details
|
||||||
|
{% if resourceCount.arresourcegroup > 0 %}
|
||||||
|
|
||||||
|
{{accessModifier}} struct {{arResourceGroupType}} {
|
||||||
|
{{accessModifier}} fileprivate(set) var name: String
|
||||||
|
|
||||||
|
#if os(iOS)
|
||||||
|
@available(iOS 11.3, *)
|
||||||
|
{{accessModifier}} var referenceImages: Set<ARReferenceImage> {
|
||||||
|
return ARReferenceImage.referenceImages(in: self)
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(iOS 12.0, *)
|
||||||
|
{{accessModifier}} var referenceObjects: Set<ARReferenceObject> {
|
||||||
|
return ARReferenceObject.referenceObjects(in: self)
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#if os(iOS)
|
||||||
|
@available(iOS 11.3, *)
|
||||||
|
{{accessModifier}} extension ARReferenceImage {
|
||||||
|
static func referenceImages(in asset: {{arResourceGroupType}}) -> Set<ARReferenceImage> {
|
||||||
|
let bundle = {{param.bundle|default:"BundleToken.bundle"}}
|
||||||
|
return referenceImages(inGroupNamed: asset.name, bundle: bundle) ?? Set()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(iOS 12.0, *)
|
||||||
|
{{accessModifier}} extension ARReferenceObject {
|
||||||
|
static func referenceObjects(in asset: {{arResourceGroupType}}) -> Set<ARReferenceObject> {
|
||||||
|
let bundle = {{param.bundle|default:"BundleToken.bundle"}}
|
||||||
|
return referenceObjects(inGroupNamed: asset.name, bundle: bundle) ?? Set()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
{% endif %}
|
||||||
|
{% if resourceCount.color > 0 %}
|
||||||
|
|
||||||
|
{{accessModifier}} final class {{colorType}} {
|
||||||
|
{{accessModifier}} fileprivate(set) var name: String
|
||||||
|
|
||||||
|
#if os(macOS)
|
||||||
|
{{accessModifier}} typealias Color = NSColor
|
||||||
|
#elseif os(iOS) || os(tvOS) || os(watchOS)
|
||||||
|
{{accessModifier}} typealias Color = UIColor
|
||||||
|
#endif
|
||||||
|
|
||||||
|
@available(iOS 11.0, tvOS 11.0, watchOS 4.0, macOS 10.13, *)
|
||||||
|
{{accessModifier}} private(set) lazy var color: Color = Color(asset: self)
|
||||||
|
|
||||||
|
#if os(iOS) || os(tvOS)
|
||||||
|
@available(iOS 11.0, tvOS 11.0, *)
|
||||||
|
{{accessModifier}} func color(compatibleWith traitCollection: UITraitCollection) -> Color {
|
||||||
|
let bundle = {{param.bundle|default:"BundleToken.bundle"}}
|
||||||
|
guard let color = Color(named: name, in: bundle, compatibleWith: traitCollection) else {
|
||||||
|
fatalError("Unable to load color asset named \(name).")
|
||||||
|
}
|
||||||
|
return color
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
fileprivate init(name: String) {
|
||||||
|
self.name = name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{{accessModifier}} extension {{colorType}}.Color {
|
||||||
|
@available(iOS 11.0, tvOS 11.0, watchOS 4.0, macOS 10.13, *)
|
||||||
|
convenience init!(asset: {{colorType}}) {
|
||||||
|
let bundle = {{param.bundle|default:"BundleToken.bundle"}}
|
||||||
|
#if os(iOS) || os(tvOS)
|
||||||
|
self.init(named: asset.name, in: bundle, compatibleWith: nil)
|
||||||
|
#elseif os(macOS)
|
||||||
|
self.init(named: NSColor.Name(asset.name), bundle: bundle)
|
||||||
|
#elseif os(watchOS)
|
||||||
|
self.init(named: asset.name)
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{% endif %}
|
||||||
|
{% if resourceCount.data > 0 %}
|
||||||
|
|
||||||
|
{{accessModifier}} struct {{dataType}} {
|
||||||
|
{{accessModifier}} fileprivate(set) var name: String
|
||||||
|
|
||||||
|
@available(iOS 9.0, tvOS 9.0, watchOS 6.0, macOS 10.11, *)
|
||||||
|
{{accessModifier}} var data: NSDataAsset {
|
||||||
|
return NSDataAsset(asset: self)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(iOS 9.0, tvOS 9.0, watchOS 6.0, macOS 10.11, *)
|
||||||
|
{{accessModifier}} extension NSDataAsset {
|
||||||
|
convenience init!(asset: {{dataType}}) {
|
||||||
|
let bundle = {{param.bundle|default:"BundleToken.bundle"}}
|
||||||
|
#if os(iOS) || os(tvOS) || os(watchOS)
|
||||||
|
self.init(name: asset.name, bundle: bundle)
|
||||||
|
#elseif os(macOS)
|
||||||
|
self.init(name: NSDataAsset.Name(asset.name), bundle: bundle)
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{% endif %}
|
||||||
|
{% if resourceCount.image > 0 %}
|
||||||
|
|
||||||
|
{{accessModifier}} struct {{imageType}} {
|
||||||
|
{{accessModifier}} fileprivate(set) var name: String
|
||||||
|
|
||||||
|
#if os(macOS)
|
||||||
|
{{accessModifier}} typealias Image = NSImage
|
||||||
|
#elseif os(iOS) || os(tvOS) || os(watchOS)
|
||||||
|
{{accessModifier}} typealias Image = UIImage
|
||||||
|
#endif
|
||||||
|
|
||||||
|
@available(iOS 8.0, tvOS 9.0, watchOS 2.0, macOS 10.7, *)
|
||||||
|
{{accessModifier}} var image: Image {
|
||||||
|
let bundle = {{param.bundle|default:"BundleToken.bundle"}}
|
||||||
|
#if os(iOS) || os(tvOS)
|
||||||
|
let image = Image(named: name, in: bundle, compatibleWith: nil)
|
||||||
|
#elseif os(macOS)
|
||||||
|
let name = NSImage.Name(self.name)
|
||||||
|
let image = (bundle == .main) ? NSImage(named: name) : bundle.image(forResource: name)
|
||||||
|
#elseif os(watchOS)
|
||||||
|
let image = Image(named: name)
|
||||||
|
#endif
|
||||||
|
guard let result = image else {
|
||||||
|
fatalError("Unable to load image asset named \(name).")
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
#if os(iOS) || os(tvOS)
|
||||||
|
@available(iOS 8.0, tvOS 9.0, *)
|
||||||
|
{{accessModifier}} func image(compatibleWith traitCollection: UITraitCollection) -> Image {
|
||||||
|
let bundle = {{param.bundle|default:"BundleToken.bundle"}}
|
||||||
|
guard let result = Image(named: name, in: bundle, compatibleWith: traitCollection) else {
|
||||||
|
fatalError("Unable to load image asset named \(name).")
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
{{accessModifier}} extension {{imageType}}.Image {
|
||||||
|
@available(iOS 8.0, tvOS 9.0, watchOS 2.0, *)
|
||||||
|
@available(macOS, deprecated,
|
||||||
|
message: "This initializer is unsafe on macOS, please use the {{imageType}}.image property")
|
||||||
|
convenience init!(asset: {{imageType}}) {
|
||||||
|
#if os(iOS) || os(tvOS)
|
||||||
|
let bundle = {{param.bundle|default:"BundleToken.bundle"}}
|
||||||
|
self.init(named: asset.name, in: bundle, compatibleWith: nil)
|
||||||
|
#elseif os(macOS)
|
||||||
|
self.init(named: NSImage.Name(asset.name))
|
||||||
|
#elseif os(watchOS)
|
||||||
|
self.init(named: asset.name)
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{% endif %}
|
||||||
|
{% if resourceCount.symbol > 0 %}
|
||||||
|
|
||||||
|
{{accessModifier}} struct {{symbolType}} {
|
||||||
|
{{accessModifier}} fileprivate(set) var name: String
|
||||||
|
|
||||||
|
#if os(iOS) || os(tvOS) || os(watchOS)
|
||||||
|
@available(iOS 13.0, tvOS 13.0, watchOS 6.0, *)
|
||||||
|
{{accessModifier}} typealias Configuration = UIImage.SymbolConfiguration
|
||||||
|
{{accessModifier}} typealias Image = UIImage
|
||||||
|
|
||||||
|
@available(iOS 12.0, tvOS 12.0, watchOS 5.0, *)
|
||||||
|
{{accessModifier}} var image: Image {
|
||||||
|
let bundle = {{param.bundle|default:"BundleToken.bundle"}}
|
||||||
|
#if os(iOS) || os(tvOS)
|
||||||
|
let image = Image(named: name, in: bundle, compatibleWith: nil)
|
||||||
|
#elseif os(watchOS)
|
||||||
|
let image = Image(named: name)
|
||||||
|
#endif
|
||||||
|
guard let result = image else {
|
||||||
|
fatalError("Unable to load symbol asset named \(name).")
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(iOS 13.0, tvOS 13.0, watchOS 6.0, *)
|
||||||
|
{{accessModifier}} func image(with configuration: Configuration) -> Image {
|
||||||
|
let bundle = {{param.bundle|default:"BundleToken.bundle"}}
|
||||||
|
guard let result = Image(named: name, in: bundle, with: configuration) else {
|
||||||
|
fatalError("Unable to load symbol asset named \(name).")
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
{% endif %}
|
||||||
|
{% if not param.bundle %}
|
||||||
|
|
||||||
|
// swiftlint:disable convenience_type
|
||||||
|
private final class BundleToken {
|
||||||
|
static let bundle: Bundle = {
|
||||||
|
#if SWIFT_PACKAGE
|
||||||
|
return Bundle.module
|
||||||
|
#else
|
||||||
|
return Bundle(for: BundleToken.self)
|
||||||
|
#endif
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
// swiftlint:enable convenience_type
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
// No assets found
|
||||||
|
{% endif %}
|
|
@ -0,0 +1,337 @@
|
||||||
|
// swiftlint:disable all
|
||||||
|
// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen
|
||||||
|
|
||||||
|
{% if catalogs %}
|
||||||
|
{% set enumName %}{{param.enumName|default:"Asset"}}{% endset %}
|
||||||
|
{% set arResourceGroupType %}{{param.arResourceGroupTypeName|default:"ARResourceGroupAsset"}}{% endset %}
|
||||||
|
{% set colorType %}{{param.colorTypeName|default:"ColorAsset"}}{% endset %}
|
||||||
|
{% set dataType %}{{param.dataTypeName|default:"DataAsset"}}{% endset %}
|
||||||
|
{% set imageType %}{{param.imageTypeName|default:"ImageAsset"}}{% endset %}
|
||||||
|
{% set symbolType %}{{param.symbolTypeName|default:"SymbolAsset"}}{% endset %}
|
||||||
|
{% set forceNamespaces %}{{param.forceProvidesNamespaces|default:"false"}}{% endset %}
|
||||||
|
{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %}
|
||||||
|
#if os(macOS)
|
||||||
|
import AppKit
|
||||||
|
#elseif os(iOS)
|
||||||
|
{% if resourceCount.arresourcegroup > 0 %}
|
||||||
|
import ARKit
|
||||||
|
{% endif %}
|
||||||
|
import UIKit
|
||||||
|
#elseif os(tvOS) || os(watchOS)
|
||||||
|
import UIKit
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Deprecated typealiases
|
||||||
|
{% if resourceCount.color > 0 %}
|
||||||
|
@available(*, deprecated, renamed: "{{colorType}}.Color", message: "This typealias will be removed in SwiftGen 7.0")
|
||||||
|
{{accessModifier}} typealias {{param.colorAliasName|default:"AssetColorTypeAlias"}} = {{colorType}}.Color
|
||||||
|
{% endif %}
|
||||||
|
{% if resourceCount.image > 0 %}
|
||||||
|
@available(*, deprecated, renamed: "{{imageType}}.Image", message: "This typealias will be removed in SwiftGen 7.0")
|
||||||
|
{{accessModifier}} typealias {{param.imageAliasName|default:"AssetImageTypeAlias"}} = {{imageType}}.Image
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
// swiftlint:disable superfluous_disable_command file_length implicit_return
|
||||||
|
|
||||||
|
// MARK: - Asset Catalogs
|
||||||
|
|
||||||
|
{% macro enumBlock assets %}
|
||||||
|
{% call casesBlock assets %}
|
||||||
|
{% if param.allValues %}
|
||||||
|
|
||||||
|
// swiftlint:disable trailing_comma
|
||||||
|
{% if resourceCount.arresourcegroup > 0 %}
|
||||||
|
{{accessModifier}} static let allResourceGroups: [{{arResourceGroupType}}] = [
|
||||||
|
{% filter indent:2 %}{% call allValuesBlock assets "arresourcegroup" "" %}{% endfilter %}
|
||||||
|
]
|
||||||
|
{% endif %}
|
||||||
|
{% if resourceCount.color > 0 %}
|
||||||
|
{{accessModifier}} static let allColors: [{{colorType}}] = [
|
||||||
|
{% filter indent:2 %}{% call allValuesBlock assets "color" "" %}{% endfilter %}
|
||||||
|
]
|
||||||
|
{% endif %}
|
||||||
|
{% if resourceCount.data > 0 %}
|
||||||
|
{{accessModifier}} static let allDataAssets: [{{dataType}}] = [
|
||||||
|
{% filter indent:2 %}{% call allValuesBlock assets "data" "" %}{% endfilter %}
|
||||||
|
]
|
||||||
|
{% endif %}
|
||||||
|
{% if resourceCount.image > 0 %}
|
||||||
|
{{accessModifier}} static let allImages: [{{imageType}}] = [
|
||||||
|
{% filter indent:2 %}{% call allValuesBlock assets "image" "" %}{% endfilter %}
|
||||||
|
]
|
||||||
|
{% endif %}
|
||||||
|
{% if resourceCount.symbol > 0 %}
|
||||||
|
{{accessModifier}} static let allSymbols: [{{symbolType}}] = [
|
||||||
|
{% filter indent:2 %}{% call allValuesBlock assets "symbol" "" %}{% endfilter %}
|
||||||
|
]
|
||||||
|
{% endif %}
|
||||||
|
// swiftlint:enable trailing_comma
|
||||||
|
{% endif %}
|
||||||
|
{% endmacro %}
|
||||||
|
{% macro casesBlock assets %}
|
||||||
|
{% for asset in assets %}
|
||||||
|
{% if asset.type == "arresourcegroup" %}
|
||||||
|
{{accessModifier}} static let {{asset.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}} = {{arResourceGroupType}}(name: "{{asset.value}}")
|
||||||
|
{% elif asset.type == "color" %}
|
||||||
|
{{accessModifier}} static let {{asset.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}} = {{colorType}}(name: "{{asset.value}}")
|
||||||
|
{% elif asset.type == "data" %}
|
||||||
|
{{accessModifier}} static let {{asset.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}} = {{dataType}}(name: "{{asset.value}}")
|
||||||
|
{% elif asset.type == "image" %}
|
||||||
|
{{accessModifier}} static let {{asset.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}} = {{imageType}}(name: "{{asset.value}}")
|
||||||
|
{% elif asset.type == "symbol" %}
|
||||||
|
{{accessModifier}} static let {{asset.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}} = {{symbolType}}(name: "{{asset.value}}")
|
||||||
|
{% elif asset.items and ( forceNamespaces == "true" or asset.isNamespaced == "true" ) %}
|
||||||
|
{{accessModifier}} enum {{asset.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} {
|
||||||
|
{% filter indent:2 %}{% call casesBlock asset.items %}{% endfilter %}
|
||||||
|
}
|
||||||
|
{% elif asset.items %}
|
||||||
|
{% call casesBlock asset.items %}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endmacro %}
|
||||||
|
{% macro allValuesBlock assets filter prefix %}
|
||||||
|
{% for asset in assets %}
|
||||||
|
{% if asset.type == filter %}
|
||||||
|
{{prefix}}{{asset.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}},
|
||||||
|
{% elif asset.items and ( forceNamespaces == "true" or asset.isNamespaced == "true" ) %}
|
||||||
|
{% set prefix2 %}{{prefix}}{{asset.name|swiftIdentifier:"pretty"|escapeReservedKeywords}}.{% endset %}
|
||||||
|
{% call allValuesBlock asset.items filter prefix2 %}
|
||||||
|
{% elif asset.items %}
|
||||||
|
{% call allValuesBlock asset.items filter prefix %}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endmacro %}
|
||||||
|
// swiftlint:disable identifier_name line_length nesting type_body_length type_name
|
||||||
|
{{accessModifier}} enum {{enumName}} {
|
||||||
|
{% if catalogs.count > 1 or param.forceFileNameEnum %}
|
||||||
|
{% for catalog in catalogs %}
|
||||||
|
{{accessModifier}} enum {{catalog.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} {
|
||||||
|
{% filter indent:2 %}{% call enumBlock catalog.assets %}{% endfilter %}
|
||||||
|
}
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
{% call enumBlock catalogs.first.assets %}
|
||||||
|
{% endif %}
|
||||||
|
}
|
||||||
|
// swiftlint:enable identifier_name line_length nesting type_body_length type_name
|
||||||
|
|
||||||
|
// MARK: - Implementation Details
|
||||||
|
{% if resourceCount.arresourcegroup > 0 %}
|
||||||
|
|
||||||
|
{{accessModifier}} struct {{arResourceGroupType}} {
|
||||||
|
{{accessModifier}} fileprivate(set) var name: String
|
||||||
|
|
||||||
|
#if os(iOS)
|
||||||
|
@available(iOS 11.3, *)
|
||||||
|
{{accessModifier}} var referenceImages: Set<ARReferenceImage> {
|
||||||
|
return ARReferenceImage.referenceImages(in: self)
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(iOS 12.0, *)
|
||||||
|
{{accessModifier}} var referenceObjects: Set<ARReferenceObject> {
|
||||||
|
return ARReferenceObject.referenceObjects(in: self)
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
#if os(iOS)
|
||||||
|
@available(iOS 11.3, *)
|
||||||
|
{{accessModifier}} extension ARReferenceImage {
|
||||||
|
static func referenceImages(in asset: {{arResourceGroupType}}) -> Set<ARReferenceImage> {
|
||||||
|
let bundle = {{param.bundle|default:"BundleToken.bundle"}}
|
||||||
|
return referenceImages(inGroupNamed: asset.name, bundle: bundle) ?? Set()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(iOS 12.0, *)
|
||||||
|
{{accessModifier}} extension ARReferenceObject {
|
||||||
|
static func referenceObjects(in asset: {{arResourceGroupType}}) -> Set<ARReferenceObject> {
|
||||||
|
let bundle = {{param.bundle|default:"BundleToken.bundle"}}
|
||||||
|
return referenceObjects(inGroupNamed: asset.name, bundle: bundle) ?? Set()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
{% endif %}
|
||||||
|
{% if resourceCount.color > 0 %}
|
||||||
|
|
||||||
|
{{accessModifier}} final class {{colorType}} {
|
||||||
|
{{accessModifier}} fileprivate(set) var name: String
|
||||||
|
|
||||||
|
#if os(macOS)
|
||||||
|
{{accessModifier}} typealias Color = NSColor
|
||||||
|
#elseif os(iOS) || os(tvOS) || os(watchOS)
|
||||||
|
{{accessModifier}} typealias Color = UIColor
|
||||||
|
#endif
|
||||||
|
|
||||||
|
@available(iOS 11.0, tvOS 11.0, watchOS 4.0, macOS 10.13, *)
|
||||||
|
{{accessModifier}} private(set) lazy var color: Color = {
|
||||||
|
guard let color = Color(asset: self) else {
|
||||||
|
fatalError("Unable to load color asset named \(name).")
|
||||||
|
}
|
||||||
|
return color
|
||||||
|
}()
|
||||||
|
|
||||||
|
#if os(iOS) || os(tvOS)
|
||||||
|
@available(iOS 11.0, tvOS 11.0, *)
|
||||||
|
{{accessModifier}} func color(compatibleWith traitCollection: UITraitCollection) -> Color {
|
||||||
|
let bundle = {{param.bundle|default:"BundleToken.bundle"}}
|
||||||
|
guard let color = Color(named: name, in: bundle, compatibleWith: traitCollection) else {
|
||||||
|
fatalError("Unable to load color asset named \(name).")
|
||||||
|
}
|
||||||
|
return color
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
fileprivate init(name: String) {
|
||||||
|
self.name = name
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{{accessModifier}} extension {{colorType}}.Color {
|
||||||
|
@available(iOS 11.0, tvOS 11.0, watchOS 4.0, macOS 10.13, *)
|
||||||
|
convenience init?(asset: {{colorType}}) {
|
||||||
|
let bundle = {{param.bundle|default:"BundleToken.bundle"}}
|
||||||
|
#if os(iOS) || os(tvOS)
|
||||||
|
self.init(named: asset.name, in: bundle, compatibleWith: nil)
|
||||||
|
#elseif os(macOS)
|
||||||
|
self.init(named: NSColor.Name(asset.name), bundle: bundle)
|
||||||
|
#elseif os(watchOS)
|
||||||
|
self.init(named: asset.name)
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{% endif %}
|
||||||
|
{% if resourceCount.data > 0 %}
|
||||||
|
|
||||||
|
{{accessModifier}} struct {{dataType}} {
|
||||||
|
{{accessModifier}} fileprivate(set) var name: String
|
||||||
|
|
||||||
|
@available(iOS 9.0, tvOS 9.0, watchOS 6.0, macOS 10.11, *)
|
||||||
|
{{accessModifier}} var data: NSDataAsset {
|
||||||
|
guard let data = NSDataAsset(asset: self) else {
|
||||||
|
fatalError("Unable to load data asset named \(name).")
|
||||||
|
}
|
||||||
|
return data
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(iOS 9.0, tvOS 9.0, watchOS 6.0, macOS 10.11, *)
|
||||||
|
{{accessModifier}} extension NSDataAsset {
|
||||||
|
convenience init?(asset: {{dataType}}) {
|
||||||
|
let bundle = {{param.bundle|default:"BundleToken.bundle"}}
|
||||||
|
#if os(iOS) || os(tvOS) || os(watchOS)
|
||||||
|
self.init(name: asset.name, bundle: bundle)
|
||||||
|
#elseif os(macOS)
|
||||||
|
self.init(name: NSDataAsset.Name(asset.name), bundle: bundle)
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{% endif %}
|
||||||
|
{% if resourceCount.image > 0 %}
|
||||||
|
|
||||||
|
{{accessModifier}} struct {{imageType}} {
|
||||||
|
{{accessModifier}} fileprivate(set) var name: String
|
||||||
|
|
||||||
|
#if os(macOS)
|
||||||
|
{{accessModifier}} typealias Image = NSImage
|
||||||
|
#elseif os(iOS) || os(tvOS) || os(watchOS)
|
||||||
|
{{accessModifier}} typealias Image = UIImage
|
||||||
|
#endif
|
||||||
|
|
||||||
|
@available(iOS 8.0, tvOS 9.0, watchOS 2.0, macOS 10.7, *)
|
||||||
|
{{accessModifier}} var image: Image {
|
||||||
|
let bundle = {{param.bundle|default:"BundleToken.bundle"}}
|
||||||
|
#if os(iOS) || os(tvOS)
|
||||||
|
let image = Image(named: name, in: bundle, compatibleWith: nil)
|
||||||
|
#elseif os(macOS)
|
||||||
|
let name = NSImage.Name(self.name)
|
||||||
|
let image = (bundle == .main) ? NSImage(named: name) : bundle.image(forResource: name)
|
||||||
|
#elseif os(watchOS)
|
||||||
|
let image = Image(named: name)
|
||||||
|
#endif
|
||||||
|
guard let result = image else {
|
||||||
|
fatalError("Unable to load image asset named \(name).")
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
#if os(iOS) || os(tvOS)
|
||||||
|
@available(iOS 8.0, tvOS 9.0, *)
|
||||||
|
{{accessModifier}} func image(compatibleWith traitCollection: UITraitCollection) -> Image {
|
||||||
|
let bundle = {{param.bundle|default:"BundleToken.bundle"}}
|
||||||
|
guard let result = Image(named: name, in: bundle, compatibleWith: traitCollection) else {
|
||||||
|
fatalError("Unable to load image asset named \(name).")
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
{{accessModifier}} extension {{imageType}}.Image {
|
||||||
|
@available(iOS 8.0, tvOS 9.0, watchOS 2.0, *)
|
||||||
|
@available(macOS, deprecated,
|
||||||
|
message: "This initializer is unsafe on macOS, please use the {{imageType}}.image property")
|
||||||
|
convenience init?(asset: {{imageType}}) {
|
||||||
|
#if os(iOS) || os(tvOS)
|
||||||
|
let bundle = {{param.bundle|default:"BundleToken.bundle"}}
|
||||||
|
self.init(named: asset.name, in: bundle, compatibleWith: nil)
|
||||||
|
#elseif os(macOS)
|
||||||
|
self.init(named: NSImage.Name(asset.name))
|
||||||
|
#elseif os(watchOS)
|
||||||
|
self.init(named: asset.name)
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{% endif %}
|
||||||
|
{% if resourceCount.symbol > 0 %}
|
||||||
|
|
||||||
|
{{accessModifier}} struct {{symbolType}} {
|
||||||
|
{{accessModifier}} fileprivate(set) var name: String
|
||||||
|
|
||||||
|
#if os(iOS) || os(tvOS) || os(watchOS)
|
||||||
|
@available(iOS 13.0, tvOS 13.0, watchOS 6.0, *)
|
||||||
|
{{accessModifier}} typealias Configuration = UIImage.SymbolConfiguration
|
||||||
|
{{accessModifier}} typealias Image = UIImage
|
||||||
|
|
||||||
|
@available(iOS 12.0, tvOS 12.0, watchOS 5.0, *)
|
||||||
|
{{accessModifier}} var image: Image {
|
||||||
|
let bundle = {{param.bundle|default:"BundleToken.bundle"}}
|
||||||
|
#if os(iOS) || os(tvOS)
|
||||||
|
let image = Image(named: name, in: bundle, compatibleWith: nil)
|
||||||
|
#elseif os(watchOS)
|
||||||
|
let image = Image(named: name)
|
||||||
|
#endif
|
||||||
|
guard let result = image else {
|
||||||
|
fatalError("Unable to load symbol asset named \(name).")
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
@available(iOS 13.0, tvOS 13.0, watchOS 6.0, *)
|
||||||
|
{{accessModifier}} func image(with configuration: Configuration) -> Image {
|
||||||
|
let bundle = {{param.bundle|default:"BundleToken.bundle"}}
|
||||||
|
guard let result = Image(named: name, in: bundle, with: configuration) else {
|
||||||
|
fatalError("Unable to load symbol asset named \(name).")
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
{% endif %}
|
||||||
|
{% if not param.bundle %}
|
||||||
|
|
||||||
|
// swiftlint:disable convenience_type
|
||||||
|
private final class BundleToken {
|
||||||
|
static let bundle: Bundle = {
|
||||||
|
#if SWIFT_PACKAGE
|
||||||
|
return Bundle.module
|
||||||
|
#else
|
||||||
|
return Bundle(for: BundleToken.self)
|
||||||
|
#endif
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
// swiftlint:enable convenience_type
|
||||||
|
{% endif %}
|
||||||
|
{% else %}
|
||||||
|
// No assets found
|
||||||
|
{% endif %}
|
|
@ -0,0 +1,92 @@
|
||||||
|
// swiftlint:disable all
|
||||||
|
// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen
|
||||||
|
|
||||||
|
{% if files %}
|
||||||
|
{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %}
|
||||||
|
{% set documentPrefix %}{{param.documentName|default:"Document"}}{% endset %}
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
// swiftlint:disable superfluous_disable_command
|
||||||
|
// swiftlint:disable file_length
|
||||||
|
|
||||||
|
// MARK: - YAML Files
|
||||||
|
{% macro fileBlock file %}
|
||||||
|
{% if file.documents.count > 1 %}
|
||||||
|
{% for document in file.documents %}
|
||||||
|
{% set documentName %}{{documentPrefix}}{{forloop.counter}}{% endset %}
|
||||||
|
{{accessModifier}} enum {{documentName|swiftIdentifier:"pretty"|escapeReservedKeywords}} {
|
||||||
|
{% filter indent:2 %}{% call documentBlock file document %}{% endfilter %}
|
||||||
|
}
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
{% call documentBlock file file.documents.first %}
|
||||||
|
{% endif %}
|
||||||
|
{% endmacro %}
|
||||||
|
{% macro documentBlock file document %}
|
||||||
|
{% set rootType %}{% call typeBlock document.metadata %}{% endset %}
|
||||||
|
{% if document.metadata.type == "Array" %}
|
||||||
|
{{accessModifier}} static let items: {{rootType}} = {% call valueBlock document.data document.metadata %}
|
||||||
|
{% elif document.metadata.type == "Dictionary" %}
|
||||||
|
{% for key,value in document.metadata.properties %}
|
||||||
|
{{accessModifier}} {% call propertyBlock key value document.data %}
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
{{accessModifier}} static let value: {{rootType}} = {% call valueBlock document.data document.metadata %}
|
||||||
|
{% endif %}
|
||||||
|
{% endmacro %}
|
||||||
|
{% macro typeBlock metadata %}{% filter removeNewlines:"leading" %}
|
||||||
|
{% if metadata.type == "Array" %}
|
||||||
|
[{% call typeBlock metadata.element %}]
|
||||||
|
{% elif metadata.type == "Dictionary" %}
|
||||||
|
[String: Any]
|
||||||
|
{% elif metadata.type == "Optional" %}
|
||||||
|
Any?
|
||||||
|
{% else %}
|
||||||
|
{{metadata.type}}
|
||||||
|
{% endif %}
|
||||||
|
{% endfilter %}{% endmacro %}
|
||||||
|
{% macro propertyBlock key metadata data %}{% filter removeNewlines:"leading" %}
|
||||||
|
{% set propertyName %}{{key|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}}{% endset %}
|
||||||
|
{% set propertyType %}{% call typeBlock metadata %}{% endset %}
|
||||||
|
static let {{propertyName}}: {{propertyType}} = {% call valueBlock data[key] metadata %}
|
||||||
|
{% endfilter %}{% endmacro %}
|
||||||
|
{% macro valueBlock value metadata %}{% filter removeNewlines:"leading" %}
|
||||||
|
{% if metadata.type == "String" %}
|
||||||
|
"{{ value }}"
|
||||||
|
{% elif metadata.type == "Optional" %}
|
||||||
|
nil
|
||||||
|
{% elif metadata.type == "Array" and value %}
|
||||||
|
[{% for value in value %}
|
||||||
|
{% call valueBlock value metadata.element.items[forloop.counter0]|default:metadata.element %}
|
||||||
|
{{ ", " if not forloop.last }}
|
||||||
|
{% endfor %}]
|
||||||
|
{% elif metadata.type == "Dictionary" %}
|
||||||
|
[{% for key,value in value %}
|
||||||
|
"{{key}}": {% call valueBlock value metadata.properties[key] %}
|
||||||
|
{{ ", " if not forloop.last }}
|
||||||
|
{% empty %}
|
||||||
|
:
|
||||||
|
{% endfor %}]
|
||||||
|
{% elif metadata.type == "Bool" %}
|
||||||
|
{% if value %}true{% else %}false{% endif %}
|
||||||
|
{% else %}
|
||||||
|
{{ value }}
|
||||||
|
{% endif %}
|
||||||
|
{% endfilter %}{% endmacro %}
|
||||||
|
|
||||||
|
// swiftlint:disable identifier_name line_length number_separator type_body_length
|
||||||
|
{{accessModifier}} enum {{param.enumName|default:"YAMLFiles"}} {
|
||||||
|
{% if files.count > 1 or param.forceFileNameEnum %}
|
||||||
|
{% for file in files %}
|
||||||
|
{{accessModifier}} enum {{file.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} {
|
||||||
|
{% filter indent:2 %}{% call fileBlock file %}{% endfilter %}
|
||||||
|
}
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
{% call fileBlock files.first %}
|
||||||
|
{% endif %}
|
||||||
|
}
|
||||||
|
// swiftlint:enable identifier_name line_length number_separator type_body_length
|
||||||
|
{% else %}
|
||||||
|
// No files found
|
||||||
|
{% endif %}
|
|
@ -0,0 +1,92 @@
|
||||||
|
// swiftlint:disable all
|
||||||
|
// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen
|
||||||
|
|
||||||
|
{% if files %}
|
||||||
|
{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %}
|
||||||
|
{% set documentPrefix %}{{param.documentName|default:"Document"}}{% endset %}
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
// swiftlint:disable superfluous_disable_command
|
||||||
|
// swiftlint:disable file_length
|
||||||
|
|
||||||
|
// MARK: - YAML Files
|
||||||
|
{% macro fileBlock file %}
|
||||||
|
{% if file.documents.count > 1 %}
|
||||||
|
{% for document in file.documents %}
|
||||||
|
{% set documentName %}{{documentPrefix}}{{forloop.counter}}{% endset %}
|
||||||
|
{{accessModifier}} enum {{documentName|swiftIdentifier:"pretty"|escapeReservedKeywords}} {
|
||||||
|
{% filter indent:2 %}{% call documentBlock file document %}{% endfilter %}
|
||||||
|
}
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
{% call documentBlock file file.documents.first %}
|
||||||
|
{% endif %}
|
||||||
|
{% endmacro %}
|
||||||
|
{% macro documentBlock file document %}
|
||||||
|
{% set rootType %}{% call typeBlock document.metadata %}{% endset %}
|
||||||
|
{% if document.metadata.type == "Array" %}
|
||||||
|
{{accessModifier}} static let items: {{rootType}} = {% call valueBlock document.data document.metadata %}
|
||||||
|
{% elif document.metadata.type == "Dictionary" %}
|
||||||
|
{% for key,value in document.metadata.properties %}
|
||||||
|
{{accessModifier}} {% call propertyBlock key value document.data %}
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
{{accessModifier}} static let value: {{rootType}} = {% call valueBlock document.data document.metadata %}
|
||||||
|
{% endif %}
|
||||||
|
{% endmacro %}
|
||||||
|
{% macro typeBlock metadata %}{% filter removeNewlines:"leading" %}
|
||||||
|
{% if metadata.type == "Array" %}
|
||||||
|
[{% call typeBlock metadata.element %}]
|
||||||
|
{% elif metadata.type == "Dictionary" %}
|
||||||
|
[String: Any]
|
||||||
|
{% elif metadata.type == "Optional" %}
|
||||||
|
Any?
|
||||||
|
{% else %}
|
||||||
|
{{metadata.type}}
|
||||||
|
{% endif %}
|
||||||
|
{% endfilter %}{% endmacro %}
|
||||||
|
{% macro propertyBlock key metadata data %}{% filter removeNewlines:"leading" %}
|
||||||
|
{% set propertyName %}{{key|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}}{% endset %}
|
||||||
|
{% set propertyType %}{% call typeBlock metadata %}{% endset %}
|
||||||
|
static let {{propertyName}}: {{propertyType}} = {% call valueBlock data[key] metadata %}
|
||||||
|
{% endfilter %}{% endmacro %}
|
||||||
|
{% macro valueBlock value metadata %}{% filter removeNewlines:"leading" %}
|
||||||
|
{% if metadata.type == "String" %}
|
||||||
|
"{{ value }}"
|
||||||
|
{% elif metadata.type == "Optional" %}
|
||||||
|
nil
|
||||||
|
{% elif metadata.type == "Array" and value %}
|
||||||
|
[{% for value in value %}
|
||||||
|
{% call valueBlock value metadata.element.items[forloop.counter0]|default:metadata.element %}
|
||||||
|
{{ ", " if not forloop.last }}
|
||||||
|
{% endfor %}]
|
||||||
|
{% elif metadata.type == "Dictionary" %}
|
||||||
|
[{% for key,value in value %}
|
||||||
|
"{{key}}": {% call valueBlock value metadata.properties[key] %}
|
||||||
|
{{ ", " if not forloop.last }}
|
||||||
|
{% empty %}
|
||||||
|
:
|
||||||
|
{% endfor %}]
|
||||||
|
{% elif metadata.type == "Bool" %}
|
||||||
|
{% if value %}true{% else %}false{% endif %}
|
||||||
|
{% else %}
|
||||||
|
{{ value }}
|
||||||
|
{% endif %}
|
||||||
|
{% endfilter %}{% endmacro %}
|
||||||
|
|
||||||
|
// swiftlint:disable identifier_name line_length number_separator type_body_length
|
||||||
|
{{accessModifier}} enum {{param.enumName|default:"YAMLFiles"}} {
|
||||||
|
{% if files.count > 1 or param.forceFileNameEnum %}
|
||||||
|
{% for file in files %}
|
||||||
|
{{accessModifier}} enum {{file.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} {
|
||||||
|
{% filter indent:2 %}{% call fileBlock file %}{% endfilter %}
|
||||||
|
}
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
{% call fileBlock files.first %}
|
||||||
|
{% endif %}
|
||||||
|
}
|
||||||
|
// swiftlint:enable identifier_name line_length number_separator type_body_length
|
||||||
|
{% else %}
|
||||||
|
// No files found
|
||||||
|
{% endif %}
|
BIN
.swiftgen/bin/swiftgen
Executable file
BIN
.swiftgen/bin/swiftgen
Executable file
Binary file not shown.
29
.swiftgen/templates/fonts.stencil
Normal file
29
.swiftgen/templates/fonts.stencil
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
// swiftlint:disable all
|
||||||
|
// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen
|
||||||
|
|
||||||
|
{% if families %}
|
||||||
|
import SwiftUI
|
||||||
|
{% for family in families %}
|
||||||
|
{% set identifierName %}{{family.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}}{% endset %}
|
||||||
|
{% set styleTypeName %}{{family.name|swiftIdentifier:"pretty"|escapeReservedKeywords}}Style{% endset %}
|
||||||
|
|
||||||
|
extension Font {
|
||||||
|
public static func {{identifierName}}(_ style: {{styleTypeName}}, fixedSize: CGFloat) -> Font {
|
||||||
|
return Font.custom(style.rawValue, fixedSize: fixedSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func {{identifierName}}(_ style: {{styleTypeName}}, size: CGFloat, relativeTo textStyle: TextStyle = .body) -> Font {
|
||||||
|
return Font.custom(style.rawValue, size: size, relativeTo: textStyle)
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum {{styleTypeName}}: String {
|
||||||
|
{% for font in family.fonts %}
|
||||||
|
case {{font.style|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}} = "{{font.name}}"
|
||||||
|
{% endfor %}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
// No fonts found
|
||||||
|
{% endif %}
|
||||||
|
// swiftlint:enable all
|
85
.swiftgen/templates/strings.stencil
Normal file
85
.swiftgen/templates/strings.stencil
Normal file
|
@ -0,0 +1,85 @@
|
||||||
|
// swiftlint:disable all
|
||||||
|
// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen
|
||||||
|
|
||||||
|
{% if tables.count > 0 %}
|
||||||
|
{% set accessModifier %}{% if param.publicAccess %}public{% else %}internal{% endif %}{% endset %}
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
// swiftlint:disable superfluous_disable_command file_length implicit_return
|
||||||
|
|
||||||
|
// MARK: - Strings
|
||||||
|
|
||||||
|
{% macro parametersBlock types %}{% filter removeNewlines:"leading" %}
|
||||||
|
{% for type in types %}
|
||||||
|
{% if type == "String" %}
|
||||||
|
_ p{{forloop.counter}}: Any
|
||||||
|
{% else %}
|
||||||
|
_ p{{forloop.counter}}: {{type}}
|
||||||
|
{% endif %}
|
||||||
|
{{ ", " if not forloop.last }}
|
||||||
|
{% endfor %}
|
||||||
|
{% endfilter %}{% endmacro %}
|
||||||
|
{% macro argumentsBlock types %}{% filter removeNewlines:"leading" %}
|
||||||
|
{% for type in types %}
|
||||||
|
{% if type == "String" %}
|
||||||
|
String(describing: p{{forloop.counter}})
|
||||||
|
{% elif type == "UnsafeRawPointer" %}
|
||||||
|
Int(bitPattern: p{{forloop.counter}})
|
||||||
|
{% else %}
|
||||||
|
p{{forloop.counter}}
|
||||||
|
{% endif %}
|
||||||
|
{{ ", " if not forloop.last }}
|
||||||
|
{% endfor %}
|
||||||
|
{% endfilter %}{% endmacro %}
|
||||||
|
{% macro recursiveBlock table item %}
|
||||||
|
{% for string in item.strings %}
|
||||||
|
{% if not param.noComments %}
|
||||||
|
{% for line in string.translation|split:"\n" %}
|
||||||
|
/// {{line}}
|
||||||
|
{% endfor %}
|
||||||
|
{% endif %}
|
||||||
|
{% if string.types %}
|
||||||
|
{{accessModifier}} static func {{string.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}}({% call parametersBlock string.types %}) -> String {
|
||||||
|
{{enumName}}.tr("{{table}}", "{{string.key}}", {% call argumentsBlock string.types %})
|
||||||
|
}
|
||||||
|
{% else %}
|
||||||
|
{{accessModifier}} static var {{string.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}}: String { {{enumName}}.tr("{{table}}", "{{string.key}}") }
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% for child in item.children %}
|
||||||
|
|
||||||
|
{{accessModifier}} enum {{child.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} {
|
||||||
|
{% filter indent:2 %}{% call recursiveBlock table child %}{% endfilter %}
|
||||||
|
}
|
||||||
|
{% endfor %}
|
||||||
|
{% endmacro %}
|
||||||
|
// swiftlint:disable explicit_type_interface function_parameter_count identifier_name line_length
|
||||||
|
// swiftlint:disable nesting type_body_length type_name vertical_whitespace_opening_braces
|
||||||
|
{% set enumName %}{{param.enumName|default:"L10n"}}{% endset %}
|
||||||
|
{{accessModifier}} enum {{enumName}} {
|
||||||
|
{% if tables.count > 1 or param.forceFileNameEnum %}
|
||||||
|
{% for table in tables %}
|
||||||
|
{{accessModifier}} enum {{table.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} {
|
||||||
|
{% filter indent:2 %}{% call recursiveBlock table.name table.levels %}{% endfilter %}
|
||||||
|
}
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
{% call recursiveBlock tables.first.name tables.first.levels %}
|
||||||
|
{% endif %}
|
||||||
|
}
|
||||||
|
// swiftlint:enable explicit_type_interface function_parameter_count identifier_name line_length
|
||||||
|
// swiftlint:enable nesting type_body_length type_name vertical_whitespace_opening_braces
|
||||||
|
|
||||||
|
// MARK: - Implementation Details
|
||||||
|
import Localize_Swift
|
||||||
|
extension {{enumName}} {
|
||||||
|
static func tr(_ table: String, _ key: String, _ args: CVarArg...) -> String {
|
||||||
|
let selectedLanguage = Localize.currentLanguage()
|
||||||
|
guard let path = Bundle.main.path(forResource: selectedLanguage, ofType: "lproj"),
|
||||||
|
let bundle = Bundle(path: path) else { return "Setup language error" }
|
||||||
|
return NSLocalizedString(key, tableName: table, bundle: bundle, comment: "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
{% endif %}
|
||||||
|
// swiftlint: enable all
|
48
.swiftgen/templates/xcassets.stencil
Normal file
48
.swiftgen/templates/xcassets.stencil
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
// swiftlint:disable all
|
||||||
|
// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen
|
||||||
|
|
||||||
|
{% if catalogs %}
|
||||||
|
import SwiftUI
|
||||||
|
{% macro casesBlock assets %}
|
||||||
|
{% for asset in assets %}
|
||||||
|
{% if asset.items and asset.isNamespaced == "true" %}
|
||||||
|
public enum {{asset.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} {
|
||||||
|
{% filter indent:2 %}{% call casesBlock asset.items %}{% endfilter %}
|
||||||
|
}
|
||||||
|
{% elif asset.items %}
|
||||||
|
{% call casesBlock asset.items %}
|
||||||
|
{% elif asset.type == "color" %}
|
||||||
|
public static let {{asset.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}} = Color("{{asset.value}}")
|
||||||
|
{% elif asset.type == "image" %}
|
||||||
|
public static let {{asset.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}} = Image("{{asset.value}}")
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endmacro %}
|
||||||
|
{% for catalog in catalogs %}
|
||||||
|
{% if catalog.name == "Colors" %}
|
||||||
|
|
||||||
|
extension Color {
|
||||||
|
{% for catalog in catalogs %}
|
||||||
|
{% if catalog.name == "Colors" %}
|
||||||
|
{% call casesBlock catalog.assets %}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% for catalog in catalogs %}
|
||||||
|
{% if catalog.name == "Images" %}
|
||||||
|
|
||||||
|
extension Image {
|
||||||
|
{% for catalog in catalogs %}
|
||||||
|
{% if catalog.name == "Images" %}
|
||||||
|
{% call casesBlock catalog.assets %}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
// No assets found
|
||||||
|
{% endif %}
|
||||||
|
// swiftlint: enable all
|
36
.swiftgen/templates/xcassets_strings.stencil
Normal file
36
.swiftgen/templates/xcassets_strings.stencil
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
// swiftlint:disable all
|
||||||
|
// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen
|
||||||
|
|
||||||
|
{% if catalogs %}
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
typealias AssetStrings = String
|
||||||
|
{% macro casesBlock assets %}
|
||||||
|
{% for asset in assets %}
|
||||||
|
{% if asset.items and asset.isNamespaced == "true" %}
|
||||||
|
public enum {{asset.name|swiftIdentifier:"pretty"|escapeReservedKeywords}} {
|
||||||
|
{% filter indent:2 %}{% call casesBlock asset.items %}{% endfilter %}
|
||||||
|
}
|
||||||
|
{% elif asset.items %}
|
||||||
|
{% call casesBlock asset.items %}
|
||||||
|
{% elif asset.type == "image" %}
|
||||||
|
public static let {{asset.name|swiftIdentifier:"pretty"|lowerFirstWord|escapeReservedKeywords}} = String("{{asset.value}}")
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endmacro %}
|
||||||
|
{% for catalog in catalogs %}
|
||||||
|
{% if catalog.name == "Images" %}
|
||||||
|
|
||||||
|
extension String {
|
||||||
|
{% for catalog in catalogs %}
|
||||||
|
{% if catalog.name == "Images" %}
|
||||||
|
{% call casesBlock catalog.assets %}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
}
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% else %}
|
||||||
|
// No assets found
|
||||||
|
{% endif %}
|
||||||
|
// swiftlint: enable all
|
115
.swiftlint.yml
Normal file
115
.swiftlint.yml
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
---
|
||||||
|
colon:
|
||||||
|
severity: error
|
||||||
|
|
||||||
|
line_length:
|
||||||
|
ignores_comments: true
|
||||||
|
warning: 260
|
||||||
|
error: 300
|
||||||
|
|
||||||
|
type_body_length:
|
||||||
|
warning: 300
|
||||||
|
error: 500
|
||||||
|
|
||||||
|
file_length:
|
||||||
|
warning: 800
|
||||||
|
error: 1000
|
||||||
|
|
||||||
|
function_parameter_count:
|
||||||
|
warning: 20
|
||||||
|
error: 30
|
||||||
|
|
||||||
|
function_body_length:
|
||||||
|
warning: 120
|
||||||
|
error: 150
|
||||||
|
|
||||||
|
cyclomatic_complexity:
|
||||||
|
warning: 40
|
||||||
|
error: 50
|
||||||
|
|
||||||
|
nesting:
|
||||||
|
type_level:
|
||||||
|
warning: 3
|
||||||
|
error: 6
|
||||||
|
function_level:
|
||||||
|
warning: 500
|
||||||
|
error: 10
|
||||||
|
|
||||||
|
vertical_parameter_alignment:
|
||||||
|
severity: warning
|
||||||
|
|
||||||
|
implicitly_unwrapped_optional:
|
||||||
|
severity: warning
|
||||||
|
|
||||||
|
force_unwrapping:
|
||||||
|
severity: error
|
||||||
|
|
||||||
|
vertical_whitespace:
|
||||||
|
severity: error
|
||||||
|
|
||||||
|
force_try:
|
||||||
|
severity: error
|
||||||
|
|
||||||
|
trailing_semicolon:
|
||||||
|
severity: error
|
||||||
|
|
||||||
|
type_name:
|
||||||
|
min_length: 3
|
||||||
|
severity: warning
|
||||||
|
|
||||||
|
identifier_name:
|
||||||
|
min_length: 3
|
||||||
|
max_length: 60
|
||||||
|
# validates_start_with_lowercase: true
|
||||||
|
allowed_symbols: "_"
|
||||||
|
excluded:
|
||||||
|
- iv
|
||||||
|
- id
|
||||||
|
- ip
|
||||||
|
- on
|
||||||
|
- ui
|
||||||
|
- x
|
||||||
|
- y
|
||||||
|
- tz
|
||||||
|
- to
|
||||||
|
- db
|
||||||
|
|
||||||
|
# Disable rules from the default enabled set.
|
||||||
|
disabled_rules:
|
||||||
|
- trailing_whitespace
|
||||||
|
- implicit_getter
|
||||||
|
- redundant_string_enum_value
|
||||||
|
- switch_case_alignment
|
||||||
|
|
||||||
|
# Enable rules not from the default set.
|
||||||
|
opt_in_rules:
|
||||||
|
# - function_default_parameter_at_end
|
||||||
|
- empty_count
|
||||||
|
- indentation_width
|
||||||
|
# - index_at_zero
|
||||||
|
- legacy_constant
|
||||||
|
# - implicitly_unwrapped_optional
|
||||||
|
- force_unwrapping
|
||||||
|
# - no header
|
||||||
|
- file_header
|
||||||
|
# - for force unwrapping
|
||||||
|
- implicitly_unwrapped_optional
|
||||||
|
- vertical_parameter_alignment_on_call
|
||||||
|
- vertical_whitespace_between_cases
|
||||||
|
- vertical_whitespace_closing_braces
|
||||||
|
- vertical_whitespace_opening_braces
|
||||||
|
|
||||||
|
# Acts as a whitelist, only the rules specified in this list will be enabled. Can not be specified alongside disabled_rules or opt_in_rules.
|
||||||
|
only_rules:
|
||||||
|
|
||||||
|
# This is an entirely separate list of rules that are only run by the analyze command. All analyzer rules are opt-in, so this is the only configurable rule list (there is no disabled/whitelist equivalent).
|
||||||
|
analyzer_rules:
|
||||||
|
- unused_import
|
||||||
|
- unused_declaration
|
||||||
|
|
||||||
|
unused_declaration:
|
||||||
|
include_public_and_open: true
|
||||||
|
|
||||||
|
# paths to ignore during linting. Takes precedence over `included`.
|
||||||
|
excluded:
|
||||||
|
- SomePathHere
|
10
ConversationsClassic/AppCore/Actions/AccountsActions.swift
Normal file
10
ConversationsClassic/AppCore/Actions/AccountsActions.swift
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
enum AccountsAction: Codable {
|
||||||
|
case accountsListUpdated(accounts: [Account])
|
||||||
|
|
||||||
|
case goTo(AccountNavigationState)
|
||||||
|
|
||||||
|
case tryAddAccountWithCredentials(login: String, password: String)
|
||||||
|
case addAccountError(jid: String, reason: String?)
|
||||||
|
|
||||||
|
case makeAccountPermanent(account: Account)
|
||||||
|
}
|
12
ConversationsClassic/AppCore/Actions/AppActions.swift
Normal file
12
ConversationsClassic/AppCore/Actions/AppActions.swift
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
enum AppAction: Codable {
|
||||||
|
case empty
|
||||||
|
case flushState
|
||||||
|
case changeFlow(_ flow: AppFlow)
|
||||||
|
|
||||||
|
case startAction(_ action: StartAction)
|
||||||
|
case databaseAction(_ action: DatabaseAction)
|
||||||
|
case accountsAction(_ action: AccountsAction)
|
||||||
|
case xmppAction(_ action: XMPPAction)
|
||||||
|
case rostersAction(_ action: RostersAction)
|
||||||
|
case chatsAction(_ action: ChatsAction)
|
||||||
|
}
|
3
ConversationsClassic/AppCore/Actions/ChatsActions.swift
Normal file
3
ConversationsClassic/AppCore/Actions/ChatsActions.swift
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
enum ChatsAction: Codable {
|
||||||
|
case chatsListUpdated(chats: [Chat])
|
||||||
|
}
|
|
@ -0,0 +1,8 @@
|
||||||
|
enum DatabaseAction: Codable {
|
||||||
|
case storedAccountsLoaded(accounts: [Account])
|
||||||
|
case loadingStoredAccountsFailed
|
||||||
|
case updateAccountFailed
|
||||||
|
|
||||||
|
case storedRostersLoaded(rosters: [Roster])
|
||||||
|
case storedChatsLoaded(chats: [Chat])
|
||||||
|
}
|
12
ConversationsClassic/AppCore/Actions/RostersActions.swift
Normal file
12
ConversationsClassic/AppCore/Actions/RostersActions.swift
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
enum RostersAction: Codable {
|
||||||
|
case addRoster(ownerJID: String, contactJID: String, name: String?, groups: [String])
|
||||||
|
case addRosterDone(jid: String)
|
||||||
|
case addRosterError(reason: String)
|
||||||
|
|
||||||
|
case rostersListUpdated([Roster])
|
||||||
|
|
||||||
|
case markRosterAsLocallyDeleted(ownerJID: String, contactJID: String)
|
||||||
|
case unmarkRosterAsLocallyDeleted(ownerJID: String, contactJID: String)
|
||||||
|
case deleteRoster(ownerJID: String, contactJID: String)
|
||||||
|
case rosterDeletingFailed(reason: String)
|
||||||
|
}
|
5
ConversationsClassic/AppCore/Actions/StartActions.swift
Normal file
5
ConversationsClassic/AppCore/Actions/StartActions.swift
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
enum StartAction: Codable {
|
||||||
|
case loadStoredAccounts
|
||||||
|
|
||||||
|
case goTo(StartNavigationState)
|
||||||
|
}
|
3
ConversationsClassic/AppCore/Actions/XMPPActions.swift
Normal file
3
ConversationsClassic/AppCore/Actions/XMPPActions.swift
Normal file
|
@ -0,0 +1,3 @@
|
||||||
|
enum XMPPAction: Codable {
|
||||||
|
case clientConnectionChanged(jid: String, state: ConnectionStatus)
|
||||||
|
}
|
95
ConversationsClassic/AppCore/AppStore.swift
Normal file
95
ConversationsClassic/AppCore/AppStore.swift
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
/*
|
||||||
|
In 99,99% of time YOU DON'T NEEDED TO CHANGE ANYTHING in this file!
|
||||||
|
|
||||||
|
This file declare global state object for whole app
|
||||||
|
and reducers/actions/middleware types. Core of app.
|
||||||
|
*/
|
||||||
|
import Combine
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
typealias Stateable = Codable & Equatable
|
||||||
|
typealias AppStore = Store<AppState, AppAction>
|
||||||
|
typealias Reducer<State: Stateable, Action: Codable> = (inout State, Action) -> Void
|
||||||
|
typealias Middleware<State: Stateable, Action: Codable> = (State, Action) -> AnyPublisher<Action, Never>?
|
||||||
|
|
||||||
|
final class Store<State: Stateable, Action: Codable>: ObservableObject {
|
||||||
|
// Fake variable for be able to trigger SwiftUI redraw after app state completely changed
|
||||||
|
// this hack is needed because @Published wrapper sends signals on "willSet:"
|
||||||
|
@Published private var dumbVar: UUID = .init()
|
||||||
|
|
||||||
|
// State is read-only (changes only over reducers)
|
||||||
|
private(set) var state: State {
|
||||||
|
didSet {
|
||||||
|
DispatchQueue.main.async { [weak self] in
|
||||||
|
self?.dumbVar = UUID()
|
||||||
|
}
|
||||||
|
} // signal to SwiftUI only when new state did set
|
||||||
|
}
|
||||||
|
|
||||||
|
// Serial queue for performing any actions sequentially
|
||||||
|
private let serialQueue = DispatchQueue(label: "im.narayana.conversations.classic.serial.queue", qos: .userInteractive)
|
||||||
|
|
||||||
|
private let reducer: Reducer<State, Action>
|
||||||
|
private let middlewares: [Middleware<State, Action>]
|
||||||
|
private var middlewareCancellables: Set<AnyCancellable> = []
|
||||||
|
|
||||||
|
// Init
|
||||||
|
init(
|
||||||
|
initialState: State,
|
||||||
|
reducer: @escaping Reducer<State, Action>,
|
||||||
|
middlewares: [Middleware<State, Action>] = []
|
||||||
|
) {
|
||||||
|
state = initialState
|
||||||
|
self.reducer = reducer
|
||||||
|
self.middlewares = middlewares
|
||||||
|
}
|
||||||
|
|
||||||
|
// Run reducers/middlewares
|
||||||
|
func dispatch(_ action: Action) {
|
||||||
|
serialQueue.sync { [weak self] in
|
||||||
|
guard let wSelf = self else { return }
|
||||||
|
let newState = wSelf.dispatch(wSelf.state, action)
|
||||||
|
wSelf.state = newState
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func dispatch(_ currentState: State, _ action: Action) -> State {
|
||||||
|
let startTime = CFAbsoluteTimeGetCurrent()
|
||||||
|
|
||||||
|
// Do reducing
|
||||||
|
var newState = currentState
|
||||||
|
reducer(&newState, action)
|
||||||
|
|
||||||
|
// Dispatch all middleware functions
|
||||||
|
for middleware in middlewares {
|
||||||
|
guard let middleware = middleware(newState, action) else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
middleware
|
||||||
|
.receive(on: DispatchQueue.main)
|
||||||
|
.sink(receiveValue: dispatch)
|
||||||
|
.store(in: &middlewareCancellables)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check performance
|
||||||
|
let timeElapsed = CFAbsoluteTimeGetCurrent() - startTime
|
||||||
|
if timeElapsed > 0.05 {
|
||||||
|
#if DEBUG
|
||||||
|
print(
|
||||||
|
"""
|
||||||
|
--
|
||||||
|
(Ignore this warning ONLY in case, when execution is paused by your breakpoint)
|
||||||
|
🕐Execution time: \(timeElapsed)
|
||||||
|
❌WARNING! Some reducers/middlewares work too long! It will lead to issues in production build!
|
||||||
|
Because of execution each action is synchronous the any stuck will reduce performance dramatically.
|
||||||
|
Probably you need check which part of reducer/middleware should be async (wrapped with Futures, as example)
|
||||||
|
--
|
||||||
|
"""
|
||||||
|
)
|
||||||
|
#else
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
return newState
|
||||||
|
}
|
||||||
|
}
|
222
ConversationsClassic/AppCore/Database/Database+Martin.swift
Normal file
222
ConversationsClassic/AppCore/Database/Database+Martin.swift
Normal file
|
@ -0,0 +1,222 @@
|
||||||
|
import Foundation
|
||||||
|
import GRDB
|
||||||
|
import Martin
|
||||||
|
|
||||||
|
extension Database: MartinsManager {
|
||||||
|
// MARK: - Martin's roster manager
|
||||||
|
func clear(for context: Martin.Context) {
|
||||||
|
print("Clearing roster for context: \(context)")
|
||||||
|
do {
|
||||||
|
try _db.write { db in
|
||||||
|
try Roster
|
||||||
|
.filter(Column("bareJid") == context.userBareJid.stringValue)
|
||||||
|
.deleteAll(db)
|
||||||
|
|
||||||
|
try RosterVersion
|
||||||
|
.filter(Column("bareJid") == context.userBareJid.stringValue)
|
||||||
|
.deleteAll(db)
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
logIt(.error, "Error clearing roster: \(error.localizedDescription)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func items(for context: Martin.Context) -> [any Martin.RosterItemProtocol] {
|
||||||
|
do {
|
||||||
|
let rosters: [Roster] = try _db.read { db in
|
||||||
|
try Roster.filter(Column("bareJid") == context.userBareJid.stringValue).fetchAll(db)
|
||||||
|
}
|
||||||
|
return rosters.map { roster in
|
||||||
|
RosterItemBase(
|
||||||
|
jid: JID(roster.bareJid),
|
||||||
|
name: roster.name,
|
||||||
|
subscription: RosterItemSubscription(rawValue: roster.subscription) ?? .none,
|
||||||
|
groups: roster.data.groups,
|
||||||
|
ask: roster.ask,
|
||||||
|
annotations: roster.data.annotations
|
||||||
|
)
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
logIt(.error, "Error fetching roster items: \(error.localizedDescription)")
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func item(for context: Martin.Context, jid: Martin.JID) -> (any Martin.RosterItemProtocol)? {
|
||||||
|
do {
|
||||||
|
let roster: Roster? = try _db.read { db in
|
||||||
|
try Roster
|
||||||
|
.filter(Column("bareJid") == context.userBareJid.stringValue)
|
||||||
|
.filter(Column("contactBareJid") == jid.stringValue)
|
||||||
|
.fetchOne(db)
|
||||||
|
}
|
||||||
|
if let roster {
|
||||||
|
return RosterItemBase(
|
||||||
|
jid: JID(roster.bareJid),
|
||||||
|
name: roster.name,
|
||||||
|
subscription: RosterItemSubscription(rawValue: roster.subscription) ?? .none,
|
||||||
|
groups: roster.data.groups,
|
||||||
|
ask: roster.ask,
|
||||||
|
annotations: roster.data.annotations
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
logIt(.error, "Error fetching roster item: \(error.localizedDescription)")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateItem(for context: Martin.Context, jid: Martin.JID, name: String?, subscription: Martin.RosterItemSubscription, groups: [String], ask: Bool, annotations: [Martin.RosterItemAnnotation]) -> (any Martin.RosterItemProtocol)? {
|
||||||
|
do {
|
||||||
|
let roster = Roster(
|
||||||
|
bareJid: context.userBareJid.stringValue,
|
||||||
|
contactBareJid: jid.stringValue,
|
||||||
|
name: name,
|
||||||
|
subscription: subscription.rawValue,
|
||||||
|
ask: ask,
|
||||||
|
data: DBRosterData(
|
||||||
|
groups: groups,
|
||||||
|
annotations: annotations
|
||||||
|
)
|
||||||
|
)
|
||||||
|
try _db.write { db in
|
||||||
|
try roster.save(db)
|
||||||
|
}
|
||||||
|
return RosterItemBase(jid: jid, name: name, subscription: subscription, groups: groups, ask: ask, annotations: annotations)
|
||||||
|
} catch {
|
||||||
|
logIt(.error, "Error updating roster item: \(error.localizedDescription)")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func deleteItem(for context: Martin.Context, jid: Martin.JID) -> (any Martin.RosterItemProtocol)? {
|
||||||
|
do {
|
||||||
|
let roster: Roster? = try _db.read { db in
|
||||||
|
try Roster
|
||||||
|
.filter(Column("bareJid") == context.userBareJid.stringValue)
|
||||||
|
.filter(Column("contactBareJid") == jid.stringValue)
|
||||||
|
.fetchOne(db)
|
||||||
|
}
|
||||||
|
if let roster {
|
||||||
|
_ = try _db.write { db in
|
||||||
|
try roster.delete(db)
|
||||||
|
}
|
||||||
|
return RosterItemBase(
|
||||||
|
jid: JID(roster.bareJid),
|
||||||
|
name: roster.name,
|
||||||
|
subscription: RosterItemSubscription(rawValue: roster.subscription) ?? .none,
|
||||||
|
groups: roster.data.groups,
|
||||||
|
ask: roster.ask,
|
||||||
|
annotations: roster.data.annotations
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
logIt(.error, "Error fetching roster version: \(error.localizedDescription)")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func version(for context: Martin.Context) -> String? {
|
||||||
|
do {
|
||||||
|
let version: RosterVersion? = try _db.read { db in
|
||||||
|
try RosterVersion
|
||||||
|
.filter(Column("bareJid") == context.userBareJid.stringValue)
|
||||||
|
.fetchOne(db)
|
||||||
|
}
|
||||||
|
return version?.version
|
||||||
|
} catch {
|
||||||
|
logIt(.error, "Error fetching roster version: \(error.localizedDescription)")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func set(version: String?, for context: Martin.Context) {
|
||||||
|
guard let version else { return }
|
||||||
|
do {
|
||||||
|
try _db.write { db in
|
||||||
|
let rosterVersion = RosterVersion(
|
||||||
|
bareJid: context.userBareJid.stringValue,
|
||||||
|
version: version
|
||||||
|
)
|
||||||
|
try rosterVersion.save(db)
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
logIt(.error, "Error setting roster version: \(error.localizedDescription)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func initialize(context _: Martin.Context) {}
|
||||||
|
func deinitialize(context _: Martin.Context) {}
|
||||||
|
|
||||||
|
// MARK: - Martin's chats manager
|
||||||
|
func chats(for context: Martin.Context) -> [any Martin.ChatProtocol] {
|
||||||
|
do {
|
||||||
|
let chats: [Chat] = try _db.read { db in
|
||||||
|
try Chat.filter(Column("account") == context.userBareJid.stringValue).fetchAll(db)
|
||||||
|
}
|
||||||
|
return chats.map { chat in
|
||||||
|
Martin.ChatBase(context: context, jid: BareJID(chat.participant))
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
logIt(.error, "Error fetching chats: \(error.localizedDescription)")
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func chat(for context: Martin.Context, with: Martin.BareJID) -> (any Martin.ChatProtocol)? {
|
||||||
|
do {
|
||||||
|
let chat: Chat? = try _db.read { db in
|
||||||
|
try Chat
|
||||||
|
.filter(Column("account") == context.userBareJid.stringValue)
|
||||||
|
.filter(Column("participant") == with.stringValue)
|
||||||
|
.fetchOne(db)
|
||||||
|
}
|
||||||
|
if chat != nil {
|
||||||
|
return Martin.ChatBase(context: context, jid: with)
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
logIt(.error, "Error fetching chat: \(error.localizedDescription)")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func createChat(for context: Martin.Context, with: Martin.BareJID) -> (any Martin.ChatProtocol)? {
|
||||||
|
do {
|
||||||
|
let chat: Chat? = try _db.read { db in
|
||||||
|
try Chat
|
||||||
|
.filter(Column("account") == context.userBareJid.stringValue)
|
||||||
|
.filter(Column("participant") == with.stringValue)
|
||||||
|
.fetchOne(db)
|
||||||
|
}
|
||||||
|
if chat != nil {
|
||||||
|
return Martin.ChatBase(context: context, jid: with)
|
||||||
|
} else {
|
||||||
|
let chat = Chat(
|
||||||
|
id: UUID().uuidString,
|
||||||
|
account: context.userBareJid.stringValue,
|
||||||
|
participant: with.stringValue,
|
||||||
|
type: .chat
|
||||||
|
)
|
||||||
|
try _db.write { db in
|
||||||
|
try chat.save(db)
|
||||||
|
}
|
||||||
|
return Martin.ChatBase(context: context, jid: with)
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
logIt(.error, "Error fetching chat: \(error.localizedDescription)")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func close(chat: any Martin.ChatProtocol) -> Bool {
|
||||||
|
// not used in Martin library for now
|
||||||
|
print("Closing chat: \(chat)")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,65 @@
|
||||||
|
import Foundation
|
||||||
|
import GRDB
|
||||||
|
|
||||||
|
extension Database {
|
||||||
|
static var migrator: DatabaseMigrator = {
|
||||||
|
var migrator = DatabaseMigrator()
|
||||||
|
|
||||||
|
// flush db on schema change (only in DEV mode)
|
||||||
|
#if DEBUG
|
||||||
|
migrator.eraseDatabaseOnSchemaChange = true
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// 1st migration - basic tables
|
||||||
|
migrator.registerMigration("Add basic tables") { db in
|
||||||
|
// accounts
|
||||||
|
try db.create(table: "accounts", options: [.ifNotExists]) { table in
|
||||||
|
table.column("bareJid", .text).notNull().primaryKey().unique(onConflict: .replace)
|
||||||
|
table.column("pass", .text).notNull()
|
||||||
|
table.column("isActive", .boolean).notNull().defaults(to: true)
|
||||||
|
table.column("isTemp", .boolean).notNull().defaults(to: false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// rosters
|
||||||
|
try db.create(table: "rosterVersions", options: [.ifNotExists]) { table in
|
||||||
|
table.column("bareJid", .text).notNull().primaryKey().unique(onConflict: .replace)
|
||||||
|
table.column("version", .text).notNull()
|
||||||
|
}
|
||||||
|
try db.create(table: "rosters", options: [.ifNotExists]) { table in
|
||||||
|
table.column("bareJid", .text).notNull()
|
||||||
|
table.column("contactBareJid", .text).notNull()
|
||||||
|
table.column("name", .text)
|
||||||
|
table.column("subscription", .text).notNull()
|
||||||
|
table.column("ask", .boolean).notNull().defaults(to: false)
|
||||||
|
table.column("data", .text).notNull()
|
||||||
|
table.primaryKey(["bareJid", "contactBareJid"], onConflict: .replace)
|
||||||
|
table.column("locallyDeleted", .boolean).notNull().defaults(to: false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// chats
|
||||||
|
try db.create(table: "chats", options: [.ifNotExists]) { table in
|
||||||
|
table.column("id", .text).notNull().primaryKey().unique(onConflict: .replace)
|
||||||
|
table.column("account", .text).notNull()
|
||||||
|
table.column("participant", .text).notNull()
|
||||||
|
table.column("type", .integer).notNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
// messages
|
||||||
|
try db.create(table: "messages", options: [.ifNotExists]) { table in
|
||||||
|
table.column("id", .text).notNull().primaryKey().unique(onConflict: .replace)
|
||||||
|
table.column("chatId", .text).notNull().references("chats", onDelete: .cascade)
|
||||||
|
table.column("fromJid", .text).notNull()
|
||||||
|
table.column("toJid", .text).notNull()
|
||||||
|
table.column("timestamp", .datetime).notNull()
|
||||||
|
table.column("body", .text)
|
||||||
|
// table.column("isReaded", .boolean).notNull().defaults(to: false)
|
||||||
|
// table.column("subject", .text)
|
||||||
|
// table.column("threadId", .text)
|
||||||
|
// table.column("errorType", .text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// return migrator
|
||||||
|
return migrator
|
||||||
|
}()
|
||||||
|
}
|
55
ConversationsClassic/AppCore/Database/Database.swift
Normal file
55
ConversationsClassic/AppCore/Database/Database.swift
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
import Combine
|
||||||
|
import Foundation
|
||||||
|
import GRDB
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
// MARK: - Models protocol
|
||||||
|
typealias DBStorable = Codable & FetchableRecord & Identifiable & PersistableRecord & TableRecord
|
||||||
|
|
||||||
|
// MARK: - Database init
|
||||||
|
final class Database {
|
||||||
|
static let shared = Database()
|
||||||
|
let _db: DatabaseQueue
|
||||||
|
|
||||||
|
private init() {
|
||||||
|
do {
|
||||||
|
// Create db folder if not exists
|
||||||
|
let fileManager = FileManager.default
|
||||||
|
let appSupportURL = try fileManager.url(
|
||||||
|
for: .applicationSupportDirectory, in: .userDomainMask,
|
||||||
|
appropriateFor: nil, create: true
|
||||||
|
)
|
||||||
|
let directoryURL = appSupportURL.appendingPathComponent("ConversationsClassic", isDirectory: true)
|
||||||
|
try fileManager.createDirectory(at: directoryURL, withIntermediateDirectories: true)
|
||||||
|
|
||||||
|
// Open or create the database
|
||||||
|
let databaseURL = directoryURL.appendingPathComponent("db.sqlite")
|
||||||
|
_db = try DatabaseQueue(path: databaseURL.path, configuration: Database.config)
|
||||||
|
|
||||||
|
// Some debug info
|
||||||
|
#if DEBUG
|
||||||
|
print("Database path: \(databaseURL.path)")
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// Apply migrations
|
||||||
|
try Database.migrator.migrate(_db)
|
||||||
|
} catch {
|
||||||
|
fatalError("Database initialization failed: \(error)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: - Config
|
||||||
|
private extension Database {
|
||||||
|
static let config: Configuration = {
|
||||||
|
var config = Configuration()
|
||||||
|
#if DEBUG
|
||||||
|
// verbose and debugging in DEBUG builds only.
|
||||||
|
config.publicStatementArguments = true
|
||||||
|
config.prepareDatabase { db in
|
||||||
|
db.trace { print("SQL> \($0)") }
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return config
|
||||||
|
}()
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
import Combine
|
||||||
|
|
||||||
|
final class AccountsMiddleware {
|
||||||
|
static let shared = AccountsMiddleware()
|
||||||
|
|
||||||
|
func middleware(state: AppState, action: AppAction) -> AnyPublisher<AppAction, Never> {
|
||||||
|
switch action {
|
||||||
|
case .databaseAction(.storedAccountsLoaded(let accounts)):
|
||||||
|
return Just(.accountsAction(.accountsListUpdated(accounts: accounts)))
|
||||||
|
.eraseToAnyPublisher()
|
||||||
|
|
||||||
|
case .xmppAction(.clientConnectionChanged(let jid, let connectionStatus)):
|
||||||
|
return Future<AppAction, Never> { promise in
|
||||||
|
guard let account = state.accountsState.accounts.first(where: { $0.bareJid == jid }) else {
|
||||||
|
promise(.success(.empty))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if account.isTemp {
|
||||||
|
switch connectionStatus {
|
||||||
|
case .connected:
|
||||||
|
promise(.success(.accountsAction(.makeAccountPermanent(account: account))))
|
||||||
|
|
||||||
|
case .disconnected(let reason):
|
||||||
|
if reason != "No error!" {
|
||||||
|
promise(.success(.accountsAction(.addAccountError(jid: jid, reason: reason))))
|
||||||
|
} else {
|
||||||
|
promise(.success(.empty))
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
promise(.success(.empty))
|
||||||
|
}
|
||||||
|
|
||||||
|
} else {
|
||||||
|
promise(.success(.empty))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.eraseToAnyPublisher()
|
||||||
|
|
||||||
|
default:
|
||||||
|
return Empty().eraseToAnyPublisher()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
import Combine
|
||||||
|
|
||||||
|
final class ChatsMiddleware {
|
||||||
|
static let shared = ChatsMiddleware()
|
||||||
|
|
||||||
|
func middleware(state _: AppState, action: AppAction) -> AnyPublisher<AppAction, Never> {
|
||||||
|
switch action {
|
||||||
|
case .databaseAction(.storedChatsLoaded(let chats)):
|
||||||
|
return Just(.chatsAction(.chatsListUpdated(chats: chats)))
|
||||||
|
.eraseToAnyPublisher()
|
||||||
|
|
||||||
|
default:
|
||||||
|
return Empty().eraseToAnyPublisher()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,132 @@
|
||||||
|
import Combine
|
||||||
|
import Foundation
|
||||||
|
import GRDB
|
||||||
|
|
||||||
|
final class DatabaseMiddleware {
|
||||||
|
static let shared = DatabaseMiddleware()
|
||||||
|
private let database = Database.shared
|
||||||
|
private var cancellables: Set<AnyCancellable> = []
|
||||||
|
|
||||||
|
private init() {
|
||||||
|
// Database changes
|
||||||
|
ValueObservation
|
||||||
|
.tracking(Roster.fetchAll)
|
||||||
|
.publisher(in: database._db, scheduling: .immediate)
|
||||||
|
.sink { _ in
|
||||||
|
// Handle completion
|
||||||
|
} receiveValue: { rosters in
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
store.dispatch(.databaseAction(.storedRostersLoaded(rosters: rosters)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.store(in: &cancellables)
|
||||||
|
ValueObservation
|
||||||
|
.tracking(Chat.fetchAll)
|
||||||
|
.publisher(in: database._db, scheduling: .immediate)
|
||||||
|
.sink { _ in
|
||||||
|
// Handle completion
|
||||||
|
} receiveValue: { chats in
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
store.dispatch(.databaseAction(.storedChatsLoaded(chats: chats)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.store(in: &cancellables)
|
||||||
|
}
|
||||||
|
|
||||||
|
func middleware(state _: AppState, action: AppAction) -> AnyPublisher<AppAction, Never> {
|
||||||
|
switch action {
|
||||||
|
case .startAction(.loadStoredAccounts):
|
||||||
|
return Future<AppAction, Never> { promise in
|
||||||
|
Task(priority: .background) { [weak self] in
|
||||||
|
guard let database = self?.database else {
|
||||||
|
promise(.success(.databaseAction(.loadingStoredAccountsFailed)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
try database._db.read { db in
|
||||||
|
let accounts = try Account.fetchAll(db)
|
||||||
|
promise(.success(.databaseAction(.storedAccountsLoaded(accounts: accounts))))
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
promise(.success(.databaseAction(.loadingStoredAccountsFailed)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.eraseToAnyPublisher()
|
||||||
|
|
||||||
|
case .accountsAction(.makeAccountPermanent(let account)):
|
||||||
|
return Future<AppAction, Never> { promise in
|
||||||
|
Task(priority: .background) { [weak self] in
|
||||||
|
guard let database = self?.database else {
|
||||||
|
promise(.success(.databaseAction(.updateAccountFailed)))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
try database._db.write { db in
|
||||||
|
// make permanent and store to database
|
||||||
|
var acc = account
|
||||||
|
acc.isTemp = false
|
||||||
|
try acc.insert(db)
|
||||||
|
|
||||||
|
// Re-Fetch all accounts
|
||||||
|
let accounts = try Account.fetchAll(db)
|
||||||
|
|
||||||
|
// Use the accounts
|
||||||
|
promise(.success(.databaseAction(.storedAccountsLoaded(accounts: accounts))))
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
promise(.success(.databaseAction(.updateAccountFailed)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.eraseToAnyPublisher()
|
||||||
|
|
||||||
|
case .rostersAction(.markRosterAsLocallyDeleted(let ownerJID, let contactJID)):
|
||||||
|
return Future<AppAction, Never> { promise in
|
||||||
|
Task(priority: .background) { [weak self] in
|
||||||
|
guard let database = self?.database else {
|
||||||
|
promise(.success(.rostersAction(.rosterDeletingFailed(reason: L10n.Global.Error.genericDbError))))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
_ = try database._db.write { db in
|
||||||
|
try Roster
|
||||||
|
.filter(Column("bareJid") == ownerJID)
|
||||||
|
.filter(Column("contactBareJid") == contactJID)
|
||||||
|
.updateAll(db, Column("locallyDeleted").set(to: true))
|
||||||
|
}
|
||||||
|
promise(.success(.empty))
|
||||||
|
} catch {
|
||||||
|
promise(.success(.rostersAction(.rosterDeletingFailed(reason: L10n.Global.Error.genericDbError))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.eraseToAnyPublisher()
|
||||||
|
|
||||||
|
case .rostersAction(.unmarkRosterAsLocallyDeleted(let ownerJID, let contactJID)):
|
||||||
|
return Future<AppAction, Never> { promise in
|
||||||
|
Task(priority: .background) { [weak self] in
|
||||||
|
guard let database = self?.database else {
|
||||||
|
promise(.success(.rostersAction(.rosterDeletingFailed(reason: L10n.Global.Error.genericDbError))))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
_ = try database._db.write { db in
|
||||||
|
try Roster
|
||||||
|
.filter(Column("bareJid") == ownerJID)
|
||||||
|
.filter(Column("contactBareJid") == contactJID)
|
||||||
|
.updateAll(db, Column("locallyDeleted").set(to: false))
|
||||||
|
}
|
||||||
|
promise(.success(.empty))
|
||||||
|
} catch {
|
||||||
|
promise(.success(.rostersAction(.rosterDeletingFailed(reason: L10n.Global.Error.genericDbError))))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.eraseToAnyPublisher()
|
||||||
|
|
||||||
|
default:
|
||||||
|
return Empty().eraseToAnyPublisher()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,55 @@
|
||||||
|
import Combine
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
let isConsoleLoggingEnabled = false
|
||||||
|
|
||||||
|
#if DEBUG
|
||||||
|
let prefixLength = 2000
|
||||||
|
func loggerMiddleware() -> Middleware<AppState, AppAction> {
|
||||||
|
{ state, action in
|
||||||
|
let timeStr = dateFormatter.string(from: Date())
|
||||||
|
var actionStr = "\(action)"
|
||||||
|
actionStr = String(actionStr.prefix(prefixLength)) + " ..."
|
||||||
|
var stateStr = "\(state)"
|
||||||
|
stateStr = String(stateStr.prefix(prefixLength)) + " ..."
|
||||||
|
|
||||||
|
let str = "\(timeStr) ➡️ \(actionStr)\n\(timeStr) ✅ \(stateStr)\n"
|
||||||
|
print(str)
|
||||||
|
if isConsoleLoggingEnabled {
|
||||||
|
NSLog(str)
|
||||||
|
}
|
||||||
|
return Empty().eraseToAnyPublisher()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
func loggerMiddleware() -> Middleware<AppState, AppAction> {
|
||||||
|
{ _, _ in
|
||||||
|
Empty().eraseToAnyPublisher()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
enum LogLevels: String {
|
||||||
|
case info = "ℹ️"
|
||||||
|
case warning = "⚠️"
|
||||||
|
case error = "❌"
|
||||||
|
}
|
||||||
|
|
||||||
|
// For database errors logging
|
||||||
|
func logIt(_ level: LogLevels, _ message: String) {
|
||||||
|
#if DEBUG
|
||||||
|
let timeStr = dateFormatter.string(from: Date())
|
||||||
|
let str = "\(timeStr) \(level.rawValue) \(message)"
|
||||||
|
print(str)
|
||||||
|
if isConsoleLoggingEnabled {
|
||||||
|
NSLog(str)
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
private var dateFormatter: DateFormatter {
|
||||||
|
let formatter = DateFormatter()
|
||||||
|
formatter.locale = NSLocale(localeIdentifier: "en_US_POSIX") as Locale
|
||||||
|
formatter.dateFormat = "MM-dd HH:mm:ss.SSS"
|
||||||
|
return formatter
|
||||||
|
}
|
|
@ -0,0 +1,16 @@
|
||||||
|
import Combine
|
||||||
|
|
||||||
|
final class RostersMiddleware {
|
||||||
|
static let shared = RostersMiddleware()
|
||||||
|
|
||||||
|
func middleware(state _: AppState, action: AppAction) -> AnyPublisher<AppAction, Never> {
|
||||||
|
switch action {
|
||||||
|
case .databaseAction(.storedRostersLoaded(let rosters)):
|
||||||
|
return Just(.rostersAction(.rostersListUpdated(rosters)))
|
||||||
|
.eraseToAnyPublisher()
|
||||||
|
|
||||||
|
default:
|
||||||
|
return Empty().eraseToAnyPublisher()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,32 @@
|
||||||
|
import Combine
|
||||||
|
|
||||||
|
final class StartMiddleware {
|
||||||
|
static let shared = StartMiddleware()
|
||||||
|
|
||||||
|
func middleware(state: AppState, action: AppAction) -> AnyPublisher<AppAction, Never> {
|
||||||
|
switch action {
|
||||||
|
case .accountsAction(.accountsListUpdated(let accounts)):
|
||||||
|
if accounts.isEmpty {
|
||||||
|
if state.currentFlow == .start {
|
||||||
|
return Just(.startAction(.goTo(.welcomeScreen)))
|
||||||
|
.eraseToAnyPublisher()
|
||||||
|
} else {
|
||||||
|
return Empty().eraseToAnyPublisher()
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if state.currentFlow == .accounts, state.accountsState.navigation == .addAccount {
|
||||||
|
return Just(.changeFlow(.chats))
|
||||||
|
.eraseToAnyPublisher()
|
||||||
|
} else if state.currentFlow == .start {
|
||||||
|
return Just(.changeFlow(.chats))
|
||||||
|
.eraseToAnyPublisher()
|
||||||
|
} else {
|
||||||
|
return Empty().eraseToAnyPublisher()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
return Empty().eraseToAnyPublisher()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,85 @@
|
||||||
|
import Combine
|
||||||
|
import Foundation
|
||||||
|
import Martin
|
||||||
|
|
||||||
|
final class XMPPMiddleware {
|
||||||
|
static let shared = XMPPMiddleware()
|
||||||
|
private let service = XMPPService(manager: Database.shared)
|
||||||
|
private var cancellables: Set<AnyCancellable> = []
|
||||||
|
|
||||||
|
private init() {
|
||||||
|
service.clientState.sink { client, state in
|
||||||
|
let jid = client.userBareJid.stringValue
|
||||||
|
let status = ConnectionStatus.from(state)
|
||||||
|
let action = AppAction.xmppAction(.clientConnectionChanged(jid: jid, state: status))
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
store.dispatch(action)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.store(in: &cancellables)
|
||||||
|
}
|
||||||
|
|
||||||
|
func middleware(state: AppState, action: AppAction) -> AnyPublisher<AppAction, Never> {
|
||||||
|
switch action {
|
||||||
|
case .accountsAction(.tryAddAccountWithCredentials):
|
||||||
|
return Future<AppAction, Never> { [weak self] promise in
|
||||||
|
self?.service.updateClients(for: state.accountsState.accounts)
|
||||||
|
promise(.success(.empty))
|
||||||
|
}
|
||||||
|
.eraseToAnyPublisher()
|
||||||
|
|
||||||
|
case .accountsAction(.addAccountError):
|
||||||
|
return Future<AppAction, Never> { [weak self] promise in
|
||||||
|
self?.service.updateClients(for: state.accountsState.accounts)
|
||||||
|
promise(.success(.empty))
|
||||||
|
}
|
||||||
|
.eraseToAnyPublisher()
|
||||||
|
|
||||||
|
case .databaseAction(.storedAccountsLoaded(let accounts)):
|
||||||
|
return Future<AppAction, Never> { [weak self] promise in
|
||||||
|
self?.service.updateClients(for: accounts.filter { $0.isActive && !$0.isTemp })
|
||||||
|
promise(.success(.empty))
|
||||||
|
}
|
||||||
|
.eraseToAnyPublisher()
|
||||||
|
|
||||||
|
case .rostersAction(.addRoster(let ownerJID, let contactJID, let name, let groups)):
|
||||||
|
return Future<AppAction, Never> { [weak self] promise in
|
||||||
|
guard let service = self?.service, let client = service.clients.first(where: { $0.connectionConfiguration.userJid.stringValue == ownerJID }) else {
|
||||||
|
return promise(.success(.rostersAction(.addRosterError(reason: XMPPError.item_not_found.localizedDescription))))
|
||||||
|
}
|
||||||
|
let module = client.modulesManager.module(RosterModule.self)
|
||||||
|
module.addItem(jid: JID(contactJID), name: name, groups: groups, completionHandler: { result in
|
||||||
|
switch result {
|
||||||
|
case .success:
|
||||||
|
promise(.success(.rostersAction(.addRosterDone(jid: contactJID))))
|
||||||
|
|
||||||
|
case .failure(let error):
|
||||||
|
promise(.success(.rostersAction(.addRosterError(reason: error.localizedDescription))))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
.eraseToAnyPublisher()
|
||||||
|
|
||||||
|
case .rostersAction(.deleteRoster(let ownerJID, let contactJID)):
|
||||||
|
return Future<AppAction, Never> { [weak self] promise in
|
||||||
|
guard let service = self?.service, let client = service.clients.first(where: { $0.connectionConfiguration.userJid.stringValue == ownerJID }) else {
|
||||||
|
return promise(.success(.rostersAction(.rosterDeletingFailed(reason: XMPPError.item_not_found.localizedDescription))))
|
||||||
|
}
|
||||||
|
let module = client.modulesManager.module(RosterModule.self)
|
||||||
|
module.removeItem(jid: JID(contactJID), completionHandler: { result in
|
||||||
|
switch result {
|
||||||
|
case .success:
|
||||||
|
promise(.success(.empty))
|
||||||
|
|
||||||
|
case .failure(let error):
|
||||||
|
promise(.success(.rostersAction(.rosterDeletingFailed(reason: error.localizedDescription))))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
.eraseToAnyPublisher()
|
||||||
|
|
||||||
|
default:
|
||||||
|
return Empty().eraseToAnyPublisher()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
22
ConversationsClassic/AppCore/Models/Account.swift
Normal file
22
ConversationsClassic/AppCore/Models/Account.swift
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
import Foundation
|
||||||
|
import GRDB
|
||||||
|
import Martin
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
// MARK: - Account
|
||||||
|
struct Account: DBStorable {
|
||||||
|
var bareJid: String
|
||||||
|
var pass: String
|
||||||
|
var isActive: Bool
|
||||||
|
var isTemp: Bool // account which is added by user, but not yet logged in
|
||||||
|
var id: String { bareJid }
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Account: UniversalInputSelectionElement {
|
||||||
|
var text: String? { bareJid }
|
||||||
|
var icon: Image? { nil }
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Account {
|
||||||
|
static let databaseTableName = "accounts"
|
||||||
|
}
|
19
ConversationsClassic/AppCore/Models/Chat.swift
Normal file
19
ConversationsClassic/AppCore/Models/Chat.swift
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
import Foundation
|
||||||
|
import GRDB
|
||||||
|
|
||||||
|
enum ConversationType: Int, Codable, DatabaseValueConvertible {
|
||||||
|
case chat = 0
|
||||||
|
case room = 1
|
||||||
|
case channel = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Chat: DBStorable {
|
||||||
|
static let databaseTableName = "chats"
|
||||||
|
|
||||||
|
var id: String
|
||||||
|
var account: String
|
||||||
|
var participant: String
|
||||||
|
var type: ConversationType
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Chat: Equatable {}
|
27
ConversationsClassic/AppCore/Models/ConnectionStatus.swift
Normal file
27
ConversationsClassic/AppCore/Models/ConnectionStatus.swift
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
// This struct is simpliest variant of Martin's Client State.
|
||||||
|
// Just for more comfortable using in App State
|
||||||
|
import Foundation
|
||||||
|
import Martin
|
||||||
|
|
||||||
|
enum ConnectionStatus: Stateable {
|
||||||
|
case connecting
|
||||||
|
case connected(resumed: Bool = false)
|
||||||
|
case disconnecting
|
||||||
|
case disconnected(reason: String)
|
||||||
|
|
||||||
|
static func from(_ state: XMPPClient.State) -> ConnectionStatus {
|
||||||
|
switch state {
|
||||||
|
case .connecting:
|
||||||
|
return .connecting
|
||||||
|
|
||||||
|
case .connected(let resumed):
|
||||||
|
return .connected(resumed: resumed)
|
||||||
|
|
||||||
|
case .disconnecting:
|
||||||
|
return .disconnecting
|
||||||
|
|
||||||
|
case .disconnected(let reason):
|
||||||
|
return .disconnected(reason: reason.localizedDescription)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
30
ConversationsClassic/AppCore/Models/Message.swift
Normal file
30
ConversationsClassic/AppCore/Models/Message.swift
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import Foundation
|
||||||
|
import GRDB
|
||||||
|
|
||||||
|
enum MessageType: String, Codable, DatabaseValueConvertible {
|
||||||
|
case text
|
||||||
|
case image
|
||||||
|
case video
|
||||||
|
case audio
|
||||||
|
case file
|
||||||
|
case location
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Message: DBStorable, Equatable {
|
||||||
|
static let databaseTableName = "messages"
|
||||||
|
|
||||||
|
let id: String
|
||||||
|
let chatId: String
|
||||||
|
let fromJid: String
|
||||||
|
let toJid: String
|
||||||
|
let timestamp: Date
|
||||||
|
let body: String?
|
||||||
|
// var isReaded: Bool
|
||||||
|
// let subject: String?
|
||||||
|
// let threadId: String?
|
||||||
|
// let errorType: String?
|
||||||
|
|
||||||
|
var type: MessageType {
|
||||||
|
.text
|
||||||
|
}
|
||||||
|
}
|
62
ConversationsClassic/AppCore/Models/Roster.swift
Normal file
62
ConversationsClassic/AppCore/Models/Roster.swift
Normal file
|
@ -0,0 +1,62 @@
|
||||||
|
import Foundation
|
||||||
|
import GRDB
|
||||||
|
import Martin
|
||||||
|
|
||||||
|
struct RosterVersion: DBStorable {
|
||||||
|
static let databaseTableName = "rosterVersions"
|
||||||
|
|
||||||
|
var bareJid: String
|
||||||
|
var version: String
|
||||||
|
var id: String { bareJid }
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Roster: DBStorable {
|
||||||
|
static let databaseTableName = "rosters"
|
||||||
|
|
||||||
|
var bareJid: String = ""
|
||||||
|
var contactBareJid: String
|
||||||
|
var name: String?
|
||||||
|
var subscription: String
|
||||||
|
var ask: Bool
|
||||||
|
var data: DBRosterData
|
||||||
|
var locallyDeleted: Bool = false
|
||||||
|
|
||||||
|
var id: String { "\(bareJid)-\(contactBareJid)" }
|
||||||
|
}
|
||||||
|
|
||||||
|
struct DBRosterData: Codable, DatabaseValueConvertible {
|
||||||
|
let groups: [String]
|
||||||
|
let annotations: [RosterItemAnnotation]
|
||||||
|
|
||||||
|
public var databaseValue: DatabaseValue {
|
||||||
|
let encoder = JSONEncoder()
|
||||||
|
// swiftlint:disable:next force_try
|
||||||
|
let data = try! encoder.encode(self)
|
||||||
|
return data.databaseValue
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func fromDatabaseValue(_ dbValue: DatabaseValue) -> Self? {
|
||||||
|
guard let data = Data.fromDatabaseValue(dbValue) else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
let decoder = JSONDecoder()
|
||||||
|
// swiftlint:disable:next force_try
|
||||||
|
return try! decoder.decode(Self.self, from: data)
|
||||||
|
}
|
||||||
|
|
||||||
|
static func == (lhs: DBRosterData, rhs: DBRosterData) -> Bool {
|
||||||
|
lhs.groups == rhs.groups && lhs.annotations == rhs.annotations
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension RosterItemAnnotation: Equatable {
|
||||||
|
public static func == (lhs: RosterItemAnnotation, rhs: RosterItemAnnotation) -> Bool {
|
||||||
|
lhs.type == rhs.type && lhs.values == rhs.values
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension Roster: Equatable {
|
||||||
|
static func == (lhs: Roster, rhs: Roster) -> Bool {
|
||||||
|
lhs.bareJid == rhs.bareJid && lhs.contactBareJid == rhs.contactBareJid
|
||||||
|
}
|
||||||
|
}
|
22
ConversationsClassic/AppCore/Reducers/AccountsReducer.swift
Normal file
22
ConversationsClassic/AppCore/Reducers/AccountsReducer.swift
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
extension AccountsState {
|
||||||
|
static func reducer(state: inout AccountsState, action: AccountsAction) {
|
||||||
|
switch action {
|
||||||
|
case .accountsListUpdated(let accounts):
|
||||||
|
state.accounts = accounts
|
||||||
|
|
||||||
|
case .goTo(let navigation):
|
||||||
|
state.navigation = navigation
|
||||||
|
|
||||||
|
case .tryAddAccountWithCredentials(let login, let password):
|
||||||
|
let account = Account(bareJid: login, pass: password, isActive: true, isTemp: true)
|
||||||
|
state.accounts.append(account)
|
||||||
|
|
||||||
|
case .addAccountError(let jid, let reason):
|
||||||
|
state.accounts = state.accounts.filter { $0.bareJid != jid }
|
||||||
|
state.addAccountError = reason
|
||||||
|
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
29
ConversationsClassic/AppCore/Reducers/AppReducer.swift
Normal file
29
ConversationsClassic/AppCore/Reducers/AppReducer.swift
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
extension AppState {
|
||||||
|
static func reducer(state: inout AppState, action: AppAction) {
|
||||||
|
switch action {
|
||||||
|
case .flushState:
|
||||||
|
state = AppState()
|
||||||
|
|
||||||
|
case .changeFlow(let flow):
|
||||||
|
state.previousFlow = state.currentFlow
|
||||||
|
state.currentFlow = flow
|
||||||
|
|
||||||
|
case .startAction(let action):
|
||||||
|
StartState.reducer(state: &state.startState, action: action)
|
||||||
|
|
||||||
|
case .databaseAction, .xmppAction, .empty:
|
||||||
|
break // database and xmpp actions are processed by other middlewares
|
||||||
|
|
||||||
|
case .accountsAction(let action):
|
||||||
|
AccountsState.reducer(state: &state.accountsState, action: action)
|
||||||
|
|
||||||
|
case .rostersAction(let action):
|
||||||
|
RostersState.reducer(state: &state.rostersState, action: action)
|
||||||
|
|
||||||
|
case .chatsAction(let action):
|
||||||
|
ChatsState.reducer(state: &state.chatsState, action: action)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
ConversationsClassic/AppCore/Reducers/ChatsReducer.swift
Normal file
11
ConversationsClassic/AppCore/Reducers/ChatsReducer.swift
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
extension ChatsState {
|
||||||
|
static func reducer(state: inout ChatsState, action: ChatsAction) {
|
||||||
|
switch action {
|
||||||
|
case .chatsListUpdated(let chats):
|
||||||
|
state.chats = chats
|
||||||
|
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
25
ConversationsClassic/AppCore/Reducers/RostersReducer.swift
Normal file
25
ConversationsClassic/AppCore/Reducers/RostersReducer.swift
Normal file
|
@ -0,0 +1,25 @@
|
||||||
|
extension RostersState {
|
||||||
|
static func reducer(state: inout RostersState, action: RostersAction) {
|
||||||
|
switch action {
|
||||||
|
case .addRosterDone(let jid):
|
||||||
|
state.newAddedRosterJid = jid
|
||||||
|
state.newAddedRosterError = nil
|
||||||
|
|
||||||
|
case .addRosterError(let reason):
|
||||||
|
state.newAddedRosterJid = nil
|
||||||
|
state.newAddedRosterError = reason
|
||||||
|
|
||||||
|
case .rostersListUpdated(let rosters):
|
||||||
|
state.rosters = rosters
|
||||||
|
|
||||||
|
case .markRosterAsLocallyDeleted, .deleteRoster:
|
||||||
|
state.deleteRosterError = nil
|
||||||
|
|
||||||
|
case .rosterDeletingFailed(let reson):
|
||||||
|
state.deleteRosterError = reson
|
||||||
|
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
11
ConversationsClassic/AppCore/Reducers/StartReducer.swift
Normal file
11
ConversationsClassic/AppCore/Reducers/StartReducer.swift
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
extension StartState {
|
||||||
|
static func reducer(state: inout StartState, action: StartAction) {
|
||||||
|
switch action {
|
||||||
|
case .loadStoredAccounts:
|
||||||
|
break
|
||||||
|
|
||||||
|
case .goTo(let navigation):
|
||||||
|
state.navigation = navigation
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
18
ConversationsClassic/AppCore/State/AccountsState.swift
Normal file
18
ConversationsClassic/AppCore/State/AccountsState.swift
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
enum AccountNavigationState: Stateable {
|
||||||
|
case addAccount
|
||||||
|
}
|
||||||
|
|
||||||
|
struct AccountsState: Stateable {
|
||||||
|
var navigation: AccountNavigationState
|
||||||
|
var accounts: [Account]
|
||||||
|
|
||||||
|
var addAccountError: String?
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Init
|
||||||
|
extension AccountsState {
|
||||||
|
init() {
|
||||||
|
navigation = .addAccount
|
||||||
|
accounts = []
|
||||||
|
}
|
||||||
|
}
|
34
ConversationsClassic/AppCore/State/AppState.swift
Normal file
34
ConversationsClassic/AppCore/State/AppState.swift
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
enum AppFlow: Codable {
|
||||||
|
case start
|
||||||
|
case accounts
|
||||||
|
case chats
|
||||||
|
case contacts
|
||||||
|
case settings
|
||||||
|
}
|
||||||
|
|
||||||
|
struct AppState: Stateable {
|
||||||
|
var appVersion: String
|
||||||
|
var previousFlow: AppFlow
|
||||||
|
var currentFlow: AppFlow
|
||||||
|
|
||||||
|
var startState: StartState
|
||||||
|
var accountsState: AccountsState
|
||||||
|
var rostersState: RostersState
|
||||||
|
var chatsState: ChatsState
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Init
|
||||||
|
extension AppState {
|
||||||
|
init() {
|
||||||
|
appVersion = Const.appVersion
|
||||||
|
previousFlow = .start
|
||||||
|
currentFlow = .start
|
||||||
|
|
||||||
|
startState = StartState()
|
||||||
|
accountsState = AccountsState()
|
||||||
|
rostersState = RostersState()
|
||||||
|
chatsState = ChatsState()
|
||||||
|
}
|
||||||
|
}
|
10
ConversationsClassic/AppCore/State/ChatsState.swift
Normal file
10
ConversationsClassic/AppCore/State/ChatsState.swift
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
struct ChatsState: Stateable {
|
||||||
|
var chats: [Chat]
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Init
|
||||||
|
extension ChatsState {
|
||||||
|
init() {
|
||||||
|
chats = []
|
||||||
|
}
|
||||||
|
}
|
15
ConversationsClassic/AppCore/State/RostersState.swift
Normal file
15
ConversationsClassic/AppCore/State/RostersState.swift
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
struct RostersState: Stateable {
|
||||||
|
var rosters: [Roster]
|
||||||
|
|
||||||
|
var newAddedRosterJid: String?
|
||||||
|
var newAddedRosterError: String?
|
||||||
|
|
||||||
|
var deleteRosterError: String?
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Init
|
||||||
|
extension RostersState {
|
||||||
|
init() {
|
||||||
|
rosters = []
|
||||||
|
}
|
||||||
|
}
|
15
ConversationsClassic/AppCore/State/StartState.swift
Normal file
15
ConversationsClassic/AppCore/State/StartState.swift
Normal file
|
@ -0,0 +1,15 @@
|
||||||
|
enum StartNavigationState: Stateable {
|
||||||
|
case startScreen
|
||||||
|
case welcomeScreen
|
||||||
|
}
|
||||||
|
|
||||||
|
struct StartState: Stateable {
|
||||||
|
var navigation: StartNavigationState
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Init
|
||||||
|
extension StartState {
|
||||||
|
init() {
|
||||||
|
navigation = .startScreen
|
||||||
|
}
|
||||||
|
}
|
90
ConversationsClassic/AppCore/XMPP/XMPPService.swift
Normal file
90
ConversationsClassic/AppCore/XMPP/XMPPService.swift
Normal file
|
@ -0,0 +1,90 @@
|
||||||
|
import Combine
|
||||||
|
import Foundation
|
||||||
|
import GRDB
|
||||||
|
import Martin
|
||||||
|
|
||||||
|
protocol MartinsManager: Martin.RosterManager & Martin.ChatManager {}
|
||||||
|
|
||||||
|
final class XMPPService: ObservableObject {
|
||||||
|
private let manager: MartinsManager
|
||||||
|
private let clientStatePublisher = PassthroughSubject<(XMPPClient, XMPPClient.State), Never>()
|
||||||
|
private var clientStateCancellables: [AnyCancellable] = []
|
||||||
|
|
||||||
|
@Published private(set) var clients: [XMPPClient] = []
|
||||||
|
var clientState: AnyPublisher<(XMPPClient, XMPPClient.State), Never> {
|
||||||
|
clientStatePublisher.eraseToAnyPublisher()
|
||||||
|
}
|
||||||
|
|
||||||
|
init(manager: MartinsManager) {
|
||||||
|
self.manager = manager
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateClients(for accounts: [Account]) {
|
||||||
|
// get simple diff
|
||||||
|
let forAdd = accounts
|
||||||
|
.filter { !self.clients.map { $0.connectionConfiguration.userJid.stringValue }.contains($0.bareJid) }
|
||||||
|
let forRemove = clients
|
||||||
|
.map { $0.connectionConfiguration.userJid.stringValue }
|
||||||
|
.filter { !accounts.map { $0.bareJid }.contains($0) }
|
||||||
|
|
||||||
|
// init and add clients
|
||||||
|
for account in forAdd {
|
||||||
|
let client = makeClient(for: account, with: manager)
|
||||||
|
clients.append(client)
|
||||||
|
let cancellable = client.$state
|
||||||
|
.sink { [weak self] state in
|
||||||
|
self?.clientStatePublisher.send((client, state))
|
||||||
|
}
|
||||||
|
|
||||||
|
clientStateCancellables.append(cancellable)
|
||||||
|
client.login()
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove clients
|
||||||
|
for jid in forRemove {
|
||||||
|
deinitClient(jid: jid)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func makeClient(for account: Account, with manager: MartinsManager) -> XMPPClient {
|
||||||
|
let client = XMPPClient()
|
||||||
|
|
||||||
|
// register modules
|
||||||
|
// core modules RFC 6120
|
||||||
|
client.modulesManager.register(StreamFeaturesModule())
|
||||||
|
client.modulesManager.register(SaslModule())
|
||||||
|
client.modulesManager.register(AuthModule())
|
||||||
|
client.modulesManager.register(SessionEstablishmentModule())
|
||||||
|
client.modulesManager.register(ResourceBinderModule())
|
||||||
|
client.modulesManager.register(DiscoveryModule(identity: .init(category: "client", type: "iOS", name: Const.appName)))
|
||||||
|
|
||||||
|
// messaging modules RFC 6121
|
||||||
|
client.modulesManager.register(RosterModule(rosterManager: manager))
|
||||||
|
client.modulesManager.register(PresenceModule())
|
||||||
|
|
||||||
|
client.modulesManager.register(PubSubModule())
|
||||||
|
client.modulesManager.register(MessageModule(chatManager: manager))
|
||||||
|
client.modulesManager.register(MessageCarbonsModule())
|
||||||
|
client.modulesManager.register(MessageArchiveManagementModule())
|
||||||
|
|
||||||
|
// extensions
|
||||||
|
client.modulesManager.register(SoftwareVersionModule())
|
||||||
|
client.modulesManager.register(PingModule())
|
||||||
|
client.connectionConfiguration.userJid = .init(account.bareJid)
|
||||||
|
client.connectionConfiguration.credentials = .password(password: account.pass)
|
||||||
|
|
||||||
|
// add client to clients
|
||||||
|
return client
|
||||||
|
}
|
||||||
|
|
||||||
|
func deinitClient(jid: String) {
|
||||||
|
if let index = clients.firstIndex(where: { $0.connectionConfiguration.userJid.stringValue == jid }) {
|
||||||
|
let client = clients.remove(at: index)
|
||||||
|
_ = client.disconnect()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getClient(for jid: String) -> XMPPClient? {
|
||||||
|
clients.first { $0.connectionConfiguration.userJid.stringValue == jid }
|
||||||
|
}
|
||||||
|
}
|
27
ConversationsClassic/ConversationsClassicApp.swift
Normal file
27
ConversationsClassic/ConversationsClassicApp.swift
Normal file
|
@ -0,0 +1,27 @@
|
||||||
|
import Combine
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
let appState = AppState()
|
||||||
|
let store = AppStore(
|
||||||
|
initialState: appState,
|
||||||
|
reducer: AppState.reducer,
|
||||||
|
middlewares: [
|
||||||
|
loggerMiddleware(),
|
||||||
|
StartMiddleware.shared.middleware,
|
||||||
|
DatabaseMiddleware.shared.middleware,
|
||||||
|
AccountsMiddleware.shared.middleware,
|
||||||
|
XMPPMiddleware.shared.middleware,
|
||||||
|
RostersMiddleware.shared.middleware,
|
||||||
|
ChatsMiddleware.shared.middleware
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
@main
|
||||||
|
struct ConversationsClassic: App {
|
||||||
|
var body: some Scene {
|
||||||
|
WindowGroup {
|
||||||
|
BaseNavigationView()
|
||||||
|
.environmentObject(store)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
2
ConversationsClassic/Generated/.gitignore
vendored
Normal file
2
ConversationsClassic/Generated/.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
*
|
||||||
|
!.gitignore
|
7
ConversationsClassic/Helpers/Bool+Extensions.swift
Normal file
7
ConversationsClassic/Helpers/Bool+Extensions.swift
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
extension Bool {
|
||||||
|
var intValue: Int {
|
||||||
|
self ? 1 : 0
|
||||||
|
}
|
||||||
|
}
|
30
ConversationsClassic/Helpers/Const.swift
Normal file
30
ConversationsClassic/Helpers/Const.swift
Normal file
|
@ -0,0 +1,30 @@
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
enum Const {
|
||||||
|
// Network
|
||||||
|
#if DEBUG
|
||||||
|
static let baseUrl = "staging.some.com/api"
|
||||||
|
#else
|
||||||
|
static let baseUrl = "prod.some.com/api"
|
||||||
|
#endif
|
||||||
|
static let requestTimeout = 15.0
|
||||||
|
static let networkLogging = true
|
||||||
|
|
||||||
|
// App
|
||||||
|
static var appVersion: String {
|
||||||
|
let info = Bundle.main.infoDictionary
|
||||||
|
let appVersion = info?["CFBundleShortVersionString"] as? String ?? "Unknown"
|
||||||
|
let appBuild = info?[kCFBundleVersionKey as String] as? String ?? "Unknown"
|
||||||
|
return "v \(appVersion)(\(appBuild))"
|
||||||
|
}
|
||||||
|
|
||||||
|
static var appName: String {
|
||||||
|
Bundle.main.bundleIdentifier ?? "Conversations Classic iOS"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trusted servers
|
||||||
|
enum TrustedServers: String {
|
||||||
|
case narayana = "narayana.im"
|
||||||
|
case conversations = "conversations.im"
|
||||||
|
}
|
||||||
|
}
|
7
ConversationsClassic/Helpers/String+Extensions.swift
Normal file
7
ConversationsClassic/Helpers/String+Extensions.swift
Normal file
|
@ -0,0 +1,7 @@
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
extension String {
|
||||||
|
var firstLetter: String {
|
||||||
|
String(prefix(1)).uppercased()
|
||||||
|
}
|
||||||
|
}
|
32
ConversationsClassic/Helpers/UserDefaultsWrapper.swift
Normal file
32
ConversationsClassic/Helpers/UserDefaultsWrapper.swift
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
// Wrapper
|
||||||
|
@propertyWrapper
|
||||||
|
struct Storage<T> {
|
||||||
|
private let key: String
|
||||||
|
private let defaultValue: T
|
||||||
|
|
||||||
|
init(key: String, defaultValue: T) {
|
||||||
|
self.key = key
|
||||||
|
self.defaultValue = defaultValue
|
||||||
|
}
|
||||||
|
|
||||||
|
var wrappedValue: T {
|
||||||
|
get {
|
||||||
|
// Read value from UserDefaults
|
||||||
|
UserDefaults.standard.object(forKey: key) as? T ?? defaultValue
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
// Set value to UserDefaults
|
||||||
|
UserDefaults.standard.set(newValue, forKey: key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Storage
|
||||||
|
private let keyLocalizationSelected = "conversations.classic.user.defaults.localizationSelected"
|
||||||
|
|
||||||
|
enum UserSettings {
|
||||||
|
@Storage(key: keyLocalizationSelected, defaultValue: false)
|
||||||
|
static var localizationSelectedByUser: Bool
|
||||||
|
}
|
|
@ -0,0 +1,6 @@
|
||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
},
|
||||||
|
"properties" : {
|
||||||
|
"provides-namespace" : true
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "228",
|
||||||
|
"green" : "228",
|
||||||
|
"red" : "228"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "235",
|
||||||
|
"green" : "235",
|
||||||
|
"red" : "235"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0.180",
|
||||||
|
"green" : "0.180",
|
||||||
|
"red" : "0.180"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0.471",
|
||||||
|
"green" : "0.471",
|
||||||
|
"red" : "0.471"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "189",
|
||||||
|
"green" : "189",
|
||||||
|
"red" : "189"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "1.000",
|
||||||
|
"green" : "1.000",
|
||||||
|
"red" : "1.000"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,9 @@
|
||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
},
|
||||||
|
"properties" : {
|
||||||
|
"provides-namespace" : true
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0.976",
|
||||||
|
"green" : "0.792",
|
||||||
|
"red" : "0.565"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0.965",
|
||||||
|
"green" : "0.710",
|
||||||
|
"red" : "0.392"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0.953",
|
||||||
|
"green" : "0.588",
|
||||||
|
"red" : "0.129"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0.824",
|
||||||
|
"green" : "0.463",
|
||||||
|
"red" : "0.098"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0.855",
|
||||||
|
"green" : "0.659",
|
||||||
|
"red" : "0.624"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0.796",
|
||||||
|
"green" : "0.525",
|
||||||
|
"red" : "0.475"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0.710",
|
||||||
|
"green" : "0.318",
|
||||||
|
"red" : "0.247"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0.624",
|
||||||
|
"green" : "0.247",
|
||||||
|
"red" : "0.188"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0.980",
|
||||||
|
"green" : "0.831",
|
||||||
|
"red" : "0.506"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"color" : {
|
||||||
|
"color-space" : "srgb",
|
||||||
|
"components" : {
|
||||||
|
"alpha" : "1.000",
|
||||||
|
"blue" : "0.969",
|
||||||
|
"green" : "0.765",
|
||||||
|
"red" : "0.310"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue