diff --git a/Documentation/Unio0_6_0MigrationGuide.md b/Documentation/Unio0_6_0MigrationGuide.md new file mode 100644 index 0000000..f8563ae --- /dev/null +++ b/Documentation/Unio0_6_0MigrationGuide.md @@ -0,0 +1,15 @@ +# Unio 0.6.0 Migration Guide + +Unio 0.6.0 introduces some breaking changes. + +## Classes + +- [RENAME] `UnioStream` -> `PrimitiveStream` + +## Methods + +- [REPLACE] `LogicType func bind(from:)` -> `LogicType static func bind(from:disposeBag:)` + +## Typealias + +- [ADD] `typealias UnioStream = PrimitiveStream & LogicType` diff --git a/Example/UnioSample/GitHubSearchAPIStream.swift b/Example/UnioSample/GitHubSearchAPIStream.swift index 2fe8d88..d517203 100644 --- a/Example/UnioSample/GitHubSearchAPIStream.swift +++ b/Example/UnioSample/GitHubSearchAPIStream.swift @@ -15,18 +15,16 @@ protocol GitHubSearchAPIStreamType: AnyObject { var output: OutputWrapper { get } } -final class GitHubSearchAPIStream: UnioStream, GitHubSearchAPIStreamType { +final class GitHubSearchAPIStream: UnioStream, GitHubSearchAPIStreamType { - init(extra: Extra = .init()) { + init(extra: Extra = .init(session: .shared)) { super.init(input: Input(), state: State(), - extra: extra, - logic: Logic()) + extra: extra) } } extension GitHubSearchAPIStream { - typealias State = NoState struct Input: InputType { @@ -41,20 +39,10 @@ extension GitHubSearchAPIStream { struct Extra: ExtraType { - let session = URLSession.shared + let session: URLSession } - struct Logic: LogicType { - typealias Input = GitHubSearchAPIStream.Input - typealias Output = GitHubSearchAPIStream.Output - typealias State = GitHubSearchAPIStream.State - typealias Extra = GitHubSearchAPIStream.Extra - } -} - -extension GitHubSearchAPIStream.Logic { - - func bind(from dependency: Dependency) -> Output { + static func bind(from dependency: Dependency, disposeBag: DisposeBag) -> Output { let session = dependency.extra.session diff --git a/Example/UnioSample/GitHubSearchLogicStream.swift b/Example/UnioSample/GitHubSearchLogicStream.swift index c0b1c35..4930f76 100644 --- a/Example/UnioSample/GitHubSearchLogicStream.swift +++ b/Example/UnioSample/GitHubSearchLogicStream.swift @@ -15,13 +15,12 @@ protocol GitHubSearchLogicStreamType: AnyObject { var output: OutputWrapper { get } } -final class GitHubSearchLogicStream: UnioStream, GitHubSearchLogicStreamType { +final class GitHubSearchLogicStream: UnioStream, GitHubSearchLogicStreamType { init(extra: Extra = .init(searchAPIStream: GitHubSearchAPIStream(), scheduler: ConcurrentMainScheduler.instance)) { super.init(input: Input(), state: State(), - extra: extra, - logic: Logic()) + extra: extra) } } @@ -49,19 +48,7 @@ extension GitHubSearchLogicStream { let scheduler: SchedulerType } - struct Logic: LogicType { - typealias Input = GitHubSearchLogicStream.Input - typealias Output = GitHubSearchLogicStream.Output - typealias State = GitHubSearchLogicStream.State - typealias Extra = GitHubSearchLogicStream.Extra - - let disposeBag = DisposeBag() - } -} - -extension GitHubSearchLogicStream.Logic { - - func bind(from dependency: Dependency) -> Output { + static func bind(from dependency: Dependency, disposeBag: DisposeBag) -> Output { let state = dependency.state let extra = dependency.extra diff --git a/Example/UnioSample/GitHubSearchViewStream.swift b/Example/UnioSample/GitHubSearchViewStream.swift index 0e12891..f70a771 100644 --- a/Example/UnioSample/GitHubSearchViewStream.swift +++ b/Example/UnioSample/GitHubSearchViewStream.swift @@ -15,20 +15,17 @@ protocol GitHubSearchViewStreamType: AnyObject { var output: OutputWrapper { get } } -final class GitHubSearchViewStream: UnioStream, GitHubSearchViewStreamType { +final class GitHubSearchViewStream: UnioStream, GitHubSearchViewStreamType { init(extra: Extra = .init(logicStream: GitHubSearchLogicStream())) { super.init(input: Input(), state: State(), - extra: extra, - logic: Logic()) + extra: extra) } } extension GitHubSearchViewStream { - typealias State = NoState - struct Input: InputType { let searchText = PublishRelay() @@ -43,22 +40,9 @@ extension GitHubSearchViewStream { struct Extra: ExtraType { let logicStream: GitHubSearchLogicStreamType - let disposeBag = DisposeBag() - } - - struct Logic: LogicType { - typealias Input = GitHubSearchViewStream.Input - typealias Output = GitHubSearchViewStream.Output - typealias State = GitHubSearchViewStream.State - typealias Extra = GitHubSearchViewStream.Extra - - let disposeBag = DisposeBag() } -} - -extension GitHubSearchViewStream.Logic { - func bind(from dependency: Dependency) -> Output { + static func bind(from dependency: Dependency, disposeBag: DisposeBag) -> Output { let logicStream = dependency.extra.logicStream diff --git a/README.md b/README.md index e9bbb6d..04a5fb5 100644 --- a/README.md +++ b/README.md @@ -160,35 +160,33 @@ struct Extra: ExtraType { ### Logic The rule of Logic is generating [Output](#output) from Dependency. -It generates [Output](#output) to call `func bind(from:)`. -`func bind(from:)` is called once when [UnioStream](#uniostream) is initialized. -If you want to use DisposeBags in `func bind(from:)`, define properties of DisposeBag in Logic. +It generates [Output](#output) to call `static func bind(from:disposeBag:)`. +`static func bind(from:disposeBag:)` is called once when [UnioStream](#uniostream) is initialized. ```swift -struct Logic: LogicType { +enum Logic: LogicType { typealias Input = GitHubSearchViewStream.Input typealias Output = GitHubSearchViewStream.Output typealias State = GitHubSearchViewStream.State typealias Extra = GitHubSearchViewStream.Extra - let disposeBag = DisposeBag() - - func bind(from dependency: Dependency) -> Output + static func bind(from dependency: Dependency, disposeBag: DisposeBag) -> Output } ``` -Connect sequences and generate [Output](#output) in `func bind(from:)` to use below properties and methods. +Connect sequences and generate [Output](#output) in `static func bind(from:disposeBag:)` to use below properties and methods. - `dependency.state` - `dependency.extra` -- `dependency.inputObservables` ... returns a Observable that is property of [Input](#input). +- `dependency.inputObservables` ... Returns a Observable that is property of [Input](#input). +- `disposeBag` ... Same lifecycle with UnioStream. Here is a exmaple of implementation. ```swift -extension GitHubSearchViewStream.Logic { +extension Logic { - func bind(from dependency: Dependency) -> Output { + static func bind(from dependency: Dependency, disposeBag: DisposeBag) -> Output { let apiStream = dependency.extra.apiStream dependency.inputObservables.searchText @@ -210,22 +208,24 @@ It has `input: InputWrapper` and `output: OutputWrapper`. It automatically generates `input: InputWrapper` and `output: OutputWrapper` from instances of [Input](#input), [State](#state), [Extra](#extra) and [Logic](#logic). ```swift -class UnioStream { +typealias UnioStream = PrimitiveStream & LogicType + +class PrimitiveStream { let input: InputWrapper let output: OutputWrapper - init(input: Logic.Input, state: Logic.State, extra: Logic.Extra, logic: Logic) + init(input: Logic.Input, state: Logic.State, extra: Logic.Extra) } ``` Be able to define a subclass of UnioStream like this. ```swift -fianl class GitHubSearchViewStream: UnioStream { +fianl class GitHubSearchViewStream: UnioStream { init() { - super.init(input: Input(), state: State(), extra: Extra(), logic: Logic()) + super.init(input: Input(), state: State(), extra: Extra()) } } ``` @@ -244,10 +244,10 @@ protocol GitHubSearchViewStreamType: AnyObject { var output: OutputWrapper { get } } -final class GitHubSearchViewStream: UnioStream, GitHubSearchViewStreamType { +final class GitHubSearchViewStream: UnioStream, GitHubSearchViewStreamType { init() { - super.init(input: Input(), state: State(), extra: Extra(), logic: Logic()) + super.init(input: Input(), state: State(), extra: Extra()) } typealias State = NoState @@ -264,19 +264,7 @@ final class GitHubSearchViewStream: UnioStream, Gi let apiStream: GitHubSearchAPIStream() } - struct Logic: LogicType { - typealias Input = GitHubSearchViewStream.Input - typealias Output = GitHubSearchViewStream.Output - typealias State = GitHubSearchViewStream.State - typealias Extra = GitHubSearchViewStream.Extra - - let disposeBag = DisposeBag() - } -} - -extension GitHubSearchViewStream.Logic { - - func bind(from dependency: Dependency) -> Output { + static func bind(from dependency: Dependency, disposeBag: DisposeBag) -> Output { let apiStream = dependency.extra.apiStream dependency.inputObservables.searchText @@ -299,7 +287,7 @@ final class GitHubSearchViewController: UIViewController { let searchBar = UISearchBar(frame: .zero) let tableView = UITableView(frame: .zero) - private let viewStream = GitHubSearchViewStream() + private let viewStream: GitHubSearchViewStreamType = GitHubSearchViewStream() private let disposeBag = DisposeBag() override func viewDidLoad() { @@ -325,6 +313,7 @@ The documentation which does not use `KeyPath Dynamic Member Lookup` is [here](h #### Migration Guides - [Unio 0.5.0 Migration Guide](./Documentation/Unio0_5_0MigrationGuide.md) +- [Unio 0.6.0 Migration Guide](./Documentation/Unio0_6_0MigrationGuide.md) ### Xcode Template diff --git a/Tools/Unio Components.xctemplate/Default/___FILEBASENAME___ViewStream.swift b/Tools/Unio Components.xctemplate/Default/___FILEBASENAME___ViewStream.swift index d17a679..e8423ed 100644 --- a/Tools/Unio Components.xctemplate/Default/___FILEBASENAME___ViewStream.swift +++ b/Tools/Unio Components.xctemplate/Default/___FILEBASENAME___ViewStream.swift @@ -9,13 +9,12 @@ protocol ___VARIABLE_productName___ViewStreamType: AnyObject { var output: OutputWrapper<___VARIABLE_productName___ViewStream.Output> { get } } -final class ___VARIABLE_productName___ViewStream: UnioStream<___VARIABLE_productName___ViewStream.Logic>, ___VARIABLE_productName___ViewStreamType { +final class ___VARIABLE_productName___ViewStream: UnioStream<___VARIABLE_productName___ViewStream>, ___VARIABLE_productName___ViewStreamType { init(extra: Extra = .init()) { super.init(input: Input(), state: State(), - extra: extra, - logic: Logic()) + extra: extra) } } @@ -52,19 +51,7 @@ extension ___VARIABLE_productName___ViewStream { } - struct Logic: LogicType { - typealias Input = ___VARIABLE_productName___ViewStream.Input - typealias Output = ___VARIABLE_productName___ViewStream.Output - typealias State = ___VARIABLE_productName___ViewStream.State - typealias Extra = ___VARIABLE_productName___ViewStream.Extra - - let disposeBag = DisposeBag() - } -} - -extension ___VARIABLE_productName___ViewStream.Logic { - - func bind(from dependency: Dependency) -> Output { + static func bind(from dependency: Dependency, disposeBag: DisposeBag) -> Output { let state = dependency.state diff --git a/Tools/UnioStream.xctemplate/___FILEBASENAME___Stream.swift b/Tools/UnioStream.xctemplate/___FILEBASENAME___Stream.swift index fff3fb3..9fc3031 100644 --- a/Tools/UnioStream.xctemplate/___FILEBASENAME___Stream.swift +++ b/Tools/UnioStream.xctemplate/___FILEBASENAME___Stream.swift @@ -9,13 +9,12 @@ protocol ___VARIABLE_productName___StreamType: AnyObject { var output: OutputWrapper<___VARIABLE_productName___Stream.Output> { get } } -final class ___VARIABLE_productName___Stream: UnioStream<___VARIABLE_productName___Stream.Logic>, ___VARIABLE_productName___StreamType { +final class ___VARIABLE_productName___Stream: UnioStream<___VARIABLE_productName___Stream>, ___VARIABLE_productName___StreamType { init(extra: Extra = .init()) { super.init(input: Input(), state: State(), - extra: extra, - logic: Logic()) + extra: extra) } } @@ -52,19 +51,7 @@ extension ___VARIABLE_productName___Stream { } - struct Logic: LogicType { - typealias Input = ___VARIABLE_productName___Stream.Input - typealias Output = ___VARIABLE_productName___Stream.Output - typealias State = ___VARIABLE_productName___Stream.State - typealias Extra = ___VARIABLE_productName___Stream.Extra - - let disposeBag = DisposeBag() - } -} - -extension ___VARIABLE_productName___Stream.Logic { - - func bind(from dependency: Dependency) -> Output { + static func bind(from dependency: Dependency, disposeBag: DisposeBag) -> Output { let state = dependency.state diff --git a/Unio.podspec b/Unio.podspec index 4209baf..0f8307f 100644 --- a/Unio.podspec +++ b/Unio.podspec @@ -8,7 +8,7 @@ Pod::Spec.new do |s| s.name = "Unio" - s.version = "0.5.0" + s.version = "0.6.0" s.summary = "KeyPath based Unidirectionarl Input / Output framework with RxSwift." s.homepage = "https://github.com/cats-oss/Unio" s.license = { :type => "MIT", :file => "LICENSE" } diff --git a/Unio/LogicType.swift b/Unio/LogicType.swift index 45e3a45..a9441eb 100644 --- a/Unio/LogicType.swift +++ b/Unio/LogicType.swift @@ -6,6 +6,8 @@ // Copyright © 2019 tv.abema. All rights reserved. // +import RxSwift + /// Represents definitions and implementations of UnioStream logic. public protocol LogicType { associatedtype Input: InputType @@ -16,5 +18,5 @@ public protocol LogicType { /// Generates Output from Dependency. /// /// - note: This method called once when a linked UnioStream is initialized. - func bind(from dependency: Dependency) -> Output + static func bind(from dependency: Dependency, disposeBag: DisposeBag) -> Output } diff --git a/Unio/UnioStream.swift b/Unio/UnioStream.swift index 6b15299..47b7ec7 100644 --- a/Unio/UnioStream.swift +++ b/Unio/UnioStream.swift @@ -6,24 +6,45 @@ // Copyright © 2019 tv.abema. All rights reserved. // +import RxSwift + +/// Makes possible to implement Unidirectional input / output stream and be able to implement LogicType to its self. +/// +/// ``` +/// // Usage Example +/// class GithubSearchStream: UnioStream { +/// struct Input: InputType {} +/// +/// struct Output: OutputType {} +/// +/// struct State: StateType {} +/// +/// struct Extra: ExtraType {} +/// +/// static func bind(from dependency: Dependency, disposeBag: DisposeBag) -> Output { +/// return Output() +/// } +/// } +/// ``` +public typealias UnioStream = PrimitiveStream & LogicType + /// Makes possible to implement Unidirectional input / output stream. -open class UnioStream { +open class PrimitiveStream { public let input: InputWrapper public let output: OutputWrapper private let _state: Logic.State private let _extra: Logic.Extra - private let _logic: Logic + private let _disposeBag = DisposeBag() /// - note: initialize parameters are retained in UnioStream - public init(input: Logic.Input, state: Logic.State, extra: Logic.Extra, logic: Logic) { + public init(input: Logic.Input, state: Logic.State, extra: Logic.Extra) { let dependency = Dependency(input: input, state: state, extra: extra) - let output = logic.bind(from: dependency) + let output = Logic.bind(from: dependency, disposeBag: _disposeBag) self.input = InputWrapper(input) self.output = OutputWrapper(output) self._state = state self._extra = extra - self._logic = logic } } diff --git a/UnioTests/TestCases/UnioStreamTests.swift b/UnioTests/TestCases/UnioStreamTests.swift index c56c74f..4cc07cd 100644 --- a/UnioTests/TestCases/UnioStreamTests.swift +++ b/UnioTests/TestCases/UnioStreamTests.swift @@ -110,40 +110,39 @@ extension UnioStreamTests { fileprivate struct Extra: ExtraType { let id = UUID().uuidString + let bindCalled: PublishRelay> + let bindResponse: BehaviorRelay } - fileprivate struct Logic: LogicType { + fileprivate enum Logic: LogicType { typealias Input = UnioStreamTests.Input typealias Output = UnioStreamTests.Output typealias State = UnioStreamTests.State typealias Extra = UnioStreamTests.Extra - - let bindCalled: PublishRelay> - let bindResponse: BehaviorRelay } private struct Dependency { - let testTarget: UnioStream + let testTarget: PrimitiveStream let output: Output let input = Input() let state = State() - let extra = Extra() + let extra: Extra init(bindCalled: PublishRelay> = .init()) { let output = Output() - let logic = Logic(bindCalled: bindCalled, bindResponse: BehaviorRelay(value: output)) self.output = output - self.testTarget = UnioStream(input: input, state: state, extra: extra, logic: logic) + self.extra = Extra(bindCalled: bindCalled, bindResponse: BehaviorRelay(value: output)) + self.testTarget = PrimitiveStream(input: input, state: state, extra: extra) } } } extension UnioStreamTests.Logic { - func bind(from dependency: Unio.Dependency) -> Output { - bindCalled.accept(dependency) - return bindResponse.value ?? Output() + static func bind(from dependency: Unio.Dependency, disposeBag: DisposeBag) -> Output { + dependency.extra.bindCalled.accept(dependency) + return dependency.extra.bindResponse.value } }