Skip to content

Commit

Permalink
Support Linux (#10)
Browse files Browse the repository at this point in the history
  • Loading branch information
tgrapperon authored Jan 10, 2023
1 parent 742fc72 commit c75118f
Show file tree
Hide file tree
Showing 37 changed files with 1,074 additions and 1,037 deletions.
10 changes: 10 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,13 @@ jobs:
run: CONFIG=${{ matrix.config }} make build-all-platforms
- name: Build for library evolution
run: make build-for-library-evolution

ubuntu-tests:
strategy:
matrix:
os: [ubuntu-18.04, ubuntu-20.04]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v3
- name: Run tests
run: make test-swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,16 @@
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "_NotificationDependencyTests"
BuildableName = "_NotificationDependencyTests"
BlueprintName = "_NotificationDependencyTests"
ReferencedContainer = "container:">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction
Expand Down
2 changes: 2 additions & 0 deletions Sources/CompressionDependency/Compressor.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#if canImport(Compression)
import Compression
import Dependencies
import Foundation
Expand Down Expand Up @@ -58,3 +59,4 @@ public struct Compressor: Sendable {
try await self.compressAsync(data, algorithm())
}
}
#endif
2 changes: 2 additions & 0 deletions Sources/CompressionDependency/Decompressor.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#if canImport(Compression)
import Compression
import Dependencies
import Foundation
Expand Down Expand Up @@ -62,3 +63,4 @@ public struct Decompressor: Sendable {
try await self.decompressAsync(data, algorithm())
}
}
#endif
2 changes: 2 additions & 0 deletions Sources/CompressionDependency/Internal/_Compression.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#if canImport(Compression)
import Compression
import Dependencies
import Foundation
Expand Down Expand Up @@ -64,3 +65,4 @@ func defaultAsync(_ operation: FilterOperation) -> @Sendable (
return processed
}
}
#endif
Original file line number Diff line number Diff line change
@@ -1,32 +1,37 @@
@preconcurrency import Combine
import Dependencies
import Foundation

#if canImport(Combine)
@preconcurrency import Combine
#endif

public protocol BroadcastableAsyncSequence: AsyncSequence {}

