Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SwiftGen Errors on Swift 6 #1110

Open
Nathan-Molby opened this issue Mar 5, 2024 · 5 comments
Open

SwiftGen Errors on Swift 6 #1110

Nathan-Molby opened this issue Mar 5, 2024 · 5 comments

Comments

@Nathan-Molby
Copy link

SwiftGen has multiple errors with concurrency in Swift 6. Specifically, all of the Storyboard functions result in the following error:
Call to main actor-isolated initializer 'init(name:bundle:)' in a synchronous nonisolated context.

In order to replicate Swift 6, you can turn on "Complete" strict concurrency checking in Build Settings and see the errors.

This will make SwiftGen unusable when Swift 6 is released. Please update SwiftGen to conform to the concurrency checks introduced in Swift 6.

@brzzdev
Copy link

brzzdev commented Mar 12, 2024

Can confirm generated Images and Sounds also have issues.

Static property 'exampleImage' is not concurrency-safe because it is not either conforming to 'Sendable' or isolated to a global actor; this is an error in Swift 6

@uhooi
Copy link

uhooi commented Mar 14, 2024

The same is true for ColorAsset and ImageAsset within an Asset.

Static property 'exampleColor' is not concurrency-safe because it is not either conforming to 'Sendable' or isolated to a global actor; this is an error in Swift 6

@Iron-Ham
Copy link

Iron-Ham commented Apr 3, 2024

One option is to create your own stencil template for generating assets, like this one:

Potential Swift-6 Stencil template

// swiftlint:disable all
// Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen

{% if catalogs %}
{% macro hasValuesBlock assets filter %}
  {%- for asset in assets -%}
    {%- if asset.type == filter -%}
      1
    {%- elif asset.items -%}
      {% call hasValuesBlock asset.items filter %}
    {%- endif -%}
  {%- endfor -%}
{% endmacro %}
{% 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 %}
{% set hasARResourceGroup %}{% for catalog in catalogs %}{% call hasValuesBlock catalog.assets "arresourcegroup" %}{% endfor %}{% endset %}
{% set hasColor %}{% for catalog in catalogs %}{% call hasValuesBlock catalog.assets "color" %}{% endfor %}{% endset %}
{% set hasData %}{% for catalog in catalogs %}{% call hasValuesBlock catalog.assets "data" %}{% endfor %}{% endset %}
{% set hasImage %}{% for catalog in catalogs %}{% call hasValuesBlock catalog.assets "image" %}{% endfor %}{% endset %}
{% set hasSymbol %}{% for catalog in catalogs %}{% call hasValuesBlock catalog.assets "symbol" %}{% endfor %}{% endset %}
#if os(macOS)
  import AppKit
#elseif os(iOS)
{% if hasARResourceGroup %}
  import ARKit
{% endif %}
  import UIKit
#elseif os(tvOS) || os(watchOS)
  import UIKit
#endif
#if canImport(SwiftUI)
  import SwiftUI
#endif

// Deprecated typealiases
{% if hasColor %}
@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 hasImage %}
@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
  {% set hasItems %}{% call hasValuesBlock assets "arresourcegroup" %}{% endset %}
  {% if hasItems %}
  @available(*, deprecated, message: "All values properties are now deprecated")
  {{accessModifier}} static let allResourceGroups: [{{arResourceGroupType}}] = [
    {% filter indent:2," ",true %}{% call allValuesBlock assets "arresourcegroup" "" %}{% endfilter %}
  ]
  {% endif %}
  {% set hasItems %}{% call hasValuesBlock assets "color" %}{% endset %}
  {% if hasItems %}
  @available(*, deprecated, message: "All values properties are now deprecated")
  {{accessModifier}} static let allColors: [{{colorType}}] = [
    {% filter indent:2," ",true %}{% call allValuesBlock assets "color" "" %}{% endfilter %}
  ]
  {% endif %}
  {% set hasItems %}{% call hasValuesBlock assets "data" %}{% endset %}
  {% if hasItems %}
  @available(*, deprecated, message: "All values properties are now deprecated")
  {{accessModifier}} static let allDataAssets: [{{dataType}}] = [
    {% filter indent:2," ",true %}{% call allValuesBlock assets "data" "" %}{% endfilter %}
  ]
  {% endif %}
  {% set hasItems %}{% call hasValuesBlock assets "image" %}{% endset %}
  {% if hasItems %}
  @available(*, deprecated, message: "All values properties are now deprecated")
  {{accessModifier}} static let allImages: [{{imageType}}] = [
    {% filter indent:2," ",true %}{% call allValuesBlock assets "image" "" %}{% endfilter %}
  ]
  {% endif %}
  {% set hasItems %}{% call hasValuesBlock assets "symbol" %}{% endset %}
  {% if hasItems %}
  @available(*, deprecated, message: "All values properties are now deprecated")
  {{accessModifier}} static let allSymbols: [{{symbolType}}] = [
    {% filter indent:2," ",true %}{% 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," ",true %}{% 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}} {
    {% if catalog.assets %}
    {% filter indent:2," ",true %}{% call enumBlock catalog.assets %}{% endfilter %}
    {% endif %}
  }
  {% endfor %}
  {% else %}
  {% call enumBlock catalogs.first.assets %}
  {% endif %}
}
// swiftlint:enable identifier_name line_length nesting type_body_length type_name

