Coder Social home page Coder Social logo

pointfreeco / swift-dependencies Goto Github PK

View Code? Open in Web Editor NEW
1.4K 21.0 82.0 1.22 MB

A dependency management library inspired by SwiftUI's "environment."

Home Page: https://www.pointfree.co

License: MIT License

Swift 99.18% Makefile 0.82%
dependency-injection dependency-management architecture swift

swift-dependencies's Introduction

Dependencies

A dependency management library inspired by SwiftUI's "environment."

CI Slack

Learn More

This library was motivated and designed over the course of many episodes on Point-Free, a video series exploring functional programming and the Swift language, hosted by Brandon Williams and Stephen Celis.

video poster image

Overview

Dependencies are the types and functions in your application that need to interact with outside systems that you do not control. Classic examples of this are API clients that make network requests to servers, but also seemingly innocuous things such as UUID and Date initializers, file access, user defaults, and even clocks and timers, can all be thought of as dependencies.

You can get really far in application development without ever thinking about dependency management (or, as some like to call it, "dependency injection"), but eventually uncontrolled dependencies can cause many problems in your code base and development cycle:

  • Uncontrolled dependencies make it difficult to write fast, deterministic tests because you are susceptible to the vagaries of the outside world, such as file systems, network connectivity, internet speed, server uptime, and more.

  • Many dependencies do not work well in SwiftUI previews, such as location managers and speech recognizers, and some do not work even in simulators, such as motion managers, and more. This prevents you from being able to easily iterate on the design of features if you make use of those frameworks.

  • Dependencies that interact with 3rd party, non-Apple libraries (such as Firebase, web socket libraries, network libraries, etc.) tend to be heavyweight and take a long time to compile. This can slow down your development cycle.

For these reasons, and a lot more, it is highly encouraged for you to take control of your dependencies rather than letting them control you.

But, controlling a dependency is only the beginning. Once you have controlled your dependencies, you are faced with a whole set of new problems:

  • How can you propagate dependencies throughout your entire application in a way that is more ergonomic than explicitly passing them around everywhere, but safer than having a global dependency?

  • How can you override dependencies for just one portion of your application? This can be handy for overriding dependencies for tests and SwiftUI previews, as well as specific user flows such as onboarding experiences.

  • How can you be sure you overrode all dependencies a feature uses in tests? It would be incorrect for a test to mock out some dependencies but leave others as interacting with the outside world.

This library addresses all of the points above, and much, much more.

Quick start

The library allows you to register your own dependencies, but it also comes with many controllable dependencies out of the box (see DependencyValues for a full list), and there is a good chance you can immediately make use of one. If you are using Date(), UUID(), Task.sleep, or Combine schedulers directly in your feature's logic, you can already start to use this library.

final class FeatureModel: ObservableObject {
  @Dependency(\.continuousClock) var clock  // Controllable way to sleep a task
  @Dependency(\.date.now) var now           // Controllable way to ask for current date
  @Dependency(\.mainQueue) var mainQueue    // Controllable scheduling on main queue
  @Dependency(\.uuid) var uuid              // Controllable UUID creation

  // ...
}

Once your dependencies are declared, rather than reaching out to the Date(), UUID(), etc., directly, you can use the dependency that is defined on your feature's model:

final class FeatureModel: ObservableObject {
  // ...

  func addButtonTapped() async throws {
    try await self.clock.sleep(for: .seconds(1))  // 👈 Don't use 'Task.sleep'
    self.items.append(
      Item(
        id: self.uuid(),  // 👈 Don't use 'UUID()'
        name: "",
        createdAt: self.now  // 👈 Don't use 'Date()'
      )
    )
  }
}

That is all it takes to start using controllable dependencies in your features. With that little bit of upfront work done you can start to take advantage of the library's powers.

For example, you can easily control these dependencies in tests. If you want to test the logic inside the addButtonTapped method, you can use the withDependencies function to override any dependencies for the scope of one single test. It's as easy as 1-2-3:

func testAdd() async throws {
  let model = withDependencies {
    // 1️⃣ Override any dependencies that your feature uses.
    $0.clock = ImmediateClock()
    $0.date.now = Date(timeIntervalSinceReferenceDate: 1234567890)
    $0.uuid = .incrementing
  } operation: {
    // 2️⃣ Construct the feature's model
    FeatureModel()
  }

  // 3️⃣ The model now executes in a controlled environment of dependencies,
  //    and so we can make assertions against its behavior.
  try await model.addButtonTapped()
  XCTAssertEqual(
    model.items,
    [
      Item(
        id: UUID(uuidString: "00000000-0000-0000-0000-000000000000")!,
        name: "",
        createdAt: Date(timeIntervalSinceReferenceDate: 1234567890)
      )
    ]
  )
}

Here we controlled the date dependency to always return the same date, and we controlled the uuid dependency to return an auto-incrementing UUID every time it is invoked, and we even controlled the clock dependency using an ImmediateClock to squash all of time into a single instant. If we did not control these dependencies this test would be very difficult to write since there is no way to accurately predict what will be returned by Date() and UUID(), and we'd have to wait for real world time to pass, making the test slow.

But, controllable dependencies aren't only useful for tests. They can also be used in Xcode previews. Suppose the feature above makes use of a clock to sleep for an amount of time before something happens in the view. If you don't want to literally wait for time to pass in order to see how the view changes, you can override the clock dependency to be an "immediate" clock using the withDependencies helper:

struct Feature_Previews: PreviewProvider {
  static var previews: some View {
    FeatureView(
      model: withDependencies {
        $0.clock = ImmediateClock()
      } operation: {
        FeatureModel()
      }
    )
  }
}

This will make it so that the preview uses an immediate clock when run, but when running in a simulator or on device it will still use a live ContinuousClock. This makes it possible to override dependencies just for previews without affecting how your app will run in production.

That is the basics to getting started with using the library, but there is still a lot more you can do. You can learn more in depth about the library by exploring the documentation and articles:

Getting started

  • Quick start (Same as the information above): Learn the basics of getting started with the library before diving deep into all of its features.

  • What are dependencies?: Learn what dependencies are, how they complicate your code, and why you want to control them.

Essentials

  • Using dependencies: Learn how to use the dependencies that are registered with the library.

  • Registering dependencies: Learn how to register your own dependencies with the library so that they immediately become available from any part of your code base.

  • Live, preview, and test dependencies: Learn how to provide different implementations of your dependencies for use in the live application, as well as in Xcode previews, and even in tests.

  • Testing: One of the main reasons to control dependencies is to allow for easier testing. Learn some tips and tricks for writing better tests with the library.

Advanced

  • Designing dependencies: Learn techniques on designing your dependencies so that they are most flexible for injecting into features and overriding for tests.

  • Overriding dependencies: Learn how dependencies can be changed at runtime so that certain parts of your application can use different dependencies.

  • Dependency lifetimes: Learn about the lifetimes of dependencies, how to prolong the lifetime of a dependency, and how dependencies are inherited.

  • Single entry point systems: Learn about "single entry point" systems, and why they are best suited for this dependencies library, although it is possible to use the library with non-single entry point systems.

Examples

We rebuilt Apple's Scrumdinger demo application using modern, best practices for SwiftUI development, including using this library to control dependencies on file system access, timers and speech recognition APIs. That demo can be found here.

Documentation

The latest documentation for the Dependencies APIs is available here.

Installation

You can add Dependencies to an Xcode project by adding it to your project as a package.

https://github.com/pointfreeco/swift-dependencies

If you want to use Dependencies in a SwiftPM project, it's as simple as adding it to your Package.swift:

dependencies: [
  .package(url: "https://github.com/pointfreeco/swift-dependencies", from: "1.0.0")
]

And then adding the product to any target that needs access to the library:

.product(name: "Dependencies", package: "swift-dependencies"),

Community

If you want to discuss this library or have a question about how to use it to solve a particular problem, there are a number of places you can discuss with fellow Point-Free enthusiasts:

