Add dependency injection

This commit is contained in:
Artur Gurgul 2025-08-03 13:39:29 +02:00
parent 151ed6d78b
commit bf94769573
9 changed files with 140 additions and 51 deletions

View file

@ -9,14 +9,16 @@ import Combine
import React import React
@objc(Emitter) @objc(Emitter)
class Emitter: RCTEventEmitter { class Emitter: RCTEventEmitter, MessageEmitter {
@Injected private var eventEmitter: EventEmitter
override static func requiresMainQueueSetup() -> Bool { override static func requiresMainQueueSetup() -> Bool {
return true return true
} }
override init() { override init() {
super.init() super.init()
EventEmitter.sharedInstance.register(eventEmitter: self) eventEmitter.register(eventEmitter: self)
} }
override func supportedEvents() -> [String]! { override func supportedEvents() -> [String]! {

View file

@ -7,22 +7,34 @@
import Foundation import Foundation
class EventEmitter { protocol MessageEmitter {
static let sharedInstance = EventEmitter() func send(message: String)
}
private var eventEmitter: Emitter? class NullMessageEmitter: MessageEmitter {
func send(message: String) {
print("Warning: Emiter is not ready yet")
}
}
private init() {}
protocol EventEmitter {
func send(message: String)
func register(eventEmitter: Emitter)
}
class DefaultEventEmitter: EventEmitter {
private var eventEmitter: MessageEmitter = NullMessageEmitter()
func register(eventEmitter: Emitter) { func register(eventEmitter: Emitter) {
self.eventEmitter = eventEmitter self.eventEmitter = eventEmitter
} }
func send(message: String) { func send(message: String) {
eventEmitter?.send(message: message) eventEmitter.send(message: message)
} }
var isReady: Bool { var isReady: Bool {
return eventEmitter != nil return (eventEmitter is NullMessageEmitter) == false
} }
} }

View file

@ -13,12 +13,12 @@ import ReactAppDependencyProvider
final class SharedState: ObservableObject { final class SharedState: ObservableObject {
var reactNativeFactory: RCTReactNativeFactory? var reactNativeFactory: RCTReactNativeFactory?
private let emitter = EventEmitter.sharedInstance let container = DependencyContainer()
// SwiftUI => RN => SwiftUI // SwiftUI => RN => SwiftUI
@Published var message: String = "" @Published var message: String = ""
func send(message: String) { init() {
emitter.send(message: message) container.register(EventEmitter.self) { _ in DefaultEventEmitter() }
} }
} }

View file

@ -11,6 +11,7 @@ import SwiftUI
@objc(CustomButton) @objc(CustomButton)
class CustomButton: UIView { class CustomButton: UIView {
@Injected private var emitter: EventEmitter
private let label: UILabel = { private let label: UILabel = {
let lbl = UILabel() let lbl = UILabel()
@ -55,6 +56,6 @@ class CustomButton: UIView {
} }
@objc private func buttonTapped() { @objc private func buttonTapped() {
emitter.send(message: "Clicked in UIButton button that was created in RN")
} }
} }

View file

@ -9,13 +9,14 @@ import SwiftUI
struct ToolboxHeader: View { struct ToolboxHeader: View {
@EnvironmentObject var sharedState: SharedState @EnvironmentObject var sharedState: SharedState
@Injected var emitter: EventEmitter
var body: some View { var body: some View {
VStack { VStack {
HStack { HStack {
Text("Actions") Text("Actions")
Button("Make it blue") { Button("Make it blue") {
sharedState.send(message: "hello from Swift!") emitter.send(message: "hello from Swift!")
} }
} }
Text("Message: \(sharedState.message)") Text("Message: \(sharedState.message)")

View file

@ -0,0 +1,47 @@
//
// DependencyContainer.swift
// Vitaway
//
// Created by Artur Gurgul on 20/06/2025.
//
final class DependencyContainer: Resolver {
private var factories: [String: (Resolver) -> Any] = [:]
private var instances: [String: Any] = [:]
func register<T>(_ type: T.Type, cache: Bool = true, factory: @escaping (Resolver) -> T) {
let key = String(describing: type)
factories[key] = factory
if cache {
instances[key] = factory(self)
}
}
func registerSingleton<T>(_ type: T.Type, factory: @escaping (Resolver) -> T) {
let key = String(describing: type)
factories[key] = factory
}
func resolve<T>() -> T {
resolve(type: T.self)
}
func resolve<T>(type: T.Type) -> T {
let key = String(describing: T.self)
if let instance = instances[key] as? T {
return instance
}
guard let factory = factories[key], let instance = factory(self) as? T else {
fatalError("No registered entry for type \(key)")
}
return instance
}
static var shared: DependencyContainer {
(UIApplication.shared.delegate as! AppDelegate).sharedState.container
}
}

View file

@ -0,0 +1,16 @@
//
// Injected.swift
// Vitaway
//
// Created by Artur Gurgul on 05/07/2025.
//
import UIKit
@propertyWrapper
class Injected<T> {
var wrappedValue: T {
DependencyContainer.shared.resolve(type: T.self)
}
}

View file

@ -0,0 +1,10 @@
//
// Resolver.swift
// Vitaway
//
// Created by Artur Gurgul on 20/06/2025.
//
protocol Resolver {
func resolve<T>() -> T
}

View file

@ -9,7 +9,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow? var window: UIWindow?
var reactNativeDelegate: ReactNativeDelegate? var reactNativeDelegate: ReactNativeDelegate?
private let sharedState = SharedState() let sharedState = SharedState()
func application( func application(
_ application: UIApplication, _ application: UIApplication,