Skip to content

Commit

Permalink
Sync DocC files and Update README
Browse files Browse the repository at this point in the history
  • Loading branch information
xiaofei86 committed Feb 18, 2022
1 parent 788a5ef commit 708b1af
Show file tree
Hide file tree
Showing 12 changed files with 482 additions and 107 deletions.
105 changes: 105 additions & 0 deletions CarbonCore/CarbonCore/CarbonCore.docc/Advanced usage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
# Advanced usage

Advanced usage and design of DSL

## Overview

### Design of DSL

DefinitionBuilder is a DSL Syntax used to build ObjectDefinition.
ObjectDefinition is the definition of the object and stores the data needed to resolve the object.

These data are divided into 4 types:
* Key: The unique identifier of the object definition, used to locate
* Factory: The closure used to create the object and the dependencies that need to be built during creation
* Autowired: Dependencies that need to be built after creation
* Configuration: Other configuration of the object

DefinitionBuilder has 10 subclasses. Each subclass is used to set a type of ObjectDefinition data.

Their corresponding relationship is as follows:
* Key: ``KeyDefinitionBuilder``, ``AliasDefinitionBuilder``, ``DynamicAliasDefinitionBuilder``
* Factory: ``FactoryDefinitionBuilder``, ``DynamicFactoryDefinitionBuilder``, ``DynamicClassDefinitionBuilder``
* Autowired: ``AutowiredDefinitionBuilder``, ``DynamicAutowiredDefinitionBuilder``
* Configuration: ``AttributeDefinitionBuilder``, ``ActionDefinitionBuilder``

The method of each DefinitionBuilder controls which methods can be used in the next step by controlling the returned DefinitionBuilder type. So you can only complete an ObjectDefinition in a set order. Each DefinitionBuilder controls which settings can be omitted by controlling the inheritance relationship.

The specifications are as follows, '>' means order, italics means it can be omitted. It doesn't matter if you are confused, the compiler will tell you the correct way.

* *Key* > *Alias* > Factory > *Autowired* > *Attribute* > *Action*
* Key > *DynamicAlias* > *DynamicFactory* > DynamicClass > *DynamicAutowired* > *Attribute* > *Action*

### Circular dependency

During the life cycle of object creation, the creation of dependent objects will be temporarily stored. This design can solve most recursion caused by circular dependencies.

When there is a circular dependency, it is recommended that at least one object in the circular chain use property or setter injection to build dependencies, this will create dependent objects after the root object is created, using previously created object will break the loop.

In the following example, both parent and childParent have values and are the same object.

```swift
let parentBuilder = Definition()
.protocol(ParentProtocol.self)
.object(Parent())
.property(\.child)
let childBuilder = Definition()
.protocol(ChildProtocol.self)
.object(Child())
.property(\.parent)

context.register(builder: parentBuilder)
context.register(builder: childBuilder)

let parent = context[ParentProtocol.self] as! Parent
let childParent = (parent.child as! Child).parent
```
The same as:
```swift
let parentBuilder = Definition()
.protocol(ParentProtocol.self)
.object(Parent())
.property(\.child)
let childBuilder = Definition()
.protocol(ChildProtocol.self)
.constructor(Child.init(parent:))
```

If the constructor is used to build dependencies, this will create dependent objects when the object creation is not completed, which will inevitably cause recursion.

In the following example, resolving ParentProtocol will trigger the dead lock assertion.

```swift
let parentBuilder = Definition()
.protocol(ParentProtocol.self)
.constructor(Parent.init(child:))

let childBuilder = Definition()
.protocol(ChildProtocol.self)
.constructor(Child.init(parent:))

context.register(builder: parentBuilder)
context.register(builder: childBuilder)
```

> Tips: If you must use the constructor to build dependencies, at least one object in the circular chain is a singleton, which also avoids recursion.
### Thread safety

