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

Support Strict Concurrency #27

Merged
merged 2 commits into from
Aug 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/build-and-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,3 +37,5 @@ jobs:
uses: StanfordSpezi/.github/.github/workflows/create-and-upload-coverage-report.yml@v2
with:
coveragereports: SpeziContact.xcresult TestApp.xcresult
secrets:
token: ${{ secrets.CODECOV_TOKEN }}
42 changes: 38 additions & 4 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,17 @@
// SPDX-License-Identifier: MIT
//

import class Foundation.ProcessInfo
import PackageDescription


#if swift(<6)
let swiftConcurrency: SwiftSetting = .enableExperimentalFeature("StrictConcurrency")
#else
let swiftConcurrency: SwiftSetting = .enableUpcomingFeature("StrictConcurrency")
#endif


let package = Package(
name: "SpeziContact",
defaultLocalization: "en",
Expand All @@ -21,21 +29,47 @@ let package = Package(
.library(name: "SpeziContact", targets: ["SpeziContact"])
],
dependencies: [
.package(url: "https://github.com/StanfordSpezi/SpeziViews", from: "1.0.0")
],
.package(url: "https://github.com/StanfordSpezi/SpeziViews", from: "1.5.0")
] + swiftLintPackage(),
targets: [
.target(
name: "SpeziContact",
dependencies: [
.product(name: "SpeziViews", package: "SpeziViews"),
.product(name: "SpeziPersonalInfo", package: "SpeziViews")
]
],
swiftSettings: [
swiftConcurrency
],
plugins: [] + swiftLintPlugin()
),
.testTarget(
name: "SpeziContactTests",
dependencies: [
.target(name: "SpeziContact")
]
],
swiftSettings: [
swiftConcurrency
],
plugins: [] + swiftLintPlugin()
)
]
)


func swiftLintPlugin() -> [Target.PluginUsage] {
// Fully quit Xcode and open again with `open --env SPEZI_DEVELOPMENT_SWIFTLINT /Applications/Xcode.app`
if ProcessInfo.processInfo.environment["SPEZI_DEVELOPMENT_SWIFTLINT"] != nil {
[.plugin(name: "SwiftLintBuildToolPlugin", package: "SwiftLint")]
} else {
[]
}
}

func swiftLintPackage() -> [PackageDescription.Package.Dependency] {
if ProcessInfo.processInfo.environment["SPEZI_DEVELOPMENT_SWIFTLINT"] != nil {
[.package(url: "https://github.com/realm/SwiftLint.git", .upToNextMinor(from: "0.55.1"))]
} else {
[]
}
}
8 changes: 5 additions & 3 deletions Sources/SpeziContact/Contact Views/ContactView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,13 +7,15 @@
//

import Contacts
@_implementationOnly import MessageUI

Check warning on line 10 in Sources/SpeziContact/Contact Views/ContactView.swift

View workflow job for this annotation

GitHub Actions / Build and Test Swift Package / Test using xcodebuild or run fastlane

using '@_implementationOnly' without enabling library evolution for 'SpeziContact' may lead to instability during execution

Check warning on line 10 in Sources/SpeziContact/Contact Views/ContactView.swift

View workflow job for this annotation

GitHub Actions / Build and Test Swift Package / Test using xcodebuild or run fastlane

using '@_implementationOnly' without enabling library evolution for 'SpeziContact' may lead to instability during execution

Check warning on line 10 in Sources/SpeziContact/Contact Views/ContactView.swift

View workflow job for this annotation

GitHub Actions / Build and Test UI Tests / Test using xcodebuild or run fastlane

using '@_implementationOnly' without enabling library evolution for 'SpeziContact' may lead to instability during execution

Check warning on line 10 in Sources/SpeziContact/Contact Views/ContactView.swift

View workflow job for this annotation

GitHub Actions / Build and Test UI Tests / Test using xcodebuild or run fastlane

using '@_implementationOnly' without enabling library evolution for 'SpeziContact' may lead to instability during execution
import SpeziPersonalInfo
import SpeziViews
import SwiftUI


