-
Notifications
You must be signed in to change notification settings - Fork 59
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Query rule custom data widget (#132)
* feat: Implement Query rule custom data widget
- Loading branch information
1 parent
f9d948f
commit 6261916
Showing
9 changed files
with
571 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
// | ||
// Decodable+JSON.swift | ||
// | ||
// | ||
// Created by Vladislav Fitc on 12/10/2020. | ||
// | ||
|
||
import Foundation | ||
|
||
extension Decodable { | ||
|
||
init(json: JSON) throws { | ||
let data = try JSONEncoder().encode(json) | ||
self = try JSONDecoder().decode(Self.self, from: data) | ||
} | ||
|
||
} |
112 changes: 112 additions & 0 deletions
112
...antSearchCore/QueryRuleCustomData/Connector/QueryRuleCustomDataConnector+Controller.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
// | ||
// QueryRuleCustomDataConnector+Controller.swift | ||
// | ||
// | ||
// Created by Vladislav Fitc on 10/10/2020. | ||
// | ||
|
||
import Foundation | ||
|
||
public extension QueryRuleCustomDataConnector { | ||
|
||
/** | ||
- Parameters: | ||
- searcher: Searcher that handles your searches | ||
- interactor: External custom data interactor | ||
- controller: Controller interfacing with a concrete custom data view | ||
- presenter: Presenter defining how a model appears in the controller | ||
*/ | ||
convenience init<Controller: ItemController, Output>(searcher: SingleIndexSearcher, | ||
interactor: Interactor = .init(), | ||
controller: Controller, | ||
presenter: @escaping (Model?) -> Output) where Controller.Item == Output { | ||
|
||
self.init(searcher: searcher, interactor: interactor) | ||
let controllerConnection = interactor.connectController(controller, presenter: presenter) | ||
controllerConnections.append(controllerConnection) | ||
} | ||
|
||
/** | ||
- Parameters: | ||
- searcher: Searcher that handles your searches. | ||
- interactor: External custom data interactor | ||
- controller: Controller interfacing with a concrete custom data view | ||
*/ | ||
convenience init<Controller: ItemController>(searcher: SingleIndexSearcher, | ||
interactor: Interactor = .init(), | ||
controller: Controller) where Controller.Item == Model? { | ||
self.init(searcher: searcher, interactor: interactor) | ||
let controllerConnection = interactor.connectController(controller, presenter: { $0 }) | ||
controllerConnections.append(controllerConnection) | ||
} | ||
|
||
} | ||
|
||
public extension QueryRuleCustomDataConnector { | ||
|
||
/** | ||
- Parameters: | ||
- searcher: Searcher that handles your searches. | ||
- queryIndex: Index of query from response of which the user data will be extracted | ||
- interactor: External custom data interactor | ||
- controller: Controller interfacing with a concrete custom data view | ||
- presenter: Presenter defining how a model appears in the controller | ||
*/ | ||
convenience init<Controller: ItemController, Output>(searcher: MultiIndexSearcher, | ||
queryIndex: Int, | ||
interactor: Interactor = .init(), | ||
controller: Controller, | ||
presenter: @escaping (Model?) -> Output) where Controller.Item == Output { | ||
|
||
self.init(searcher: searcher, queryIndex: queryIndex, interactor: interactor) | ||
let controllerConnection = interactor.connectController(controller, presenter: presenter) | ||
controllerConnections = [controllerConnection] | ||
} | ||
|
||
/** | ||
- Parameters: | ||
- searcher: Searcher that handles your searches. | ||
- queryIndex: Index of query from response of which the user data will be extracted | ||
- interactor: External custom data interactor | ||
- controller: Controller interfacing with a concrete custom data view | ||
*/ | ||
convenience init<Controller: ItemController>(searcher: MultiIndexSearcher, | ||
queryIndex: Int, | ||
interactor: Interactor = .init(), | ||
controller: Controller) where Controller.Item == Model? { | ||
self.init(searcher: searcher, queryIndex: queryIndex, interactor: interactor) | ||
let controllerConnection = interactor.connectController(controller, presenter: { $0 }) | ||
controllerConnections.append(controllerConnection) | ||
} | ||
|
||
} | ||
|
||
public extension QueryRuleCustomDataConnector { | ||
|
||
/** | ||
Establishes a connection with the controller | ||
- Parameters: | ||
- controller: Controller interfacing with a concrete custom data view | ||
- presenter: Presenter defining how a model appears in the controller | ||
- Returns: Established connection | ||
*/ | ||
@discardableResult func connectController<Controller: ItemController, Output>(_ controller: Controller, | ||
presenter: @escaping (Model?) -> Output) -> QueryRuleCustomDataInteractor<Model>.ControllerConnection<Controller, Output> { | ||
let connection = interactor.connectController(controller, presenter: presenter) | ||
controllerConnections.append(connection) | ||
return connection | ||
} | ||
|
||
/** | ||
Establishes a connection with the controller | ||
- Parameters: | ||
- controller: Controller interfacing with a concrete custom data view | ||
- Returns: Established connection | ||
*/ | ||
@discardableResult func connectController<Controller: ItemController>(_ controller: Controller) -> QueryRuleCustomDataInteractor<Model>.ControllerConnection<Controller, Model?> { | ||
let connection = interactor.connectController(controller, presenter: { $0 }) | ||
controllerConnections.append(connection) | ||
return connection | ||
} | ||
|
||
} |
78 changes: 78 additions & 0 deletions
78
Sources/InstantSearchCore/QueryRuleCustomData/Connector/QueryRuleCustomDataConnector.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,78 @@ | ||
// | ||
// QueryRuleCustomDataConnector.swift | ||
// | ||
// | ||
// Created by Vladislav Fitc on 09/10/2020. | ||
// | ||
|
||
import Foundation | ||
|
||
/// Component that displays custom data from rules. | ||
/// | ||
/// [Documentation](https://www.algolia.com/doc/api-reference/widgets/query-rule-custom-data/ios/) | ||
public class QueryRuleCustomDataConnector<Model: Decodable> { | ||
|
||
public typealias Interactor = QueryRuleCustomDataInteractor<Model> | ||
|
||
/// Logic applied to the custom model | ||
public let interactor: Interactor | ||
|
||
/// Connection between hits interactor and searcher | ||
public let searcherConnection: Connection | ||
|
||
/// Connections between interactor and controllers | ||
public var controllerConnections: [Connection] | ||
|
||
internal init(interactor: Interactor, | ||
connectSearcher: (Interactor) -> Connection) { | ||
self.interactor = interactor | ||
searcherConnection = connectSearcher(interactor) | ||
controllerConnections = [] | ||
searcherConnection.connect() | ||
} | ||
|
||
} | ||
|
||
public extension QueryRuleCustomDataConnector { | ||
|
||
/** | ||
- Parameters: | ||
- searcher: Searcher that handles your searches | ||
- interactor: External custom data interactor | ||
*/ | ||
convenience init(searcher: SingleIndexSearcher, | ||
interactor: Interactor = .init()) { | ||
self.init(interactor: interactor) { | ||
QueryRuleCustomDataInteractor<Model>.SingleIndexSearcherConnection(interactor: $0, searcher: searcher) | ||
} | ||
} | ||
|
||
/** | ||
- Parameters: | ||
- searcher: Searcher that handles your searches | ||
- queryIndex: Index of query from response of which the user data will be extracted | ||
- interactor: External custom data interactor | ||
*/ | ||
convenience init(searcher: MultiIndexSearcher, | ||
queryIndex: Int, | ||
interactor: Interactor = .init()) { | ||
self.init(interactor: interactor) { | ||
QueryRuleCustomDataInteractor<Model>.MultiIndexSearcherConnection(interactor: $0, searcher: searcher, queryIndex: queryIndex) | ||
} | ||
} | ||
|
||
} | ||
|
||
extension QueryRuleCustomDataConnector: Connection { | ||
|
||
public func connect() { | ||
searcherConnection.connect() | ||
controllerConnections.forEach { $0.connect() } | ||
} | ||
|
||
public func disconnect() { | ||
searcherConnection.disconnect() | ||
controllerConnections.forEach { $0.disconnect() } | ||
} | ||
|
||
} |
67 changes: 67 additions & 0 deletions
67
...tantSearchCore/QueryRuleCustomData/QueryRuleCustomDataInteractor+MultiIndexSearcher.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,67 @@ | ||
// | ||
// QueryRuleCustomDataInteractor+MultiIndexSearcher.swift | ||
// | ||
// | ||
// Created by Vladislav Fitc on 10/10/2020. | ||
// | ||
|
||
import Foundation | ||
|
||
extension QueryRuleCustomDataInteractor { | ||
|
||
/// Connection between a rule custom data logic and a multi-index searcher | ||
public struct MultiIndexSearcherConnection: Connection { | ||
|
||
/// Logic applied to the custom model | ||
public let interactor: QueryRuleCustomDataInteractor | ||
|
||
/// Searcher that handles your searches | ||
public let searcher: MultiIndexSearcher | ||
|
||
/// Index of query from response of which the user data will be extracted | ||
public let queryIndex: Int | ||
|
||
/** | ||
- Parameters: | ||
- interactor: Interactor to connect | ||
- searcher: Searcher to connect | ||
- queryIndex: Index of query from response of which the user data will be extracted | ||
*/ | ||
public init(interactor: QueryRuleCustomDataInteractor, | ||
searcher: MultiIndexSearcher, | ||
queryIndex: Int) { | ||
self.searcher = searcher | ||
self.queryIndex = queryIndex | ||
self.interactor = interactor | ||
} | ||
|
||
public func connect() { | ||
searcher.onResults.subscribe(with: interactor) { (interactor, searchResponse) in | ||
interactor.extractModel(from: searchResponse.results[queryIndex]) | ||
} | ||
} | ||
|
||
public func disconnect() { | ||
searcher.onResults.cancelSubscription(for: interactor) | ||
} | ||
|
||
} | ||
|
||
} | ||
|
||
public extension QueryRuleCustomDataInteractor { | ||
|
||
/** | ||
- Parameters: | ||
- searcher: Searcher to connect | ||
- queryIndex: Index of query from response of which the user data will be extracted | ||
*/ | ||
@discardableResult func connectSearcher(_ searcher: MultiIndexSearcher, | ||
toQueryAtIndex queryIndex: Int) -> MultiIndexSearcherConnection { | ||
let connection = MultiIndexSearcherConnection(interactor: self, searcher: searcher, queryIndex: queryIndex) | ||
connection.connect() | ||
return connection | ||
} | ||
|
||
} | ||
|
58 changes: 58 additions & 0 deletions
58
...antSearchCore/QueryRuleCustomData/QueryRuleCustomDataInteractor+SingleIndexSearcher.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
// | ||
// QueryRuleCustomDataInteractor+SingleIndexSearcher.swift | ||
// | ||
// | ||
// Created by Vladislav Fitc on 10/10/2020. | ||
// | ||
|
||
import Foundation | ||
|
||
extension QueryRuleCustomDataInteractor { | ||
|
||
/// Connection between a rule custom data logic and a single index searcher | ||
public struct SingleIndexSearcherConnection: Connection { | ||
|
||
/// Logic applied to the custom model | ||
public let interactor: QueryRuleCustomDataInteractor | ||
|
||
/// Searcher that handles your searches | ||
public let searcher: SingleIndexSearcher | ||
|
||
/** | ||
- Parameters: | ||
- interactor: Interactor to connect | ||
- searcher: Searcher to connect | ||
*/ | ||
public init(interactor: QueryRuleCustomDataInteractor, | ||
searcher: SingleIndexSearcher) { | ||
self.searcher = searcher | ||
self.interactor = interactor | ||
} | ||
|
||
public func connect() { | ||
searcher.onResults.subscribe(with: interactor) { (interactor, searchResponse) in | ||
interactor.extractModel(from: searchResponse) | ||
} | ||
} | ||
|
||
public func disconnect() { | ||
searcher.onResults.cancelSubscription(for: interactor) | ||
} | ||
|
||
} | ||
|
||
} | ||
|
||
public extension QueryRuleCustomDataInteractor { | ||
|
||
/** | ||
- Parameters: | ||
- searcher: Searcher to connect | ||
*/ | ||
@discardableResult func connectSearcher(_ searcher: SingleIndexSearcher) -> SingleIndexSearcherConnection { | ||
let connection = SingleIndexSearcherConnection(interactor: self, searcher: searcher) | ||
connection.connect() | ||
return connection | ||
} | ||
|
||
} |
44 changes: 44 additions & 0 deletions
44
Sources/InstantSearchCore/QueryRuleCustomData/QueryRuleCustomDataInteractor.swift
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,44 @@ | ||
// | ||
// QueryRuleCustomDataInteractor.swift | ||
// | ||
// | ||
// Created by Vladislav Fitc on 10/10/2020. | ||
// | ||
|
||
import Foundation | ||
|
||
/// Component encapsulating the logic applied to the custom model | ||
public class QueryRuleCustomDataInteractor<Model: Decodable>: ItemInteractor<Model?> { | ||
|
||
public override init(item: Model? = nil) { | ||
super.init(item: item) | ||
} | ||
|
||
} | ||
|
||
extension QueryRuleCustomDataInteractor { | ||
|
||
func extractModel(from searchResponse: SearchResponse) { | ||
if let userData = searchResponse.userData, | ||
let model = userData.compactMap({ try? Model(json: $0) }).first { | ||
item = model | ||
} else { | ||
item = nil | ||
} | ||
} | ||
|
||
} | ||
|
||
public extension QueryRuleCustomDataInteractor { | ||
|
||
/** | ||
Establishes a connection with the controller | ||
- Parameters: | ||
- controller: Controller interfacing with a concrete custom data view | ||
- Returns: Established connection | ||
*/ | ||
@discardableResult func connectController<Controller: ItemController>(_ controller: Controller) -> ItemInteractor<Model?>.ControllerConnection<Controller, Model?> { | ||
super.connectController(controller, presenter: { $0 }) | ||
} | ||
|
||
} |
Oops, something went wrong.