diff --git a/Sources/Spezi/Dependencies/Property/DependencyBuilder.swift b/Sources/Spezi/Dependencies/Property/DependencyBuilder.swift index 644144e..22d07d8 100644 --- a/Sources/Spezi/Dependencies/Property/DependencyBuilder.swift +++ b/Sources/Spezi/Dependencies/Property/DependencyBuilder.swift @@ -7,10 +7,12 @@ // -/// A result builder to build a ``DependencyCollection``. +/// A result builder to build a `DependencyCollection`. +/// +/// For more information refer to ``DependencyCollection``. @resultBuilder public enum DependencyBuilder: DependencyCollectionBuilder { - /// An auto-closure expression, providing the default dependency value, building the ``DependencyCollection``. + /// An auto-closure expression, providing the default dependency value, building the `DependencyCollection`. public static func buildExpression(_ expression: M) -> DependencyCollection { DependencyCollection(expression) } diff --git a/Sources/Spezi/Dependencies/Property/DependencyCollection.swift b/Sources/Spezi/Dependencies/Property/DependencyCollection.swift index d8f4443..cdf1049 100644 --- a/Sources/Spezi/Dependencies/Property/DependencyCollection.swift +++ b/Sources/Spezi/Dependencies/Property/DependencyCollection.swift @@ -10,6 +10,19 @@ /// A collection of dependencies. /// /// This collection contains a collection of Modules that are meant to be declared as the dependencies of another module. +/// +/// The code example below demonstrates how you can easily create your collection of dependencies from multiple different types of ``Module``s. +/// +/// - Tip: You can also use ``append(contentsOf:)`` to combine two collections. +/// +/// ```swift +/// var collection = DependencyCollection(ModuleA(), ModuleB(), ModuleC()) +/// +/// collection.append(ModuleD()) +/// ``` +/// +/// - Note: Use the ``DependencyCollectionBuilder`` if you want to create your own result builder that can build a ``DependencyCollection`` component +/// out of multiple `Module` expressions. public struct DependencyCollection { private var entries: [AnyDependencyContext] diff --git a/Sources/Spezi/Dependencies/Property/DependencyCollectionBuilder.swift b/Sources/Spezi/Dependencies/Property/DependencyCollectionBuilder.swift index 9f6e36d..0b872e1 100644 --- a/Sources/Spezi/Dependencies/Property/DependencyCollectionBuilder.swift +++ b/Sources/Spezi/Dependencies/Property/DependencyCollectionBuilder.swift @@ -9,8 +9,8 @@ /// Implement a custom result builder to build a `DependencyCollection`. /// -/// A ``DependencyCollection`` is a collection of dependencies that can be passed to the ``Module/Dependency`` property wrapper of a `Module`. -/// This protocol allows you to easily implement a result builder with custom expression, building a ``DependencyCollection`` component. +/// A ``DependencyCollection`` is a collection of dependencies that can be passed to the ``Module/Dependency`` property wrapper of a ``Module``. +/// This protocol allows you to easily implement a result builder with custom expression, building a `DependencyCollection` component. /// /// To create your own result builder, just add adopt the `DependencyCollectionBuilder` protocol and add your custom expressions. /// The code example below shows the implementation of a `SpecialModuleBuilder` that only allows to build modules of type `SpecialModule`. @@ -39,32 +39,32 @@ public protocol DependencyCollectionBuilder {} /// Default protocol implementations of a result builder constructing a ``DependencyCollection``. extension DependencyCollectionBuilder { - /// Build a block of ``DependencyCollection``s. + /// Build a block of `DependencyCollection`s. public static func buildBlock(_ components: DependencyCollection...) -> DependencyCollection { buildArray(components) } - /// Build the first block of an conditional ``DependencyCollection`` component. + /// Build the first block of an conditional `DependencyCollection` component. public static func buildEither(first component: DependencyCollection) -> DependencyCollection { component } - /// Build the second block of an conditional ``DependencyCollection`` component. + /// Build the second block of an conditional `DependencyCollection` component. public static func buildEither(second component: DependencyCollection) -> DependencyCollection { component } - /// Build an optional ``DependencyCollection`` component. + /// Build an optional `DependencyCollection` component. public static func buildOptional(_ component: DependencyCollection?) -> DependencyCollection { component ?? DependencyCollection() } - /// Build an ``DependencyCollection`` component with limited availability. + /// Build an `DependencyCollection` component with limited availability. public static func buildLimitedAvailability(_ component: DependencyCollection) -> DependencyCollection { component } - /// Build an array of ``DependencyCollection`` components. + /// Build an array of `DependencyCollection` components. public static func buildArray(_ components: [DependencyCollection]) -> DependencyCollection { components.reduce(into: DependencyCollection()) { partialResult, collection in partialResult.append(contentsOf: collection) diff --git a/Sources/Spezi/Dependencies/Property/DependencyPropertyWrapper.swift b/Sources/Spezi/Dependencies/Property/DependencyPropertyWrapper.swift index a8bd42c..02277d6 100644 --- a/Sources/Spezi/Dependencies/Property/DependencyPropertyWrapper.swift +++ b/Sources/Spezi/Dependencies/Property/DependencyPropertyWrapper.swift @@ -62,17 +62,6 @@ public class _DependencyPropertyWrapper { // swiftlint:disable:this type_ self.init(DependencyCollection(DependencyContext(for: T.self, type: .optional))) } - /// Create an optional dependency with a default value. - /// - Parameters: - /// - dependencyType: The wrapped type of the optional dependency. - /// - defaultValue: The default value that is used if no instance was supplied otherwise. - public convenience init( - wrappedValue defaultValue: @escaping @autoclosure () -> T, - _ dependencyType: T.Type = T.self - ) where Value == T?, T: Module { // does that make sense? - self.init(DependencyContext(for: T.self, type: .optional, defaultValue: defaultValue)) - } - /// Declare a dependency to a module that can provide a default value on its own. @available( *, deprecated, renamed: "init(wrappedValue:_:)", diff --git a/Sources/Spezi/Dependencies/Property/Module+Dependencies.swift b/Sources/Spezi/Dependencies/Property/Module+Dependencies.swift index c49ee73..19e99fd 100644 --- a/Sources/Spezi/Dependencies/Property/Module+Dependencies.swift +++ b/Sources/Spezi/Dependencies/Property/Module+Dependencies.swift @@ -10,22 +10,29 @@ extension Module { /// Define dependency to other `Module`s. /// - /// You can use this property wrapper inside your `Module` to define dependencies to other ``Module``s. + /// You can use the `@Dependency` property wrapper inside your ``Module`` to define dependencies to other `Module`s. /// - /// - Note: You can access the contents of `@Dependency` once your ``Module/configure()-5pa83`` method is called (e.g., it must not be used in the `init`). + /// - Note: You can access the contents of `@Dependency` once your ``Module/configure()-5pa83`` method is called. You cannot + /// access it within your `init`. /// - /// The below code sample demonstrates a simple, singular dependence on the `ExampleModuleDependency` module. + /// ### Required Dependencies + /// + /// The below code sample demonstrates a simple, singular dependence on the `ExampleModuleDependency` module. If the dependency + /// is not available (because the user didn't configure it), the application will crash at runtime. /// /// ```swift /// class ExampleModule: Module { - /// @Dependency var exampleDependency = ExampleModuleDependency() + /// @Dependency(ExampleModuleDependency.self) var exampleDependency /// } /// ``` /// - /// Some modules do not need a default value assigned to the property if they provide a default configuration and conform to ``DefaultInitializable``. + /// Some `Module`s can be used without explicit configuration, for example if they provide a default configuration by conforming to the + /// ``DefaultInitializable`` protocol. In those cases, you can provide a default value to the `@Dependency` property wrapper + /// that is used in case the user didn't configure the `Module` separately. + /// /// ```swift /// class ExampleModule: Module { - /// @Dependency(ExampleModuleDependency.self) var exampleDependency + /// @Dependency var exampleDependency = ExampleModuleDependency() /// } /// ``` /// @@ -50,9 +57,13 @@ extension Module { /// /// In certain circumstances your list of dependencies might not be statically known. Instead you might want to generate /// your list of dependencies within the initializer, based on external factors. - /// The `@Dependency` property wrapper, allows you to define your dependencies using a result-builder-based appraoch. + /// The `@Dependency` property wrapper, allows you to define your dependencies using a result-builder-based approach or by creating + /// a ``DependencyCollection`` yourself. /// - /// Below is a short code example that demonstrates this functionality. + /// - Tip: If a collection of `Module`s is part of your `Module`'s configuration and you want to impose certain protocol requirements, + /// you can implement your own result builder using the ``DependencyCollectionBuilder`` protocol. Refer to the documentation for more information. + /// + /// Below is a short code example that demonstrates how to dynamically create a list of dependencies. /// /// ```swift /// class ExampleModule: Module { @@ -68,5 +79,7 @@ extension Module { /// } /// } /// ``` + /// + /// - Note: Use `Dependency/init(using:)` to initialize the `@Dependency` property wrapper using a ``DependencyCollection``. public typealias Dependency = _DependencyPropertyWrapper } diff --git a/Sources/Spezi/Module/ModuleBuilder.swift b/Sources/Spezi/Module/ModuleBuilder.swift index 2010c62..df370e4 100644 --- a/Sources/Spezi/Module/ModuleBuilder.swift +++ b/Sources/Spezi/Module/ModuleBuilder.swift @@ -7,7 +7,7 @@ // -/// A function builder used to aggregate multiple ``Module``s +/// A function builder used to aggregate multiple `Modul``s @resultBuilder public enum ModuleBuilder { /// If declared, provides contextual type information for statement expressions to translate them into partial results. diff --git a/Sources/Spezi/Spezi.docc/Module/Module Dependency.md b/Sources/Spezi/Spezi.docc/Module/Module Dependency.md index c9d8f8b..c2eaa23 100644 --- a/Sources/Spezi/Spezi.docc/Module/Module Dependency.md +++ b/Sources/Spezi/Spezi.docc/Module/Module Dependency.md @@ -21,6 +21,9 @@ to ensure functionality of a dependency is available at configuration. > Note: Declaring a cyclic dependency will result in a runtime error. +Below is a example of declaring a dependence on the `ExampleModuleDependency` and additionally providing a default value that is used +in the case that the module wasn't already configured by the user (for more information, see section below). + ```swift class ExampleModule: Module { @Dependency var exampleModuleDependency = ExampleModuleDependency() @@ -36,7 +39,8 @@ section. If you declare a dependency to a `Module` that is not configured by the users (e.g., some underlying configuration `Module`s might not event be publicly accessible), is initialized with the instance that was passed to the ``Module/Dependency`` property wrapper. -- Tip: `Module`s can easily provide a default configuration by adopting the ``DefaultInitializable`` protocol. +- Tip: `Module`s can adopt the ``DefaultInitializable`` protocol to opt into being default configurable. This mandates the presence of a + default initializer. Below is a short code example. ```swift @@ -48,7 +52,8 @@ class ExampleModuleDependency: Module, DefaultInitializable { class ExampleModule: Module { - @Dependency var exampleModuleDependency: ExampleModuleDependency + // dependency that uses the default init, if module is not externally configured. + @Dependency var exampleModuleDependency = ExampleModuleDependency() } ``` diff --git a/Sources/Spezi/Spezi.docc/Spezi.md b/Sources/Spezi/Spezi.docc/Spezi.md index e832692..66116d7 100644 --- a/Sources/Spezi/Spezi.docc/Spezi.md +++ b/Sources/Spezi/Spezi.docc/Spezi.md @@ -82,11 +82,8 @@ You can learn more about the ``Standard`` protocol and when it is advised to cre A ``Module`` defines a software subsystem providing distinct and reusable functionality. Modules can use the constraint mechanism to enforce a set of requirements to the standard used in the Spezi-based software where the module is used. Modules also define dependencies on each other to reuse functionality and can communicate with other modules by offering and collecting information. -They can also conform to different protocols to provide additional access to Spezi features, such as lifecycle management and triggering view updates in SwiftUI using the observable mechanisms in Swift. You can learn more about modules in the documentation. -To simplify the creation of modules, a common set of functionalities typically used by modules is summarized in the ``Module`` protocol, making it an easy one-stop solution to support all these different functionalities and build a capable Spezi module. - ## Topics