Extensions

This library controls a number of dependencies out of the box, but is also open to extension. The following projects all build on top of Dependencies:

Alternatives

There are many other dependency injection libraries in the Swift community. Each has its own set of priorities and trade-offs that differ from Dependencies. Here are a few well-known examples:

License

This library is released under the MIT license. See LICENSE for details.

swift-dependencies's People

Contributors

adriansergheev avatar alex293 avatar alexhunsley avatar arasan01 avatar compnerd avatar finestructure avatar gibachan avatar iampatbrown avatar jager-yoo avatar kabiroberai avatar karlis avatar kgrigsby59 avatar kuglee avatar mbrandonw avatar mika5652 avatar nhenri avatar oronbz avatar pyrtsa avatar rhysm94 avatar stephencelis avatar tgrapperon avatar x-0o0 avatar xiii111 avatar zeveisenberg avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

swift-dependencies's Issues

Visibility of downstream imports

Description

Dynamic libraries via SPM are a pain. It just doesn't work reliably.

.library(name: "MyPackage", type: .dynamic, targets: ["MyTarget"])

As a consequence, we use a lot of embedded frameworks in our project.

Screenshot 2023-04-19 at 08 31 26

We use your dependencies library (which is great) in a lot of the above frameworks, but we have run into this issue.

❌  /foo/bar/SessionStore.swift:8:8: missing required modules: 'Clocks', 'CombineSchedulers', 'XCTestDynamicOverlay'
import Dependencies

The problem is this exported attribute.

@_exported import Clocks
@_exported import CombineSchedulers
@_exported import XCTestDynamicOverlay

https://forums.swift.org/t/exported-and-fixing-import-visibility/9415

The @_implementationOnly attribute seems to be the solution but attempts to install it lead to issues with the @_transparent attribute.

https://forums.swift.org/t/update-on-implementation-only-imports/26996

We are still digging into possible workarounds. The attached zip illustrates how the code is packaged on our end. Do you have any ideas how we might workaround this issue? Your support would be much appreciated.

Pointfree.zip

Checklist

  • I have determined whether this bug is also reproducible in a vanilla SwiftUI project.
  • If possible, I've reproduced the issue using the main branch of this package.
  • This issue hasn't been addressed in an existing GitHub issue or discussion.

Expected behavior

Expected behaviour

Actual behavior

Actual behaviour

Steps to reproduce

Steps to reproduce

Dependencies version information

0.1.4

Destination operating system

iOS 16

Xcode version information

14.3

Swift Compiler version information

swift-driver version: 1.75.2 Apple Swift version 5.8 (swiftlang-5.8.0.124.2 clang-1403.0.22.11.100)
Target: arm64-apple-macosx13.0

`@DependencyClient` and `@autoclosure` parameters

Description

This is similar to #146. The code generated by @DependencyClient is incorrect for @autoclosure parameters and generate invalid swift code. it should explicitly refuse to generate code or if possible generate valid code.

Checklist

  • I have determined whether this bug is also reproducible in a vanilla SwiftUI project.
  • If possible, I've reproduced the issue using the main branch of this package.
  • This issue hasn't been addressed in an existing GitHub issue or discussion.

Expected behavior

@DependencyClient
public struct DemoClinet {
    public var execute: (_ closure: @autoclosure () -> String) -> Void
}

Should generate valid swift code

Actual behavior

The code generated :

public func execute(closure p0: @autoclosure () -> String) -> Void {
    self.execute(p0)  // Error occurs: 'Add () to forward @autoclosure parameter'
}

private var _execute: (_ closure: @autoclosure () -> String) -> Void = { _ in
    XCTestDynamicOverlay.XCTFail("Unimplemented: 'execute'")
}

is not valid code: Add () to forward @autoclosure parameter

Steps to reproduce

No response

Dependencies version information

1.1.2

Destination operating system

No response

Xcode version information

15.0.0

Swift Compiler version information

No response

Unit test: Unimplemented: ContinuousClock.now / ContinuousClock.sleep

Description

Thanks for creating this and the other libraries! They are awesome!

I created a sample project (see attached at the repo steps) to show the problem I am facing. Please open the attached project and run the unit test called: SampleClocksTests. It might pass the first time around, so please run the test again.

The error message says:

testSplashScreenModel(): Unimplemented: ContinuousClock.now
testSplashScreenModel(): Unimplemented: ContinuousClock.sleep

If the project entry point is not the SplashScreen (so no SplashScreenModel is used), then the test passes. See SampleClocksApp.swift.

Checklist

  • I have determined whether this bug is also reproducible in a vanilla SwiftUI project.
  • If possible, I've reproduced the issue using the main branch of this package.
  • This issue hasn't been addressed in an existing GitHub issue or discussion.

Expected behavior

The test should pass, and it should pass consistently.

Actual behavior

The test fails (it passes sometimes at the first run).

Steps to reproduce

  1. Open the Xcode project.
  2. Run the SampleClocksTests unit test.
    SampleClocks.zip

IssueScreenshot

Deps

Dependencies version information

No response

Destination operating system

16.2

Xcode version information

14.2

Swift Compiler version information

xcrun swiftc --version
swift-driver version: 1.62.15 Apple Swift version 5.7.2 (swiftlang-5.7.2.135.5 clang-1400.0.29.51)
Target: arm64-apple-macosx12.0

A random testing error stating that dependency is not overridden

Description

I have a few states implemented using TCA w/ ReducerProtocol. One of the states has a Bool depedency. And I always get random tests in random suites failing with an error saying:

testFoo(): @Dependency(\.boolDep) has no test implementation, but was accessed from a test context:

  Location:
    App/Feature.swift:9
  Key:
    BoolDependencyKey
  Value:
    Bool

Dependencies registered with the library are not allowed to use their default, live implementations when run from tests.

To fix, override 'boolDep' with a test value. If you are using the Composable Architecture, mutate the 'dependencies' property on your 'TestStore'. Otherwise, use 'withDependencies' to define a scope for the override. If you'd like to provide a default value for all tests, implement the 'testValue' requirement of the 'DependencyKey' protocol.

The weird part is that I get this error on state tests that don't use the state with aforementioned dependency. Just for the clarity I also update the dependency using either the store.dependencies property or the trailing closure of TestStore:

let store = TestStore(....) { $0.boolDep = false }

or

let store = TestStore(...)
store.dependencies.boolDep = false

I also tried extending the dependency key enum with testValue to no avail.

Nothing makes the tests more predictable. All test suite classes are marked with @MainActor.

Checklist

  • I have determined whether this bug is also reproducible in a vanilla SwiftUI project.
  • If possible, I've reproduced the issue using the main branch of this package.
  • This issue hasn't been addressed in an existing GitHub issue or discussion.

Expected behavior

Deterministic failures and passes of tests.

Actual behavior

Random unit tests in random test suites failing with an unrelated dependency.

Steps to reproduce

I could not reproduce it on a fresh SwiftUI project.

Dependencies version information

0.1.4

Destination operating system

watchOS 9.1

Xcode version information

14.2

Swift Compiler version information

swift-driver version: 1.62.15 Apple Swift version 5.7.2 (swiftlang-5.7.2.135.5 clang-1400.0.29.51)
Target: x86_64-apple-macosx13.0

this repo no longer builds on windows

Description

before #84 went in, this repo was able to build on Windows, now that there are API calls to dlopen and dlsym pulling the latest package no longer builds

Checklist

  • I have determined whether this bug is also reproducible in a vanilla SwiftUI project.
  • If possible, I've reproduced the issue using the main branch of this package.
  • This issue hasn't been addressed in an existing GitHub issue or discussion.

Expected behavior

No response

Actual behavior

No response

Steps to reproduce

No response

Dependencies version information

No response

Destination operating system

Windows

Xcode version information

No response

Swift Compiler version information

No response

Library should not export XCTestDynamicOverlay