// MARK: - Implementation Details
{% if hasARResourceGroup %}

{{accessModifier}} struct {{arResourceGroupType}}: Sendable {
  {{accessModifier}} let 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 hasColor %}

{{accessModifier}} final class {{colorType}}: Sendable {
  {{accessModifier}} let 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}} let color: 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

  #if canImport(SwiftUI)
  @available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *)
  {{accessModifier}} var swiftUIColor: SwiftUI.Color {
    SwiftUI.Color(uiColor: color)
  }
  #endif

  fileprivate init(name: String) {
    self.name = name
    guard let color = Color(assetName: name) else {
      fatalError("Unable to load color asset named \(name).")
    }
    self.color = color
  }
}

{{accessModifier}} extension {{colorType}}.Color {
  @available(iOS 11.0, tvOS 11.0, watchOS 4.0, macOS 10.13, *)
  convenience init?(assetName: String) {
    let bundle = {{param.bundle|default:"BundleToken.bundle"}}
    #if os(iOS) || os(tvOS)
    self.init(named: assetName, in: bundle, compatibleWith: nil)
    #elseif os(macOS)
    self.init(named: NSColor.Name(assetName), bundle: bundle)
    #elseif os(watchOS)
    self.init(named: assetName)
    #endif
  }
}

#if canImport(SwiftUI)
@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *)
{{accessModifier}} extension SwiftUI.Color {
  init(assetName: String) {
    let bundle = {{param.bundle|default:"BundleToken.bundle"}}
    self.init(assetName, bundle: bundle)
  }
}
#endif
{% endif %}
{% if hasData %}

{{accessModifier}} struct {{dataType}}: Sendable {
  {{accessModifier}} let 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 hasImage %}

{{accessModifier}} struct {{imageType}}: Sendable {
  {{accessModifier}} let 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

  #if canImport(SwiftUI)
  @available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *)
  {{accessModifier}} var swiftUIImage: SwiftUI.Image {
    SwiftUI.Image(asset: self)
  }
  #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
  }
}

#if canImport(SwiftUI)
@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *)
{{accessModifier}} extension SwiftUI.Image {
  init(asset: {{imageType}}) {
    let bundle = {{param.bundle|default:"BundleToken.bundle"}}
    self.init(asset.name, bundle: bundle)
  }

  init(asset: {{imageType}}, label: Text) {
    let bundle = {{param.bundle|default:"BundleToken.bundle"}}
    self.init(asset.name, bundle: bundle, label: label)
  }

  init(decorative asset: {{imageType}}) {
    let bundle = {{param.bundle|default:"BundleToken.bundle"}}
    self.init(decorative: asset.name, bundle: bundle)
  }
}
#endif
{% endif %}
{% if hasSymbol %}

{{accessModifier}} struct {{symbolType}}: Sendable {
  {{accessModifier}} let 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

  #if canImport(SwiftUI)
  @available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *)
  {{accessModifier}} var swiftUIImage: SwiftUI.Image {
    SwiftUI.Image(asset: self)
  }
  #endif
}

#if canImport(SwiftUI)
@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *)
{{accessModifier}} extension SwiftUI.Image {
  init(asset: {{symbolType}}) {
    let bundle = {{param.bundle|default:"BundleToken.bundle"}}
    self.init(asset.name, bundle: bundle)
  }

  init(asset: {{symbolType}}, label: Text) {
    let bundle = {{param.bundle|default:"BundleToken.bundle"}}
    self.init(asset.name, bundle: bundle, label: label)
  }

  init(decorative asset: {{symbolType}}) {
    let bundle = {{param.bundle|default:"BundleToken.bundle"}}
    self.init(decorative: asset.name, bundle: bundle)
  }
}
#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 %}

This will change the generated Assets to look something like this:

Image

Since these will all be defined in an enum like so:

public enum Asset {
  static let myColor = ColorAsset(name: "...")
  static let myColor2 = ColorAsset(name: "...")
  // etc
}

the static lets are inherently lazy – and so using a let property should be the same as using a lazy var.

@emadhegab
Copy link

any update or intension to fix this ?

@SergeyPetrachkov
Copy link

SergeyPetrachkov commented Apr 29, 2024

Just opened this PR #1119 , should cover at least assets part

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

6 participants