Skip to content

Commit

Permalink
overhaul of the injection utility to better fit my needs
Browse files Browse the repository at this point in the history
  • Loading branch information
JARMourato committed Jun 12, 2024
1 parent 0348fd8 commit ce340a1
Show file tree
Hide file tree
Showing 10 changed files with 581 additions and 515 deletions.
28 changes: 25 additions & 3 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,16 +1,38 @@
// swift-tools-version:5.10
// The swift-tools-version declares the minimum version of Swift required to build this package.

import CompilerPluginSupport
import PackageDescription

let package = Package(
name: "Injection",
platforms: [.iOS(.v13), .macOS(.v12), .watchOS(.v6), .tvOS(.v13)],
platforms: [.iOS(.v13), .macOS(.v12), .watchOS(.v6), .tvOS(.v13), .visionOS(.v1)],
products: [
.library(name: "Injection", targets: ["Injection"]),
],
dependencies: [
.package(url: "https://github.com/apple/swift-syntax", from: "510.0.0"),
],
targets: [
.target(name: "Injection", dependencies: [], path: "Sources"),
.testTarget(name: "InjectionTests", dependencies: ["Injection"], path: "Tests"),
.macro(
name: "InjectionMacroImpl",
dependencies: [
.product(name: "SwiftSyntaxMacros", package: "swift-syntax"),
.product(name: "SwiftCompilerPlugin", package: "swift-syntax"),
],
path: "Sources/InjectionMacroImpl"
),

.target(name: "Injection", dependencies: ["InjectionMacroImpl"], path: "Sources/Main"),

.testTarget(
name: "InjectionTests",
dependencies: [
"Injection",
"InjectionMacroImpl",
.product(name: "SwiftSyntaxMacrosTestSupport", package: "swift-syntax"),
],
path: "Tests"
),
]
)
124 changes: 54 additions & 70 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,107 +5,91 @@
[![codecov][codecov status badge]][codecov status]
![Platforms][platforms badge]

`Injection` is a tiny utility to help managing dependency injection with SwiftUI.

`Injection` is a tiny utility to help managing dependency injection.
## Why would I use this?

**Features:**
- `Singleton`, dependencies that are only instantiated once and are shared amongst its users
- `LazySingleton`, dependencies that are only instantiated once, lazily, and are shared amongst its users
- `Factory`, dependencies that are instancied upon usage, unique to each usage
- Utility property wrappers
You shouldn't 😅 there's plenty of dependency injection libraries for swift out there - by no means is this intended to replace any of those.
For my personal use cases, there's two main intents:
- Simplify adding custom `EnvironmentValues`
- Be able to use a similar api to `EnvironmentValues` for objects that aren't meant to be `Observable` or should not belong in the `View`'s `EnvironmentValues`

## Usage

## Installation

### Swift Package Manager
```swift
import Injection

If you're working directly in a Package, add Injection to your Package.swift file
struct DependencyOne { ... }
struct DependencyTwo { ... }

```swift
dependencies: [
.package(url: "https://github.com/JARMourato/Injection.git", .upToNextMajor(from: "1.0.0" )),
]
```
// 1: Add the @Inject macro to the extension
@Inject
extension EnvironmentValues {
var dep1 = DependencyOne()
var dep2 = DependencyTwo()
}

If working in an Xcode project select `File->Swift Packages->Add Package Dependency...` and search for the package name: `Injection` or the git url:
// 2: Use it normally with the @Environment property wrapper
struct ExampleView: View {
@Environment(\.dep1) var dep1
@Environment(\.dep2) var dep2

`https://github.com/JARMourato/Injection.git`
var body: some View { ... }
}

```

## Usage
The same logic also applies to the simpler container `DependencyValues`:

1. Define dependencies:
```swift
import Injection

// A dependency that gets created every time it needs to be resolved, and therefore its lifetime is bounded to the instance that uses it
let reader = factory { RSSReader() }

// A dependency that gets created immediately and is shared throughout the lifetime of the application.
let database = singleton { Analytics() }
struct DependencyOne { ... }
struct DependencyTwo { ... }

// A dependency that gets created only once, the first time it needs to be resolved and has the lifetime of the application.
let database = lazySingleton { Realm() }

// Syntatic sugar to combine dependencies to inject
let cacheModule = module {
singleton { ImageCache() }
factory { AudioCache() }
factory { VideoCache() }
// 1: Add the @Inject macro to the extension
@Inject
extension DependencyValues {
var dep1 = DependencyOne()
var dep2 = DependencyTwo()
}
```

2. Inject the dependencies before the application is initialized:
```swift
import Injection
// (optionally) set it elsewhere in the view hierarchy

@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
do {
try inject {
reader
database
cacheModule
}
} catch {
print("TODO: Handle error properly... \(error)")
struct ExampleApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.dependency(\.dep1, DependencyOne())
}
return true
}
}
```

3. Use the injected dependencies using the provided property wrappers:
```swift
import Injection
// 2: Use it with the @Dependency property wrapper
struct ExampleView: View {
@Dependency(\.dep1) var dep1
@Dependency(\.dep2) var dep2

class VideoPlayer: BackendProtocol {
// Will resolve the dependency immediately upon type instantiation
@Inject var database: Database

// The dependency only gets resolved on the first time the property gets accessed
@LazyInject var videoCache: VideoCache

// The functionality is similar to `LazyInject` except the property may or may not have been injected.
@OptionalInject var network: Network?
var body: some View { ... }
}
```

or via the initializer:
## Installation

```swift
import Injection
### Swift Package Manager

struct Reader {
private let rssReader: RSSReader

init(rssReader: RSSReader = resolve()) {
self.rssReader = rssReader
}
}
If you're working directly in a Package, add Injection to your Package.swift file

```swift
dependencies: [
.package(url: "https://github.com/JARMourato/Injection.git", .upToNextMajor(from: "2.0.0" )),
]
```

If working in an Xcode project select `File->Swift Packages->Add Package Dependency...` and search for the package name: `Injection` or the git url:

`https://github.com/JARMourato/Injection.git`

## Contributions

Expand Down
184 changes: 0 additions & 184 deletions Sources/Injection.swift

This file was deleted.

Loading

0 comments on commit ce340a1

Please sign in to comment.