Description

The @exported import of XCTestDynamicOverlay causes the use of XCTFail in tests and test helpers where you import both XCTest and Dependencies (or something that implicitly imports Dependencies like ComposableArchitecture) to result in a compiler error due to am ambiguous use of XCTFail.

This is effectively a regression of pointfreeco/swift-composable-architecture#747 and I ran into this upgrading from TCA 0.45 to 0.49.

Checklist

  • I have determined whether this bug is also reproducible in a vanilla SwiftUI project.
  • If possible, I've reproduced the issue using the main branch of this package.
  • This issue hasn't been addressed in an existing GitHub issue or discussion.

Expected behavior

You should be able to use XCTFail in tests/test helpers without error.

Actual behavior

Fails to compile.

Steps to reproduce

No response

Dependencies version information

0.1.2

Destination operating system

No response

Xcode version information

Xcode 14.2

Swift Compiler version information

No response

Unable to import

Description

I created an iOS demo and then imported this library.
I thought it should be enough straightforward to use it, then I found Xcode can not recognize this library at all.
Anything I missing ?

image

Demo.zip

Check the attached demo project.

Checklist

  • I have determined whether this bug is also reproducible in a vanilla SwiftUI project.
  • If possible, I've reproduced the issue using the main branch of this package.
  • This issue hasn't been addressed in an existing GitHub issue or discussion.

Expected behavior

No response

Actual behavior

No response

Steps to reproduce

No response

Dependencies version information

1.1

Destination operating system

iOS 17

Xcode version information

15

Swift Compiler version information

swift-driver version: 1.87.1 Apple Swift version 5.9 (swiftlang-5.9.0.128.108 clang-1500.0.40.1)
Target: arm64-apple-macosx14.0

Unpredictable Crash on App Start Due to Test Observer Registration Outside Main Thread

Description

The TCA based application is unpredictably crashing upon start during the configuration load. The crash occurs on the line let config = try? await remoteConfigService.fetchRemoteConfig() from the AppConfig module, producing the error message: 'Test observers can only be registered and unregistered on the main thread.'

On further investigation, it was found that the crash likely happens during the testing environment check in DependencyValues.swift of the Dependencies framework, specifically at else if _XCTIsTesting. The crash seems to continue at the initialization of XCTCurrentTestCase where the test observers are launched.

Additional Notes:

  • The bug does not occur all the time but is more prevalent on slower machines compared to more powerful ones.
  • It is suggested that the issue lies within the XCTCurrentTestCase where test observers are being initialized.

Temporary workaround that helped:

        case .loadConfig:
            return .run { @MainActor send in
                let config = try? await remoteConfigService.fetchRemoteConfig()
                send(.configLoaded(config))
            }

Implementation of AppFeature, AppConfigFeature and RemoteConfigService:

struct AppFeature: ReducerProtocol {
    
    // MARK: - State
    struct State: Equatable {
        var appConfig: AppConfigFeature.State
    }
    
    // MARK: - Action
    enum Action: Equatable {
        case appDidFinishLaunching

        case appConfig(AppConfigFeature.Action)
    }

    // MARK: - Dependencies
    @Dependency(\.datadogActivator)
    var datadogActivator
    
    // MARK: - Reducer
    var body: some ReducerProtocol<State, Action> {
        Scope(state: \.appConfig, action: /Action.appConfig, child: AppConfigFeature.init)
        Reduce { state, action in
            switch action {
            case .appDidFinishLaunching:
                return .run { send in await send(Action.appConfig(.loadConfig)) }
            case .appConfig(.configLoaded):
                guard let info = state.appConfig.config?.dataDog else {
                    assertionFailure()
                    return .none
                }
                return .run { _ in await datadogActivator.activate(info: info) }
            case .appConfig:
                return .none
            }
        }
    }
}
public struct AppConfigFeature: ReducerProtocol, Sendable {

    // MARK: - State
    public struct State: Equatable {
        public var config: RemoteConfigValue?
    }

    // MARK: - Action
    public enum Action: Equatable, Sendable {
        case configLoaded(RemoteConfigValue?)
        case loadConfig
    }

    // MARK: - Dependencies
    @Dependency(\.remoteConfigService)
    var remoteConfigService

    // MARK: - Reducer
    public func reduce(into state: inout State, action: Action) -> EffectTask<Action> {
        switch action {
        case .configLoaded(let config):
            state.config = config
            return .none
        case .loadConfig:
            return .run { send in
                let config = try? await remoteConfigService.fetchRemoteConfig()
                await send(.configLoaded(config))
            }
        }
    }
}
import Dependencies
import FirebaseRemoteConfig

struct RemoteConfigService: Sendable {
    var fetchRemoteConfig: @Sendable () async throws -> RemoteConfigValue
}

// MARK: - DependencyKey
extension RemoteConfigService: DependencyKey {
    static let liveValue: RemoteConfigService = {
        @Dependency(\.appClient.isRunningUITests)
        var isRunningUITests
        return isRunningUITests()
        ? .uiTestStub
        : .live
    }()
    static let testValue = RemoteConfigService.failing
}
extension DependencyValues {
    var remoteConfigService: RemoteConfigService {
        get { self[RemoteConfigService.self] }
        set { self[RemoteConfigService.self] = newValue }
    }
}

// MARK: - Implementations
private extension RemoteConfigService {

    static let live = Self {

        let firebaseConfig = RemoteConfig.remoteConfig()
        firebaseConfig.setDefaults(fromPlist: "RemoteConfigDefaults")

        return try await withCheckedThrowingContinuation { continuation in
            firebaseConfig.fetchAndActivate { _, error in

                if let error {
                    continuation.resume(throwing: error)
                    return
                }

                do {
                    continuation.resume(returning: try RemoteConfigValue(from: firebaseConfig))
                } catch {
                    continuation.resume(throwing: error)
                }

            }
        }
    }

    static let failing = Self(
        fetchRemoteConfig: unimplemented("\(Self.self).fetchRemoteConfig")
    )

    static let uiTestStub = Self(
        fetchRemoteConfig: { RemoteConfigValue.mock() }
    )
}

Checklist

  • I have determined whether this bug is also reproducible in a vanilla SwiftUI project.
  • If possible, I've reproduced the issue using the main branch of this package.
  • This issue hasn't been addressed in an existing GitHub issue or discussion.

Expected behavior

The application should start and load the app config successfully.

Actual behavior

The application crashes on the line let config = try? await remoteConfigService.fetchRemoteConfig() with the error:

libc++abi: terminating with uncaught exception of type NSException
*** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: 'Test observers can only be registered and unregistered on the main thread.'
terminating with uncaught exception of type NSException
CoreSimulator 857.14 - Device: iPhone 14 Pro (12DDE56D-B64A-4155-85C5-9D807F586EB2) - Runtime: iOS 16.2 (20C52) - DeviceType: iPhone 14 Pro

Steps to reproduce

  1. Start the application
  2. AppDelegate.didFinishLaunchingWithOptions sends viewStore.send(.appDidFinishLaunching) to the AppFeature.
  3. In the app reducer, the app config is loaded from a dedicated module via
case .appDidFinishLaunching:
return .run { send in await send(Action.appConfig(.loadConfig)) }
  1. Inside AppConfig module we run
case .loadConfig:
return .run { send in
    let config = try? await remoteConfigService.fetchRemoteConfig()
    await send(.configLoaded(config))
}

Dependencies version information

0.5.1

Destination operating system

iOS 16.2 Simulator

Xcode version information

14.2.0

Swift Compiler version information

swift-driver version: 1.62.15 Apple Swift version 5.7.2 (swiftlang-5.7.2.135.5 clang-1400.0.29.51)
Target: x86_64-apple-macosx13.0

TCA built-in dependencies do not currently support the new `@Dependency(XXX.self) var xxx` paradigm

Description