extension BroadcastableAsyncSequence {
@_spi(Internals) public func publisher() -> AnyPublisher<Element, Never>
where Self: Sendable, Self.Element: Sendable {
let subject = CurrentValueSubject<Element?, Never>(.none)
#if canImport(Combine)
extension BroadcastableAsyncSequence {
@_spi(Internals) public func publisher() -> AnyPublisher<Element, Never>
where Self: Sendable, Self.Element: Sendable {
let subject = CurrentValueSubject<Element?, Never>(.none)

let task = Task { @MainActor in
do {
for try await element in self {
subject.send(element)
let task = Task { @MainActor in
do {
for try await element in self {
subject.send(element)
}
} catch {
subject.send(completion: .finished)
}
} catch {
subject.send(completion: .finished)
}
}

return subject.handleEvents(
receiveCancel: {
task.cancel()
})
.compactMap { $0 }
.eraseToAnyPublisher()
return subject.handleEvents(
receiveCancel: {
task.cancel()
})
.compactMap { $0 }
.eraseToAnyPublisher()
}
}
}
#endif

extension BroadcastableAsyncSequence where Self: Sendable {
public func forEach(
Expand All @@ -42,68 +47,69 @@ extension BroadcastableAsyncSequence where Self: Sendable {
}.eraseToAnyCancellableTask()
}
}
#if canImport(Combine)
extension BroadcastableAsyncSequence where Self: Sendable, Self.Element: Sendable {
/// Assigns each element from an async sequence to a property on an object.
/// - Parameters:
/// - keyPath: A key path that indicates the property to assign.
/// - object: The object that contains the property. The subscriber assigns the object’s
/// property every time it receives a new value.
/// - Returns: An AnyCancellable instance. Call cancel() on this instance when you no longer want
/// the publisher to automatically assign the property. Deinitializing this instance will also
/// cancel automatic assignment.
@_disfavoredOverload
public func assign<Root>(to keyPath: ReferenceWritableKeyPath<Root, Element>, on object: Root)
-> AnyCancellable
{
self.publisher().assign(to: keyPath, on: object)
}

extension BroadcastableAsyncSequence where Self: Sendable, Self.Element: Sendable {
/// Assigns each element from an async sequence to a property on an object.
/// - Parameters:
/// - keyPath: A key path that indicates the property to assign.
/// - object: The object that contains the property. The subscriber assigns the object’s
/// property every time it receives a new value.
/// - Returns: An AnyCancellable instance. Call cancel() on this instance when you no longer want
/// the publisher to automatically assign the property. Deinitializing this instance will also
/// cancel automatic assignment.
@_disfavoredOverload
public func assign<Root>(to keyPath: ReferenceWritableKeyPath<Root, Element>, on object: Root)
-> AnyCancellable
{
self.publisher().assign(to: keyPath, on: object)
}

/// Assigns each element from an async sequence to a property on an object.
///
/// The assign(to:) operator manages the life cycle of the subscription, canceling the
/// subscription automatically when the Published instance deinitializes. Because of this, the
/// ``NotificationStream/assign(to:)-5dfsw`` operator doesn’t return an `AnyCancellable` that
/// you’re responsible for like ``NotificationStream/assign(to:on:)-rllq`` does.
///
/// - Parameter published: A property marked with the @Published attribute, which receives and
/// republishes all elements received from the upstream publisher.
@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
@_disfavoredOverload
public func assign(to published: inout Published<Element>.Publisher) {
self.publisher().assign(to: &published)
}
/// Assigns each element from an async sequence to a property on an object.
///
/// The assign(to:) operator manages the life cycle of the subscription, canceling the
/// subscription automatically when the Published instance deinitializes. Because of this, the
/// ``NotificationStream/assign(to:)-5dfsw`` operator doesn’t return an `AnyCancellable` that
/// you’re responsible for like ``NotificationStream/assign(to:on:)-rllq`` does.
///
/// - Parameter published: A property marked with the @Published attribute, which receives and
/// republishes all elements received from the upstream publisher.
@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
@_disfavoredOverload
public func assign(to published: inout Published<Element>.Publisher) {
self.publisher().assign(to: &published)
}

/// Assigns each element from an async sequence to a property on an object.
/// - Parameters:
/// - keyPath: A key path that indicates the property to assign.
/// - object: The object that contains the property. The subscriber assigns the object’s
/// property every time it receives a new value.
/// - Returns: An AnyCancellable instance. Call cancel() on this instance when you no longer want
/// the publisher to automatically assign the property. Deinitializing this instance will also
/// cancel automatic assignment.
@_disfavoredOverload
public func assign<Root>(to keyPath: ReferenceWritableKeyPath<Root, Element?>, on object: Root)
-> AnyCancellable
{
self.publisher().map(Optional.some).assign(to: keyPath, on: object)
}
/// Assigns each element from an async sequence to a property on an object.
/// - Parameters:
/// - keyPath: A key path that indicates the property to assign.
/// - object: The object that contains the property. The subscriber assigns the object’s
/// property every time it receives a new value.
/// - Returns: An AnyCancellable instance. Call cancel() on this instance when you no longer want
/// the publisher to automatically assign the property. Deinitializing this instance will also
/// cancel automatic assignment.
@_disfavoredOverload
public func assign<Root>(to keyPath: ReferenceWritableKeyPath<Root, Element?>, on object: Root)
-> AnyCancellable
{
self.publisher().map(Optional.some).assign(to: keyPath, on: object)
}

/// Assigns each element from an async sequence to a property on an object.
///
/// The assign(to:) operator manages the life cycle of the subscription, canceling the
/// subscription automatically when the Published instance deinitializes. Because of this, the
/// ``NotificationStream/assign(to:)-3sgyd`` operator doesn’t return an `AnyCancellable` that
/// you’re responsible for like ``NotificationStream/assign(to:on:)-fj51`` does.
///
/// - Parameter published: A property marked with the @Published attribute, which receives and
/// republishes all elements received from the upstream publisher.
@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
@_disfavoredOverload
public func assign(to published: inout Published<Element?>.Publisher) {
self.publisher().map(Optional.some).assign(to: &published)
/// Assigns each element from an async sequence to a property on an object.
///
/// The assign(to:) operator manages the life cycle of the subscription, canceling the
/// subscription automatically when the Published instance deinitializes. Because of this, the
/// ``NotificationStream/assign(to:)-3sgyd`` operator doesn’t return an `AnyCancellable` that
/// you’re responsible for like ``NotificationStream/assign(to:on:)-fj51`` does.
///
/// - Parameter published: A property marked with the @Published attribute, which receives and
/// republishes all elements received from the upstream publisher.
@available(iOS 14.0, macOS 11.0, tvOS 14.0, watchOS 7.0, *)
@_disfavoredOverload
public func assign(to published: inout Published<Element?>.Publisher) {
self.publisher().map(Optional.some).assign(to: &published)
}
}
}
#endif

extension AsyncMapSequence: BroadcastableAsyncSequence
where Base: BroadcastableAsyncSequence {}
Expand Down
3 changes: 1 addition & 2 deletions Sources/DependenciesAdditionsBasics/Proxies.swift
Original file line number Diff line number Diff line change
Expand Up @@ -455,8 +455,7 @@ public struct AnyProxyBindable<Value: Sendable>: ProxyBindable {
public var setValue: @Sendable (Value) -> Void
}

public protocol _Sendable: Sendable {}
extension LockIsolated: ProxyBindable where Value: _Sendable {
extension LockIsolated: ProxyBindable {
public var getValue: @Sendable () -> Value {
{ self.value }
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#if canImport(Security)
import Dependencies
import Security

Expand Down Expand Up @@ -27,3 +28,4 @@ public struct SecureRandomNumberGenerator: RandomNumberGenerator, Sendable {
return result
}
}
#endif
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import Foundation
import XCTestDynamicOverlay
#if os(Linux)
public let NSEC_PER_MSEC: UInt64 = 1_000_000
#endif

/// Performs an async operation that fails if it hasn't finished before a timeout expires.
///
Expand Down
2 changes: 2 additions & 0 deletions Sources/DeviceDependency/DeviceCheckDependency.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#if canImport(DeviceCheck)
import Dependencies
@_spi(Internals) import DependenciesAdditionsBasics
import DeviceCheck
Expand Down Expand Up @@ -73,3 +74,4 @@ extension DeviceCheckDevice {
)
}
}
#endif
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
@preconcurrency import Combine
import Dependencies
import Foundation
Expand Down Expand Up @@ -193,3 +194,4 @@ extension NotificationCenter.Dependency {
}
}
}
#endif
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#if canImport(CoreData)
import CoreData
import Dependencies

Expand Down Expand Up @@ -202,3 +203,4 @@ extension PersistentContainer {
return self
}
}
#endif
2 changes: 2 additions & 0 deletions Sources/ProcessInfoDependency/ProcessInfoDependency.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#if os(macOS) || os(iOS) || os(tvOS) || os(watchOS)
import Dependencies
@_spi(Internals) import DependenciesAdditionsBasics
import Foundation
Expand Down Expand Up @@ -449,3 +450,4 @@ extension ProcessInfo.Value {
)
}
}
#endif
16 changes: 11 additions & 5 deletions Sources/UserDefaultsDependency/UserDefaultsDependency.swift
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import Dependencies
import Foundation
@_spi(Internals) import DependenciesAdditionsBasics

