问题
Followed this it was working fine.
But when I tried the same thing for resolving a nested dependency (Dependency injected class has a dependency in turn - NetworkService In our case), it crashed. What am I doing wrong here? any help would be highly appreciated.
A realtime scenario
class AppContainer {
static let shared = AppContainer()
var index: [Any] = [NetworkingLibrary(), NetworkService()]
func resolve<T>(_ type: T.Type) -> T {
return index.first(where: { $0 as? T != nil }) as! T
}
}
@propertyWrapper
struct Inject<Value> {
var value: Value
var wrappedValue: Value {
get {
return value
}
set {
value = newValue
}
}
init(_ container: AppContainer = AppContainer.shared) {
value = container.resolve(Value.self)
}
}
class UserService {
@Inject() var networkLayer: NetworkLayer
init() { }
func fetchUsers() {
networkLayer.fetchData()
}
}
class PostService {
@Inject() var networkLayer: NetworkLayer
init() { }
func fetchPosts() {
networkLayer.fetchData()
}
}
protocol NetworkLayer {
func fetchData()
}
class NetworkService: NetworkLayer {
@Inject() var networkLibrary: NetworkingLibraryProtocol //Expecting networkLibrary to be resolved to NetworkingLibrary()
func fetchData() {
networkLibrary.fetch()
print("Fetching Data")
}
}
let postService = PostService() // crashes here
postService.fetchPosts()
let userService = UserService() // crashes here
userService.fetchUsers()
Code you could run on playground
class AppContainer {
static let shared = AppContainer()
var index: [Any] = ["StackOverflow", NetworkService()]
func resolve<T>(_ type: T.Type) -> T {
return index.first(where: { $0 as? T != nil }) as! T
}
}
@propertyWrapper
struct Inject<Value> {
var value: Value
var wrappedValue: Value {
get {
return value
}
set {
value = newValue
}
}
init(_ container: AppContainer = AppContainer.shared) {
value = container.resolve(Value.self)
}
}
class UserService {
@Inject() var networkLayer: NetworkLayer
init() { }
func fetchUsers() {
networkLayer.fetchData()
}
}
class PostService {
@Inject() var networkLayer: NetworkLayer
init() { }
func fetchPosts() {
networkLayer.fetchData()
}
}
protocol NetworkLayer {
func fetchData()
}
class NetworkService: NetworkLayer {
@Inject() var str: String // Expecting str to be resolved to "StackOverflow"
func fetchData() {
print(str)
print("Fetching Data")
}
}
let postService = PostService()
postService.fetchPosts()
let userService = UserService()
userService.fetchUsers()
Crash log:-
error: Execution was interrupted, reason: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0). The process has been left at the point where it was interrupted, use "thread return -x" to return to the state before expression evaluation.
回答1:
I was also searching for Dependency Injection
in Swift 5.1 using property wrappers.
This is what worked for me - it's very similar to your solution and works perfectly for nested dependencies:
enum Dependencies {
struct Name: Equatable {
let rawValue: String
static let `default` = Name(rawValue: "__default__")
static func == (lhs: Name, rhs: Name) -> Bool { lhs.rawValue == rhs.rawValue }
}
final class Container {
private var dependencies: [(key: Dependencies.Name, value: Any)] = []
static let `default` = Container()
func register(_ dependency: Any, for key: Dependencies.Name = .default) {
dependencies.append((key: key, value: dependency))
}
func resolve<T>(_ key: Dependencies.Name = .default) -> T {
return (dependencies
.filter { (dependencyTuple) -> Bool in
dependencyTuple.key == key
&& dependencyTuple.value is T
}
.first)?.value as! T
}
}
@propertyWrapper
struct Inject<T> {
private let dependencyName: Name
private let container: Container
var wrappedValue: T { container.resolve(dependencyName) }
init(_ dependencyName: Name = .default, on container: Container = .default) {
self.dependencyName = dependencyName
self.container = container
}
}
}
And this is how you use it in your code:
Dependencies.Container.default.register(NetworkService())
Dependencies.Container.default.register(TransactionRepository())
Dependencies.Container.default.register(TransactionService())
class TransactionService {
@Dependencies.Inject() private var networkService: NetworkService
@Dependencies.Inject() private var transactionRepository: TransactionRepository
}
class SomeOtherService {
@Dependencies.Inject() private var transactionService: TransactionService
}
The full explanation can be found here.
来源:https://stackoverflow.com/questions/61316547/nested-dependency-injection-through-property-wrapper-crashes