/// Display contact information as defined by a ``Contact``.
/// Display contact information as defined by a `Contact`.
///
/// This view represents the content define in a ``Contact``.
public struct ContactView: View {
private let contact: Contact

Expand Down Expand Up @@ -171,15 +173,15 @@
}


/// A ``ContactView`` enables the display of contact information as defined by a ``Contact``.
/// - Parameter contact: The ``Contact`` that should be displayed.
/// Display contact information.
/// - Parameter contact: The `Contact` that should be displayed.
public init(contact: Contact) {
self.contact = contact
}


private func contactButton(_ contactOption: ContactOption) -> some View {
Button(action: contactOption.action) {

Check warning on line 184 in Sources/SpeziContact/Contact Views/ContactView.swift

View workflow job for this annotation

GitHub Actions / Build and Test Swift Package / Test using xcodebuild or run fastlane

converting function value of type '@mainactor () -> Void' to '() -> Void' loses global actor 'MainActor'; this is an error in Swift 6

Check warning on line 184 in Sources/SpeziContact/Contact Views/ContactView.swift

View workflow job for this annotation

GitHub Actions / Build and Test UI Tests / Test using xcodebuild or run fastlane

converting function value of type '@mainactor () -> Void' to '() -> Void' loses global actor 'MainActor'; this is an error in Swift 6
ZStack {
RoundedRectangle(cornerRadius: 10)
.foregroundStyle(Color(uiColor: .tertiarySystemFill))
Expand All @@ -200,10 +202,10 @@
guard let address = contact.address,
let addressString = CNPostalAddressFormatter().string(from: address).addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed),
let url = URL(string: "maps://?address=\(addressString)"),
UIApplication.shared.canOpenURL(url) else {

Check warning on line 205 in Sources/SpeziContact/Contact Views/ContactView.swift

View workflow job for this annotation

GitHub Actions / Build and Test Swift Package / Test using xcodebuild or run fastlane

call to main actor-isolated instance method 'canOpenURL' in a synchronous nonisolated context; this is an error in Swift 6

Check warning on line 205 in Sources/SpeziContact/Contact Views/ContactView.swift

View workflow job for this annotation

GitHub Actions / Build and Test Swift Package / Test using xcodebuild or run fastlane

main actor-isolated class property 'shared' can not be referenced from a non-isolated context; this is an error in Swift 6

Check warning on line 205 in Sources/SpeziContact/Contact Views/ContactView.swift

View workflow job for this annotation

GitHub Actions / Build and Test UI Tests / Test using xcodebuild or run fastlane

call to main actor-isolated instance method 'canOpenURL' in a synchronous nonisolated context; this is an error in Swift 6

Check warning on line 205 in Sources/SpeziContact/Contact Views/ContactView.swift

View workflow job for this annotation

GitHub Actions / Build and Test UI Tests / Test using xcodebuild or run fastlane

main actor-isolated class property 'shared' can not be referenced from a non-isolated context; this is an error in Swift 6
return
}
UIApplication.shared.open(url)

Check warning on line 208 in Sources/SpeziContact/Contact Views/ContactView.swift

View workflow job for this annotation

GitHub Actions / Build and Test Swift Package / Test using xcodebuild or run fastlane

call to main actor-isolated instance method 'open(_:options:completionHandler:)' in a synchronous nonisolated context; this is an error in Swift 6

Check warning on line 208 in Sources/SpeziContact/Contact Views/ContactView.swift

View workflow job for this annotation

GitHub Actions / Build and Test Swift Package / Test using xcodebuild or run fastlane

main actor-isolated class property 'shared' can not be referenced from a non-isolated context; this is an error in Swift 6

Check warning on line 208 in Sources/SpeziContact/Contact Views/ContactView.swift

View workflow job for this annotation

GitHub Actions / Build and Test UI Tests / Test using xcodebuild or run fastlane

call to main actor-isolated instance method 'open(_:options:completionHandler:)' in a synchronous nonisolated context; this is an error in Swift 6

Check warning on line 208 in Sources/SpeziContact/Contact Views/ContactView.swift

View workflow job for this annotation

GitHub Actions / Build and Test UI Tests / Test using xcodebuild or run fastlane

main actor-isolated class property 'shared' can not be referenced from a non-isolated context; this is an error in Swift 6
}
}