Object resolving is thread-safe, but registration methods are currently not thread-safe, these methods will be marked in the interface comment - “Note: It's not thread-safe, do not use it in a multi-threaded environment.”

## Topics

### DSL Syntax

- ``DefinitionBuilder``

- ``GroupDefinitionBuilder``

- ``ConfigurationBuilder``

- ``Configuration``

## See Also

- <doc:Module-management>
169 changes: 169 additions & 0 deletions CarbonCore/CarbonCore/CarbonCore.docc/Basic usage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,169 @@
# Basic usage

Object registration and resolving.

## Overview

* Basic object registration and resolving
```swift
let context = ObjectContext()
context.register(builder: Definition().object(FileViewController()))
context[FileViewController.self]
```

* Use configuration batch registration
```swift
class MyConfiguration: ScannableObject, Configuration {
static func definitions() -> Definitions {
Definition()
.object(FilePath())
Definition()
.constructor(FileModel.init(filePath:))
}
}

let context = ObjectContext()
context.register(configuration: MyConfiguration.self)
context[FileModel.self]
```

> Note: For convenience, the registration of object definitions and the creation of context that appear in the following will be omitted. All definitions in your project must be registered before they can be resolved.
### Object Scope

* default: Default object life cycle, alias of prototype
* prototype: Local object life cycle
* singleton: Singleton object life cycle
* weakSingleton: Weak reference singleton object life cycle

```swift
Definition()
.protocol(FileManagerProtocol.self)
.object(FileManager())
.scope(.singleton)
```

* Multiple object definition methods
```swift
Definition().object(FilePath())
Definition().constructor(FilePath.init)
Definition().factory { _ in FilePath() }
context[FileModel.self]
```

* Define protocol alias for object
```swift
Definition()
.object(FileModel(path: "/") as FileModelProtocol)
context[FileModelProtocol.self]
```
The same as:
```swift
Definition()
.protocol(FileModelProtocol.self)
.object(FileModel(path: "/"))
```

* Define multiple protocol aliases for the object
```swift
Definition()
.protocol(FileManagerProtocol.self)
.alias(ImageManagerProtocol.self)
.alias(DirectoryManagerProtocol.self)
.object(FileManager())
context[FileManagerProtocol.self]
context[ImageManagerProtocol.self]
context[DirectoryManagerProtocol.self]
```
The same as:
```swift
Definition()
.protocol(FileManagerProtocol.self)
.alias([ImageManagerProtocol.self, DirectoryManagerProtocol.self])
.object(FileManager())
```

* Use constructor for dependency injection
```swift
Definition()
.protocol(FileViewControllerProtocol.self)
.constructor(FileViewController.init(fileManager:))
context[FileViewControllerProtocol.self].fileManager
```

* Use property for dependency injection
```swift
Definition()
.protocol(FileViewControllerProtocol.self)
.object(FileViewController())
.property(\.fileManager)
context[FileViewControllerProtocol.self].fileManager
```

* Use setter for dependency injection
```swift
Definition()
.protocol(FileViewControllerProtocol.self)
.object(FileViewController())
.setter(FileViewController.setFileManager)
context[FileViewControllerProtocol.self].fileManager
```

* Use static factory for manual dependency injection
```swift
Definition()
.factory(fileViewController(context:))
context[FileViewControllerProtocol.self].fileManager

static func fileViewController(context: ObjectContext) -> FileViewControllerProtocol {
let fileVC = FileViewController()
fileVC.fileManager = context[FileManagerProtocol]
return fileVC
}
```
The same as:
```swift
Definition()
.factory { context in
let fileVC = FileViewController()
fileVC.fileManager = context[FileManagerProtocol]
return fileVC as FileViewControllerProtocol
}
```

* Create objects with external parameters
```swift
Definition()
.factory(fileModel(context:path:name:))
context[FileModelProtocol.self, "/china/beijing", "family.png"]

static func fileModel(context: ObjectContext, path: String, name: String) -> FileModelProtocol {
FileModel(path: path, name: name)
}
```

