Skip to content

Commit e3672ef

Browse files
fix: resource bundle accessor with swift concurrency + swift 6 mode (#1559)
In swift 6 mode + swift concurrency enabled the logic today cannot build with the following error: ```terminal error: static property 'module' is not concurrency-safe because it is nonisolated global shared mutable state 9 | /// This is a workaround for the provided module static var that doesn't always work. 10 | /// https://forums.swift.org/t/unable-to-find-bundle-in-package-target-tests-when-package-depends-on-another-package-containing-resources-accessed-via-bundle-module/43974/5 11 | static var module: Bundle = { | |- error: static property 'module' is not concurrency-safe because it is nonisolated global shared mutable state | |- note: convert 'module' to a 'let' constant to make 'Sendable' shared state immutable | |- note: annotate 'module' with '@mainactor' if property should only be accessed from the main actor | `- note: disable concurrency-safety checks if accesses are protected by an external synchronization mechanism ``` This change makes sure it can build --------- Signed-off-by: Maxwell Elliott <maxwell@elliott.now> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
1 parent ba14d98 commit e3672ef

File tree

9 files changed

+113
-1
lines changed

9 files changed

+113
-1
lines changed

examples/resources_example/swift/Package.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ let package = Package(
88
.package(path: "../third_party/another_package_with_resources"),
99
.package(path: "../third_party/app_lovin_sdk"),
1010
.package(path: "../third_party/package_with_resources"),
11+
.package(path: "../third_party/package_with_resources_swift_6"),
1112
.package(url: "https://github.com/Iterable/swift-sdk", from: "6.5.11"),
1213
.package(url: "https://github.com/SDWebImage/SDWebImageSwiftUI.git", from: "3.1.3"),
1314
.package(url: "https://github.com/google/GoogleSignIn-iOS", from: "8.0.0"),
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
// swift-tools-version:6.0
2+
// The swift-tools-version declares the minimum version of Swift required to build this package.
3+
4+
import PackageDescription
5+
6+
let package = Package(
7+
name: "package-with-resources-swift-6",
8+
defaultLocalization: "en",
9+
platforms: [.iOS(.v13), .macOS(.v13)],
10+
products: [
11+
.library(name: "CoolUI", targets: ["CoolUI"]),
12+
],
13+
targets: [
14+
.target(
15+
name: "CoolUI",
16+
resources: [.process("Resources")],
17+
swiftSettings: [
18+
// Future Swift features
19+
.enableUpcomingFeature("ExistentialAny"),
20+
.enableUpcomingFeature("InternalImportsByDefault"),
21+
22+
.swiftLanguageMode(.v6)
23+
]
24+
),
25+
.testTarget(
26+
name: "CoolUITests",
27+
dependencies: ["CoolUI"],
28+
swiftSettings: [
29+
// Future Swift features
30+
.enableUpcomingFeature("ExistentialAny"),
31+
.enableUpcomingFeature("InternalImportsByDefault"),
32+
33+
.swiftLanguageMode(.v6)
34+
]
35+
)
36+
]
37+
)
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
public import SwiftUI
2+
3+
public enum CoolStuff {
4+
public static func title() -> Text {
5+
return Text("Title", bundle: .module)
6+
}
7+
8+
public static func image() -> Image {
9+
return Image("avatar", bundle: .module)
10+
}
11+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
{
2+
"info" : {
3+
"author" : "xcode",
4+
"version" : 1
5+
}
6+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"images" : [
3+
{
4+
"filename" : "ai_chatbot_avatar.pdf",
5+
"idiom" : "universal"
6+
}
7+
],
8+
"info" : {
9+
"author" : "xcode",
10+
"version" : 1
11+
},
12+
"properties" : {
13+
"preserves-vector-representation" : true
14+
}
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
"Title" = "Hello, Avatar!";
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
import Foundation
2+
import XCTest
3+
4+
@testable import CoolUI
5+
6+
class BundleNameTest: XCTestCase {
7+
func testBundleName() {
8+
let bundle = Bundle.bundle(named: "package-with-resources_CoolUI")
9+
XCTAssertNotNil(bundle)
10+
}
11+
}
12+
13+
private class BundleFinder {}
14+
15+
extension Foundation.Bundle {
16+
static func bundle(named bundleName: String) -> Bundle? {
17+
let candidates = [
18+
// Bundle should be present here when the package is linked into an App.
19+
Bundle.main.resourceURL,
20+
21+
// Bundle should be present here when the package is linked into a framework.
22+
Bundle(for: BundleFinder.self).resourceURL,
23+
24+
// For command-line tools.
25+
Bundle.main.bundleURL,
26+
27+
// Bundle should be present here when running previews from a different package (this is the path to "…/Debug-iphonesimulator/").
28+
Bundle(for: BundleFinder.self).resourceURL?.deletingLastPathComponent().deletingLastPathComponent().deletingLastPathComponent(),
29+
Bundle(for: BundleFinder.self).resourceURL?.deletingLastPathComponent().deletingLastPathComponent(),
30+
]
31+
32+
for candidate in candidates {
33+
let bundlePath = candidate?.appendingPathComponent(bundleName + ".bundle")
34+
if let bundle = bundlePath.flatMap(Bundle.init(url:)) {
35+
return bundle
36+
}
37+
}
38+
39+
return nil
40+
}
41+
}

swiftpkg/internal/ResourceBundleAccessor.swift.tmpl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ extension Foundation.Bundle {
88

99
/// This is a workaround for the provided module static var that doesn't always work.
1010
/// https://forums.swift.org/t/unable-to-find-bundle-in-package-target-tests-when-package-depends-on-another-package-containing-resources-accessed-via-bundle-module/43974/5
11-
static var module: Bundle = {
11+
static let module: Bundle = {
1212
let bundleName = "{BUNDLE_NAME}"
1313
let localBundleName = "LocalPackages_{BUNDLE_NAME}"
1414

0 commit comments

Comments
 (0)