In view of #172, it appears as though some (or perhaps all - I didn't check) of the TCA built-in dependencies do not support the new paradigm. I was updating one of my projects, which uses the Date dependency, and I noted that

@Dependency(Date.self) var date

results in the compiler error

Initializer 'init(_:file:fileID:line:)' requires that 'Date' conform to 'TestDependencyKey'

Now, this isn't a big deal for my project since the old

@Dependency(\.date) var date

still works but I thought I'd let you know.

Checklist

  • I have determined whether this bug is also reproducible in a vanilla SwiftUI project.
  • If possible, I've reproduced the issue using the main branch of this package.
  • This issue hasn't been addressed in an existing GitHub issue or discussion.

Expected behavior

No response

Actual behavior

No response

Steps to reproduce

No response

Dependencies version information

1.2.0

Destination operating system

iOS 15+

Xcode version information

Version 15.3 beta 3 (15E5194e)

Swift Compiler version information

swift-driver version: 1.90.11.1 Apple Swift version 5.10 (swiftlang-5.10.0.13 clang-1500.3.9.4)

@DependencyClient generates invalid code for computed property with explicit get

Description

DependencyClient macro tries to handle a computed property with an explicit get as a stored property. This impacts especially computed properties that can throw or are async.

Screenshot 2023-12-15 at 11 19 28

Checklist

  • I have determined whether this bug is also reproducible in a vanilla SwiftUI project.
  • If possible, I've reproduced the issue using the main branch of this package.
  • This issue hasn't been addressed in an existing GitHub issue or discussion.

Expected behavior

@DependencyClient should ignore the property in init as it does for simple computed properties

Actual behavior

@DependencyClient ads the explicit computed property and its body to init signature

Steps to reproduce

Try to compile the following code

@DependencyClient
struct DemoDependency {
    var getProp: () -> String = { "" }
    
    var prop: String {
        getProp()
    }
    
    var anotherProp: String {
        get {
            getProp()
        }
    }
    
    var asyncProp: String {
        get async throws {
            getProp()
        }
    }
}

Dependencies version information

1.1.4

Destination operating system

iOS 17

Xcode version information

Version 15.1 (15C65)

Swift Compiler version information

swift-driver version: 1.87.3 Apple Swift version 5.9.2 (swiftlang-5.9.2.2.56 clang-1500.1.0.2.5)
Target: arm64-apple-macosx14.0

`withEscapedDependencies` doesn't work sometimes.

Description

I encountered a strange bug.
I've created a "Command" wrapper for closures and added an extension to capture dependencies with withEscapedDependencies. This extension only worked after I moved it to the same target where it's used.
To reproduce the issue, I first attempted in a clean SPM package but failed. Then, I set up a tuist project similar to my app, where the bug reappeared, but only in the test target, whereas in my original project, it manifested in the app itself.

Checklist

  • I have determined whether this bug is also reproducible in a vanilla SwiftUI project.
  • If possible, I've reproduced the issue using the main branch of this package.
  • This issue hasn't been addressed in an existing GitHub issue or discussion.

Expected behavior

withEscapedDependencies works properly everywhere.

Actual behavior

Dependencies are lost.

Steps to reproduce

  1. Download my example project.
  2. Install tuis.
  3. go to the project root
  4. run tuist fetch
  5. run tuist generate
  6. run test_escaped_dependencies tests. Bug is unstable, sometimes tests passes, cleaning build folder helps.

Dependencies version information

1.1.5

Destination operating system

iOS 17

Xcode version information

15.0.1

Swift Compiler version information

5.9

@DependencyClient and inout parameters

Description

The code generated by @DependencyClient is incorrect for inout parameters and generate invalid swift code. it should explicitly refuse to generate code or if possible generate valid code.

Checklist

  • I have determined whether this bug is also reproducible in a vanilla SwiftUI project.
  • If possible, I've reproduced the issue using the main branch of this package.
  • This issue hasn't been addressed in an existing GitHub issue or discussion.

Expected behavior

@DependencyClient
public struct AuthenticationClient {
    var authenticateRequest: (_ urlRequest: inout URLRequest) async throws  -> Void
}

Should generate valid swift code

Actual behavior

The code generated :

func authenticateRequest(urlRequest p0: inout URLRequest) async throws  -> Void {
    try await self.authenticateRequest(p0)
}

private var _authenticateRequest: (_ urlRequest: inout URLRequest) async throws  -> Void = { _ in
    XCTestDynamicOverlay.XCTFail("Unimplemented: 'authenticateRequest'")
    throw DependenciesMacros.Unimplemented("authenticateRequest")
}

is not valid code: Passing value of type 'URLRequest' to an inout parameter requires explicit '&'

Steps to reproduce

No response

Dependencies version information

1.1.1

Destination operating system

No response

Xcode version information

15.0.1

Swift Compiler version information

No response

When on `live` context with no `liveValue` provided, the `testValue` fallback isn't cached

Description

As description suggests, testValue fallback isn't cached on live context.

lines 337...341 on DependencyValues.swift

        // should we add this: self.cached[cacheKey] = AnySendable(Key.testValue)
        return Key.testValue // <--- no cache
      }

      self.cached[cacheKey] = AnySendable(value)
      return value

This issue causes eager memory release of the acquired dependency, since @Dependency propertyWrapper isn't strong referencing the dependency, only the cache does. In result not only the provided dependency is released but also reinstantiated on every request.

Why should we want this?
Sometimes when separating TestDependencyKey in one module and DependencyKey in another, and there's an example app on the TestDependencyKey module, it's cumbersome to create a liveValue again for each of them. We already have a runtimeWarn for this, I don't see any reason why not cache and store it.

If the provided solution sounds reasonable, I would create a PR for it.

Checklist

  • I have determined whether this bug is also reproducible in a vanilla SwiftUI project.
  • If possible, I've reproduced the issue using the main branch of this package.
  • This issue hasn't been addressed in an existing GitHub issue or discussion.

Expected behavior

I expect the testValue in this case to be held to cache as a strong reference to it.

Actual behavior

When on live context with no liveValue provided, the testValue fallback isn't cached

Steps to reproduce

No response

Dependencies version information

0.2.0

Destination operating system

iOS 16.4

Xcode version information

Xcode 14.3

Swift Compiler version information

No response

Testing failure for overridden dependency

Description

I have a framework target and its unit tests. I have a dependency of shape (String,String) -> EffectTask<Result<A,B>>. It has a live value in framework target and an extension of its key in tests target with a testValue.