extension DependencyValues {
/// A dependency that exposes an ``UserDefaults.Dependency`` value that you can use to read and
/// write to `UserDefaults`.
Expand Down Expand Up @@ -73,6 +72,7 @@ extension UserDefaults.Dependency: DependencyKey {
} values: { key, type in
// We use KVO to also get out-of-process changes
AsyncStream((any Sendable)?.self) { continuation in
#if canImport(ObjectiveC)
final class Observer: NSObject, Sendable {
let key: String
let type: Any.Type
Expand Down Expand Up @@ -113,6 +113,10 @@ extension UserDefaults.Dependency: DependencyKey {
continuation.onTermination = { _ in
userDefaults.value.removeObserver(object, forKeyPath: key)
}
#else
print("AsyncStream of UserDefaults values is currently not supported on Linux")
continuation.finish()
#endif
}
}
}
Expand Down Expand Up @@ -179,8 +183,8 @@ private extension UserDefaults {
}
}
}

@available(iOS 5.0, tvOS 9.0, macOS 10.7, watchOS 9.0, *)
#if os(iOS) || os(macOS) || os(tvOS) || os(watchOS)
@available(iOS 5.0, tvOS 9.0, macOS 10.7, watchOS 9.0, *)
private extension NSUbiquitousKeyValueStore {
func contains(key: String) -> Bool {
self.object(forKey: key) != nil
Expand Down Expand Up @@ -234,7 +238,7 @@ private extension NSUbiquitousKeyValueStore {
}
}
}

#endif
extension UserDefaults.Dependency: TestDependencyKey {
public static let testValue: Self = {
XCTFail(#"Unimplemented: @Dependency(\.userDefaults)"#)
Expand Down Expand Up @@ -277,10 +281,11 @@ extension UserDefaults.Dependency: TestDependencyKey {
}
}

#if os(iOS) || os(macOS) || os(tvOS) || os(watchOS)
extension UserDefaults.Dependency {
/// An iCloud-based container of key-value pairs you use to share data among
/// instances of your app running on a user's connected devices.
@available(iOS 5.0, tvOS 9.0, macOS 10.7, watchOS 9.0, *)
@available(iOS 5.0, tvOS 9.0, macOS 10.7, watchOS 9.0, *)
public static var ubiquitous: UserDefaults.Dependency {
let store = NSUbiquitousKeyValueStore.default
let userDefaults = UncheckedSendable(store)
Expand Down Expand Up @@ -346,3 +351,4 @@ extension UserDefaults.Dependency {
}
}
}
#endif
2 changes: 2 additions & 0 deletions Sources/_CoreDataDependency/FetchRequest.swift
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#if canImport(CoreData)
@preconcurrency import CoreData
@_exported import Dependencies
@_exported import DependenciesAdditionsBasics
Expand Down Expand Up @@ -617,3 +618,4 @@ extension Array {
}
}
}
#endif
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#if canImport(CoreData)
import CoreData
@_exported import PersistentContainerDependency

Expand Down Expand Up @@ -30,3 +31,4 @@ extension PersistentContainer {
return Fetched(id: object.objectID, context: context, viewContext: context)
}
}
#endif
Loading

0 comments on commit c75118f

Please sign in to comment.