Expand Down
8 changes: 4 additions & 4 deletions Sources/SpeziContact/Contact Views/ContactsList.swift
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@
import SwiftUI


/// Display different ``Contact``s in a card-like style in a scroll view.
/// Display different `Contact`s in a card-like style in a scroll view.
///
/// You pass multiple ``Contact``s to the ``ContactsList`` to populate its content: ``ContactsList/init(contacts:)``:
/// You pass multiple ``Contact``s to the `ContactsList` to populate its content:
/// ```swift
/// ContactsList(contacts: [/* ... */])
/// .navigationTitle("Contacts")
Expand All @@ -33,8 +33,8 @@ public struct ContactsList: View {
}


/// Create a ``ContactsList`` using multiple ``Contact``s.
/// - Parameter contact: The ``Contact`` instances to populate the list.
/// Create a view displaying a list of multiple `Contact`s.
/// - Parameter contact: The `Contact` instances to populate the list.
public init(contacts: [Contact]) {
self.contacts = contacts
}
Expand Down
5 changes: 3 additions & 2 deletions Sources/SpeziContact/Models/Contact.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
// SPDX-License-Identifier: MIT
//

@_exported import Contacts.CNPostalAddress
@preconcurrency @_exported import class Contacts.CNPostalAddress
import SwiftUI


