Skip to content

Commit bd6300c

Browse files
committed
Enable targeted strict concurrency checking
1 parent 69abd01 commit bd6300c

File tree

31 files changed

+155
-130
lines changed

31 files changed

+155
-130
lines changed

AuthenticationExample/AuthenticationExample.xcodeproj/project.pbxproj

+2
Original file line numberDiff line numberDiff line change
@@ -358,6 +358,7 @@
358358
SUPPORTS_MACCATALYST = YES;
359359
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
360360
SWIFT_EMIT_LOC_STRINGS = YES;
361+
SWIFT_STRICT_CONCURRENCY = targeted;
361362
SWIFT_VERSION = 5.0;
362363
TARGETED_DEVICE_FAMILY = "1,2";
363364
};
@@ -392,6 +393,7 @@
392393
SUPPORTS_MACCATALYST = YES;
393394
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
394395
SWIFT_EMIT_LOC_STRINGS = YES;
396+
SWIFT_STRICT_CONCURRENCY = targeted;
395397
SWIFT_VERSION = 5.0;
396398
TARGETED_DEVICE_FAMILY = "1,2";
397399
};

AuthenticationExample/AuthenticationExample/LoadableImageView.swift

+1
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import SwiftUI
1818
/// A view that loads a `LoadableImage` and displays it.
1919
/// While the image is loading a progress view is displayed.
2020
/// If there is an error displaying the image a red exclamation circle is displayed.
21+
@MainActor
2122
struct LoadableImageView: View {
2223
/// The loadable image to display.
2324
let loadableImage: LoadableImage

AuthenticationExample/AuthenticationExample/MapItemView.swift

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import ArcGIS
1616
import SwiftUI
1717

1818
/// A view that displays a map.
19+
@MainActor
1920
struct MapItemView: View {
2021
/// The map that is to be displayed.
2122
let map: Map

Examples/Examples.xcodeproj/project.pbxproj

+2
Original file line numberDiff line numberDiff line change
@@ -432,6 +432,7 @@
432432
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
433433
SUPPORTS_MACCATALYST = YES;
434434
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
435+
SWIFT_STRICT_CONCURRENCY = targeted;
435436
SWIFT_VERSION = 5.0;
436437
TARGETED_DEVICE_FAMILY = "1,2,6";
437438
};
@@ -462,6 +463,7 @@
462463
SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
463464
SUPPORTS_MACCATALYST = YES;
464465
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
466+
SWIFT_STRICT_CONCURRENCY = targeted;
465467
SWIFT_VERSION = 5.0;
466468
TARGETED_DEVICE_FAMILY = "1,2,6";
467469
};

Examples/Examples/WorldScaleExampleView.swift