I wrote a test that exercises a reducer's event and uses said dependency. The dependency also has testValue defined in the test case file. The problem is that the testValue should override the network call. However I get a few errors where most of them I believe stem from swift-dependencies not being able to find overridden dependency. The failures are:

  • dependency doesn't have a test value (culprit)
  • an effect returned is still running (network request in live and simple EffectTask(value:) in test
  • expected but did not receive an action (because the previous effect is still running, because it didn't find a testValue for the dependency);

I read the test gotcha, however I don't believe it's valid in my case as I am testing a framework target. I also read this thread where Brandon writes

You aren't allowed to extend a protocol with default implementations from another module.

Is this the case? I am not extending a protocol, rather protocol's adoption from module A (framewokr) in module B (tests).

Checklist

  • I have determined whether this bug is also reproducible in a vanilla SwiftUI project.
  • If possible, I've reproduced the issue using the main branch of this package.
  • This issue hasn't been addressed in an existing GitHub issue or discussion.

Expected behavior

I would expect the tests to run smoothly as they don't exercise complex business logic. Simple action that triggers request/response and then maps to resulting success/failure action.

Actual behavior

The dependency is not found (ostensibly), the trigger effect is still running and result action is not received.

Steps to reproduce

No response

Dependencies version information

0.1.4

Destination operating system

iOS 16

Xcode version information

14.2

Swift Compiler version information

Apple Swift version 5.7.2 (swiftlang-5.7.2.135.5 clang-1400.0.29.51)
Target: x86_64-apple-macosx13.0

`testValue` can leak state between tests when used asynchronously

Description

This might be related to #75, but I'm creating this as a separate issue as it could be a different issue, or it's the underlying issue causing #75.

The problem:

If withDependencies isn't being used, and default testValues are being used instead, if one test kicks off a write to a dependency asynchronously, this write can be triggered during the lifetime of a test run later.
If this happens, that second test's dependency state will be unexpectedly affected by the earlier test, leading to incorrect state.

This can be shown with the contrived example here:

private final class TestObject {
    @Dependency(\.analytics) private var analytics

    func trackAction(named name: String, after delay: Duration = .zero) {
        Task {
            if delay != .zero {
                try await Task.sleep(for: delay)
            }
            analytics.trackEvent(named: name)
        }
    }
}

final class LeakageTests: XCTestCase {
    func testAsyncDependencyMutationOnDefaultDependencyLeaksPart1() async throws {
        TestObject().trackAction(named: "One", after: .milliseconds(10))
    }

    /// This demonstrates that we must avoid using default `testValue` values if they're going to be mutated asynchronously. *This test must be run as part of the suite to pass.*
    func testAsyncDependencyMutationOnDefaultDependencyLeaksPart2() async throws {
        let analytics = DependencyValues._current.analytics as! AnalyticsMock

        // Sleep, to let the async write happen at this time.
        try await Task.sleep(for: .milliseconds(15))

        XCTAssertEqual(analytics.events, ["One"])
        // 💥😱 This assertion passes if the test is run on its own, but it fails if the test is run with testAsyncDependencyMutationOnDefaultDependencyLeaksPart1().
        XCTExpectFailure {
            // What we wanted the value to be:
            XCTAssertEqual(analytics.events, [])
        }
    }
}

A potential workaround

Rather than avoiding using testValue and applying withDependencies absolutely everywhere in tests instead, I found some potential success taking inspiration from https://pointfreeco.github.io/swift-dependencies/main/documentation/dependencies/overridingdependencies#Testing

We can wrap the entire test run in a withDependencies, and just leverage default values automatically:

func withDefaultDependencies(_ operation: () -> Void) {
    withDependencies {
        $0 = .current
    } operation: {
        operation()
    }
}

private extension DependencyValues {
    static var current: Self {
        var values = Self()
        values.context = DependencyValues._current.context
        return values
    }
}

final class LeakageTests: XCTestCase {
    override func invokeTest() {
        withDefaultDependencies {
            super.invokeTest()
        }
    }

    func test_WithDefaultDependencies_OnDefaultDependencyPreventsLeaksPart1() async throws {
        TestObject().trackAction(named: "One", after: .milliseconds(10))
    }

    func test_WithDefaultDependencies_OnDefaultDependencyPreventsLeaksPart2() async throws {
        let analytics = DependencyValues._current.analytics as! AnalyticsMock
        try await Task.sleep(for: .milliseconds(15))

        XCTAssertEqual(analytics.events, [])
    }
}

This passes! 😄

Issues with the workaround

This workaround creates some new issues though, which probably make it not worth using:

  1. static initializations of objects start leaking their dependencies, which defeats the whole point of this.
    I'm not really sure why, but once I start introducing this, any statically initialized objects don't seem to get updates to their stored @dependency values. See https://github.com/dafurman/Swift-Dependencies-Exploration/blob/f1be21b6a2725a7656fc7cb799d9bfdd407af60e/Tests/SwiftDepsTests/WithDefaultDependenciesTests.swift#L52

  2. Dependencies used in .task() must be propagated with withDependencies(from: self)
    Within the scope of a .task() or any code it kicks off, we have to use stored versions of @Dependency, and propagate dependencies with withDependencies(from: self), otherwise we'll get an incorrect instance of the dependency.
    See https://github.com/dafurman/Swift-Dependencies-Exploration/blob/f1be21b6a2725a7656fc7cb799d9bfdd407af60e/Tests/SwiftDepsTests/WithDefaultDependenciesTests.swift#L38

Maybe someone else can take ideas from this and get something working that keeps testValue safe to use from asynchronous code.

For now, it seems like I have two options, and I'm probably going to go with the second:

  1. Apply withDependencies() to every dependency-using action in a test and never rely on testValue.
  2. Accept that testValue will sometimes leak and keep an eye out for that. These situations can be hard to notice, but if I do notice them, I at least have the workaround of applying withDependencies() on the leaking test or the test being leaked into.

Checklist

  • I have determined whether this bug is also reproducible in a vanilla SwiftUI project.
  • If possible, I've reproduced the issue using the main branch of this package.
  • This issue hasn't been addressed in an existing GitHub issue or discussion.

Expected behavior

I'd expect use of testValue to be safe between different test runs, and for one test to not read from or mutate another test's dependencies.

Actual behavior

When I write to a default testValue dependency asynchronously, it can affect the dependency's state when accessed from another test, causing tests to succeed if run on their own, but to fail when run as part of a test suite.

Steps to reproduce

Check out https://github.com/dafurman/Swift-Dependencies-Exploration/blob/main/Tests/SwiftDepsTests/LeakageTests.swift and run LeakageTests. Pay attention to testAsyncDependencyMutationOnDefaultDependencyLeaksPart1() and testAsyncDependencyMutationOnDefaultDependencyLeaksPart2(), then run testAsyncDependencyMutationOnDefaultDependencyLeaksPart2() on its own and see that its behavior is different when run in isolation vs having part 1 run beforehand.

Dependencies version information

1.0.0 & main

Destination operating system

iOS 16 & 17

Xcode version information

Xcode 15.0

Swift Compiler version information

swift-driver version: 1.87.2 Apple Swift version 5.9 (swiftlang-5.9.2.1.6 clang-1500.1.0.1.1)

Performance tests use liveValue

Description

I've set up a suite of UI test for performance measurements as described in the WWDC video Elimnate animation hitches with XCTest. Apple recommends setting your configuration to Release and disabling the debugger for these tests.

I want the Dependencies to use the testValue of my dependencies during these tests.

Checklist

  • I have determined whether this bug is also reproducible in a vanilla SwiftUI project.
  • If possible, I've reproduced the issue using the main branch of this package.
  • This issue hasn't been addressed in an existing GitHub issue or discussion.

Expected behavior

Dependencies library detects that I am running tests and initializes the testValue for my dependencies.

Actual behavior

The Dependencies library initializes the liveValue of my dependencies in these test.

Steps to reproduce

  1. Open an Xcode project for an iOS application that uses testValue and liveValue dependencies.
  2. Set up a UI test in a test target that runs against your application in release mode without the debugger attached.
  3. Run the UI tests.

Dependencies version information

0.1.4

Destination operating system

iOS 16.4

Xcode version information

Xcode 14.3

Swift Compiler version information

swift-driver version: 1.75.2 Apple Swift version 5.8.1 (swiftlang-5.8.0.124.5 clang-1403.0.22.11.100)
Target: arm64-apple-macosx13.0

Linux build with `--static-swift-stdlib` fails

Description

Building (the swift-dependencies package itself or with the package as a dependency) fails on Linux when using --static-swift-stdlib.

I should be able to work around the lack of static linking of the stdlib by building without and copying all the libs like we used to in the past but if there's a simple fix to support this flag that'd be great.

Checklist

  • I have determined whether this bug is also reproducible in a vanilla SwiftUI project.
  • If possible, I've reproduced the issue using the main branch of this package.
  • This issue hasn't been addressed in an existing GitHub issue or discussion.

Expected behavior

docker run --rm -v "$PWD":/host -w /host swift:5.8-focal swift build --static-swift-stdlib

should succeed, just like

docker run --rm -v "$PWD":/host -w /host swift:5.8-focal swift build

does.

Actual behavior

Fails to compile with error:

/host/Sources/Dependencies/DependencyValues.swift:353:10: error: no such module 'XCTest'
  import XCTest
         ^

Steps to reproduce

Steps to reproduce:

❯ git clone https://github.com/pointfreeco/swift-dependencies && cd swift-dependencies
❯ git rev-parse @
0f34848e741315119703b1990e5c962929ed5dff
❯ docker run --rm -v "$PWD":/host -w /host swift:5.8-focal swift build --static-swift-stdlib
Fetching https://github.com/apple/swift-argument-parser
Fetching https://github.com/pointfreeco/xctest-dynamic-overlay
...
[96/122] Emitting module Dependencies
/host/Sources/Dependencies/DependencyValues.swift:353:10: error: no such module 'XCTest'
  import XCTest
         ^
[97/139] Compiling Benchmark BenchmarkCommand.swift
...

Dependencies version information

0f34848 (current main)

Destination operating system

Linux

Xcode version information

No response

Swift Compiler version information

Swift version 5.8 (swift-5.8-RELEASE)
Target: aarch64-unknown-linux-gnu

Production hang updating from 1.1.5 to 1.2.x

Description

I'll admit this is a very weird one and I'm not sure where to start to debug it but it's a reproducible production affecting issue.

In our SPI build system we build a "builder" binary using Xcode 15.3 (Swift 5.10) and distribute it to Macs that run it to build packages with various Swift versions and platforms. We have a integration test matrix set up to run a package build for all combinations.

When I do a package update for our builder, updating swift-dependencies from 1.1.5 to 1.2.x (or main) causes a hang in the builder process. I.e. our package builds time out after 10min. This hang is only happening on macOS, Linux is working fine.

Our (closed source) builder doesn't use @Dependency itself but transitively via the open source package DocUploader.

I can tell from the logs that it's hitting @Dependency(\.uuid) var uuid. And indeed if I replace this with let uuid = { UUID() } the hang disappears. (There's a second @Dependency in the project but our test doesn't cover it, so it's unknown if it would hang, too.)

Is there anything in the 1.1.5...1.2.0 diff that could cause a hang like this on macOS?

I see a couple of #if _runtime(_ObjC) blocks that do things with DispatchQueue that seem likely candidates but I can't tell how they're used or if they could in fact be the cause.

FWIW, we have a unit test in our builder which should be doing the same as the integration test is not hanging. I've also tried running the builder with a -c debug build and it's still hanging. So it's happening in both debug and release builds but not when the dependency is being replaced via withDependencies.

I'm not sure how I could debug this further but I do have a reproducer set up that can turn around results in ~5mins. I'll see if I can boil this down into a reproducer that's less complicated to run that I can share.

For now I'm working around the issue via

#if DEBUG
    @Dependency(\.uuid) var uuid
#else
    let uuid = { UUID() }
#endif

which is of course not ideal but I at least I don't need to pin swift-dependencies to 1.1.5.

Checklist

  • I have determined whether this bug is also reproducible in a vanilla SwiftUI project.
  • If possible, I've reproduced the issue using the main branch of this package.
  • This issue hasn't been addressed in an existing GitHub issue or discussion.

Expected behavior

It should not hang 😄

Actual behavior

It hangs 😞

Steps to reproduce

Currently only internally reproducible, working on a sharable project.

Dependencies version information

1.2.x

Destination operating system

macOS 14

Xcode version information

15.3

Swift Compiler version information

swift-driver version: 1.90.11.1 Apple Swift version 5.10 (swiftlang-5.10.0.13 clang-1500.3.9.4)

Overridden date dependency not having an effect on previews

Description

Let's say as an example that we have this view and model;

import Dependencies
import SwiftUI

struct ExampleView: View {
    let model = Model()

    var body: some View {
        VStack {
            Text(model.date.description)
                .foregroundColor(model.isBeforeToday ? .blue : .red )
        }
    }
}

struct Model {
    var date = Date("2023-09-25")!
    var isBeforeToday: Bool {
        @Dependency(\.date.now) var now
        return date.isBeforeDate(now, granularity: .day)
    }
}

Here, the text in the view should be blue if the models date is before todays date. In this case the text would be blue since the day of writing is 2023-09-26. However I also want to see that the logic for the text is correct and that it can turn red. Normally this date would not be hardcoded, but gotten from an API or similar. So, I create a preview, like so;

struct ExampleView_Previews: PreviewProvider {
    static var previews: some View {
        withDependencies {
            $0.date.now = Date("2023-01-23")!
        } operation: {
            ExampleView()
        }
    }
}

However, the date would still be blue, when I believe it should be red. The dependency determining what todays date is does not get overwritten.

Checklist

  • I have determined whether this bug is also reproducible in a vanilla SwiftUI project.
  • If possible, I've reproduced the issue using the main branch of this package.
  • This issue hasn't been addressed in an existing GitHub issue or discussion.

Expected behavior

The date dependency should be overwritten by the preview and, in this case, the text should be displayed as red.

Actual behavior

The dependency is not overwritten, and the text remains blue. Instead of using the value I provide in the preview, it instead uses the standard value, that being;

static let testValue = DateGenerator {
      XCTFail(#"Unimplemented: @Dependency(\.date)"#)
      return Date()
 }

An important note to add, overriding the dependency works in unit tests, the logic also works when compiling normally/in production.

Steps to reproduce

Reuse the example code given here above.

Dependencies version information

No response

Destination operating system

No response

Xcode version information

15.0

Swift Compiler version information

No response

Crash when testing a watchOS app target with a WKApplicationDelegateAdaptor that uses a dependency

Description

When initializing a dependency in a WKApplicationDelegateAdaptor, which is initialized from a test target that tests the app delegate's main target, the dependency initialization crashes with a EXC_BREAKPOINT at DispatchQueue.mainSync(execute:):397. I believe because it is calling DispatchQueue.main.async from the main thread. Declaring the dependency inside a computed property avoids the issue, though.

import Dependencies
import SwiftUI

@main
struct DependenciesWatchAppDelegateCrash_Watch_AppApp: App {
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }

    @WKApplicationDelegateAdaptor(ApplicationDelegate.self) private var applicationDelegate
}


final class ApplicationDelegate: NSObject, WKApplicationDelegate {
    // This will crash when the app is tested
    @Dependency(\.uuid) private var uuid

    // This doesn't crash
    private var workaroundUUID: UUIDGenerator {
        @Dependency(\.uuid) var uuid
        return uuid
    }
}

Checklist

  • I have determined whether this bug is also reproducible in a vanilla SwiftUI project.
  • If possible, I've reproduced the issue using the main branch of this package.
  • This issue hasn't been addressed in an existing GitHub issue or discussion.

Expected behavior

The dependency should initialize without crashing.

Actual behavior

When initializing a dependency in a WKApplicationDelegateAdaptor, which is initialized from a test target that tests the app delegate's main target, the dependency initialization crashes with a EXC_BREAKPOINT at DispatchQueue.mainSync(execute:):397.

Steps to reproduce

Here's a small sample project demonstrating the issue. The app runs fine, but testing it immediately causes a crash:

DependenciesWatchAppDelegateCrash.zip

Dependencies version information

1.0.0

Destination operating system

watchOS 10

Xcode version information

Version 15.0 (15A240d)

Swift Compiler version information

Apple Swift version 5.9 (swiftlang-5.9.0.128.108 clang-1500.0.40.1)
Target: arm64-apple-macosx13.0

Unable to use @DependencyEndpoint with a typealias

Description

Not sure if this is a dependencies bug, macros bug, or macros feature?

works!

extension MyFeature {
  @DependencyClient
  struct Client {
    var refreshRequest: @Sendable (MyFeaturesRequest) async throws -> MyFeaturesRequest.Content
  }
}

Doesnt work

extension MyFeature {
  @DependencyClient
  struct Client {
    var refreshRequest: RequestOf<MyFeaturesRequest>
  }
}

typealias RequestOf<R: NetworkRequestProtocol> = @Sendable (R) async throws -> R.Content

Checklist

  • I have determined whether this bug is also reproducible in a vanilla SwiftUI project.
  • If possible, I've reproduced the issue using the main branch of this package.
  • This issue hasn't been addressed in an existing GitHub issue or discussion.

Expected behavior

I'd expect the DependencyEndpoint macro to work, when the typealias is representing a closure.

Actual behavior

The DependencyEndpoint macro doesn't apply when the typealias is representing a closure.

Steps to reproduce

No response

Dependencies version information

No response

Destination operating system

No response

Xcode version information

No response

Swift Compiler version information

No response

Continuous clock...or not continuous clock? That is the question. - Hamlet

Description

I have an issue with continuous clock in the following small snippet of code.

public final class Arbitrator<Value: Equatable> {
    
    @Dependency(\.continuousClock) var continuousClock
    
    private var currentTask: Task<Void, Never>?
    private var previousValue: Value?
    private let debounceInterval: TimeInterval
    
    public var ruling: @MainActor (Value) -> Void = { _ in }
    
    public init(debounceInterval: TimeInterval) {
        self.debounceInterval = debounceInterval
    }
    
    public func disclose(_ newValue: Value) {
        currentTask?.cancel()
        currentTask = Task { [weak self] in
            guard let self else { return }
            try? await continuousClock.sleep(for: .seconds(debounceInterval))
            if newValue != self.previousValue {
                await self.ruling(newValue)
                self.previousValue = newValue
            }
        }
    }
}

When running the following test

func testDebouncing() throws {
    
    // Given - an arbitrator
    
    let arbitrator = Arbitrator<String>(debounceInterval: 1)
    
    var outcome: String?
    var count: Int = 0
    
    arbitrator.ruling = { coalesced in
        count += 1
        outcome = coalesced
    }
    
    // When - we discolse several input values
    // in quick succession
    
    withDependencies {
        $0.continuousClock = .immediate
    } operation: {
        arbitrator.disclose("H")
        arbitrator.disclose("He")
        arbitrator.disclose("Hel")
        arbitrator.disclose("Hell")
        arbitrator.disclose("Hello")
    }
    
    // Then - only one ruling is provided
    
    XCTAssertTrue(count == 1)
    
    // And - the ruling is the last value disclosed
    
    XCTAssertEqual(outcome, "Hello")
}

Screenshot 2023-08-09 at 07 51 52

If I remove main actor, and restart Xcode, I sometimes see the following.

Please submit a bug report (https://swift.org/contributing/#reporting-bugs) and include the crash backtrace.
Stack dump:
/// massive dump
Command SwiftCompile failed with a nonzero exit code

👆 Massive dump parsed via ChatGPT

ChatGPT 🤖

The provided log details a crash in the Swift compiler while trying to build the "swift-toolbox-pro" project. Here are the highlights that you should pay particular attention to:

Look into the code around lines 50 to 52 in "ArbitratorTests.swift". The issue might be related to the specific code block indicated in the log, specifically the part involving the continuousClock property._

Checklist

  • I have determined whether this bug is also reproducible in a vanilla SwiftUI project.
  • If possible, I've reproduced the issue using the main branch of this package.
  • This issue hasn't been addressed in an existing GitHub issue or discussion.

Expected behavior

No error. Test passes.

Actual behavior

Error. Test fails.

Steps to reproduce

  1. Copy paste the above code.
  2. Run

Dependencies version information

0.6.0

Destination operating system

iOS 17

Xcode version information

Version 15.0 beta 5 (15A5209g)

Swift Compiler version information

swift-driver version: 1.87 Apple Swift version 5.9 (swiftlang-5.9.0.124.4 clang-1500.0.38.1)

Using UUIDGenerator causes Linker Error (‘CoreAudioTypes' not found)

Description

Not sure if this belongs here or is a TCA issue.

Im getting a Linker-Error framework (‘CoreAudioTypes' not found) when importing a framework that uses UUIDGenerator via:

@Dependency(\.uuid) var uuid

Here is a barebones demo project to demonstrate this issue:
https://github.com/nayooti/TCADemo/blob/main/DemoFramework/DemoFramework/DemoView.swift

Summery:

  • DemoApp uses DemoFramework
  • DemoFeature does compile
  • DemoFramework is a TCA module with a DemoFeature: Reducer
  • DemoFeature uses @Dependency(\.uuid) var uuid
  • When building DemoApp I get this error:
Link DemoFramework (arm64):
  ld: warning: Could not find or use auto-linked framework 'CoreAudioTypes': framework 'CoreAudioTypes' not found
  ld: Undefined symbols:
  Dependencies.Dependency.wrappedValue.getter : A, referenced from:
  DemoFramework.DemoFeature.uuid.getter : Dependencies.UUIDGenerator in DemoView.o

Observations:

  • Obviously I am not using CoreAudioTypes anywhere in the project
  • When commenting out @Dependency(\.uuid) var uuid DemoApp builds without any errors
  • Also happens to any other framework that uses DemoFramework

Checklist

  • I have determined whether this bug is also reproducible in a vanilla SwiftUI project.
  • If possible, I've reproduced the issue using the main branch of this package.
  • This issue hasn't been addressed in an existing GitHub issue or discussion.

Expected behavior

No response

Actual behavior

No response

Steps to reproduce

No response

Dependencies version information

No response

Destination operating system

No response

Xcode version information

Xcode 15.0

Swift Compiler version information

No response

Dependencies macro tries to initialize static vars

Description

Hey!
Seems like the DependencyClient macro tries to initialize static vars within the generated init.
So the following code

@DependencyClient
public struct SimpleDependency {
    static var foo: Int = 42
    var bar: () -> Void
}

generates

@DependencyClient
public struct SimpleDependency {
    static var foo: Int = 42
    @DependencyEndpoint
    var bar: () -> Void
    init(
        foo: Int = 42,
        bar: @escaping () -> Void
    ) {
        self.foo = foo
        self.bar = bar
    }
}

This does not compile for obvious reasons. Is this just an oversight in the macros implementation or should static vars just not be supported?

Checklist

  • I have determined whether this bug is also reproducible in a vanilla SwiftUI project.
  • If possible, I've reproduced the issue using the main branch of this package.
  • This issue hasn't been addressed in an existing GitHub issue or discussion.

Expected behavior

I'd expect that the static var is just ignored in the initializer. However, as I was seeing the issue, I was also searching for something like @DependencyEndpointIgnored to somehow mitigate the issue. Maybe thats also something worth exploring?

Actual behavior

The initializer tries to initialize the static var, which is not possible.

Steps to reproduce

No response

Dependencies version information

No response

Destination operating system

No response

Xcode version information

No response

Swift Compiler version information

swift-driver version: 1.90.11.1 Apple Swift version 5.10 (swiftlang-5.10.0.13 clang-1500.3.9.4)

@DependencyEndpoint & Never returning closure

Description

When applying @DependencyEndpoint to a function which return a non-optional type one must provide a closure. While this moved some runtime checks to compile time checks it's not always possible to provide a sensible value or even a value at all.
One can avoid providing a value using fatalError() but the macro then emits invalid swift code.

Checklist

  • I have determined whether this bug is also reproducible in a vanilla SwiftUI project.
  • If possible, I've reproduced the issue using the main branch of this package.
  • This issue hasn't been addressed in an existing GitHub issue or discussion.

Expected behavior

The macro should emit valid swift code even when using fatalError() but I think there should be an option to specify the behaviour of the macro so one can skip writing the fatalError entirely

Actual behavior

No response

Steps to reproduce

@DependencyEndpoint
public var fetchCategoryIds: () async throws -> Set<Article.Category.ID> = { fatalError() }
  private var _fetchCategoryIds: () async throws -> Set<Article.Category.ID> = {
      XCTestDynamicOverlay.XCTFail("Unimplemented: 'fetchCategoryIds'")
      return fatalError()
  }
{
    @storageRestrictions(initializes: _fetchCategoryIds)
    init(initialValue) {
        _fetchCategoryIds = initialValue
    }
    get {
        _fetchCategoryIds
    }
    set {
        _fetchCategoryIds = newValue
    }
}

Dependencies version information

1.1.1

Destination operating system

No response

Xcode version information

No response

Swift Compiler version information

No response

DependencyValues.swift introduces hard dependency on XCTest.dll in Release builds.

Description

When building Release builds of an app that depends on swift-dependencies on Windows, we now also have a dependency on XCTest.dll. We do not want to ship XCTest.dll. :)

I believe that DependencyValues.swift, line 402 introduces this issue.

This may be related to #79.

Checklist

  • I have determined whether this bug is also reproducible in a vanilla SwiftUI project.
  • If possible, I've reproduced the issue using the main branch of this package.
  • This issue hasn't been addressed in an existing GitHub issue or discussion.

Expected behavior

No dependency on XCTest.dll in non-test, non-debug code.

Actual behavior

There is a dependency in the release build:

PS C:\src\app\build\bin> dumpbin /IMPORTS:XCTest.dll .\app.exe
Microsoft (R) COFF/PE Dumper Version 14.36.32537.0
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file .\app.exe

File Type: EXECUTABLE IMAGE

  Section contains the following imports:

    XCTest.dll
             140F121B8 Import Address Table
             140F0A4B0 Import Name Table
                     0 time date stamp
                     0 Index of first forwarder reference

                          72 $s6XCTest0A11ObservationMp
                          73 $s6XCTest0A11ObservationPAAE17testCaseDidFinishyyAA0aD0CF
                          75 $s6XCTest0A11ObservationPAAE18testSuiteDidFinishyyAA0aD0CF
                          76 $s6XCTest0A11ObservationPAAE18testSuiteWillStartyyAA0aD0CF
                          77 $s6XCTest0A11ObservationPAAE19testBundleDidFinishyy10Foundation0D0CF
                          78 $s6XCTest0A11ObservationPAAE19testBundleWillStartyy10Foundation0D0CF
                          79 $s6XCTest0A11ObservationPAAE8testCase_22didFailWithDescription6inFile6atLineyAA0aD0C_S2SSgSitF
                          7F $s6XCTest0A17ObservationCenterC6sharedACvgZ
                          82 $s6XCTest0A17ObservationCenterCMa

Steps to reproduce

Build a project on Windows.

Dependencies version information

9e92056

Destination operating system

Windows 11

Xcode version information

n\a

Swift Compiler version information

compnerd.org Swift version 5.9-dev (LLVM c8ca153def6029d, Swift 1381d9a7b2555c6)

Flakkines on Testing Target

Description

Hello everyone,

I am facing an issue regards to the dependencies library and was wondering what might be the cause:

the project that I am working is a highly modular application, let say I have a dependency called
var sum (Int, Int) -> String and I add this to dependencies library as \.sum sometimes some modules that are not even depending on this test are failing due to the unimplemented case.

My tests are running in parallel
Its not always failing
some of the dependencies are injected in the module it self some are on the application target

Project -> Contains some dependency + Injects them to Dependencies
Module A -> Contains some dependency + Injects them to Dependencies

Checklist

  • I have determined whether this bug is also reproducible in a vanilla SwiftUI project.
  • If possible, I've reproduced the issue using the main branch of this package.
  • This issue hasn't been addressed in an existing GitHub issue or discussion.

Expected behavior

When I provide the dependency on TestStore.dependencies I would expect to make use of the value mock version that I provide.

Actual behavior

When I provide the dependency on TestStore.dependencies I would fall into the testValue which is the failing implementation.

Steps to reproduce

  • Create a Modular Project
  • Create a Dependency on Module A
  • Depend on It on Project
  • Write relevant test for it
  • override TestStore.dependencies with the mocked version
  • run tests in random order and parallel
  • observe that test cases that doesnt event depend on that specific target fails due to unimplemented state

Dependencies version information

0.1.2

Destination operating system

iOS 16

Xcode version information

14.2

Swift Compiler version information

5.7.1

When using `@DependencyClient` macro, closures with mandatory return don't use the unimplemented version on tests

Description

When using @DependencyClient macro in a client that has closures with mandatory returns, when using these closures in a test without overriding them we don't get errors about the method not being implemented.

According to this message from Brandon this is related to a swift bug. Given that I'd suggest to at least add a note to the documentation mentioning that, instead of having a closure such as { _ in true }, we unfortunately still have to use { _ in unimplemented(placeholder: true }

See also this Slack thread

Checklist

  • I have determined whether this bug is also reproducible in a vanilla SwiftUI project.
  • If possible, I've reproduced the issue using the main branch of this package.
  • This issue hasn't been addressed in an existing GitHub issue or discussion.

Expected behavior

The test shown should have 2 errors.

Actual behavior

The test only produces one error.

Steps to reproduce

Simple Client:

import Dependencies
import DependenciesMacros

@DependencyClient
struct SimpleClient {
    var noReturn: () -> Void
    var withBoolReturn: () -> Bool = { true }
}

extension SimpleClient: TestDependencyKey {
    public static let testValue = SimpleClient()
}

extension DependencyValues {
    var simpleClient: SimpleClient {
        get { self[SimpleClient.self] }
        set { self[SimpleClient.self] = newValue }
    }
}

This test should have 2 failures but only shows one:

import Dependencies
import XCTest
@testable import TCATest

final class TCATestTests: XCTestCase {
    func testExample() throws {
        @Dependency(\.simpleClient) var simpleClient
        _ = simpleClient.withBoolReturn()
        simpleClient.noReturn()
    }
}

Dependencies version information

1.2.1

Destination operating system

iOS

Xcode version information

15.2

Swift Compiler version information

swift-driver version: 1.87.3 Apple Swift version 5.9.2 (swiftlang-5.9.2.2.56 clang-1500.1.0.2.5)
Target: arm64-apple-macosx14.0

Profile builds crash on `continuousClock` dependency

Description

When profiling the app that depends on continuousClock, it crashes. Confirmed both on my pp and on CaseStudies (SwiftUI) example app from swift-composable-architecture.

Checklist

  • I have determined whether this bug is also reproducible in a vanilla SwiftUI project.
  • If possible, I've reproduced the issue using the main branch of this package.
  • This issue hasn't been addressed in an existing GitHub issue or discussion.

Expected behavior

It shouldn't crash :)

Actual behavior

App crashes.

Steps to reproduce

  1. Check out swift-composable-architecture repo
  2. Open ComposableArchitecture.xcworkspace and select CaseStudies (SwiftUI) scheme
  3. Product -> Profile
  4. Select Leaks
  5. App crashes.

Crash log points to 21 SwiftUICaseStudies 0x105ceaa06 Root.init() + 12 (00-Core.swift:63) [inlined].
In my app, it also points to the line Dependency(\.continuousClock) var clock, so looks like there is a problem with continuousClock dependency itself.

Dependencies version information

0.2.0

Destination operating system

iOS 16

Xcode version information

14.2 (14C18)

Swift Compiler version information

No response

Nested types not supported by @DependencyClient

Description

@DependencyClient
struct SomeFailingClient {
  var endpoint: () async throws -> InternalValue
  
  struct InternalValue {
    var value: Bool
  }
}

extension SomeFailingClient.InternalValue {
}
image

Checklist

  • I have determined whether this bug is also reproducible in a vanilla SwiftUI project.
  • If possible, I've reproduced the issue using the main branch of this package.
  • This issue hasn't been addressed in an existing GitHub issue or discussion.

Expected behavior

No response

Actual behavior

No response

Steps to reproduce

No response

Dependencies version information

1.1.0

Destination operating system

iOS 17

Xcode version information

No response

Swift Compiler version information

swift-driver version: 1.87.1 Apple Swift version 5.9 (swiftlang-5.9.0.128.108 clang-1500.0.40.1)
Target: arm64-apple-macosx14.0

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.