Expand Down Expand Up @@ -79,10 +79,11 @@ public struct Contact {
public let contactOptions: [ContactOption]


/// Create new contact information.
/// - Parameters:
/// - id: Identifier of the `Contact` instance.
/// - name: The name of the individual. Ideally provide at least a first and given name.
/// - image: The image of the ``Contact``.
/// - image: The image of the `Contact`.
/// - title: The title of the individual.
/// - description: The description of the individual.
/// - organization: The organization of the individual.
Expand Down
34 changes: 16 additions & 18 deletions Sources/SpeziContact/Models/ContactOption.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,13 @@
// SPDX-License-Identifier: MIT
//

@_implementationOnly import MessageUI

Check warning on line 9 in Sources/SpeziContact/Models/ContactOption.swift

View workflow job for this annotation

GitHub Actions / Build and Test Swift Package / Test using xcodebuild or run fastlane

using '@_implementationOnly' without enabling library evolution for 'SpeziContact' may lead to instability during execution

Check warning on line 9 in Sources/SpeziContact/Models/ContactOption.swift

View workflow job for this annotation

GitHub Actions / Build and Test Swift Package / Test using xcodebuild or run fastlane

using '@_implementationOnly' without enabling library evolution for 'SpeziContact' may lead to instability during execution

Check warning on line 9 in Sources/SpeziContact/Models/ContactOption.swift

View workflow job for this annotation

GitHub Actions / Build and Test UI Tests / Test using xcodebuild or run fastlane

using '@_implementationOnly' without enabling library evolution for 'SpeziContact' may lead to instability during execution

Check warning on line 9 in Sources/SpeziContact/Models/ContactOption.swift

View workflow job for this annotation

GitHub Actions / Build and Test UI Tests / Test using xcodebuild or run fastlane

using '@_implementationOnly' without enabling library evolution for 'SpeziContact' may lead to instability during execution
import SwiftUI


/// Customizable way to get in contact with an individual and usually connected to a ``Contact``.
/// Customizable way to get in contact with an individual and usually connected to a `Contact`.
///
/// ### Usage
///
/// The following example demonstrates the usage of the ``ContactOption`` within a ``Contact``. For additional details, refer to the ``Contact`` documentation.
/// The following example demonstrates the usage of the `ContactOption` within a `Contact`. For additional details, refer to the ``Contact`` documentation.
/// ```swift
/// Contact(
/// // other parameters for Contact
Expand All @@ -35,30 +33,29 @@
/// ```
public struct ContactOption {
let id = UUID()
/// The image representing the ``ContactOption`` in the user interface.
/// The image representing the `ContactOption` in the user interface.
public let image: Image
/// The title representing the ``ContactOption`` in the user interface.
/// The title representing the `ContactOption` in the user interface.
public let title: String
/// The action that should be taken when a user chooses to use the ``ContactOption``.
public let action: () -> Void
/// The action that should be taken when a user chooses to use the `ContactOption`.
public let action: @MainActor () -> Void


/// A ``ContactOption`` encodes a way to get in contact with an individual and usually connected to a ``Contact``.
/// Encodes a way to get in contact with an individual and usually connected to a `Contact`.
/// - Parameters:
/// - image: The image representing the ``ContactOption`` in the user interface.
/// - title: The title representing the ``ContactOption`` in the user interface.
/// - action: The action that should be taken when a user chooses to use the ``ContactOption``.
public init(image: Image, title: String, action: @escaping () -> Void) {
/// - image: The image representing the `ContactOption` in the user interface.
/// - title: The title representing the `ContactOption` in the user interface.
/// - action: The action that should be taken when a user chooses to use the `ContactOption`.
public init(image: Image, title: String, action: @escaping @MainActor () -> Void) {
self.image = image
self.title = title
self.action = action
}
}


extension ContactOption {
private static var delegateReference: NSObject?

private static var rootViewController: UIViewController? {
@MainActor private static var rootViewController: UIViewController? {
UIApplication
.shared
.connectedScenes
Expand Down Expand Up @@ -129,7 +126,8 @@
UIApplication.shared.open(url)
}
}


@MainActor
private static func presentAlert(title: String, message: String) {
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: String(localized: "Ok", bundle: .module, comment: "Dismiss alert"), style: .default))
Expand Down
2 changes: 1 addition & 1 deletion Sources/SpeziContact/SpeziContact.docc/SpeziContact.md
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ The contact views that can be used to display contact information.

### Models

Use the ``Contact`` and ``ContactOption`` to configure the contact views.
Use the `Contact` and `ContactOption` to configure the contact views.

- ``Contact``
- ``ContactOption``
3 changes: 2 additions & 1 deletion Tests/UITests/TestApp/ContactsTestsView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
// SPDX-License-Identifier: MIT
//

import Contacts
import SpeziContact
import SwiftUI

Expand Down Expand Up @@ -49,7 +50,7 @@ struct ContactsTestsView: View {
]
)

static let mock = Contact(
@MainActor static let mock = Contact(
name: PersonNameComponents(givenName: "Paul", familyName: "Schmiedmayer"),
image: Image(systemName: "figure.wave.circle"), // swiftlint:disable:this accessibility_label_for_image
title: "A Title",
Expand Down
3 changes: 3 additions & 0 deletions Tests/UITests/TestAppUITests/SpeziContactsTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,13 @@ import XCTest


final class ContactsTests: XCTestCase {
@MainActor
func testContactsView() throws {
let app = XCUIApplication()
app.launch()

XCTAssertTrue(app.wait(for: .runningForeground, timeout: 2.0))


XCTAssertTrue(app.staticTexts["Contact: Paul Schmiedmayer"].waitForExistence(timeout: 2.0))
XCTAssertTrue(app.staticTexts["A Title at Stanford University"].exists)
Expand Down
3 changes: 3 additions & 0 deletions Tests/UITests/UITests.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -287,6 +287,7 @@
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_STRICT_CONCURRENCY = complete;
};
name = Debug;
};
Expand Down Expand Up @@ -340,6 +341,7 @@
SDKROOT = iphoneos;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_OPTIMIZATION_LEVEL = "-O";
SWIFT_STRICT_CONCURRENCY = complete;
VALIDATE_PRODUCT = YES;
};
name = Release;
Expand Down Expand Up @@ -501,6 +503,7 @@
SDKROOT = iphoneos;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = TEST;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_STRICT_CONCURRENCY = complete;
};
name = Test;
};
Expand Down
Loading