+2-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import SwiftUI
2020
/// An example that utilizes the `WorldScaleSceneView` to show an augmented reality view
2121
/// of your current location. Because this is an example that can be run from anywhere,
2222
/// it places a red circle around your initial location which can be explored.
23+
@MainActor
2324
struct WorldScaleExampleView: View {
2425
@State private var scene: ArcGIS.Scene = {
2526
// Creates an elevation source from Terrain3D REST service.
@@ -78,7 +79,7 @@ struct WorldScaleExampleView: View {
7879
try? await locationDataSource.start()
7980

8081
// Retrieve initial location.
81-
guard let initialLocation = await locationDataSource.locations.first(where: { _ in true }) else { return }
82+
guard let initialLocation = await locationDataSource.locations.first(where: { @Sendable _ in true }) else { return }
8283

8384
// Put a circle graphic around the initial location.
8485
let circle = GeometryEngine.geodeticBuffer(around: initialLocation.position, distance: 20, distanceUnit: .meters, maxDeviation: 1, curveType: .geodesic)

Examples/ExamplesApp/AnyExample.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,9 @@ import SwiftUI
1616

1717
struct AnyExample<Content: View> {
1818
var name: String
19-
var content: () -> Content
19+
var content: @MainActor () -> Content
2020

21-
init(_ name: String, content: @autoclosure @escaping () -> Content) {
21+
init(_ name: String, content: @autoclosure @escaping @MainActor () -> Content) {
2222
self.name = name
2323
self.content = content
2424
}

Examples/ExamplesApp/Example.swift

+1
Original file line numberDiff line numberDiff line change
@@ -19,5 +19,6 @@ protocol Example {
1919
var name: String { get }
2020

2121
/// A function which creates the example view.
22+
@MainActor
2223
func makeBody() -> AnyView
2324
}

Examples/ExamplesApp/ExampleList.swift

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414

1515
import SwiftUI
1616

17+
@MainActor
1718
struct ExampleList: View {
1819
/// The name of the list of examples.
1920
var name: String

JobManagerExample/JobManagerExample.xcodeproj/project.pbxproj

+2
Original file line numberDiff line numberDiff line change
@@ -329,6 +329,7 @@
329329
PRODUCT_BUNDLE_IDENTIFIER = com.esri.JobManagerExample;
330330
PRODUCT_NAME = "$(TARGET_NAME)";
331331
SWIFT_EMIT_LOC_STRINGS = YES;
332+
SWIFT_STRICT_CONCURRENCY = targeted;
332333
SWIFT_VERSION = 5.0;
333334
TARGETED_DEVICE_FAMILY = "1,2";
334335
};
@@ -360,6 +361,7 @@
360361
PRODUCT_BUNDLE_IDENTIFIER = com.esri.JobManagerExample;
361362
PRODUCT_NAME = "$(TARGET_NAME)";
362363
SWIFT_EMIT_LOC_STRINGS = YES;
364+
SWIFT_STRICT_CONCURRENCY = targeted;
363365
SWIFT_VERSION = 5.0;
364366
TARGETED_DEVICE_FAMILY = "1,2";
365367
};

JobManagerExample/JobManagerExample/JobManagerExampleView.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ private struct JobView: View {
136136
/// Initializer that takes a job for which to show the data for.
137137
init(job: Job) {
138138
self.job = job
139-
status = job.status
139+
self._status = .init(initialValue: job.status)
140140
}
141141

142142
/// A Boolean value indicating if the progress is showing.
@@ -286,7 +286,7 @@ extension JobManagerExampleView {
286286

287287
if syncModel == .layer, let featureServiceInfo = task.featureServiceInfo {
288288
let layerOptions = featureServiceInfo.layerInfos
289-
.compactMap({ $0 as? FeatureServiceLayerIDInfo })
289+
.compactMap { $0 as? FeatureServiceLayerIDInfo }
290290
.compactMap(\.id)
291291
.map(GenerateLayerOption.init(layerID:))
292292
params.addLayerOptions(layerOptions)

JobManagerExample/JobManagerExample/View.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,8 @@ extension View {
2828
@ViewBuilder
2929
func onReceive<S>(
3030
_ sequence: S,
31-
perform action: ((S.Element) -> Void)?
32-
) -> some View where S: AsyncSequence {
31+
perform action: (@MainActor (S.Element) -> Void)?
32+
) -> some View where S: AsyncSequence & Sendable, S.Element: Sendable {
3333
if let action {
3434
task {
3535
do {

Package.swift

+2-2
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,8 @@ for target in package.targets {
5353
target.swiftSettings = (target.swiftSettings ?? []) + [
5454
// Experimental Features.
5555
.enableExperimentalFeature("AccessLevelOnImport"),
56-
/* .enableExperimentalFeature("StrictConcurrency"), */
56+
.enableExperimentalFeature("StrictConcurrency=targeted"),
5757
// Upcoming Features.
58-
/* .enableUpcomingFeature("DisableOutwardActorInference") */
58+
.enableUpcomingFeature("DisableOutwardActorInference")
5959
]
6060
}

Sources/ArcGISToolkit/Common/AttachmentModel.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
// limitations under the License.
1414

1515
import ArcGIS
16-
import QuickLook
16+
@preconcurrency import QuickLook
1717
import SwiftUI
1818

1919
internal import os

Sources/ArcGISToolkit/Components/Augmented Reality/Utilities/WorldTrackingSceneView.swift

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import SwiftUI
1717
import ArcGIS
1818

1919
/// A scene view that provides an augmented reality world scale experience using world-tracking.
20+
@MainActor
2021
struct WorldTrackingSceneView: View {
2122
/// A Boolean value indicating if the camera was initially set.
2223
@Binding var initialCameraIsSet: Bool

Sources/ArcGISToolkit/Components/BasemapGallery/BasemapGalleryItem.swift

+7-5
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,11 @@ import Combine
1717
import UIKit
1818

1919
/// The `BasemapGalleryItem` encompasses an element in a `BasemapGallery`.
20-
public class BasemapGalleryItem: ObservableObject {
20+
@MainActor
21+
@preconcurrency
22+
public final class BasemapGalleryItem: ObservableObject, Sendable {
2123
/// The status of a basemap's spatial reference in relation to a reference spatial reference.
22-
public enum SpatialReferenceStatus {
24+
public enum SpatialReferenceStatus: Sendable {
2325
/// The basemap's spatial reference status is unknown, either because the basemap's
2426
/// base layers haven't been loaded yet or the status has yet to be updated.
2527
case unknown
@@ -52,7 +54,7 @@ public class BasemapGalleryItem: ObservableObject {
5254
if basemap.loadStatus != .loaded {
5355
await loadBasemap()
5456
} else {
55-
await finalizeLoading()
57+
finalizeLoading()
5658
}
5759
}
5860
}
@@ -97,12 +99,12 @@ private extension BasemapGalleryItem {
9799
} catch {
98100
loadError = error
99101
}
100-
await finalizeLoading(error: loadError)
102+
finalizeLoading(error: loadError)
101103
}
102104

103105
/// Updates the item in response to basemap loading completion.
104106
/// - Parameter error: The basemap load error, if any.
105-
@MainActor func finalizeLoading(error: Error? = nil) {
107+
func finalizeLoading(error: Error? = nil) {
106108
if name == nil {
107109
name = basemap.name
108110
}

Sources/ArcGISToolkit/Components/FeatureFormView/AttachmentHelpers/AttachmentPhotoPicker.swift

+3
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import PhotosUI
1616
import SwiftUI
1717

1818
/// A wrapper for the PhotosPicker API.
19+
@MainActor
1920
struct AttachmentPhotoPicker: ViewModifier {
2021
/// The item selected in the photos picker.
2122
@State private var item: PhotosPickerItem?
@@ -53,3 +54,5 @@ struct AttachmentPhotoPicker: ViewModifier {
5354
}
5455
}
5556
}
57+
58+
extension PhotosPickerItem: @unchecked Swift.Sendable {}

Sources/ArcGISToolkit/Components/Popups/PopupView.swift

+2
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,8 @@ import ArcGIS
5151
/// and refer to
5252
/// [PopupExampleView.swift](https://github.com/Esri/arcgis-maps-sdk-swift-toolkit/blob/main/Examples/Examples/PopupExampleView.swift)
5353
/// in the project. To learn more about using the `PopupView` see the <doc:PopupViewTutorial>.
54+
@MainActor
55+
@preconcurrency
5456
public struct PopupView: View {
5557
/// Creates a `PopupView` with the given popup.
5658
/// - Parameters

Sources/ArcGISToolkit/Components/Search/SearchResult.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import UIKit.UIImage
1616
import ArcGIS
1717

1818
/// Wraps a search result for display.
19-
public struct SearchResult {
19+
public struct SearchResult: Sendable {
2020
/// Creates a `SearchResult`.
2121
/// - Parameters:
2222
/// - displayTitle: The string to be shown whenever a result is displayed.

Sources/ArcGISToolkit/Components/Search/SearchSource.swift

+2-1
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,8 @@
1515
import ArcGIS
1616

1717
/// Defines the contract for a search result provider.
18-
public protocol SearchSource {
18+
@preconcurrency
19+
public protocol SearchSource: Sendable {
1920
/// Name to show when presenting this source in the UI.
2021
var name: String { get set }
2122

Sources/ArcGISToolkit/Components/Search/SearchSuggestion.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ import Foundation
1616
import ArcGIS
1717

1818
/// Wraps a suggestion for display.
19-
public struct SearchSuggestion {
19+
public struct SearchSuggestion: Sendable {
2020
/// Creates a `SearchSuggestion`.
2121
/// - Parameters:
2222
/// - displayTitle: The string to be used when displaying a suggestion.

Sources/ArcGISToolkit/Components/Search/SearchViewModel.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ public enum SearchResultMode {
3333
///
3434
/// The `failure` case contains the error (if any) generated by the last search or suggestion
3535
/// operation.
36-
public enum SearchOutcome {
36+
public enum SearchOutcome: Sendable {
3737
case results([SearchResult])
3838
case suggestions([SearchSuggestion])
3939
case failure(String)

Sources/ArcGISToolkit/Components/UtilityNetworkTrace/UtilityNetworkTraceViewModel.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ import SwiftUI
129129
await withTaskGroup(of: Void.self) { [weak self] taskGroup in
130130
guard let self else { return }
131131
for layer in network?.layers ?? [] {
132-
taskGroup.addTask {
132+
taskGroup.addTask { @MainActor in
133133
if let result = try? await proxy.identify(on: layer, screenPoint: screenPoint, tolerance: 10) {
134134
for element in result.geoElements {
135135
await self.processAndAdd(

Sources/ArcGISToolkit/Extensions/ArcGIS/AttachmentHelpers/AttachmentsFeatureElement.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ public enum AttachmentsFeatureElementDisplayType {
2626
}
2727

2828
/// Common properties for elements which display feature attachments.
29-
public protocol AttachmentsFeatureElement {
29+
public protocol AttachmentsFeatureElement: Sendable {
3030
/// Indicates how to display the attachments.
3131
var attachmentsDisplayType: AttachmentsFeatureElementDisplayType { get }
3232

Test Runner/Test Runner.xcodeproj/project.pbxproj

+2
Original file line numberDiff line numberDiff line change
@@ -486,6 +486,7 @@
486486
SUPPORTS_MACCATALYST = YES;
487487
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
488488
SWIFT_EMIT_LOC_STRINGS = YES;
489+
SWIFT_STRICT_CONCURRENCY = targeted;
489490
SWIFT_VERSION = 5.0;
490491
TARGETED_DEVICE_FAMILY = "1,2,6";
491492
};
@@ -521,6 +522,7 @@
521522
SUPPORTS_MACCATALYST = YES;
522523
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
523524
SWIFT_EMIT_LOC_STRINGS = YES;
525+
SWIFT_STRICT_CONCURRENCY = targeted;
524526
SWIFT_VERSION = 5.0;
525527
TARGETED_DEVICE_FAMILY = "1,2,6";
526528
};

Test Runner/UI Tests/AttachmentCameraControllerTests.swift

+7-4
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,14 @@ final class AttachmentCameraControllerTests: XCTestCase {
2121

2222
/// Test `AttachmentCameraController.onCameraCaptureModeChanged(perform:)`
2323
func testOnCameraCaptureModeChanged() throws {
24-
#if targetEnvironment(simulator)
25-
XCTSkip("This test intended for iOS devices only.")
26-
#elseif targetEnvironment(macCatalyst)
27-
XCTSkip("This test intended for iOS devices only.")
24+
let isUnsupportedEnvironment: Bool
25+
#if targetEnvironment(simulator) || targetEnvironment(macCatalyst)
26+
isUnsupportedEnvironment = true
27+
#else
28+
isUnsupportedEnvironment = false
2829
#endif
30+
try XCTSkipIf(isUnsupportedEnvironment, "This test intended for iOS devices only.")
31+
2932
let app = XCUIApplication()
3033
let cameraModeController = app.otherElements["CameraMode"]
3134
let cameraModeLabel = app.staticTexts["Camera Capture Mode"]

Tests/ArcGISToolkitTests/BasemapGalleryItemTests.swift

+6-3
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,13 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
import Foundation
1615

17-
import XCTest
1816
import ArcGIS
1917
@testable import ArcGISToolkit
18+
@preconcurrency import Combine
19+
import Foundation
2020
import SwiftUI
21-
import Combine
21+
import XCTest
2222

2323
// Note: the iOS implementation uses the MVVM approach and SwiftUI. This
2424
// required a bit more properties/logic in the 'BasemapGalleryItem' (such
@@ -34,6 +34,7 @@ final class BasemapGalleryItemTests: XCTestCase {
3434
ArcGISEnvironment.apiKey = nil
3535
}
3636

37+
@MainActor
3738
func testInit() async throws {
3839
let basemap = Basemap(style: .arcGISLightGray)
3940
let item = BasemapGalleryItem(basemap: basemap)
@@ -83,6 +84,7 @@ final class BasemapGalleryItemTests: XCTestCase {
8384
XCTAssertNil(item3.loadBasemapError)
8485
}
8586

87+
@MainActor
8688
func testLoadBasemapError() async throws {
8789
// Create item with bad portal item URL.
8890
let item = BasemapGalleryItem(
@@ -99,6 +101,7 @@ final class BasemapGalleryItemTests: XCTestCase {
99101
XCTAssertNotNil(item.loadBasemapError)
100102
}
101103

104+
@MainActor
102105
func testSpatialReferenceAndStatus() async throws {
103106
let basemap = Basemap(style: .arcGISLightGray)
104107
let item = BasemapGalleryItem(basemap: basemap)

Tests/ArcGISToolkitTests/BasemapGalleryViewModelTests.swift

+2-5
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,10 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
import Foundation
16-
17-
import XCTest
1815
import ArcGIS
1916
@testable import ArcGISToolkit
20-
import SwiftUI
21-
import Combine
17+
@preconcurrency import Combine
18+
import XCTest
2219

2320
// Note: the iOS implementation uses the MVVM approach and SwiftUI. This
2421
// required a bit more properties/logic in the 'BasemapGalleryViewModel' (such

Tests/ArcGISToolkitTests/FloorFilterViewModelTests.swift

+1
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,7 @@ final class FloorFilterViewModelTests: XCTestCase {
259259
)
260260
}
261261

262+
@MainActor
262263
private func floorManager(
263264
forWebMapWithIdentifier id: PortalItem.ID,
264265
file: StaticString = #filePath,

Tests/ArcGISToolkitTests/Publisher.swift

+1-2
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,9 @@
1212
// See the License for the specific language governing permissions and
1313
// limitations under the License.
1414

15-
import Foundation
1615
import Combine
1716

18-
extension Publisher {
17+
extension Publisher where Self: Sendable {
1918
/// Asynchronously returns the first value emitted from the publisher.
2019
/// This property will return `nil` if this publisher completes without an error before
2120
/// it emits a value.

0 commit comments

Comments
 (0)