## Topics

### Convenience registration

- ``ObjectContext/register(protocol:cls:name:)``

- ``ObjectContext/register(protocol:object:name:)``

### Context

- ``ObjectContext``

- ``ObjectScope``

### DSL Syntax

- ``DefinitionBuilder``

- ``Configuration``

## See Also

- <doc:Advanced-usage>

- <doc:Module-management>
58 changes: 53 additions & 5 deletions CarbonCore/CarbonCore/CarbonCore.docc/CarbonCore.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,61 @@
# ``CarbonCore``

<!--@START_MENU_TOKEN@-->Summary<!--@END_MENU_TOKEN@-->
CarbonGraph is a Swift dependency injection / lookup framework for iOS. You can use it to build loose coupling between modules.

## Overview

<!--@START_MENU_TOKEN@-->Text<!--@END_MENU_TOKEN@-->
![Logo Banner](logo_banner.png)

## Topics
* Complete dependency injection capabilities
* Complete Objective-C support
* Convenient object definition DSL
* High-performance thread-safe solution
* Support resolving native Swift types
* Support resolving with external parameters
* Support resolving with circular dependencies
* Automatic scanning of configuration
* Additional module life cycle management capabilities

### <!--@START_MENU_TOKEN@-->Group<!--@END_MENU_TOKEN@-->

- <!--@START_MENU_TOKEN@-->``Symbol``<!--@END_MENU_TOKEN@-->
The CarbonGraph project contains 2 frameworks, If only used in Swift, just import CarbonCore, otherwise import both.

| Framework | Description |
| --- | --- |
| CarbonCore | Focused specifically on core DI implementations |
| CarbonObjC | CarbonCore's ObjC adaptation framework |

## Quick Start

* Basic object registration and resolving
```swift
let context = ObjectContext()
let definitionBuilder = Definition("filevc")
.protocol(UIViewController.self)
.object(FileViewController())
context.register(builder: definitionBuilder)
context[UIViewController.self, name: "filevc"]
```

```swift
let context = ObjectContext()
let definitionBuilder = Definition()
.object(FileManager() as FileManagerProtocol)
context.register(builder: definitionBuilder)
context[FileManagerProtocol.self]
```

* Use configuration batch registration
```swift
class MyConfiguration: Configuration {
static func definitions(of context: ObjectContext) -> Definitions {
Definition()
.object(FilePath())
Definition()
.constructor(FileModel.init(filePath:))
}
}

let context = ObjectContext()
context.register(configuration: MyConfiguration.self)
context[FileModel.self]
```
31 changes: 31 additions & 0 deletions CarbonCore/CarbonCore/CarbonCore.docc/Compatibility.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Compatibility

The Requirements and Compatibility

## Overview

### CarbonCore Requirements

| CarbonCore Stable Version | Required iOS Version | Required Swift Version |
| --- | --- | --- |
| 1.2.2 - 1.3.0 | 9.0 | 5.2 |

### CarbonObjC Version Compatibility

| CarbonObjC Version | CarbonCore Compatible Version |
| --- | --- |
| 1.2.2 | 1.2.2 |
| 1.3.0 | 1.3.0 |

### Build for distribution

| Xcode Version | Swift Version | MacOS Version | Build for distribution |
| --- | --- | --- | --- |
| 11.4 | 5.2 | Catalina 10.15.7 | passing |
| 12.1 | 5.3 | Catalina 10.15.7 | passing |
| 12.4 | 5.3.2 | Catalina 10.15.7 | passing |
| ~~12.5~~ | ~~5.4~~ | ~~Big Sur 11.6~~ | ~~error~~ |
| ~~12.5.1~~ | ~~5.4.2~~ | ~~Big Sur 11.6~~ | ~~error~~ |
| 13.0 | 5.5 | Big Sur 11.6 | passing |


Loading

0 comments on commit 708b1af

Please sign in to comment.