在线时间:8:00-16:00
迪恩网络APP
随时随地掌握行业动态
扫描二维码
关注迪恩网络微信公众号
开源软件名称(OpenSource Name):scribd/Weaver开源软件地址(OpenSource Url):https://github.com/scribd/Weaver开源编程语言(OpenSource Language):Swift 99.4%开源软件介绍(OpenSource Introduction):Declarative, easy-to-use and safe Dependency Injection framework for Swift (iOS/macOS/Linux) Features
TalksTutorialsIf you're looking for a step by step tutorial, check out these links.
Dependency InjectionDependency Injection basically means "giving an object its instance variables" ¹. It seems like it's not such a big deal, but as soon as a project gets bigger, it gets tricky. Initializers become too complex, passing down dependencies through several layers becomes time consuming and just figuring out where to get a dependency from can be hard enough to give up and finally use a singleton. However, Dependency Injection is a fundamental aspect of software architecture, and there is no good reason not to do it properly. That's where Weaver can help. What is Weaver?Weaver is a declarative, easy-to-use and safe Dependency Injection framework for Swift.
How does Weaver work?
Weaver scans the Swift sources of the project, looking for annotations, and generates an AST (abstract syntax tree). It uses SourceKitten which is backed by Apple's SourceKit. The AST then goes through a linking phase, which outputs a dependency graph. Some safety checks are then performed on the dependency graph in order to ensure that the generated code won't crash at runtime. Issues are friendly reported in Xcode to make their correction easier. Finally, Weaver generates the boilerplate code which can directly be used to make the dependency injections happen. Installation(1) - Weaver commandWeaver can be installed using Binary formDownload the latest release with the prebuilt binary from release tab. Unzip the archive into the desired destination and run Homebrew$ brew install weaver CocoaPodsAdd the following to your pod 'WeaverDI' This will download the Weaver binaries and dependencies in Pods/ during your next pod install execution and will allow you to invoke it via This is the best way to install a specific version of Weaver since Homebrew cannot automatically install a specific version. MintTo use Weaver via Mint, prefix the normal usage with mint run scribd/Weaver like so: mint run scribd/Weaver version To use a specific version of Weaver, add the release tag like so: mint run scribd/[email protected] version Building from sourceDownload the latest release source code from the release tab or clone the repository. In the project directory, run Check installationRun the following to check if Weaver has been installed correctly. $ weaver swift --help
Usage:
$ weaver swift
Options:
--project-path - Project's directory.
--config-path - Configuration path.
--main-output-path - Where the swift code gets generated.
--tests-output-path - Where the test helpers gets generated.
--input-path - Paths to input files.
--ignored-path - Paths to ignore.
--cache-path - Where the cache gets stored.
--recursive-off
--tests - Activates the test helpers' generation.
--testable-imports - Modules to imports in the test helpers.
--swiftlint-disable-all - Disables all swiftlint rules. (2) - Weaver build phaseIn Xcode, add the following command to a command line build phase:
Important - Move this build phase above the Basic UsageFor a more complete usage example, please check out the sample project. Let's implement a simple app displaying a list of movies. It will be composed of three noticeable objects:
Let's get into the code.
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
private let dependencies = MainDependencyContainer.appDelegateDependencyResolver()
// weaver: movieManager = MovieManager <- MovieManaging
// weaver: movieManager.scope = .container
// weaver: moviesViewController = MoviesViewController <- UIViewController
// weaver: moviesViewController.scope = .container
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
window = UIWindow()
let rootViewController = dependencies.moviesViewController
window?.rootViewController = UINavigationController(rootViewController: rootViewController)
window?.makeKeyAndVisible()
return true
}
}
These dependencies are made accessible to any object built from
A dependency registration automatically generates the registration code and one accessor in
Since Weaver 1.0.1, you can use property wrappers instead of annotations in comments. @UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
// Must be declared first!
private let dependencies = MainDependencyContainer.appDelegateDependencyResolver()
@Weaver(.registration, type: MovieManager.self, scope: .container)
private var movieManager: MovieManaging
@Weaver(.registration, type: MoviesViewController.self, scope: .container)
private var moviesViewController: UIViewController
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
window = UIWindow()
window?.rootViewController = UINavigationController(rootViewController: moviesViewController)
window?.makeKeyAndVisible()
return true
}
}
protocol MovieManaging {
func getMovies(_ completion: @escaping (Result<Page<Movie>, MovieManagerError>) -> Void)
}
final class MovieManager: MovieManaging {
func getMovies(_ completion: @escaping (Result<Page<Movie>, MovieManagerError>) -> Void) {
// fetches movies from the server...
completion(.success(movies))
}
}
final class MoviesViewController: UIViewController {
private let dependencies: MoviesViewControllerDependencyResolver
private var movies = [Movie]()
// weaver: movieManager <- MovieManaging
required init(injecting dependencies: MoviesViewControllerDependencyResolver) {
self.dependencies = dependencies
super.init(nibName: nil, bundle: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
// Setups the tableview...
// Fetches the movies
dependencies.movieManager.getMovies { result in
switch result {
case .success(let page):
self.movies = page.results
self.tableView.reloadData()
case .failure(let error):
self.showError(error)
}
}
}
// ...
}
This annotation generates an accessor in
This initializer is used to inject the DI Container. Note that
final class MoviesViewController: UIViewController {
private var movies = [Movie]()
@Weaver(.reference)
private var movieManager: MovieManaging
required init(injecting _: MoviesViewControllerDependencyResolver) {
super.init(nibName: nil, bundle: nil)
}
override func viewDidLoad() {
super.viewDidLoad()
// Setups the tableview...
// Fetches the movies
movieManager.getMovies { result in
switch result {
case .success(let page):
self.movies = page.results
self.tableView.reloadData()
case .failure(let error):
self.showError(error)
}
}
}
// ...
} APICode AnnotationsWeaver allows you to declare dependencies by annotating the code with comments like It currently supports the following annotations: - Registration
Example: // weaver: dependencyName = DependencyConcreteType <- DependencyProtocol
@Weaver(.registration, type: DependencyConcreteType.self)
var dependencyName: DependencyProtocol or // weaver: dependencyName = DependencyConcreteType
@Weaver(.registration)
var dependencyName: DependencyConcreteType
- ReferenceAdds an accessor for the dependency to the container's protocol. Example: // weaver: dependencyName <- DependencyType
@Weaver(.reference)
var dependencyName: DependencyType
- ParameterAdds a parameter to the container's resolver protocol. This means that the generated container needs to take these parameter at initialisation. It also means that all the concerned dependency accessors need to take this parameter. Example: // weaver: parameterName <= ParameterType
@Weaver(.parameter)
var parameterName: ParameterType - ScopeSets the scope of a dependency. The default scope being The
Example: // weaver: dependencyName.scope = .scopeValue
@Weaver(.registration, scope: .scopeValue)
var dependencyName: DependencyType
- Custom BuilderOverrides a dependency's default initialization code. Works for registration annotations only. Example: // weaver: dependencyName.builder = DependencyType.make
@Weaver(.registration, builder: DependencyType.make)
var dependencyName: DependencyType
Warning - Make sure you don't do anything unsafe with the - ConfigurationSets a configuration attribute to the concerned object. Example: // weaver: dependencyName.attributeName = aValue
@Weaver(..., attributeName: aValue, ...)
var dependencyName: DependencyType Configuration Attributes:
Using protperty wrappers with parameters:Types using parameter annotations need to take the said parameters as an input when being registered or referenced. This is particularly true when using property wrappers, because the signature of the annotation won't compile if not done correctly. For example, the following shows how a type taking two parameters at initialization can be annotated: final class MovieViewController {
@Weaver(.parameter) private var movieID: Int
@Weaver(.parameter) private var movieTitle: String
} And how that same type can be registered and referenced: @WeaverP2(.registration)
private var movieViewController: (Int, String) -> MovieViewController
@WeaverP2(.reference)
private var moviewViewController: (Int, String) -> MovieViewController Note that Weaver generates one property wrapper per amount of input parameters, so if a type takes one parameter Writing tests:Weaver can also generate a dependency container stub which can be used for testing. This feature is accessible by adding the option To compile, the stub expects certain type doubles to be implemented. For example, given the following code: final class MovieViewController {
@Weaver(.reference) private var movieManager: MovieManaging
} The generated stub expects Testing final class MovieViewControllerTests: XCTestCase {
func test_view_controller() {
let dependencies = MainDependencyResolverStub()
let viewController = dependencies.buildMovieViewController()
viewController.viewDidLoad()
XCTAssertEqual(dependencies.movieManagerDouble.didRequestMovies, true)
}
} Generate Swift FilesTo generate the boilerplate code, the $ weaver swift --help
Usage:
$ weaver swift
Options:
--project-path - Project's directory.
--config-path - Configuration path.
--main-output-path - Where the swift code gets generated.
--tests-output-path - Where the test helpers gets generated.
--input-path - Paths to input files.
--ignored-path - Paths to ignore.
--cache-path - Where the cache gets stored.
--recursive-off
--tests - Activates the test helpers' generation.
--testable-imports - Modules to imports in the test helpers.
--swiftlint-disable-all - Disables all swiftlint rules.
--platform - Targeted platform.
--included-imports - Included imports.
--excluded-imports - Excluded imports. Example:weaver swift --project-path $PROJECT_DIR/$PROJECT_NAME --main-output-path Generated |
2023-10-27
2022-08-15
2022-08-17
2022-09-23
2022-08-13
请发表评论