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

Prepare for Swift 6 Language Mode #11

Merged
merged 4 commits into from
Jul 15, 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: 1 addition & 1 deletion Package.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// swift-tools-version:5.8
// swift-tools-version:5.10

import PackageDescription

Expand Down
2 changes: 1 addition & 1 deletion Sources/Etcetera/BackgroundTasks/BackgroundTask.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ private typealias Internals = UnsupportedBackgroundTask
#endif

/// A cross-platform wrapper for requesting background execution time.
public final class BackgroundTask: @unchecked Sendable {
public final class BackgroundTask: Sendable {

/// Convenience for initializing a task with a default expiration handler.
///
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import UIKit

/// For environments that do not support background tasks.
final class UnsupportedBackgroundTask: @unchecked Sendable {
final class UnsupportedBackgroundTask: Sendable {

@MainActor static func start() -> UnsupportedBackgroundTask? {
return nil
Expand Down
9 changes: 7 additions & 2 deletions Sources/Etcetera/BackgroundTasks/iOSBackgroundTask.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
import UIKit

/// A quality-of-life wrapper around requesting iOS background execution time.
class iOSBackgroundTask: @unchecked Sendable {
final class iOSBackgroundTask: Sendable {

/// Convenience for initializing a task with a default expiration handler.
///
Expand Down Expand Up @@ -50,7 +50,12 @@ class iOSBackgroundTask: @unchecked Sendable {

init() {}

private var taskId: UIBackgroundTaskIdentifier = .invalid
private var taskId: UIBackgroundTaskIdentifier {
get { _taskId.current }
set { _taskId.current = newValue }
}

private let _taskId = Protected(UIBackgroundTaskIdentifier.invalid)

}

Expand Down
13 changes: 11 additions & 2 deletions Sources/Etcetera/Foundation/Locking.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import os.lock
/// A high-performance lock supported by all Apple platforms.
///
/// This lock is **not** recursive.
public final class Lock: Sendable {
public final class Lock: @unchecked Sendable {

/// See WWDC 2016 Session 720. Using C struct locks like `pthread_mutex_t`
/// or `os_unfair_lock` directly from Swift code is discouraged because of
Expand Down Expand Up @@ -64,9 +64,18 @@ public final class Protected<T>: @unchecked Sendable {
}
}

extension Protected {

/// Convenience initializer that defaults to `nil`.
public convenience init<O>() where T == Optional<O> {
self.init(nil)
}

}

/// A dictionary-like object that provides synchronized read/writes via an
/// underlying `Protected` value.
public final class ProtectedDictionary<Key: Hashable, Value> {
public final class ProtectedDictionary<Key: Hashable, Value>: Sendable {

private let protected: Protected<[Key: Value]>

Expand Down
28 changes: 24 additions & 4 deletions Sources/Etcetera/Foundation/NotificationObserver.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import Foundation
/// and trust ARC to release the observer at the appropriate time, which will
/// remove all observations. This assumes, of course, that all blocks passed to
/// `when(_:perform:)` do not strongly capture `self`.
public class NotificationObserver: NSObject, @unchecked Sendable {
public final class NotificationObserver: NSObject, Sendable {

// MARK: - Typealiases

Expand All @@ -35,8 +35,18 @@ public class NotificationObserver: NSObject, @unchecked Sendable {
/// The target queue for all observation callbacks.
private let queue: OperationQueue

/// The (optional) target object to be used when observing notifications.
private weak var object: AnyObject?
/// The target object to be used when observing notifications.
private var object: AnyObject? {
get { _object.access {
$0.object
}}
set { _object.access {
$0.object = newValue
}}
}

/// The backing storage for `object`.
private let _object: Protected<WeakReferencingBox>

/// A tote bag of observation tokens.
private let tokens = Protected<[Token]>([])
Expand All @@ -55,7 +65,7 @@ public class NotificationObserver: NSObject, @unchecked Sendable {
///
/// - parameter queue: The target queue for all observation callbacks.
public init(object: AnyObject? = nil, queue: OperationQueue = .main) {
self.object = object
self._object = Protected(WeakReferencingBox(object: object))
self.wasInitializedWithTargetObject = (object != nil)
self.queue = queue
}
Expand Down Expand Up @@ -118,4 +128,14 @@ public class NotificationObserver: NSObject, @unchecked Sendable {
return when(name, perform: { _ in block() })
}

// MARK: - Nested Types

private final class WeakReferencingBox {
weak var object: AnyObject?

init(object: AnyObject? = nil) {
self.object = object
}
}

}
13 changes: 9 additions & 4 deletions Sources/Etcetera/Foundation/ProcessInfo.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,16 @@ extension ProcessInfo {
/// or:
///
/// if ProcessInfo.Argument.resetCachesOnLaunch.isEnabled { ... }
public struct Argument: RawRepresentable, ExpressibleByStringLiteral {
public struct Argument: RawRepresentable, ExpressibleByStringLiteral, Sendable {

/// Supply your own "-com.domain.MyApp." prefix which must be present in
/// all the custom arguments defined in your target's scheme editor.
public static var commonPrefix: String {
public static nonisolated var commonPrefix: String {
get { _commonPrefix.current }
set { _commonPrefix.current = newValue }
}

/// Backing storage for `commonPrefix`.
private static let _commonPrefix = Protected<String>("")

/// Required by `RawRepresentable`.
Expand All @@ -62,23 +64,26 @@ extension ProcessInfo {
return ProcessInfo.isArgumentEnabled(self)
}

/// Required by `RawRepresentable`.
public init(rawValue: String) {
self.rawValue = rawValue
}

/// Required by `RawRepresentable`.
public init(stringLiteral value: String) {
self.rawValue = value
}

}

/// - returns: Returns `true` if `argument` is found among the arguments.
public static func isArgumentEnabled(_ argument: Argument) -> Bool {
public static nonisolated func isArgumentEnabled(_ argument: Argument) -> Bool {
let string = Argument.commonPrefix + argument.rawValue
return processInfo.arguments.contains(string)
}

/// Returns `true` if the app is running as a test runner for unit tests.
public static var isRunningInUnitTests: Bool {
public static nonisolated var isRunningInUnitTests: Bool {
processInfo.environment["XCTestConfigurationFilePath"] != nil
}

Expand Down
4 changes: 2 additions & 2 deletions Sources/Etcetera/Global/Global.swift
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
/// directly when declaring an `@Global`-wrapped property. Instead, Global
/// wrappers are regularly initialized via init methods declared in
/// extensions (see the README for details).
public init(initializer: @MainActor @escaping (Container) -> Wrapped) {
public init(initializer: @MainActor @escaping @Sendable (Container) -> Wrapped) {
resolver = {
Container.shared.resolveInstance(via: initializer)
}
Expand All @@ -62,7 +62,7 @@
/// directly when declaring an `@Global`-wrapped property. Instead, Global
/// wrappers are regularly initialized via init methods declared in
/// extensions (see the README for details).
public init<InstanceIdentifier: Hashable>(instanceIdentifier: InstanceIdentifier, initializer: @MainActor @escaping (Container) -> Wrapped) {
public init<InstanceIdentifier: Hashable & Sendable>(instanceIdentifier: InstanceIdentifier, initializer: @MainActor @escaping @Sendable (Container) -> Wrapped) {
resolver = {
let someKey = AnyInstanceIdentifier<Wrapped, InstanceIdentifier>(instanceIdentifier)
return Container.shared.resolveInstance(for: someKey, via: initializer)
Expand Down
2 changes: 1 addition & 1 deletion Sources/Etcetera/Global/GloballyIdentifiable.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
public protocol GloballyIdentifiable {

/// The type to be used for per-instance identifiers.
associatedtype InstanceIdentifier: Hashable
associatedtype InstanceIdentifier: Hashable & Sendable

/// Produces an instance of `Self` on demand. This method is called from the
/// shared dependency container when a cached instance of `Self` cannot be
Expand Down
24 changes: 10 additions & 14 deletions Sources/Etcetera/Networking/Reachability.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ extension Notification.Name {
}

/// A class that reports whether or not the network is currently reachable.
public class Reachability: NSObject {
public final class Reachability: NSObject, Sendable {

/// A more accurate alternative to using a Bool
public enum Status {
Expand All @@ -40,33 +40,29 @@ public class Reachability: NSObject {
return .probablyNotButWhoKnows
}

private let reachability: SCNetworkReachability?
private var lock = os_unfair_lock()
private var _flags: SCNetworkReachabilityFlags?
private let reachability: Protected<SCNetworkReachability?>
private let _flags = Protected<SCNetworkReachabilityFlags?>()

private var flags: SCNetworkReachabilityFlags? {
get {
os_unfair_lock_lock(&lock)
let value = _flags
os_unfair_lock_unlock(&lock)
return value
_flags.current
}
set {
os_unfair_lock_lock(&lock)
_flags = newValue
os_unfair_lock_unlock(&lock)
_flags.current = newValue
NotificationCenter.default.post(name: .ReachabilityChanged, object: nil)
}
}

public init(host: String = "www.google.com") {
self.reachability = SCNetworkReachabilityCreateWithName(nil, host)
let optionalReachability = SCNetworkReachabilityCreateWithName(nil, host)
self.reachability = Protected(optionalReachability)
super.init()
guard let reachability = reachability else { return }
guard let reachability = optionalReachability else { return }

// Populate the current flags asap.
var flags = SCNetworkReachabilityFlags()
SCNetworkReachabilityGetFlags(reachability, &flags)
_flags = flags
self.flags = flags

// Then configure the callback.
let callback: SCNetworkReachabilityCallBack = { (_, flags, infoPtr) in
Expand Down
2 changes: 1 addition & 1 deletion Sources/Etcetera/UnifiedLogging/OSActivity.swift
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ public struct Activity: @unchecked Sendable {
// MARK: - Nested Types

/// Swift-native type that corresponds to OS_ACTIVITY_FLAGs.
public struct Options: OptionSet {
public struct Options: OptionSet, Sendable {
public let rawValue: UInt32

public init(rawValue: UInt32) {
Expand Down