Commit 4cf0cf20 by Dmitriy Stepanets

Finished graph tint logic

parent be44c3c4
......@@ -240,18 +240,10 @@
path = CityConditions;
sourceTree = "<group>";
};
CDA69B2E2575008700CB6409 /* Models */ = {
isa = PBXGroup;
children = (
);
path = Models;
sourceTree = "<group>";
};
CDD0F1DC2572400200CF5017 /* UI */ = {
isa = PBXGroup;
children = (
CD15DB3B25DA6C2800024727 /* Controls */,
CDA69B2E2575008700CB6409 /* Models */,
CD6B3039257267FB004B34B3 /* Buttons */,
CD6B3038257267E2004B34B3 /* View controllers */,
CDD0F1F025725BD700CF5017 /* Helpers */,
......
......@@ -7,7 +7,7 @@
<key>1Weather.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>5</integer>
<integer>3</integer>
</dict>
<key>PG (Playground) 1.xcscheme</key>
<dict>
......
......@@ -9,21 +9,21 @@
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>1</integer>
<integer>4</integer>
</dict>
<key>PG (Playground) 2.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>2</integer>
<integer>6</integer>
</dict>
<key>PG (Playground) 3.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>3</integer>
<integer>7</integer>
</dict>
<key>PG (Playground) 4.xcscheme</key>
<dict>
......@@ -51,7 +51,7 @@
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>0</integer>
<integer>4</integer>
</dict>
</dict>
</dict>
......
......@@ -6,14 +6,18 @@
//
import UIKit
import BezierKit
class GraphView: UIView {
//Private
private let kIntersectAccuracy:CGFloat = 2
private let kDotRadius:CGFloat = 3
private let graphColor:UIColor
private let graphTintColor:UIColor
private let lineShape = CAShapeLayer()
private let cubicCurveAlgorithm = CubicCurveAlgorithm()
private var sections = [CubicCurve]()
private var currentPoints = [CGPoint]()
init(graphColor:UIColor, tintColor:UIColor) {
self.graphColor = graphColor
......@@ -31,6 +35,8 @@ class GraphView: UIView {
self.layer.sublayers?.forEach {
$0.removeFromSuperlayer()
}
sections.removeAll()
currentPoints.removeAll()
guard !points.isEmpty else { return }
......@@ -57,10 +63,85 @@ class GraphView: UIView {
layer.insertSublayer(lineShape, at: 0)
}
public func tintGraph(at startPoint:CGPoint, width:CGFloat) {
public func tintGraphFrom(startPointX:CGFloat, endPointX:CGFloat) {
func getSubcurvePath(baseCurve:CubicCurve, leftBoundary:LineSegment, rightBoundary:LineSegment) -> UIBezierPath? {
guard
let leftIntersection = baseCurve.intersections(with: leftBoundary).first,
let rightIntersection = baseCurve.intersections(with: rightBoundary).first
else {
return nil
}
let subcurve = baseCurve.split(from: leftIntersection.t1, to: rightIntersection.t1)
let path = UIBezierPath()
path.move(to: subcurve.startingPoint)
path.addCurve(to: subcurve.endingPoint, controlPoint1: subcurve.p1, controlPoint2: subcurve.p2)
return path
}
let leftLine = LineSegment(p0: .init(x: startPointX + kIntersectAccuracy, y: 0),
p1: .init(x: startPointX + kIntersectAccuracy, y: self.bounds.height))
let rightLine = LineSegment(p0: .init(x: endPointX - kIntersectAccuracy, y: 0),
p1: .init(x: endPointX - kIntersectAccuracy, y: self.bounds.height))
//Get all sections for the given tint
var intersectSections = [CubicCurve]()
for section in sections {
if section.startingPoint.x >= leftLine.p0.x || section.endingPoint.x <= rightLine.p0.x {
intersectSections.append(section)
}
// if section.intersects(leftLine) && section.intersects(rightLine) {
// intersectSections.append(section)
// return
// }
//
// if section.intersects(leftLine) || section.intersects(rightLine) {
// intersectSections.append(section)
// }
}
let tintPath = UIBezierPath()
for intersectSection in intersectSections {
let leftBoundary = LineSegment(p0: .init(x: max(intersectSection.startingPoint.x, leftLine.startingPoint.x), y: 0),
p1: .init(x: max(intersectSection.startingPoint.x, leftLine.startingPoint.x), y: self.bounds.height))
let rightBoundary = LineSegment(p0: .init(x: min(intersectSection.endingPoint.x, rightLine.endingPoint.x), y: 0),
p1: .init(x: min(intersectSection.endingPoint.x, rightLine.endingPoint.x), y: self.bounds.height))
guard let subcurve = getSubcurvePath(baseCurve: intersectSection, leftBoundary: leftBoundary, rightBoundary: rightBoundary) else {
continue
}
tintPath.append(subcurve)
}
// let leftIntersection = curves[0].intersections(with: leftLine)
// let rightIntersection = curves[1].intersections(with: rightLine)
//
// let leftPoint = curves[0].point(at: leftIntersection[0].t1)
// let rightPoint = curves[1].point(at: rightIntersection[0].t1)
// addDot(point: leftPoint)
// addDot(point: rightPoint)
//
// guard
// let leftSubcurvePath = getSubcurvePath(baseCurve: curves[0], leftBoundary: leftLine, rightBoundary: centerLine),
// let rightSubcurvePath = getSubcurvePath(baseCurve: curves[1], leftBoundary: centerLine, rightBoundary: rightLine)
// else {
// return
// }
//
// let tintPath = UIBezierPath()
// tintPath.append(leftSubcurvePath)
// tintPath.append(rightSubcurvePath)
//
let tintShape = CAShapeLayer()
let path = UIBezierPath()
path.move(to: startPoint)
tintShape.path = tintPath.cgPath
tintShape.fillColor = UIColor.clear.cgColor
tintShape.strokeColor = UIColor.black.cgColor
tintShape.lineWidth = 3
tintShape.lineCap = .round
tintShape.lineJoin = .round
layer.addSublayer(tintShape)
}
//Private
......@@ -69,20 +150,24 @@ class GraphView: UIView {
let startPoint = CGPoint(x: 0, y: self.frame.height)
let endPoint = CGPoint(x: self.frame.width, y: self.frame.height)
var pointsToAdd = [CGPoint]()
pointsToAdd.append(startPoint)
pointsToAdd.append(contentsOf: points)
pointsToAdd.append(endPoint)
currentPoints.append(startPoint)
currentPoints.append(contentsOf: points)
currentPoints.append(endPoint)
path.move(to: pointsToAdd.first!)
if pointsToAdd.count == 2 {
path.move(to: currentPoints.first!)
if currentPoints.count == 2 {
path.addLine(to: points[1])
return path
}
let controlPoints = cubicCurveAlgorithm.controlPointsFromPoints(dataPoints: pointsToAdd)
for index in 1..<pointsToAdd.count {
path.addCurve(to: pointsToAdd[index],
let controlPoints = cubicCurveAlgorithm.controlPointsFromPoints(dataPoints: currentPoints)
for index in 1..<currentPoints.count {
sections.append(.init(p0: currentPoints[index - 1],
p1: controlPoints[index - 1].controlPoint1,
p2: controlPoints[index - 1].controlPoint2,
p3: currentPoints[index]))
path.addCurve(to: currentPoints[index],
controlPoint1: controlPoints[index - 1].controlPoint1,
controlPoint2: controlPoints[index - 1].controlPoint2)
}
......
......@@ -49,6 +49,8 @@ class CityForecastTimePeriodCell: UITableViewCell {
//Draw points and lines
self.graphView.drawGraph(with: self.getGraphPoints())
graphView.tintGraphFrom(startPointX: 100,
endPointX: scrollView.contentSize.width - 150)
}
private func getGraphPoints() -> [CGPoint] {
......
......@@ -6,7 +6,6 @@
//
import UIKit
import Synth
private enum TodayTableCell {
case forecast
......
......@@ -7,6 +7,5 @@ target '1Weather' do
# Pods for 1Weather
pod 'SnapKit'
pod 'synth-ios'
pod 'AlgoliaSearchClient'
pod 'BezierKit'
end
PODS:
- AlgoliaSearchClient (8.6.0):
- Logging
- Logging (1.4.0)
- BezierKit (0.10.0)
- SnapKit (5.0.1)
- synth-ios (1.0.0)
DEPENDENCIES:
- AlgoliaSearchClient
- BezierKit
- SnapKit
- synth-ios
SPEC REPOS:
trunk:
- AlgoliaSearchClient
- Logging
- BezierKit
- SnapKit
- synth-ios
SPEC CHECKSUMS:
AlgoliaSearchClient: 36cb507cbf100def2d028f479eb8f194cb37c604
Logging: beeb016c9c80cf77042d62e83495816847ef108b
BezierKit: 1828aca1675d68f0659c5353bc5b0d3399a3910c
SnapKit: 97b92857e3df3a0c71833cce143274bf6ef8e5eb
synth-ios: b91d9176ecc7ff791d94df9aedafbf5a8c00f965
PODFILE CHECKSUM: e1d8b684b1d845d76a34a4f0b3756d265fdecbf9
PODFILE CHECKSUM: 9a72121885af918e1ac484fb10d514827acb4fbc
COCOAPODS: 1.10.1
<p align="center">
<a href="https://www.algolia.com">
<img alt="Algolia for Swift" src="banner.png" >
</a>
<h4 align="center">The perfect starting point to integrate <a href="https://algolia.com" target="_blank">Algolia</a> within your Swift project</h4>
<p align="center">
<a href="https://cocoapods.org/pods/AlgoliaSearchClient">
<img src="https://app.bitrise.io/app/6dcd3d9dd961c466/status.svg?token=q1GX8YovgWTvPx7Ueu77JQ&branch=develop"></img>
</a>
<a href="https://cocoapods.org/pods/AlgoliaSearchClient">
<img src="http://img.shields.io/cocoapods/v/AlgoliaSearchClient.svg?style=flat"></img>
</a>
<a href="https://cocoapods.org/pods/AlgoliaSearchClient">
<img src="https://img.shields.io/badge/platform-macOS%20%7C%20iOS%20%7C%20tvOS%20%7C%20watchOS%20%7C%20Linux%20-lightgray.svg?style=flat"></img>
</a>
<a href="https://github.com/Carthage/Carthage">
<img src="https://img.shields.io/badge/Carthage-compatible-brightgreen.svg"></img>
</a>
<a href="https://developer.apple.com/documentation/xcode/creating_a_mac_version_of_your_ipad_app/">
<img src="https://img.shields.io/badge/Catalyst-compatible-brightgreen.svg"></img>
</a>
<a href="https://opensource.org/licenses/MIT">
<img src="https://img.shields.io/badge/License-MIT-yellow.svg"></img>
</a>
</p>
</p>
<p align="center">
<a href="https://www.algolia.com/doc/api-client/getting-started/install/swift/" target="_blank">Documentation</a>
<a href="https://discourse.algolia.com" target="_blank">Community Forum</a>
<a href="http://stackoverflow.com/questions/tagged/algolia" target="_blank">Stack Overflow</a>
<a href="https://github.com/algolia/algoliasearch-client-swift/issues" target="_blank">Report a bug</a>
<a href="https://www.algolia.com/doc/api-client/troubleshooting/faq/swift/" target="_blank">FAQ</a>
<a href="https://www.algolia.com/support" target="_blank">Support</a>
</p>
## ✨ Features
- Pure cross-platform Swift client
- Typed requests and responses
- Widespread use of `Result` type
- Uses the power of `Codable` protocol for easy integration of your domain models
- Thread-safe clients
- Detailed logging
- Injectable HTTP client
## Install
### Swift Package Manager
The Swift Package Manager is a tool for managing the distribution of Swift code. It’s integrated with the Swift build system to automate the process of downloading, compiling, and linking dependencies.
Since the release of Swift 5 and Xcode 11, SPM is compatible with the iOS, macOS and tvOS build systems for creating apps.
To use SwiftPM, you should use Xcode 11 to open your project. Click `File` -> `Swift Packages` -> `Add Package Dependency`, enter [InstantSearch repo's URL](https://github.com/algolia/algoliasearch-client-swift).
If you're a framework author and use Swift API Client as a dependency, update your `Package.swift` file:
```swift
let package = Package(
// 8.5.0 ..< 9.0.0
dependencies: [
.package(url: "https://github.com/algolia/algoliasearch-client-swift", from: "8.5.0")
],
// ...
)
```
Add `import AlgoliaSearchClient` to your source files.
### Cocoapods
[CocoaPods](https://cocoapods.org/) is a dependency manager for Cocoa projects.
To install Algolia Swift Client, simply add the following line to your Podfile:
```ruby
pod 'AlgoliaSearchClient', '~> 8.5'
# pod 'InstantSearchClient', '~> 6.0'` // Swift 4.2
# pod 'InstantSearchClient', '~> 5.0'` // Swift 4.1
```
Then, run the following command:
```bash
$ pod update
```
### Carthage
[Carthage](https://github.com/Carthage/Carthage) is a simple, decentralized dependency manager for Cocoa.
- To install InstantSearch, simply add the following line to your Cartfile:
```ruby
github "algolia/algoliasearch-client-swift" ~> 8.5
# github "algolia/algoliasearch-client-swift" ~> 6.0.0` // Swift 4.2
# github "algolia/algoliasearch-client-swift" ~> 5.0.0` // Swift 4.1
```
- Launch the following commands from the project directory (for v.8.0+)
```shell
carthage update
./Carthage/Checkouts/algoliasearch-client-swift/carthage-prebuild
carthage build
```
If this is your first time using Carthage in the project, you'll need to go through some additional steps as explained [over at Carthage](https://github.com/Carthage/Carthage#adding-frameworks-to-an-application).
## 💡 Getting Started
### Initialize the client
To start, you need to initialize the client. To do this, you need your **Application ID** and **API Key**.
You can find both on [your Algolia account](https://www.algolia.com/api-keys).
```swift
let client = Client(appID: "YourApplicationID", apiKey: "YourAdminAPIKey")
let index = client.index(withName: "your_index_name")
```
### Push data
Without any prior configuration, you can start indexing contacts in the `contacts` index using the following code:
```swift
struct Contact: Encodable {
let firstname: String
let lastname: String
let followers: Int
let company: String
}
let contacts: [Contact] = [
.init(firstname: "Jimmie", lastname: "Barninger", followers: 93, company: "California Paint"),
.init(firstname: "Warren", lastname: "Speach", followers: 42, company: "Norwalk Crmc")
]
let index = client.index(withName: "contacts")
index.saveObjects(contacts, autoGeneratingObjectID: true) { result in
if case .success(let response) = result {
print("Response: \(response)")
}
}
```
### Search
You can now search for contacts by `firstname`, `lastname`, `company`, etc. (even with typos):
```swift
index.search(query: "jimmie") { result in
switch result {
case .failure(let error):
print("Error: \(error)")
case .success(let response):
print("Response: \(response)")
}
}
```
### Configure
Settings can be customized to tune the search behavior. For example, you can add a custom sort by number of followers to the already great built-in relevance:
```swift
let settings = Settings()
.set(\.customRanking, to: [.desc("followers")])
index.setSettings(settings) { result in
if case .failure(let error) = result {
print("Error when applying settings: \(error)")
}
}
```
You can also configure the list of attributes you want to index by order of importance (first = most important):
**Note:** Since the engine is designed to suggest results as you type, you'll generally search by prefix.
In this case the order of attributes is very important to decide which hit is the best:
```swift
let settings = Settings()
.set(\.searchableAttributes, to: ["lastname", "firstname", "company"])
index.setSettings(settings) { result in
if case .failure(let error) = result {
print("Error when applying settings: \(error)")
}
}
```
For full documentation, visit the [Algolia Swift API Client's documentation](https://www.algolia.com/doc/api-client/getting-started/install/swift/).
## 📝 Examples
You can find code samples in the [Algolia's API Clients playground](https://github.com/algolia/api-clients-playground/tree/master/swift).
## Use the Dockerfile
If you want to contribute to this project without installing all its dependencies, you can use our Docker image. Please check our [dedicated guide](DOCKER_README.MD) to learn more.
## 📄 License
Algolia Swift API Client is an open-sourced software licensed under the [MIT license](LICENSE).
## Notes
### Objective-C support
The Swift API client is compatible with Objective-C up to version 7.0.5. Please use this version of the client if you're working with an Objective-C project.
### Swift 3
You can use this library with Swift by one of the following ways:
- `pod 'AlgoliaSearch-Client-Swift', '~> 4.8.1'`
- `pod 'AlgoliaSearch-Client-Swift', :git => 'https://github.com/algolia/algoliasearch-client-swift.git', :branch => 'swift-3'`
## Getting Help
- **Need help**? Ask a question to the [Algolia Community](https://discourse.algolia.com/) or on [Stack Overflow](http://stackoverflow.com/questions/tagged/algolia).
- **Encountering an issue?** Before reaching out to support, we recommend heading to our [FAQ](https://www.algolia.com/doc/api-client/troubleshooting/faq/swift/) where you will find answers for the most common issues and gotchas with the client.
- **Found a bug?** You can open a [GitHub issue](https://github.com/algolia/algoliasearch-client-swift/issues).
//
// AsyncOperation.swift
//
//
// Created by Vladislav Fitc on 02/03/2020.
//
import Foundation
open class AsyncOperation: Operation {
public enum State: String {
case ready, executing, finished
fileprivate var keyPath: String {
return "is" + rawValue.capitalized
}
}
public var state = State.ready {
willSet {
willChangeValue(forKey: newValue.keyPath)
willChangeValue(forKey: state.keyPath)
}
didSet {
didChangeValue(forKey: oldValue.keyPath)
didChangeValue(forKey: state.keyPath)
}
}
// NSOperation Overrides
override open var isReady: Bool {
return super.isReady && state == .ready
}
override open var isExecuting: Bool {
return state == .executing
}
override open var isFinished: Bool {
return state == .finished
}
override open var isAsynchronous: Bool {
return true
}
override open func start() {
if isCancelled {
state = .finished
return
}
main()
state = .executing
}
open override func cancel() {
state = .finished
}
}
//
// WaitTask.swift
//
//
// Created by Vladislav Fitc on 10.03.2020.
//
import Foundation
class WaitTask: AsyncOperation, ResultContainer {
typealias TaskStatusService = (RequestOptions?, @escaping ResultCallback<TaskInfo>) -> Void
let taskStatusService: TaskStatusService
let requestOptions: RequestOptions?
let timeout: TimeInterval?
private var launchDate: Date?
let completion: (ResultCallback<TaskStatus>)
var result: Result<TaskStatus, Swift.Error> = .failure(SyncOperationError.notFinished) {
didSet {
completion(result)
state = .finished
}
}
var isTimeout: Bool {
guard let timeout = timeout, let launchDate = launchDate else {
return false
}
return Date().timeIntervalSince(launchDate) >= timeout
}
init(taskStatusService: @escaping TaskStatusService,
timeout: TimeInterval? = nil,
requestOptions: RequestOptions?,
completion: @escaping ResultCallback<TaskStatus>) {
self.taskStatusService = taskStatusService
self.timeout = timeout
self.requestOptions = requestOptions
self.completion = completion
}
override func main() {
launchDate = Date()
checkStatus()
}
private func checkStatus() {
guard !isTimeout else {
result = .failure(Error.timeout)
return
}
taskStatusService(requestOptions) { [weak self] result in
guard let request = self else { return }
switch result {
case .success(let taskStatus):
switch taskStatus.status {
case .published:
request.result = .success(taskStatus.status)
default:
sleep(1)
request.checkStatus()
}
case .failure(let error):
request.result = .failure(error)
}
}
}
enum Error: Swift.Error {
case timeout
}
}
extension WaitTask {
convenience init(index: Index,
taskID: TaskID,
timeout: TimeInterval? = nil,
requestOptions: RequestOptions?,
completion: @escaping ResultCallback<TaskStatus>) {
self.init(taskStatusService: { requestOptions, completion in index.taskStatus(for: taskID, requestOptions: requestOptions, completion: completion) },
timeout: timeout,
requestOptions: requestOptions,
completion: completion)
}
convenience init(client: Client,
taskID: AppTaskID,
timeout: TimeInterval? = nil,
requestOptions: RequestOptions?,
completion: @escaping ResultCallback<TaskStatus>) {
self.init(taskStatusService: { requestOptions, completion in client.taskStatus(for: taskID, requestOptions: requestOptions, completion: completion) },
timeout: timeout,
requestOptions: requestOptions,
completion: completion)
}
}
//
// AccountClient.swift
//
//
// Created by Vladislav Fitc on 02/07/2020.
//
import Foundation
/// Client to perform operations between applications.
public struct AccountClient {
static var operationLauncher: OperationLauncher = {
let queue = OperationQueue()
queue.qualityOfService = .userInitiated
return OperationLauncher(queue: queue)
}()
// MARK: - Copy Index
/**
Copy settings, synonyms, rules and objects from the source index to the destination index.
- Parameter source: source Index
- Parameter destination: destination Index
- Parameter requestOptions: Configure request locally with RequestOptions
- Returns: WaitableWrapper objects embedding all the tasks created while copying
- Throws: AccountClient.Error.sameApplicationID if source and destination have the same ApplicationID.
AccountClient.Error.sourceNotFound if source doesnt exist
AccountClient.Error.existingDestination if destination index already exists.
*/
@discardableResult public static func copyIndex(source: Index,
destination: Index,
requestOptions: RequestOptions? = nil,
completion: @escaping (Result<WaitableWrapper<[Task]>, Swift.Error>) -> Void) throws -> Operation {
let operation = BlockOperation {
completion(.init { try AccountClient.copyIndex(source: source, destination: destination, requestOptions: requestOptions) })
}
return operationLauncher.launch(operation)
}
/**
Copy settings, synonyms, rules and objects from the source index to the destination index.
- Parameter source: source Index
- Parameter destination: destination Index
- Parameter requestOptions: Configure request locally with RequestOptions
- Returns: WaitableWrapper objects embedding all the tasks created while copying
- Throws: AccountClient.Error.sameApplicationID if source and destination have the same ApplicationID.
AccountClient.Error.sourceNotFound if source doesnt exist
AccountClient.Error.existingDestination if destination index already exists.
*/
@discardableResult public static func copyIndex(source: Index,
destination: Index,
requestOptions: RequestOptions? = nil) throws -> WaitableWrapper<[Task]> {
guard source.applicationID != destination.applicationID else {
throw Error.sameApplicationID
}
guard try source.exists() else {
throw Error.sourceNotFound
}
guard try !destination.exists() else {
throw Error.existingDestination
}
let objects = try source.browseObjects().flatMap(\.hits).map(\.object)
let synonyms = try source.browseSynonyms().flatMap(\.hits).map(\.synonym)
let rules = try source.browseRules().flatMap(\.hits).map(\.rule)
let settings = try source.getSettings()
let waitObjects = try destination.saveObjects(objects)
let waitSynonyms = try destination.saveSynonyms(synonyms)
let waitRules = try destination.saveRules(rules)
let waitSettings = try destination.setSettings(settings)
let tasks: [Task] = [
waitSynonyms,
waitRules,
waitSettings
].map(\.task) + waitObjects.batchesResponse.tasks
return WaitableWrapper(tasks: tasks, index: destination)
}
}
extension AccountClient {
public enum Error: Swift.Error {
case sourceNotFound
case existingDestination
case sameApplicationID
}
}
//
// InsightsClient.swift
//
//
// Created by Vladislav Fitc on 23/04/2020.
//
import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif
public struct InsightsClient: Credentials {
let transport: Transport
let operationLauncher: OperationLauncher
let configuration: Configuration
public var applicationID: ApplicationID {
return transport.applicationID
}
public var apiKey: APIKey {
return transport.apiKey
}
public init(appID: ApplicationID, apiKey: APIKey, region: Region? = nil) {
let configuration = InsightsConfiguration(applicationID: appID, apiKey: apiKey, region: region)
let sessionConfiguration: URLSessionConfiguration = .default
sessionConfiguration.httpAdditionalHeaders = configuration.defaultHeaders
let session = URLSession(configuration: sessionConfiguration)
self.init(configuration: configuration, requester: session)
}
public init(configuration: InsightsConfiguration, requester: HTTPRequester) {
let queue = OperationQueue()
queue.qualityOfService = .userInitiated
let operationLauncher = OperationLauncher(queue: queue)
let retryStrategy = AlgoliaRetryStrategy(configuration: configuration)
let httpTransport = HTTPTransport(requester: requester,
configuration: configuration,
retryStrategy: retryStrategy,
credentials: configuration,
operationLauncher: operationLauncher)
self.init(transport: httpTransport, operationLauncher: operationLauncher, configuration: configuration)
}
init(transport: Transport,
operationLauncher: OperationLauncher,
configuration: Configuration) {
self.transport = transport
self.operationLauncher = operationLauncher
self.configuration = configuration
}
}
extension InsightsClient: TransportContainer {}
public extension InsightsClient {
// MARK: - Send event
/**
- Parameter requestOptions: Configure request locally with RequestOptions
- Parameter completion: Result completion
- Returns: Launched asynchronous operation
*/
@discardableResult func sendEvent(_ event: InsightsEvent, requestOptions: RequestOptions? = nil, completion: @escaping ResultCallback<Empty>) -> Operation {
return sendEvents([event], requestOptions: requestOptions, completion: completion)
}
/**
- Parameter requestOptions: Configure request locally with RequestOptions
- Returns: JSON object
*/
@discardableResult func sendEvent(_ event: InsightsEvent, requestOptions: RequestOptions? = nil) throws -> Empty {
return try sendEvents([event], requestOptions: requestOptions)
}
// MARK: - Send events
/**
- Parameter requestOptions: Configure request locally with RequestOptions
- Parameter completion: Result completion
- Returns: Launched asynchronous operation
*/
@discardableResult func sendEvents(_ events: [InsightsEvent], requestOptions: RequestOptions? = nil, completion: @escaping ResultCallback<Empty>) -> Operation {
let command = Command.Insights.SendEvents(events: events, requestOptions: requestOptions)
return execute(command, completion: completion)
}
/**
- Parameter requestOptions: Configure request locally with RequestOptions
- Returns: JSON object
*/
@discardableResult func sendEvents(_ events: [InsightsEvent], requestOptions: RequestOptions? = nil) throws -> Empty {
let command = Command.Insights.SendEvents(events: events, requestOptions: requestOptions)
return try execute(command)
}
}
//
// PlacesClient.swift
//
//
// Created by Vladislav Fitc on 10/04/2020.
//
import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif
public struct PlacesClient: Credentials {
let transport: Transport
let operationLauncher: OperationLauncher
let configuration: Configuration
public var applicationID: ApplicationID {
return transport.applicationID
}
public var apiKey: APIKey {
return transport.apiKey
}
public init(appID: ApplicationID, apiKey: APIKey) {
let configuration = PlacesConfiguration(applicationID: appID, apiKey: apiKey)
let sessionConfiguration: URLSessionConfiguration = .default
sessionConfiguration.httpAdditionalHeaders = configuration.defaultHeaders
let session = URLSession(configuration: sessionConfiguration)
self.init(configuration: configuration, requester: session)
}
public init(configuration: PlacesConfiguration, requester: HTTPRequester) {
let queue = OperationQueue()
queue.qualityOfService = .userInitiated
let operationLauncher = OperationLauncher(queue: queue)
let retryStrategy = AlgoliaRetryStrategy(configuration: configuration)
let httpTransport = HTTPTransport(requester: requester,
configuration: configuration,
retryStrategy: retryStrategy,
credentials: configuration,
operationLauncher: operationLauncher)
self.init(transport: httpTransport, operationLauncher: operationLauncher, configuration: configuration)
}
init(transport: Transport,
operationLauncher: OperationLauncher,
configuration: Configuration) {
self.transport = transport
self.operationLauncher = operationLauncher
self.configuration = configuration
}
public typealias SingleLanguageResponse = PlacesResponse<Hit<Place>>
public typealias MultiLanguageResponse = PlacesResponse<Hit<MultiLanguagePlace>>
}
extension PlacesClient: TransportContainer {}
public extension PlacesClient {
// MARK: - Search
/**
- Parameter query: The PlacesQuery used to search.
- Parameter requestOptions: Configure request locally with RequestOptions
- Parameter completion: Result completion
- Returns: Launched asynchronous operation
*/
@discardableResult func search(query: PlacesQuery, requestOptions: RequestOptions? = nil, completion: @escaping ResultCallback<MultiLanguageResponse>) -> Operation {
let command = Command.Places.Search(query: query, requestOptions: requestOptions)
return execute(command, completion: completion)
}
/**
- Parameter query: The PlacesQuery used to search.
- Parameter requestOptions: Configure request locally with RequestOptions
- Returns: PlacesResponse<MultiLanguagePlace> object
*/
@discardableResult func search(query: PlacesQuery, requestOptions: RequestOptions? = nil) throws -> MultiLanguageResponse {
let command = Command.Places.Search(query: query, requestOptions: requestOptions)
return try execute(command)
}
// MARK: - Search Multilanguage
/**
- Parameter query: The PlacesQuery used to search.
- Parameter language: The language used to specialize the results localization
- Parameter requestOptions: Configure request locally with RequestOptions
- Parameter completion: Result completion
- Returns: Launched asynchronous operation
*/
@discardableResult func search(query: PlacesQuery, language: Language, requestOptions: RequestOptions? = nil, completion: @escaping ResultCallback<SingleLanguageResponse>) -> Operation {
let query = query.set(\.language, to: language)
let command = Command.Places.Search(query: query, requestOptions: requestOptions)
return execute(command, completion: completion)
}
/**
- Parameter query: The PlacesQuery used to search.
- Parameter language: The language used to specialize the results localization
- Parameter requestOptions: Configure request locally with RequestOptions
- Returns: PlacesResponse<MultiLanguagePlace> object
*/
@discardableResult func search(query: PlacesQuery, language: Language, requestOptions: RequestOptions? = nil) throws -> SingleLanguageResponse {
let query = query.set(\.language, to: language)
let command = Command.Places.Search(query: query, requestOptions: requestOptions)
return try execute(command)
}
// MARK: - Get object
/**
- Parameter objectID: The ObjectID to identify the record.
- Parameter requestOptions: Configure request locally with RequestOptions
- Parameter completion: Result completion
- Returns: Launched asynchronous operation
*/
@discardableResult func getObject(withID objectID: ObjectID, requestOptions: RequestOptions? = nil, completion: @escaping ResultCallback<Hit<MultiLanguagePlace>>) -> Operation {
let command = Command.Places.GetObject(objectID: objectID, requestOptions: requestOptions)
return execute(command, completion: completion)
}
/**
- Parameter objectID: The ObjectID to identify the record.
- Parameter requestOptions: Configure request locally with RequestOptions
- Returns: MultiLanguagePlace object
*/
@discardableResult func getObject(withID objectID: ObjectID, requestOptions: RequestOptions? = nil) throws -> Hit<MultiLanguagePlace> {
let command = Command.Places.GetObject(objectID: objectID, requestOptions: requestOptions)
return try execute(command)
}
// MARK: - Reverse geocoding
/**
- Parameter geolocation:
- Parameter hitsPerPage: Specify the maximum number of entries to retrieve starting at the page.
- Parameter language: The language used to specialize the results localization
- Parameter requestOptions: Configure request locally with RequestOptions
- Parameter completion: Result completion
- Returns: Launched asynchronous operation
*/
@discardableResult func reverseGeocoding(geolocation: Point, hitsPerPage: Int? = nil, language: Language, requestOptions: RequestOptions? = nil, completion: @escaping ResultCallback<SingleLanguageResponse>) -> Operation {
let command = Command.Places.ReverseGeocoding(geolocation: geolocation, language: language, hitsPerPage: hitsPerPage, requestOptions: requestOptions)
return execute(command, completion: completion)
}
/**
- Parameter geolocation:
- Parameter hitsPerPage: Specify the maximum number of entries to retrieve starting at the page.
- Parameter language: The language used to specialize the results localization
- Parameter requestOptions: Configure request locally with RequestOptions
- Returns: PlacesResponse<Hit<Place>> object
*/
@discardableResult func reverseGeocoding(geolocation: Point, hitsPerPage: Int? = nil, language: Language, requestOptions: RequestOptions? = nil) throws -> SingleLanguageResponse {
let command = Command.Places.ReverseGeocoding(geolocation: geolocation, language: language, hitsPerPage: hitsPerPage, requestOptions: requestOptions)
return try execute(command)
}
// MARK: - Reverse geocoding multilanguage
/**
- Parameter geolocation:
- Parameter hitsPerPage: Specify the maximum number of entries to retrieve starting at the page.
- Parameter requestOptions: Configure request locally with RequestOptions
- Parameter completion: Result completion
- Returns: Launched asynchronous operation
*/
@discardableResult func reverseGeocoding(geolocation: Point, hitsPerPage: Int? = nil, requestOptions: RequestOptions? = nil, completion: @escaping ResultCallback<MultiLanguageResponse>) -> Operation {
let command = Command.Places.ReverseGeocoding(geolocation: geolocation, language: nil, hitsPerPage: hitsPerPage, requestOptions: requestOptions)
return execute(command, completion: completion)
}
/**
- Parameter geolocation:
- Parameter hitsPerPage: Specify the maximum number of entries to retrieve starting at the page.
- Parameter requestOptions: Configure request locally with RequestOptions
- Returns: PlacesResponse<Hit<MultiLanguagePlace>> object
*/
@discardableResult func reverseGeocoding(geolocation: Point, hitsPerPage: Int? = nil, requestOptions: RequestOptions? = nil) throws -> MultiLanguageResponse {
let command = Command.Places.ReverseGeocoding(geolocation: geolocation, language: nil, hitsPerPage: hitsPerPage, requestOptions: requestOptions)
return try execute(command)
}
}
//
// RecommendationClient.swift
//
//
// Created by Vladislav Fitc on 27/05/2020.
//
import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif
public struct RecommendationClient: Credentials {
let transport: Transport
let operationLauncher: OperationLauncher
let configuration: Configuration
public var applicationID: ApplicationID {
return transport.applicationID
}
public var apiKey: APIKey {
return transport.apiKey
}
public init(appID: ApplicationID, apiKey: APIKey, region: Region? = nil) {
let configuration = RecommendationConfiguration(applicationID: appID, apiKey: apiKey, region: region)
let sessionConfiguration: URLSessionConfiguration = .default
sessionConfiguration.httpAdditionalHeaders = configuration.defaultHeaders
let session = URLSession(configuration: sessionConfiguration)
self.init(configuration: configuration, requester: session)
}
public init(configuration: RecommendationConfiguration, requester: HTTPRequester) {
let queue = OperationQueue()
queue.qualityOfService = .userInitiated
let operationLauncher = OperationLauncher(queue: queue)
let retryStrategy = AlgoliaRetryStrategy(configuration: configuration)
let httpTransport = HTTPTransport(requester: requester,
configuration: configuration,
retryStrategy: retryStrategy,
credentials: configuration,
operationLauncher: operationLauncher)
self.init(transport: httpTransport, operationLauncher: operationLauncher, configuration: configuration)
}
init(transport: Transport,
operationLauncher: OperationLauncher,
configuration: Configuration) {
self.transport = transport
self.operationLauncher = operationLauncher
self.configuration = configuration
}
}
extension RecommendationClient: TransportContainer {}
public extension RecommendationClient {
// MARK: - Set personalization strategy
/**
Configures the personalization strategy
- Parameter personalizationStrategy: PersonalizationStrategy to set
- Parameter requestOptions: Configure request locally with RequestOptions
- Parameter completion: Result completion
- Returns: Launched asynchronous operation
*/
@discardableResult func setPersonalizationStrategy(_ personalizationStrategy: PersonalizationStrategy,
requestOptions: RequestOptions? = nil,
completion: @escaping ResultCallback<SetStrategyResponse>) -> Operation {
let command = Command.Personalization.Set(strategy: personalizationStrategy, requestOptions: requestOptions)
return execute(command, completion: completion)
}
/**
Configures the personalization strategy
- Parameter personalizationStrategy: PersonalizationStrategy to set
- Parameter requestOptions: Configure request locally with RequestOptions
- Returns: Revision object
*/
@discardableResult func setPersonalizationStrategy(_ personalizationStrategy: PersonalizationStrategy,
requestOptions: RequestOptions? = nil) throws -> SetStrategyResponse {
let command = Command.Personalization.Set(strategy: personalizationStrategy, requestOptions: requestOptions)
return try execute(command)
}
// MARK: - Get personalization strategy
/**
Returns the personalization strategy of the application
- Parameter requestOptions: Configure request locally with RequestOptions
- Parameter completion: Result completion
- Returns: Launched asynchronous operation
*/
@discardableResult func getPersonalizationStrategy(requestOptions: RequestOptions? = nil,
completion: @escaping ResultCallback<PersonalizationStrategy>) -> Operation {
let command = Command.Personalization.Get(requestOptions: requestOptions)
return execute(command, completion: completion)
}
/**
Returns the personalization strategy of the application
- Parameter requestOptions: Configure request locally with RequestOptions
- Returns: PersonalizationStrategy object
*/
@discardableResult func getPersonalizationStrategy(requestOptions: RequestOptions? = nil) throws -> PersonalizationStrategy {
let command = Command.Personalization.Get(requestOptions: requestOptions)
return try execute(command)
}
}
//
// SearchClient+APIKey.swift
//
//
// Created by Vladislav Fitc on 09/04/2020.
//
import Foundation
public extension SearchClient {
// MARK: - Add API key
/**
Add a new APIKey.
- Parameter parameters: permissions/restrictions specified by APIKeyParams
- Parameter requestOptions: Configure request locally with RequestOptions
- Parameter completion: Result completion
- Returns: Launched asynchronous operation
*/
@discardableResult func addAPIKey(with parameters: APIKeyParameters,
requestOptions: RequestOptions? = nil,
completion: @escaping ResultCallback<APIKeyCreation>) -> Operation {
let command = Command.APIKey.Add(parameters: parameters, requestOptions: requestOptions)
return execute(command, completion: completion)
}
/**
Add a new APIKey.
- Parameter parameters: permissions/restrictions specified by APIKeyParams
- Parameter requestOptions: Configure request locally with RequestOptions
- Returns: APIKeyCreation object
*/
@discardableResult func addAPIKey(with parameters: APIKeyParameters,
requestOptions: RequestOptions? = nil) throws -> APIKeyCreation {
let command = Command.APIKey.Add(parameters: parameters, requestOptions: requestOptions)
return try execute(command)
}
// MARK: - Update API key
/**
Update the permissions of an existing APIKey.
- Parameter apiKey: APIKey to update
- Parameter parameters: permissions/restrictions specified by APIKeyParameters
- Parameter requestOptions: Configure request locally with RequestOptions
- Parameter completion: Result completion
- Returns: Launched asynchronous operation
*/
@discardableResult func updateAPIKey(_ apiKey: APIKey,
with parameters: APIKeyParameters,
requestOptions: RequestOptions? = nil,
completion: @escaping ResultCallback<APIKeyRevision>) -> Operation {
let command = Command.APIKey.Update(apiKey: apiKey, parameters: parameters, requestOptions: requestOptions)
return execute(command, completion: completion)
}
/**
Update the permissions of an existing APIKey.
- Parameter apiKey: APIKey to update
- Parameter parameters: permissions/restrictions specified by APIKeyParameters
- Parameter requestOptions: Configure request locally with RequestOptions
- Returns: APIKeyRevision object
*/
@discardableResult func updateAPIKey(_ apiKey: APIKey,
with parameters: APIKeyParameters,
requestOptions: RequestOptions? = nil) throws -> APIKeyRevision {
let command = Command.APIKey.Update(apiKey: apiKey, parameters: parameters, requestOptions: requestOptions)
return try execute(command)
}
// MARK: - Delete API key
/**
Delete an existing APIKey.
- Parameter apiKey: APIKey to delete
- Parameter requestOptions: Configure request locally with RequestOptions
- Parameter completion: Result completion
- Returns: Launched asynchronous operation
*/
@discardableResult func deleteAPIKey(_ apiKey: APIKey,
requestOptions: RequestOptions? = nil,
completion: @escaping ResultCallback<APIKeyDeletion>) -> Operation {
let command = Command.APIKey.Delete(apiKey: apiKey, requestOptions: requestOptions)
let transform = APIKeyDeletion.transform(apiKey)
return execute(command, transform: transform, completion: completion)
}
/**
Delete an existing APIKey.
- Parameter apiKey: APIKey to delete
- Parameter requestOptions: Configure request locally with RequestOptions
- Returns: APIKeyDeletion object
*/
@discardableResult func deleteAPIKey(_ apiKey: APIKey,
requestOptions: RequestOptions? = nil) throws -> APIKeyDeletion {
let command = Command.APIKey.Delete(apiKey: apiKey, requestOptions: requestOptions)
let transform = APIKeyDeletion.transform(apiKey)
return try execute(command, transform: transform)
}
// MARK: - Restore API key
/**
- Parameter apiKey: APIKey to restore
- Parameter requestOptions: Configure request locally with RequestOptions
- Parameter completion: Result completion
- Returns: Launched asynchronous operation
*/
@discardableResult func restoreAPIKey(_ apiKey: APIKey,
requestOptions: RequestOptions? = nil,
completion: @escaping ResultCallback<APIKeyCreation>) -> Operation {
let command = Command.APIKey.Restore(apiKey: apiKey, requestOptions: requestOptions)
let transform = APIKeyCreation.transform(apiKey)
return execute(command, transform: transform, completion: completion)
}
/**
- Parameter apiKey: APIKey to restore
- Parameter requestOptions: Configure request locally with RequestOptions
- Returns: APIKeyDeletion object
*/
@discardableResult func restoreAPIKey(_ apiKey: APIKey,
requestOptions: RequestOptions? = nil) throws -> APIKeyCreation {
let command = Command.APIKey.Restore(apiKey: apiKey, requestOptions: requestOptions)
let transform = APIKeyCreation.transform(apiKey)
return try execute(command, transform: transform)
}
// MARK: - Get API keys list
/**
Get the full list of API Keys
- Parameter requestOptions: Configure request locally with RequestOptions
- Parameter completion: Result completion
- Returns: Launched asynchronous operation
*/
@discardableResult func listAPIKeys(requestOptions: RequestOptions? = nil,
completion: @escaping ResultCallback<ListAPIKeysResponse>) -> Operation {
let command = Command.APIKey.List(requestOptions: requestOptions)
return execute(command, completion: completion)
}
/**
Get the full list of API Keys
- Parameter requestOptions: Configure request locally with RequestOptions
- Returns: ListAPIKeyResponse object
*/
@discardableResult func listAPIKeys(requestOptions: RequestOptions? = nil) throws -> ListAPIKeysResponse {
let command = Command.APIKey.List(requestOptions: requestOptions)
return try execute(command)
}
// MARK: - Get API key
/**
Get the permissions of an APIKey. When initializing the client using the Admin APIKey, you can request information on any of your application’s API keys.
- Parameter apiKey: APIKey to retrieve permissions for
- Parameter requestOptions: Configure request locally with RequestOptions
- Parameter completion: Result completion
- Returns: Launched asynchronous operation
*/
@discardableResult func getAPIKey(_ apiKey: APIKey,
requestOptions: RequestOptions? = nil,
completion: @escaping ResultCallback<APIKeyResponse>) -> Operation {
let command = Command.APIKey.Get(apiKey: apiKey, requestOptions: requestOptions)
return execute(command, completion: completion)
}
/**
Get the permissions of an APIKey. When initializing the client using the Admin APIKey, you can request information on any of your application’s API keys.
- Parameter apiKey: APIKey to retrieve permissions for
- Parameter requestOptions: Configure request locally with RequestOptions
- Returns: APIKeyResponse object
*/
@discardableResult func getAPIKey(_ apiKey: APIKey,
requestOptions: RequestOptions? = nil) throws -> APIKeyResponse {
let command = Command.APIKey.Get(apiKey: apiKey, requestOptions: requestOptions)
return try execute(command)
}
}
//
// SearchClient+Logs.swift
//
//
// Created by Vladislav Fitc on 18/04/2020.
//
import Foundation
public extension SearchClient {
// MARK: - Get logs
/**
Get the logs of the latest search and indexing operations.
You can retrieve the logs of your last 1,000 API calls. It is designed for immediate, real-time debugging.
All logs older than 7 days will be removed and won’t be accessible anymore from the API.
This API is counted in your operation quota but is not logged.
- Parameter offset: Specify the first entry to retrieve (0-based, 0 is the most recent log entry).
- Parameter length: Specify the maximum number of entries to retrieve starting at the page. Maximum allowed value: 1,000.
- Parameter logType: Type of logs to retrieve.
- Parameter requestOptions: Configure request locally with RequestOptions
- Parameter completion: Result completion
- Returns: Launched asynchronous operation
*/
@discardableResult func getLogs(offset: Int? = nil,
length: Int? = nil,
type: LogType = .all,
requestOptions: RequestOptions? = nil,
completion: @escaping ResultCallback<LogsResponse>) -> Operation {
let command = Command.Advanced.GetLogs(indexName: nil, offset: offset, length: length, logType: type, requestOptions: requestOptions)
return execute(command, completion: completion)
}
/**
Get the logs of the latest search and indexing operations.
You can retrieve the logs of your last 1,000 API calls. It is designed for immediate, real-time debugging.
All logs older than 7 days will be removed and won’t be accessible anymore from the API.
This API is counted in your operation quota but is not logged.
- Parameter offset: Specify the first entry to retrieve (0-based, 0 is the most recent log entry).
- Parameter length: Specify the maximum number of entries to retrieve starting at the page. Maximum allowed value: 1,000.
- Parameter logType: Type of logs to retrieve.
- Parameter requestOptions: Configure request locally with RequestOptions
- Returns: LogsResponse object
*/
@discardableResult func getLogs(offset: Int? = nil,
length: Int? = nil,
type: LogType = .all,
requestOptions: RequestOptions? = nil) throws -> LogsResponse {
let command = Command.Advanced.GetLogs(indexName: nil, offset: offset, length: length, logType: type, requestOptions: requestOptions)
return try execute(command)
}
}
//
// SearchClient+Management.swift
//
//
// Created by Vladislav Fitc on 02/07/2020.
//
import Foundation
public extension SearchClient {
// MARK: - Copy index
/**
Make a copy of an index, including its objects, settings, synonyms, and query rules.
- Note: This method enables you to copy the entire index (b, settings, synonyms, and rules) OR one or more of the following index elements:
- setting
- synonyms
- and rules (query rules)
- Parameter source: IndexName of the source Index
- Parameter destination: IndexName of the destination Index.
- Parameter scope: Scope set. If empty (.all alias), then all objects and all scopes are copied.
- Parameter requestOptions: Configure request locally with RequestOptions
- Parameter completion: Result completion
- Returns: Launched asynchronous operation
*/
@discardableResult func copyIndex(from source: IndexName,
to destination: IndexName,
scope: Scope = .all,
requestOptions: RequestOptions? = nil,
completion: @escaping ResultTaskCallback<IndexRevision>) -> Operation & TransportTask {
index(withName: source).copy(scope, to: destination, requestOptions: requestOptions, completion: completion)
}
/**
Make a copy of an index, including its objects, settings, synonyms, and query rules.
- Note: This method enables you to copy the entire index (objects, settings, synonyms, and rules) OR one or more of the following index elements:
- setting
- synonyms
- and rules (query rules)
- Parameter source: IndexName of the source Index
- Parameter destination: IndexName of the destination Index.
- Parameter scope: Scope set. If empty (.all alias), then all objects and all scopes are copied.
- Parameter requestOptions: Configure request locally with RequestOptions
- Returns: RevisionIndex object
*/
@discardableResult func copyIndex(from source: IndexName,
to destination: IndexName,
scope: Scope = .all,
requestOptions: RequestOptions? = nil) throws -> WaitableWrapper<IndexRevision> {
try index(withName: source).copy(scope, to: destination, requestOptions: requestOptions)
}
// MARK: - Move index
/**
Rename an index. Normally used to reindex your data atomically, without any down time.
The move index method is a safe and atomic way to rename an index.
- Parameter source: IndexName of the source Index
- Parameter destination: IndexName of the destination Index.
- Parameter requestOptions: Configure request locally with RequestOptions
- Parameter completion: Result completion
- Returns: Launched asynchronous operation
*/
@discardableResult func moveIndex(from source: IndexName,
to destination: IndexName,
requestOptions: RequestOptions? = nil,
completion: @escaping ResultTaskCallback<IndexRevision>) -> Operation & TransportTask {
index(withName: source).move(to: destination, requestOptions: requestOptions, completion: completion)
}
/**
Rename an index. Normally used to reindex your data atomically, without any down time.
The move index method is a safe and atomic way to rename an index.
- Parameter source: IndexName of the source Index
- Parameter destination: IndexName of the destination Index.
- Parameter requestOptions: Configure request locally with RequestOptions
- Returns: RevisionIndex object
*/
@discardableResult func moveIndex(from source: IndexName,
to destination: IndexName,
requestOptions: RequestOptions? = nil) throws -> WaitableWrapper<IndexRevision> {
try index(withName: source).move(to: destination, requestOptions: requestOptions)
}
// MARK: - Copy rules
/**
Convenience method. Perform copyIndex with a specified Scope of .rules.
- Parameter source: IndexName of the source Index
- Parameter destination: IndexName of the destination Index.
- Parameter requestOptions: Configure request locally with RequestOptions
- Parameter completion: Result completion
- Returns: Launched asynchronous operation
*/
@discardableResult func copyRules(from source: IndexName,
to destination: IndexName,
requestOptions: RequestOptions? = nil,
completion: @escaping ResultTaskCallback<IndexRevision>) -> Operation & TransportTask {
index(withName: source).copy(.rules, to: destination, requestOptions: requestOptions, completion: completion)
}
/**
Convenience method. Perform copyIndex with a specified Scope of .rules.
- Parameter source: IndexName of the source Index
- Parameter destination: IndexName of the destination Index.
- Parameter requestOptions: Configure request locally with RequestOptions
- Returns: RevisionIndex object
*/
@discardableResult func copyRules(from source: IndexName,
to destination: IndexName,
requestOptions: RequestOptions? = nil) throws -> WaitableWrapper<IndexRevision> {
try index(withName: source).copy(.rules, to: destination, requestOptions: requestOptions)
}
// MARK: - Copy synonyms
/**
Convenience method. Perform copyIndex with a specified Scope of .synonyms.
- Parameter source: IndexName of the source Index
- Parameter destination: IndexName of the destination Index.
- Parameter requestOptions: Configure request locally with RequestOptions
- Parameter completion: Result completion
- Returns: Launched asynchronous operation
*/
@discardableResult func copySynonyms(from source: IndexName,
to destination: IndexName,
requestOptions: RequestOptions? = nil,
completion: @escaping ResultTaskCallback<IndexRevision>) -> Operation & TransportTask {
index(withName: source).copy(.synonyms, to: destination, requestOptions: requestOptions, completion: completion)
}
/**
Convenience method. Perform copyIndex with a specified Scope of .synonyms.
- Parameter source: IndexName of the source Index
- Parameter destination: IndexName of the destination Index.
- Parameter requestOptions: Configure request locally with RequestOptions
- Returns: RevisionIndex object
*/
@discardableResult func copySynonyms(from source: IndexName,
to destination: IndexName,
requestOptions: RequestOptions? = nil) throws -> WaitableWrapper<IndexRevision> {
try index(withName: source).copy(.synonyms, to: destination, requestOptions: requestOptions)
}
// MARK: - Copy settings
/**
Convenience method. Perform copyIndex with a specified Scope of .settings.
- Parameter source: IndexName of the source Index
- Parameter destination: IndexName of the destination Index.
- Parameter requestOptions: Configure request locally with RequestOptions
- Parameter completion: Result completion
- Returns: Launched asynchronous operation
*/
@discardableResult func copySettings(from source: IndexName,
to destination: IndexName,
requestOptions: RequestOptions? = nil,
completion: @escaping ResultTaskCallback<IndexRevision>) -> Operation & TransportTask {
index(withName: source).copy(.settings, to: destination, requestOptions: requestOptions, completion: completion)
}
/**
Convenience method. Perform copyIndex with a specified Scope of .settings.
- Parameter source: IndexName of the source Index
- Parameter destination: IndexName of the destination Index.
- Parameter requestOptions: Configure request locally with RequestOptions
- Returns: RevisionIndex object
*/
@discardableResult func copySettings(from source: IndexName,
to destination: IndexName,
requestOptions: RequestOptions? = nil) throws -> WaitableWrapper<IndexRevision> {
try index(withName: source).copy(.settings, to: destination, requestOptions: requestOptions)
}
}
//
// SearchClient+MultiIndex.swift
//
//
// Created by Vladislav Fitc on 04/04/2020.
//
import Foundation
public extension SearchClient {
// MARK: - List indices
/**
Get a list of indices with their associated metadata.
This method retrieves a list of all indices associated with a given ApplicationID.
The returned list includes the name of the index as well as its associated metadata,
such as the number of records, size, last build time, and pending tasks.
- Parameter requestOptions: Configure request locally with RequestOptions
- Parameter completion: Result completion
- Returns: Launched asynchronous operation
*/
@discardableResult func listIndices(requestOptions: RequestOptions? = nil,
completion: @escaping ResultCallback<IndicesListResponse>) -> Operation {
let command = Command.MultipleIndex.ListIndices(requestOptions: requestOptions)
return execute(command, completion: completion)
}
/**
Get a list of indices with their associated metadata.
This method retrieves a list of all indices associated with a given ApplicationID.
The returned list includes the name of the index as well as its associated metadata,
such as the number of records, size, last build time, and pending tasks.
- Parameter requestOptions: Configure request locally with RequestOptions
- Returns: IndicesListResponse object
*/
@discardableResult func listIndices(requestOptions: RequestOptions? = nil) throws -> IndicesListResponse {
let command = Command.MultipleIndex.ListIndices(requestOptions: requestOptions)
return try execute(command)
}
// MARK: - List index API key
/**
Get the full list of API Keys.
- Parameter requestOptions: Configure request locally with RequestOptions
- Parameter completion: Result completion
- Returns: Launched asynchronous operation
*/
@discardableResult func listIndexAPIKeys(requestOptions: RequestOptions? = nil,
completion: @escaping ResultCallback<ListAPIKeysResponse>) -> Operation {
let command = Command.MultipleIndex.ListIndexAPIKeys(requestOptions: requestOptions)
return execute(command, completion: completion)
}
/**
Get the full list of API Keys.
- Parameter requestOptions: Configure request locally with RequestOptions
- Returns: ListAPIKeyResponse object
*/
@discardableResult func listIndexAPIKeys(requestOptions: RequestOptions? = nil) throws -> ListAPIKeysResponse {
let command = Command.MultipleIndex.ListIndexAPIKeys(requestOptions: requestOptions)
return try execute(command)
}
// MARK: - Multiple queries
/**
Perform a search on several indices at the same time, with one method call.
- Parameter queries: The list of IndexedQuery objects mathcing index name and a query to execute on it.
- Parameter strategy: The MultipleQueriesStrategy of the query.
- Parameter requestOptions: Configure request locally with RequestOptions
- Parameter completion: Result completion
- Returns: Launched asynchronous operation
*/
@discardableResult func multipleQueries(queries: [IndexedQuery],
strategy: MultipleQueriesStrategy = .none,
requestOptions: RequestOptions? = nil,
completion: @escaping ResultCallback<SearchesResponse>) -> Operation {
let command = Command.MultipleIndex.Queries(queries: queries, strategy: strategy, requestOptions: requestOptions)
return execute(command, completion: completion)
}
/**
Perform a search on several indices at the same time, with one method call.
- Parameter queries: The list of IndexedQuery objects mathcing index name and a query to execute on it.
- Parameter strategy: The MultipleQueriesStrategy of the query.
- Parameter requestOptions: Configure request locally with RequestOptions
- Returns: SearchesResponse object
*/
@discardableResult func multipleQueries(queries: [IndexedQuery],
strategy: MultipleQueriesStrategy = .none,
requestOptions: RequestOptions? = nil) throws -> SearchesResponse {
let command = Command.MultipleIndex.Queries(queries: queries, strategy: strategy, requestOptions: requestOptions)
return try execute(command)
}
// MARK: - Multiple get objects
/**
Retrieve one or more objects, potentially from different indices, in a single API call.
Results will be received in the same order as the requests.
- Parameter requests: The list of objects to retrieve.
- Parameter requestOptions: Configure request locally with RequestOptions
- Parameter completion: Result completion
- Returns: Launched asynchronous operation
*/
@discardableResult func multipleGetObjects(requests: [ObjectRequest],
requestOptions: RequestOptions? = nil,
completion: @escaping ResultCallback<ObjectsResponse<JSON>>) -> Operation {
let command = Command.MultipleIndex.GetObjects(requests: requests, requestOptions: requestOptions)
return execute(command, completion: completion)
}
/**
Retrieve one or more objects, potentially from different indices, in a single API call.
Results will be received in the same order as the requests.
- Parameter requests: The list of objects to retrieve.
- Parameter requestOptions: Configure request locally with RequestOptions
- Returns: ObjectsResponse object
*/
@discardableResult func multipleGetObjects(requests: [ObjectRequest],
requestOptions: RequestOptions? = nil) throws -> ObjectsResponse<JSON> {
let command = Command.MultipleIndex.GetObjects(requests: requests, requestOptions: requestOptions)
return try execute(command)
}
// MARK: - Multiple batch
/**
Perform several indexing operations in one API call.
This method enables you to batch multiple different indexing operations in one API call, like add or delete objects, potentially targeting multiple indices.
- Parameter operations: List of IndexName and an associated BatchOperation.
- Parameter requestOptions: Configure request locally with RequestOptions
- Parameter completion: Result completion
- Returns: Launched asynchronous operation
*/
@discardableResult func multipleBatchObjects(operations: [(IndexName, BatchOperation)],
requestOptions: RequestOptions? = nil,
completion: @escaping ResultCallback<WaitableWrapper<BatchesResponse>>) -> Operation {
let command = Command.MultipleIndex.BatchObjects(operations: operations, requestOptions: requestOptions)
return execute(command, transform: { .init(batchesResponse: $0, client: self) }, completion: completion)
}
/**
Perform several indexing operations in one API call.
This method enables you to batch multiple different indexing operations in one API call, like add or delete objects, potentially targeting multiple indices.
- Parameter operations: List of IndexName and an associated BatchOperation.
- Parameter requestOptions: Configure request locally with RequestOptions
- Returns: BatchesResponse object
*/
@discardableResult func multipleBatchObjects(operations: [(IndexName, BatchOperation)],
requestOptions: RequestOptions? = nil) throws -> WaitableWrapper<BatchesResponse> {
let command = Command.MultipleIndex.BatchObjects(operations: operations, requestOptions: requestOptions)
return try execute(command, transform: { .init(batchesResponse: $0, client: self) })
}
}
//
// SearchClient+SecuredAPIKey.swift
//
//
// Created by Vladislav Fitc on 02/06/2020.
//
import Foundation
public extension SearchClient {
func generateSecuredApiKey(parentApiKey: APIKey,
with restriction: SecuredAPIKeyRestriction) -> APIKey {
let queryParams = restriction.urlEncodedString
let hash = queryParams.hmac256(withKey: parentApiKey.rawValue)
return APIKey(rawValue: "\(hash)\(queryParams)".toBase64())
}
func getSecuredApiKeyRemainingValidity(_ securedAPIKey: APIKey) -> TimeInterval? {
guard let rawDecodedAPIKey = securedAPIKey.rawValue.fromBase64() else { return nil }
let prefix = "validUntil="
guard let range = rawDecodedAPIKey.range(of: "\(prefix)\\d+", options: .regularExpression) else { return nil }
let validitySubstring = String(rawDecodedAPIKey[range].dropFirst(prefix.count))
guard let timestamp = TimeInterval(String(validitySubstring)) else { return nil }
let timestampDate = Date(timeIntervalSince1970: timestamp)
return timestampDate.timeIntervalSince1970 - Date().timeIntervalSince1970
}
}
//
// SearchClient+Wait.swift
//
//
// Created by Vladislav Fitc on 22/01/2021.
//
import Foundation
public extension Client {
// MARK: - Task status
/**
Check the current TaskStatus of a given Task.
- parameter taskID: of the indexing [Task].
- parameter requestOptions: Configure request locally with [RequestOptions]
*/
@discardableResult func taskStatus(for taskID: AppTaskID,
requestOptions: RequestOptions? = nil,
completion: @escaping ResultCallback<TaskInfo>) -> Operation & TransportTask {
let command = Command.Advanced.TaskStatus(taskID: taskID, requestOptions: requestOptions)
return execute(command, completion: completion)
}
/**
Check the current TaskStatus of a given Task.
- parameter taskID: of the indexing [Task].
- parameter requestOptions: Configure request locally with [RequestOptions]
*/
@discardableResult func taskStatus(for taskID: AppTaskID,
requestOptions: RequestOptions? = nil) throws -> TaskInfo {
let command = Command.Advanced.TaskStatus(taskID: taskID, requestOptions: requestOptions)
return try execute(command)
}
// MARK: - Wait task
/**
Wait for a Task to complete before executing the next line of code, to synchronize index updates.
All write operations in Algolia are asynchronous by design.
It means that when you add or update an object to your index, our servers will reply to your request with
a TaskID as soon as they understood the write operation.
The actual insert and indexing will be done after replying to your code.
You can wait for a task to complete by using the TaskID and this method.
- parameter taskID: of the indexing task to wait for.
- parameter requestOptions: Configure request locally with RequestOptions
*/
@discardableResult func waitTask(withID taskID: AppTaskID,
timeout: TimeInterval? = nil,
requestOptions: RequestOptions? = nil,
completion: @escaping ResultCallback<TaskStatus>) -> Operation {
let task = WaitTask(client: self,
taskID: taskID,
timeout: timeout,
requestOptions: requestOptions,
completion: completion)
return launch(task)
}
/**
Wait for a Task to complete before executing the next line of code, to synchronize index updates.
All write operations in Algolia are asynchronous by design.
It means that when you add or update an object to your index, our servers will reply to your request with
a TaskID as soon as they understood the write operation.
The actual insert and indexing will be done after replying to your code.
You can wait for a task to complete by using the TaskID and this method.
- parameter taskID: of the indexing task to wait for.
- parameter requestOptions: Configure request locally with RequestOptions
*/
@discardableResult func waitTask(withID taskID: AppTaskID,
timeout: TimeInterval? = nil,
requestOptions: RequestOptions? = nil) throws -> TaskStatus {
let task = WaitTask(client: self,
taskID: taskID,
timeout: timeout,
requestOptions: requestOptions,
completion: { _ in })
return try launch(task)
}
}
//
// SearchClient.swift
//
//
// Created by Vladislav Fitc on 17.02.2020.
//
import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif
typealias Client = SearchClient
/// Client to perform operations on indices.
public struct SearchClient: Credentials {
let transport: Transport
let operationLauncher: OperationLauncher
let configuration: Configuration
public var applicationID: ApplicationID {
return transport.applicationID
}
public var apiKey: APIKey {
return transport.apiKey
}
public init(appID: ApplicationID, apiKey: APIKey) {
let configuration = SearchConfiguration(applicationID: appID, apiKey: apiKey)
let sessionConfiguration: URLSessionConfiguration = .default
sessionConfiguration.httpAdditionalHeaders = configuration.defaultHeaders
let session = URLSession(configuration: sessionConfiguration)
self.init(configuration: configuration, requester: session)
}
public init(configuration: SearchConfiguration,
requester: HTTPRequester = URLSession(configuration: .default)) {
let queue = OperationQueue()
queue.qualityOfService = .userInitiated
let operationLauncher = OperationLauncher(queue: queue)
let retryStrategy = AlgoliaRetryStrategy(configuration: configuration)
let httpTransport = HTTPTransport(requester: requester,
configuration: configuration,
retryStrategy: retryStrategy,
credentials: configuration,
operationLauncher: operationLauncher)
self.init(transport: httpTransport, operationLauncher: operationLauncher, configuration: configuration)
}
init(transport: Transport,
operationLauncher: OperationLauncher,
configuration: Configuration) {
self.transport = transport
self.operationLauncher = operationLauncher
self.configuration = configuration
}
/// Initialize an Index configured with SearchConfiguration.
public func index(withName indexName: IndexName) -> Index {
return Index(name: indexName, transport: transport, operationLauncher: operationLauncher, configuration: configuration)
}
}
extension SearchClient: TransportContainer {}
extension SearchClient {
func execute<Output: Codable & AppTask>(_ command: AlgoliaCommand, completion: @escaping ResultAppTaskCallback<Output>) -> Operation & TransportTask {
transport.execute(command, transform: WaitableWrapper.wrap(with: self), completion: completion)
}
func execute<Output: Codable & AppTask>(_ command: AlgoliaCommand) throws -> WaitableWrapper<Output> {
try transport.execute(command, transform: WaitableWrapper.wrap(with: self))
}
}
extension SearchClient {
@discardableResult func launch<O: Operation>(_ operation: O) -> O {
return operationLauncher.launch(operation)
}
func launch<O: OperationWithResult>(_ operation: O) throws -> O.ResultValue {
return try operationLauncher.launchSync(operation)
}
}
//
// AlgoliaCommand.swift
//
//
// Created by Vladislav Fitc on 10.03.2020.
//
import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif
protocol AlgoliaCommand {
var callType: CallType { get }
var urlRequest: URLRequest { get }
var requestOptions: RequestOptions? { get }
}
//
// Command+ABTest.swift
//
//
// Created by Vladislav Fitc on 28/05/2020.
//
import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif
extension Command {
enum ABTest {
struct Add: AlgoliaCommand {
let callType: CallType = .write
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(abTest: AlgoliaSearchClient.ABTest, requestOptions: RequestOptions?) {
self.requestOptions = requestOptions
self.urlRequest = .init(method: .post, path: Path.ABTestsV2, body: abTest.httpBody, requestOptions: self.requestOptions)
}
}
struct Get: AlgoliaCommand {
let callType: CallType = .read
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(abTestID: ABTestID, requestOptions: RequestOptions?) {
self.requestOptions = requestOptions
self.urlRequest = .init(method: .get, path: .ABTestsV2 >>> ABTestRoute.ABTestID(abTestID), requestOptions: self.requestOptions)
}
}
struct Stop: AlgoliaCommand {
let callType: CallType = .write
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(abTestID: ABTestID, requestOptions: RequestOptions?) {
self.requestOptions = requestOptions
self.urlRequest = .init(method: .post, path: .ABTestsV2 >>> .ABTestID(abTestID) >>> ABTestCompletion.stop, requestOptions: self.requestOptions)
}
}
struct Delete: AlgoliaCommand {
let callType: CallType = .write
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(abTestID: ABTestID, requestOptions: RequestOptions?) {
self.requestOptions = requestOptions
self.urlRequest = .init(method: .delete, path: .ABTestsV2 >>> ABTestRoute.ABTestID(abTestID), requestOptions: self.requestOptions)
}
}
struct List: AlgoliaCommand {
let callType: CallType = .read
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(offset: Int?, limit: Int?, requestOptions: RequestOptions?) {
self.requestOptions = requestOptions.updateOrCreate(
[
.offset: offset.flatMap(String.init),
.limit: limit.flatMap(String.init)
])
self.urlRequest = .init(method: .get, path: Path.ABTestsV2, requestOptions: self.requestOptions)
}
}
}
}
//
// Command+APIKeys.swift
//
//
// Created by Vladislav Fitc on 08/04/2020.
//
import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif
extension Command {
enum APIKey {
struct Add: AlgoliaCommand {
let callType: CallType = .write
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(parameters: APIKeyParameters, requestOptions: RequestOptions?) {
self.urlRequest = URLRequest(method: .post, path: Path.keysV1, body: parameters.httpBody, requestOptions: requestOptions)
self.requestOptions = requestOptions
}
}
struct Update: AlgoliaCommand {
let callType: CallType = .write
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(apiKey: AlgoliaSearchClient.APIKey, parameters: APIKeyParameters, requestOptions: RequestOptions?) {
self.urlRequest = URLRequest(method: .put, path: .keysV1 >>> APIKeyCompletion.apiKey(apiKey), body: parameters.httpBody, requestOptions: requestOptions)
self.requestOptions = requestOptions
}
}
struct Delete: AlgoliaCommand {
let callType: CallType = .write
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(apiKey: AlgoliaSearchClient.APIKey, requestOptions: RequestOptions?) {
self.urlRequest = URLRequest(method: .delete, path: .keysV1 >>> APIKeyCompletion.apiKey(apiKey), requestOptions: requestOptions)
self.requestOptions = requestOptions
}
}
struct Restore: AlgoliaCommand {
let callType: CallType = .write
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(apiKey: AlgoliaSearchClient.APIKey, requestOptions: RequestOptions?) {
self.urlRequest = URLRequest(method: .post, path: .keysV1 >>> APIKeyCompletion.restoreAPIKey(apiKey), requestOptions: requestOptions)
self.requestOptions = requestOptions
}
}
struct Get: AlgoliaCommand {
let callType: CallType = .read
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(apiKey: AlgoliaSearchClient.APIKey, requestOptions: RequestOptions?) {
self.urlRequest = URLRequest(method: .get, path: .keysV1 >>> APIKeyCompletion.apiKey(apiKey), requestOptions: requestOptions)
self.requestOptions = requestOptions
}
}
struct List: AlgoliaCommand {
let callType: CallType = .read
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(requestOptions: RequestOptions?) {
self.urlRequest = URLRequest(method: .get, path: Path.keysV1, requestOptions: requestOptions)
self.requestOptions = requestOptions
}
}
}
}
//
// Command+Advanced.swift
//
//
// Created by Vladislav Fitc on 10.03.2020.
//
import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif
extension Command {
enum Advanced {
struct TaskStatus: AlgoliaCommand {
let callType: CallType = .read
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(indexName: IndexName, taskID: TaskID, requestOptions: RequestOptions?) {
self.requestOptions = requestOptions
let path = .indexesV1 >>> .index(indexName) >>> IndexCompletion.task(for: taskID)
urlRequest = .init(method: .get, path: path, requestOptions: self.requestOptions)
}
init(taskID: AppTaskID, requestOptions: RequestOptions?) {
self.requestOptions = requestOptions
let path = .task >>> TaskCompletion.task(withID: taskID)
urlRequest = .init(method: .get, path: path, requestOptions: self.requestOptions)
}
}
struct GetLogs: AlgoliaCommand {
let callType: CallType = .read
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(indexName: IndexName?, offset: Int?, length: Int?, logType: LogType, requestOptions: RequestOptions?) {
let requestOptions = requestOptions.updateOrCreate(
[:]
.merging(indexName.flatMap { [.indexName: $0.rawValue] } ?? [:])
.merging(offset.flatMap { [.offset: String($0)] } ?? [:])
.merging(length.flatMap { [.length: String($0)] } ?? [:])
.merging([.type: logType.rawValue])
)
self.requestOptions = requestOptions
urlRequest = .init(method: .get, path: Path.logs, requestOptions: self.requestOptions)
}
}
}
}
//
// Command+Answers.swift
//
//
// Created by Vladislav Fitc on 19/11/2020.
//
import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif
extension Command {
enum Answers {
struct Find: AlgoliaCommand {
let callType: CallType = .read
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(indexName: IndexName,
query: AnswersQuery,
requestOptions: RequestOptions?) {
self.requestOptions = requestOptions
let path: IndexCompletion = .answers >>> .index(indexName) >>> .prediction
urlRequest = .init(method: .post, path: path, body: query.httpBody, requestOptions: self.requestOptions)
}
}
}
}
//
// Command+Dictionaries.swift
//
//
// Created by Vladislav Fitc on 20/01/2021.
//
import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif
extension Command {
enum Dictionaries {
struct Batch: AlgoliaCommand {
let callType: CallType = .write
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init<D: CustomDictionary>(dictionary: D.Type,
requests: [DictionaryRequest<D.Entry>],
clearExistingDictionaryEntries: Bool,
requestOptions: RequestOptions?) where D.Entry: Encodable {
let path = .dictionaries >>> .dictionaryName(D.name) >>> DictionaryCompletion.batch
let body = Payload(requests: requests, clearExistingDictionaryEntries: clearExistingDictionaryEntries).httpBody
self.urlRequest = URLRequest(method: .post, path: path, body: body, requestOptions: requestOptions)
self.requestOptions = requestOptions
}
// swiftlint:disable:next nesting
struct Payload<E: DictionaryEntry & Encodable>: Encodable {
let requests: [DictionaryRequest<E>]
let clearExistingDictionaryEntries: Bool
}
}
struct Search: AlgoliaCommand {
let callType: CallType = .read
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(dictionaryName: DictionaryName,
query: DictionaryQuery,
requestOptions: RequestOptions?) {
self.requestOptions = requestOptions
let path = .dictionaries >>> .dictionaryName(dictionaryName) >>> DictionaryCompletion.search
let body = query.httpBody
self.urlRequest = .init(method: .post, path: path, body: body, requestOptions: self.requestOptions)
}
init<D: CustomDictionary>(dictionary: D.Type,
query: DictionaryQuery,
requestOptions: RequestOptions?) {
self.requestOptions = requestOptions
let path = .dictionaries >>> .dictionaryName(D.name) >>> DictionaryCompletion.search
let body = query.httpBody
self.urlRequest = .init(method: .post, path: path, body: body, requestOptions: self.requestOptions)
}
}
struct GetSettings: AlgoliaCommand {
let callType: CallType = .read
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(requestOptions: RequestOptions?) {
self.requestOptions = requestOptions
let path = .dictionaries >>> .common >>> DictionaryCompletion.settings
self.urlRequest = .init(method: .get, path: path, requestOptions: self.requestOptions)
}
}
struct SetSettings: AlgoliaCommand {
let callType: CallType = .write
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(settings: DictionarySettings,
requestOptions: RequestOptions?) {
self.requestOptions = requestOptions
let path = .dictionaries >>> .common >>> DictionaryCompletion.settings
self.urlRequest = .init(method: .put, path: path, body: settings.httpBody, requestOptions: self.requestOptions)
}
}
struct LanguagesList: AlgoliaCommand {
let callType: CallType = .read
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(requestOptions: RequestOptions?) {
self.requestOptions = requestOptions
let path = .dictionaries >>> .common >>> DictionaryCompletion.languages
self.urlRequest = .init(method: .get, path: path, requestOptions: self.requestOptions)
}
}
}
}
//
// Command+Index.swift
//
//
// Created by Vladislav Fitc on 19/03/2020.
//
import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif
extension Command {
struct Custom: AlgoliaCommand {
let callType: CallType
let urlRequest: URLRequest
let requestOptions: RequestOptions?
}
enum Index {}
}
extension Command.Index {
struct DeleteIndex: AlgoliaCommand {
let callType: CallType = .write
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(indexName: IndexName,
requestOptions: RequestOptions?) {
self.requestOptions = requestOptions
let path = .indexesV1 >>> IndexRoute.index(indexName)
urlRequest = .init(method: .delete, path: path, requestOptions: self.requestOptions)
}
}
struct Batch: AlgoliaCommand {
let callType: CallType = .write
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(indexName: IndexName,
batchOperations: [BatchOperation],
requestOptions: RequestOptions?) {
self.requestOptions = requestOptions
let path = .indexesV1 >>> .index(indexName) >>> IndexCompletion.batch
let body = RequestsWrapper(batchOperations).httpBody
urlRequest = .init(method: .post, path: path, body: body, requestOptions: self.requestOptions)
}
}
struct Operation: AlgoliaCommand {
let callType: CallType = .write
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(indexName: IndexName, operation: IndexOperation, requestOptions: RequestOptions?) {
self.requestOptions = requestOptions
let path = .indexesV1 >>> .index(indexName) >>> IndexCompletion.operation
urlRequest = .init(method: .post, path: path, body: operation.httpBody, requestOptions: self.requestOptions)
}
}
}
//
// Command+Indexing.swift
//
//
// Created by Vladislav Fitc on 10.03.2020.
//
import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif
extension Command {
enum Indexing {
struct SaveObject: AlgoliaCommand {
let callType: CallType = .write
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init<T: Encodable>(indexName: IndexName, record: T, requestOptions: RequestOptions?) {
self.requestOptions = requestOptions
let path = .indexesV1 >>> IndexRoute.index(indexName)
urlRequest = .init(method: .post, path: path, body: record.httpBody, requestOptions: self.requestOptions)
}
}
struct GetObject: AlgoliaCommand {
let callType: CallType = .read
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(indexName: IndexName, objectID: ObjectID, attributesToRetrieve: [Attribute], requestOptions: RequestOptions?) {
let requestOptions = requestOptions.updateOrCreate({
guard !attributesToRetrieve.isEmpty else { return [:] }
let attributesValue = attributesToRetrieve.map(\.rawValue).joined(separator: ",")
return [.attributesToRetreive: attributesValue]
}() )
self.requestOptions = requestOptions
let path = .indexesV1 >>> .index(indexName) >>> IndexCompletion.objectID(objectID)
urlRequest = .init(method: .get, path: path, requestOptions: self.requestOptions)
}
}
struct ReplaceObject: AlgoliaCommand {
let callType: CallType = .write
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init<T: Encodable>(indexName: IndexName, objectID: ObjectID, replacementObject record: T, requestOptions: RequestOptions?) {
self.requestOptions = requestOptions
let path = .indexesV1 >>> .index(indexName) >>> IndexCompletion.objectID(objectID)
urlRequest = .init(method: .put, path: path, body: record.httpBody, requestOptions: self.requestOptions)
}
}
struct DeleteObject: AlgoliaCommand {
let callType: CallType = .write
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(indexName: IndexName, objectID: ObjectID, requestOptions: RequestOptions?) {
self.requestOptions = requestOptions
let path = .indexesV1 >>> .index(indexName) >>> IndexCompletion.objectID(objectID)
urlRequest = .init(method: .delete, path: path, requestOptions: self.requestOptions)
}
}
struct DeleteByQuery: AlgoliaCommand {
let callType: CallType = .write
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(indexName: IndexName, query: AlgoliaSearchClient.DeleteByQuery, requestOptions: RequestOptions?) {
self.requestOptions = requestOptions
let path = .indexesV1 >>> .index(indexName) >>> IndexCompletion.deleteByQuery
let body = ParamsWrapper(query.urlEncodedString).httpBody
urlRequest = .init(method: .post, path: path, body: body, requestOptions: self.requestOptions)
}
}
struct PartialUpdate: AlgoliaCommand {
let callType: CallType = .write
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(indexName: IndexName, objectID: ObjectID, partialUpdate: AlgoliaSearchClient.PartialUpdate, createIfNotExists: Bool?, requestOptions: RequestOptions?) {
let requestOptions = requestOptions.updateOrCreate({
guard let createIfNotExists = createIfNotExists else { return [:] }
return [.createIfNotExists: String(createIfNotExists)]
}() )
self.requestOptions = requestOptions
let path = .indexesV1 >>> .index(indexName) >>> IndexCompletion.objectID(objectID, partial: true)
let body = partialUpdate.httpBody
urlRequest = .init(method: .post, path: path, body: body, requestOptions: self.requestOptions)
}
}
struct ClearObjects: AlgoliaCommand {
let callType: CallType = .write
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(indexName: IndexName, requestOptions: RequestOptions?) {
self.requestOptions = requestOptions
let path = .indexesV1 >>> .index(indexName) >>> IndexCompletion.clear
urlRequest = .init(method: .post, path: path, requestOptions: self.requestOptions)
}
}
}
}
//
// Command+Insights.swift
//
//
// Created by Vladislav Fitc on 23/04/2020.
//
import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif
extension Command {
enum Insights {
struct SendEvents: AlgoliaCommand {
let callType: CallType = .write
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(events: [InsightsEvent], requestOptions: RequestOptions?) {
let body = EventsWrapper(events)
var requestOptions = requestOptions.unwrapOrCreate()
requestOptions.setHeader("application/json", forKey: .contentType)
self.requestOptions = requestOptions
self.urlRequest = .init(method: .post, path: Path.eventsV1, body: body.httpBody, requestOptions: self.requestOptions)
}
}
}
}
//
// Command+MultiCluster.swift
//
//
// Created by Vladislav Fitc on 25/05/2020.
//
import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif
extension Command {
enum MultiCluster {
struct ListClusters: AlgoliaCommand {
let callType: CallType = .read
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(requestOptions: RequestOptions?) {
self.requestOptions = requestOptions
let path = Path.clustersV1
urlRequest = .init(method: .get, path: path, requestOptions: self.requestOptions)
}
}
struct HasPendingMapping: AlgoliaCommand {
let callType: CallType = .read
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(retrieveMapping: Bool, requestOptions: RequestOptions?) {
self.requestOptions = requestOptions.updateOrCreate([.getClusters: String(retrieveMapping)])
let path = .clustersV1 >>> .mapping >>> MappingCompletion.pending
urlRequest = .init(method: .get, path: path, requestOptions: self.requestOptions)
}
}
}
}
extension Command.MultiCluster {
enum User {
struct Assign: AlgoliaCommand {
let callType: CallType = .write
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(userID: UserID, clusterName: ClusterName, requestOptions: RequestOptions?) {
var updatedRequestOptions = requestOptions ?? .init()
updatedRequestOptions.setHeader(userID.rawValue, forKey: .algoliaUserID)
self.requestOptions = updatedRequestOptions
let path = .clustersV1 >>> MappingRoute.mapping
let body = ClusterWrapper(clusterName).httpBody
urlRequest = .init(method: .post, path: path, body: body, requestOptions: self.requestOptions)
}
}
struct BatchAssign: AlgoliaCommand {
let callType: CallType = .write
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(userIDs: [UserID], clusterName: ClusterName, requestOptions: RequestOptions?) {
self.requestOptions = requestOptions
let path = .clustersV1 >>> .mapping >>> MappingCompletion.batch
let body = AssignUserIDRequest(clusterName: clusterName, userIDs: userIDs).httpBody
urlRequest = .init(method: .post, path: path, body: body, requestOptions: self.requestOptions)
}
}
struct Get: AlgoliaCommand {
let callType: CallType = .read
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(userID: UserID, requestOptions: RequestOptions?) {
self.requestOptions = requestOptions
let path = .clustersV1 >>> .mapping >>> MappingCompletion.userID(userID)
urlRequest = .init(method: .get, path: path, requestOptions: self.requestOptions)
}
}
struct GetTop: AlgoliaCommand {
let callType: CallType = .read
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(requestOptions: RequestOptions?) {
self.requestOptions = requestOptions
let path = .clustersV1 >>> .mapping >>> MappingCompletion.top
urlRequest = .init(method: .get, path: path, requestOptions: self.requestOptions)
}
}
struct GetList: AlgoliaCommand {
let callType: CallType = .read
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(page: Int?, hitsPerPage: Int?, requestOptions: RequestOptions?) {
self.requestOptions = requestOptions.updateOrCreate(
[
.page: page.flatMap(String.init),
.hitsPerPage: hitsPerPage.flatMap(String.init)
])
let path = .clustersV1 >>> MappingRoute.mapping
urlRequest = .init(method: .get, path: path, requestOptions: self.requestOptions)
}
}
struct Remove: AlgoliaCommand {
let callType: CallType = .write
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(userID: UserID, requestOptions: RequestOptions?) {
var updatedRequestOptions = requestOptions ?? .init()
updatedRequestOptions.setHeader(userID.rawValue, forKey: .algoliaUserID)
self.requestOptions = updatedRequestOptions
let path = .clustersV1 >>> MappingRoute.mapping
urlRequest = .init(method: .delete, path: path, requestOptions: self.requestOptions)
}
}
struct Search: AlgoliaCommand {
let callType: CallType = .read
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(userIDQuery: UserIDQuery, requestOptions: RequestOptions?) {
self.requestOptions = requestOptions
let path = .clustersV1 >>> .mapping >>> MappingCompletion.search
urlRequest = .init(method: .post, path: path, body: userIDQuery.httpBody, requestOptions: self.requestOptions)
}
}
}
}
struct AssignUserIDRequest: Codable {
let clusterName: ClusterName
let userIDs: [UserID]
enum CodingKeys: String, CodingKey {
case clusterName = "cluster"
case userIDs = "users"
}
}
//
// Command+MultipleIndex.swift
//
//
// Created by Vladislav Fitc on 04/04/2020.
//
import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif
extension Command {
enum MultipleIndex {
struct ListIndices: AlgoliaCommand {
let callType: CallType = .read
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(requestOptions: RequestOptions?) {
self.requestOptions = requestOptions
self.urlRequest = .init(method: .get, path: Path.indexesV1, requestOptions: requestOptions)
}
}
struct ListIndexAPIKeys: AlgoliaCommand {
let callType: CallType = .read
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(requestOptions: RequestOptions?) {
self.requestOptions = requestOptions
self.urlRequest = .init(method: .get, path: .indexesV1 >>> .multiIndex >>> MultiIndexCompletion.keys, requestOptions: self.requestOptions)
}
}
struct Queries: AlgoliaCommand {
let callType: CallType = .read
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(indexName: IndexName, queries: [Query], strategy: MultipleQueriesStrategy = .none, requestOptions: RequestOptions?) {
let queries = queries.map { IndexedQuery(indexName: indexName, query: $0) }
self.init(queries: queries, strategy: strategy, requestOptions: requestOptions)
}
init(queries: [IndexedQuery], strategy: MultipleQueriesStrategy = .none, requestOptions: RequestOptions?) {
self.requestOptions = requestOptions
let body = MultipleQueriesRequest(requests: queries, strategy: strategy).httpBody
self.urlRequest = .init(method: .post, path: .indexesV1 >>> .multiIndex >>> MultiIndexCompletion.queries, body: body, requestOptions: self.requestOptions)
}
}
struct GetObjects: AlgoliaCommand {
let callType: CallType = .read
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(indexName: IndexName, objectIDs: [ObjectID], attributesToRetreive: [Attribute]?, requestOptions: RequestOptions?) {
let requests = objectIDs.map { ObjectRequest(indexName: indexName, objectID: $0, attributesToRetrieve: attributesToRetreive) }
self.init(requests: requests, requestOptions: requestOptions)
}
init(requests: [ObjectRequest], requestOptions: RequestOptions?) {
self.requestOptions = requestOptions
let body = RequestsWrapper(requests).httpBody
self.urlRequest = .init(method: .post, path: .indexesV1 >>> .multiIndex >>> MultiIndexCompletion.objects, body: body, requestOptions: self.requestOptions)
}
}
struct BatchObjects: AlgoliaCommand {
let callType: CallType = .write
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(operations: [(IndexName, BatchOperation)], requestOptions: RequestOptions?) {
self.init(operations: operations.map { IndexBatchOperation(indexName: $0.0, operation: $0.1) }, requestOptions: requestOptions)
}
init(operations: [IndexBatchOperation], requestOptions: RequestOptions?) {
self.requestOptions = requestOptions
let body = RequestsWrapper(operations).httpBody
self.urlRequest = .init(method: .post, path: .indexesV1 >>> .multiIndex >>> MultiIndexCompletion.batch, body: body, requestOptions: self.requestOptions)
}
}
}
}
//
// Command+Personalization.swift
//
//
// Created by Vladislav Fitc on 27/05/2020.
//
import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif
extension Command {
enum Personalization {
struct Get: AlgoliaCommand {
let callType: CallType = .read
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(requestOptions: RequestOptions?) {
self.requestOptions = requestOptions
self.urlRequest = .init(method: .get, path: .strategies >>> PersonalizationRoute.personalization, requestOptions: self.requestOptions)
}
}
struct Set: AlgoliaCommand {
let callType: CallType = .write
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(strategy: PersonalizationStrategy, requestOptions: RequestOptions?) {
var requestOptions = requestOptions.unwrapOrCreate()
requestOptions.setHeader("application/json", forKey: .contentType)
self.requestOptions = requestOptions
self.urlRequest = .init(method: .post, path: .strategies >>> PersonalizationRoute.personalization, body: strategy.httpBody, requestOptions: self.requestOptions)
}
}
}
}
//
// Command+Places.swift
//
//
// Created by Vladislav Fitc on 10/04/2020.
//
import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif
extension Command {
enum Places {
struct Search: AlgoliaCommand {
let callType: CallType = .read
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(query: PlacesQuery, requestOptions: RequestOptions?) {
self.requestOptions = requestOptions
self.urlRequest = .init(method: .post, path: .places >>> PlacesCompletion.query, body: query.httpBody, requestOptions: self.requestOptions)
}
}
struct GetObject: AlgoliaCommand {
let callType: CallType = .read
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(objectID: ObjectID, requestOptions: RequestOptions?) {
self.requestOptions = requestOptions
self.urlRequest = .init(method: .get, path: .places >>> PlacesCompletion.objectID(objectID), requestOptions: self.requestOptions)
}
}
struct ReverseGeocoding: AlgoliaCommand {
let callType: CallType = .read
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(geolocation: Point, language: Language?, hitsPerPage: Int?, requestOptions: RequestOptions?) {
self.requestOptions = requestOptions.updateOrCreate([
.aroundLatLng: geolocation.stringForm,
.hitsPerPage: hitsPerPage.flatMap(String.init),
.language: language?.rawValue
])
self.urlRequest = .init(method: .get, path: .places >>> PlacesCompletion.reverse, requestOptions: self.requestOptions)
}
}
}
}
//
// Command+Rule.swift
//
//
// Created by Vladislav Fitc on 04/05/2020.
//
import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif
extension Command {
enum Rule {
struct Save: AlgoliaCommand {
let callType: CallType = .write
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(indexName: IndexName, rule: AlgoliaSearchClient.Rule, forwardToReplicas: Bool?, requestOptions: RequestOptions?) {
self.requestOptions = forwardToReplicas.flatMap { requestOptions.updateOrCreate([.forwardToReplicas: String($0)]) } ?? requestOptions
let path = .indexesV1 >>> .index(indexName) >>> .rules >>> RuleCompletion.objectID(rule.objectID)
urlRequest = .init(method: .put, path: path, body: rule.httpBody, requestOptions: self.requestOptions)
}
}
struct Get: AlgoliaCommand {
let callType: CallType = .read
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(indexName: IndexName, objectID: ObjectID, requestOptions: RequestOptions?) {
self.requestOptions = requestOptions
let path = .indexesV1 >>> .index(indexName) >>> .rules >>> RuleCompletion.objectID(objectID)
urlRequest = .init(method: .get, path: path, requestOptions: self.requestOptions)
}
}
struct Delete: AlgoliaCommand {
let callType: CallType = .write
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(indexName: IndexName, objectID: ObjectID, forwardToReplicas: Bool?, requestOptions: RequestOptions?) {
self.requestOptions = forwardToReplicas.flatMap { requestOptions.updateOrCreate([.forwardToReplicas: String($0)]) } ?? requestOptions
let path = .indexesV1 >>> .index(indexName) >>> .rules >>> RuleCompletion.objectID(objectID)
urlRequest = .init(method: .delete, path: path, requestOptions: self.requestOptions)
}
}
struct Search: AlgoliaCommand {
let callType: CallType = .read
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(indexName: IndexName, query: RuleQuery, requestOptions: RequestOptions?) {
self.requestOptions = requestOptions
let path = .indexesV1 >>> .index(indexName) >>> .rules >>> RuleCompletion.search
urlRequest = .init(method: .post, path: path, body: query.httpBody, requestOptions: self.requestOptions)
}
}
struct Clear: AlgoliaCommand {
let callType: CallType = .write
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(indexName: IndexName, forwardToReplicas: Bool?, requestOptions: RequestOptions?) {
self.requestOptions = forwardToReplicas.flatMap { requestOptions.updateOrCreate([.forwardToReplicas: String($0)]) } ?? requestOptions
let path = .indexesV1 >>> .index(indexName) >>> .rules >>> RuleCompletion.clear
urlRequest = .init(method: .post, path: path, requestOptions: self.requestOptions)
}
}
struct SaveList: AlgoliaCommand {
let callType: CallType = .write
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(indexName: IndexName, rules: [AlgoliaSearchClient.Rule], forwardToReplicas: Bool?, clearExistingRules: Bool?, requestOptions: RequestOptions?) {
var parameters: [HTTPParameterKey: String] = [:]
forwardToReplicas.flatMap { parameters[.forwardToReplicas] = String($0) }
clearExistingRules.flatMap { parameters[.clearExistingRules] = String($0) }
self.requestOptions = requestOptions.updateOrCreate(parameters)
let path = .indexesV1 >>> .index(indexName) >>> .rules >>> RuleCompletion.batch
urlRequest = .init(method: .post, path: path, body: rules.httpBody, requestOptions: self.requestOptions)
}
}
}
}
//
// Command+Search.swift
//
//
// Created by Vladislav Fitc on 10.03.2020.
//
import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif
extension Command {
enum Search {
struct Search: AlgoliaCommand {
let callType: CallType = .read
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(indexName: IndexName,
query: Query,
requestOptions: RequestOptions?) {
self.requestOptions = requestOptions
let path = .indexesV1 >>> .index(indexName) >>> IndexCompletion.query
urlRequest = .init(method: .post, path: path, body: query.httpBody, requestOptions: self.requestOptions)
}
}
struct Browse: AlgoliaCommand {
let callType: CallType = .read
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(indexName: IndexName, query: Query, requestOptions: RequestOptions?) {
self.requestOptions = requestOptions
let path = .indexesV1 >>> .index(indexName) >>> IndexCompletion.browse
urlRequest = .init(method: .post, path: path, body: query.httpBody, requestOptions: requestOptions)
}
init(indexName: IndexName, cursor: Cursor, requestOptions: RequestOptions?) {
self.requestOptions = requestOptions
let body = CursorWrapper(cursor)
let path = .indexesV1 >>> .index(indexName) >>> IndexCompletion.browse
urlRequest = .init(method: .post, path: path, body: body.httpBody, requestOptions: self.requestOptions)
}
}
struct SearchForFacets: AlgoliaCommand {
let callType: CallType = .read
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(indexName: IndexName, attribute: Attribute, facetQuery: String, query: Query?, requestOptions: RequestOptions?) {
self.requestOptions = requestOptions
var parameters = query?.customParameters ?? [:]
parameters["facetQuery"] = .init(facetQuery)
var effectiveQuery = query ?? .init()
effectiveQuery.customParameters = parameters
let body = ParamsWrapper(effectiveQuery.urlEncodedString).httpBody
let path = .indexesV1 >>> .index(indexName) >>> IndexCompletion.searchFacets(for: attribute)
urlRequest = .init(method: .post, path: path, body: body, requestOptions: self.requestOptions)
}
}
}
}
//
// Command+Settings.swift
//
//
// Created by Vladislav Fitc on 10.03.2020.
//
import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif
extension Command {
enum Settings {
struct GetSettings: AlgoliaCommand {
let callType: CallType = .read
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(indexName: IndexName,
requestOptions: RequestOptions?) {
self.requestOptions = requestOptions
let path = .indexesV1 >>> .index(indexName) >>> IndexCompletion.settings
urlRequest = .init(method: .get, path: path, requestOptions: self.requestOptions)
}
}
struct SetSettings: AlgoliaCommand {
let callType: CallType = .write
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(indexName: IndexName,
settings: AlgoliaSearchClient.Settings,
resetToDefault: [AlgoliaSearchClient.Settings.Key],
forwardToReplicas: Bool?,
requestOptions: RequestOptions?) {
self.requestOptions = requestOptions.updateOrCreate({
guard let forwardToReplicas = forwardToReplicas else { return [:] }
return [.forwardToReplicas: "\(forwardToReplicas)"]
}())
let path = .indexesV1 >>> .index(indexName) >>> IndexCompletion.settings
urlRequest = .init(method: .put, path: path, body: settings.httpBody, requestOptions: self.requestOptions)
}
}
}
}
//
// Command+Synonym.swift
//
//
// Created by Vladislav Fitc on 11/05/2020.
//
import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif
extension Command {
enum Synonym {
struct Save: AlgoliaCommand {
let callType: CallType = .write
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(indexName: IndexName, synonym: AlgoliaSearchClient.Synonym, forwardToReplicas: Bool?, requestOptions: RequestOptions?) {
self.requestOptions = forwardToReplicas.flatMap { requestOptions.updateOrCreate([.forwardToReplicas: String($0)]) } ?? requestOptions
let path = .indexesV1 >>> .index(indexName) >>> .synonyms >>> SynonymCompletion.objectID(synonym.objectID)
urlRequest = .init(method: .put, path: path, body: synonym.httpBody, requestOptions: self.requestOptions)
}
}
struct SaveList: AlgoliaCommand {
let callType: CallType = .write
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(indexName: IndexName, synonyms: [AlgoliaSearchClient.Synonym], forwardToReplicas: Bool?, clearExistingSynonyms: Bool?, requestOptions: RequestOptions?) {
var parameters: [HTTPParameterKey: String] = [:]
forwardToReplicas.flatMap { parameters[.forwardToReplicas] = String($0) }
clearExistingSynonyms.flatMap { parameters[.replaceExistingSynonyms] = String($0) }
self.requestOptions = requestOptions.updateOrCreate(parameters)
let path = .indexesV1 >>> .index(indexName) >>> .synonyms >>> SynonymCompletion.batch
urlRequest = .init(method: .post, path: path, body: synonyms.httpBody, requestOptions: self.requestOptions)
}
}
struct Get: AlgoliaCommand {
let callType: CallType = .read
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(indexName: IndexName, objectID: ObjectID, requestOptions: RequestOptions?) {
self.requestOptions = requestOptions
let path = .indexesV1 >>> .index(indexName) >>> .synonyms >>> SynonymCompletion.objectID(objectID)
urlRequest = .init(method: .get, path: path, requestOptions: self.requestOptions)
}
}
struct Delete: AlgoliaCommand {
let callType: CallType = .write
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(indexName: IndexName, objectID: ObjectID, forwardToReplicas: Bool?, requestOptions: RequestOptions?) {
self.requestOptions = forwardToReplicas.flatMap { requestOptions.updateOrCreate([.forwardToReplicas: String($0)]) } ?? requestOptions
let path = .indexesV1 >>> .index(indexName) >>> .synonyms >>> SynonymCompletion.objectID(objectID)
urlRequest = .init(method: .delete, path: path, requestOptions: self.requestOptions)
}
}
struct Search: AlgoliaCommand {
let callType: CallType = .read
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(indexName: IndexName, query: SynonymQuery, requestOptions: RequestOptions?) {
self.requestOptions = requestOptions
let path = .indexesV1 >>> .index(indexName) >>> .synonyms >>> SynonymCompletion.search
urlRequest = .init(method: .post, path: path, body: query.httpBody, requestOptions: self.requestOptions)
}
}
struct Clear: AlgoliaCommand {
let callType: CallType = .write
let urlRequest: URLRequest
let requestOptions: RequestOptions?
init(indexName: IndexName, forwardToReplicas: Bool?, requestOptions: RequestOptions?) {
self.requestOptions = forwardToReplicas.flatMap { requestOptions.updateOrCreate([.forwardToReplicas: String($0)]) } ?? requestOptions
let path = .indexesV1 >>> .index(indexName) >>> .synonyms >>> SynonymCompletion.clear
urlRequest = .init(method: .post, path: path, requestOptions: self.requestOptions)
}
}
}
}
//
// Command.swift
//
//
// Created by Vladislav Fitc on 10.03.2020.
//
import Foundation
#if canImport(FoundationNetworking)
import FoundationNetworking
#endif
enum Command {
}
extension Command {
struct Template: AlgoliaCommand {
let callType: CallType = .read
let urlRequest: URLRequest = URLRequest(method: .get, path: Path.indexesV1)
let requestOptions: RequestOptions? = nil
}
}
//
// AssertionTestHelper.swift
//
//
// Created by Vladislav Fitc on 20/11/2020.
//
import Foundation
/// Our custom drop-in replacement `assertionFailure`.
///
/// This will call Swift's `assertionFailure` by default (and terminate the program).
/// But it can be changed at runtime to be tested instead of terminating.
func assertionFailure(_ message: @autoclosure () -> String = String(), file: StaticString = #file, line: UInt = #line) {
assertionClosure(message(), file, line)
}
/// The actual function called by our custom `precondition`.
var assertionClosure: (String, StaticString, UInt) -> Void = defaultAssertionClosure
let defaultAssertionClosure = { Swift.assertionFailure($0, file: $1, line: $2) }
//
// CustomParametersCoder.swift
//
//
// Created by Vladislav Fitc on 23/04/2020.
//
import Foundation
struct CustomParametersCoder {
static func decode<Keys: CaseIterable>(from decoder: Decoder, excludingKeys: Keys.Type) throws -> [String: JSON] where Keys.AllCases.Element: RawRepresentable, Keys.AllCases.Element.RawValue == String {
return try decode(from: decoder, excludingKeys: excludingKeys.allCases.map(\.rawValue))
}
static func decode(from decoder: Decoder, excludingKeys: [String] = []) throws -> [String: JSON] {
let singleValueContainer = try decoder.singleValueContainer()
var customParameters = try singleValueContainer.decode([String: JSON].self)
for key in excludingKeys {
customParameters.removeValue(forKey: key)
}
return customParameters
}
static func encode(_ parameters: [String: JSON], to encoder: Encoder) throws {
var container = encoder.container(keyedBy: DynamicKey.self)
for (key, value) in parameters {
try container.encode(value, forKey: DynamicKey(stringValue: key))
}
}
}
//
// ClientDateCodingStrategy.swift
//
//
// Created by Vladislav Fitc on 06/04/2020.
//
import Foundation
struct ClientDateCodingStrategy {
private static let acceptedFormats = [
"yyyy-MM-dd'T'HH:mm:ssZ",
"yyyy-MM-dd'T'HH:mm:ss.SSSZ"
]
static func decoding(decoder: Decoder) throws -> Date {
let container = try decoder.singleValueContainer()
if let unixTimeStamp = try? container.decode(TimeInterval.self) {
return Date(timeIntervalSince1970: unixTimeStamp)
}
let stringValue = try container.decode(String.self)
let formatter = DateFormatter()
for format in acceptedFormats {
formatter.dateFormat = format
if let date = formatter.date(from: stringValue) {
return date
}
}
throw DecodingError.dataCorruptedError(in: container, debugDescription: "Date string doesnt conform to iso8601 standart")
}
static let encoding: (Date, Encoder) throws -> Void = { date, encoder in
var container = encoder.singleValueContainer()
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"
let dateString = formatter.string(from: date)
try container.encode(dateString)
}
}
extension JSONDecoder.DateDecodingStrategy {
static let swiftAPIClient = JSONDecoder.DateDecodingStrategy.custom(ClientDateCodingStrategy.decoding)
}
extension JSONEncoder.DateEncodingStrategy {
static let swiftAPIClient = JSONEncoder.DateEncodingStrategy.custom(ClientDateCodingStrategy.encoding)
}
//
// KeyedDecodingContainer+DateFormat.swift
//
//
// Created by Vladislav Fitc on 29/05/2020.
//
import Foundation
extension KeyedDecodingContainer {
func decode(forKey key: K, dateFormat: String) throws -> Date {
let rawDate: String = try decode(forKey: key)
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = dateFormat
guard let date = dateFormatter.date(from: rawDate) else {
throw DecodingError.dataCorrupted(DecodingError.Context.init(codingPath: [key], debugDescription: "Date conversion failed"))
}
return date
}
func decodeIfPresent(forKey key: K, dateFormat: String) throws -> Date? {
guard let rawDate: String = try decodeIfPresent(forKey: key) else { return nil }
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = dateFormat
guard let date = dateFormatter.date(from: rawDate) else {
throw DecodingError.dataCorrupted(DecodingError.Context.init(codingPath: [key], debugDescription: "Date conversion failed"))
}
return date
}
}
//
// KeyedEncodingContainer+DateFormat.swift
//
//
// Created by Vladislav Fitc on 29/05/2020.
//
import Foundation
extension KeyedEncodingContainer {
mutating func encode(_ date: Date, forKey key: K, dateFormat: String) throws {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = dateFormat
let rawDate = dateFormatter.string(from: date)
try encode(rawDate, forKey: key)
}
mutating func encodeIfPresent(_ date: Date?, forKey key: K, dateFormat: String) throws {
guard let date = date else { return }
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = dateFormat
let rawDate = dateFormatter.string(from: date)
try encode(rawDate, forKey: key)
}
}
//
// DecodingErrorPrettyPrinter.swift
//
//
// Created by Vladislav Fitc on 20/02/2020.
//
import Foundation
struct DecodingErrorPrettyPrinter: CustomStringConvertible, CustomDebugStringConvertible {
let decodingError: DecodingError
init(decodingError: DecodingError) {
self.decodingError = decodingError
}
private let prefix = "Decoding error"
private func codingKeyDescription(_ key: CodingKey) -> String {
if let index = key.intValue {
return "[\(index)]"
} else {
return "'\(key.stringValue)'"
}
}
private func codingPathDescription(_ path: [CodingKey]) -> String {
return path.map(codingKeyDescription).joined(separator: " -> ")
}
private func additionalComponents(for error: DecodingError) -> [String] {
switch decodingError {
case .valueNotFound(_, let context):
return [codingPathDescription(context.codingPath), context.debugDescription]
case .keyNotFound(let key, let context):
return [codingPathDescription(context.codingPath), "Key not found: \(codingKeyDescription(key))"]
case .typeMismatch(let type, let context):
return [codingPathDescription(context.codingPath), "Type mismatch. Expected: \(type)"]
case .dataCorrupted(let context):
return [codingPathDescription(context.codingPath), context.debugDescription]
@unknown default:
return [decodingError.localizedDescription]
}
}
var description: String {
return ([prefix] + additionalComponents(for: decodingError)).joined(separator: ": ")
}
var debugDescription: String {
return description
}
}
public extension DecodingError {
var prettyDescription: String {
return DecodingErrorPrettyPrinter(decodingError: self).description
}
}
//
// DynamicKey.swift
//
//
// Created by Vladislav Fitc on 27/03/2020.
//
import Foundation
struct DynamicKey: CodingKey {
var intValue: Int?
var stringValue: String
init(intValue: Int) {
self.intValue = intValue
self.stringValue = String(intValue)
}
init(stringValue: String) {
self.stringValue = stringValue
}
}
//
// Encodable+HTTPBody.swift
//
//
// Created by Vladislav Fitc on 19/03/2020.
//
import Foundation
extension Encodable {
var httpBody: Data {
let jsonEncoder = JSONEncoder()
jsonEncoder.dateEncodingStrategy = .swiftAPIClient
do {
let body = try jsonEncoder.encode(self)
return body
} catch let error {
assertionFailure("\(error)")
return Data()
}
}
}
//
// KeyedDecodingContainer+Convenience.swift
//
//
// Created by Vladislav Fitc on 19/03/2020.
//
import Foundation
extension KeyedDecodingContainer {
func decode<T: Decodable>(forKey key: K) throws -> T {
return try decode(T.self, forKey: key)
}
func decodeIfPresent<T: Decodable>(forKey key: K) throws -> T? {
return try decodeIfPresent(T.self, forKey: key)
}
}
//
// BoolContainer.swift
//
//
// Created by Vladislav Fitc on 17/07/2020.
//
import Foundation
/**
Helper structure ensuring the decoding of a bool value from a "volatile" JSON
occasionally providing bool values in the form of String
*/
struct BoolContainer: RawRepresentable, Codable {
let rawValue: Bool
init(rawValue: Bool) {
self.rawValue = rawValue
}
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let boolValue = try? container.decode(Bool.self) {
self.rawValue = boolValue
return
}
if let boolFromString = Bool(try container.decode(String.self)) {
self.rawValue = boolFromString
return
}
throw DecodingError.dataCorruptedError(in: container, debugDescription: "Value cannot be decoded neither to Bool nor to String representing Bool value")
}
}
//
// CustomKey.swift
//
//
// Created by Vladislav Fitc on 13/04/2020.
//
import Foundation
@propertyWrapper
public struct CustomKey<Key: Hashable & RawRepresentable, Value: Codable>: Codable where Key.RawValue == String {
public var wrappedValue: [Key: Value]
public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: DynamicKey.self)
var storage: [Key: Value] = [:]
for rawKey in container.allKeys {
guard let key = Key(rawValue: rawKey.stringValue) else { throw DecodingError.dataCorruptedError(forKey: rawKey, in: container, debugDescription: "Cannot create \(Value.self) value") }
let value = try container.decode(Value.self, forKey: rawKey)
storage[key] = value
}
self.wrappedValue = storage
}
public func encode(to encoder: Encoder) throws {
let rawWrappedSequence = wrappedValue.map { ($0.key.rawValue, $0.value) }
let rawWrappedValue = [String: Value](uniqueKeysWithValues: rawWrappedSequence)
try rawWrappedValue.encode(to: encoder)
}
}
//
// StringNumberContainer.swift
//
//
// Created by Vladislav Fitc on 06/04/2020.
//
import Foundation
/**
Helper structure ensuring the decoding of a number value from a "volatile" JSON
occasionally providing number values in the form of String
*/
struct StringNumberContainer: RawRepresentable, Codable {
let rawValue: Double
var intValue: Int {
return Int(rawValue)
}
var floatValue: Float {
return Float(rawValue)
}
init(rawValue: Double) {
self.rawValue = rawValue
}
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let doubleValue = try? container.decode(Double.self) {
self.rawValue = doubleValue
return
}
if let doubleFromString = Double(try container.decode(String.self)) {
self.rawValue = doubleFromString
return
}
throw DecodingError.dataCorruptedError(in: container, debugDescription: "Value cannot be decoded neither to Double nor to String representing Double value")
}
}
//
// HMAC.swift
//
//
// Created by Vladislav Fitc on 02/06/2020.
//
import Foundation
#if os(Linux)
#else
import CommonCrypto
enum HmacAlgorithm {
case sha1, md5, sha256, sha384, sha512, sha224
var algorithm: CCHmacAlgorithm {
var alg = 0
switch self {
case .sha1:
alg = kCCHmacAlgSHA1
case .md5:
alg = kCCHmacAlgMD5
case .sha256:
alg = kCCHmacAlgSHA256
case .sha384:
alg = kCCHmacAlgSHA384
case .sha512:
alg = kCCHmacAlgSHA512
case .sha224:
alg = kCCHmacAlgSHA224
}
return CCHmacAlgorithm(alg)
}
var digestLength: Int {
var len: Int32 = 0
switch self {
case .sha1:
len = CC_SHA1_DIGEST_LENGTH
case .md5:
len = CC_MD5_DIGEST_LENGTH
case .sha256:
len = CC_SHA256_DIGEST_LENGTH
case .sha384:
len = CC_SHA384_DIGEST_LENGTH
case .sha512:
len = CC_SHA512_DIGEST_LENGTH
case .sha224:
len = CC_SHA224_DIGEST_LENGTH
}
return Int(len)
}
}
#endif
//
// String+HMAC.swift
//
//
// Created by Vladislav Fitc on 08/07/2020.
//
import Foundation
#if os(Linux)
import Crypto
#else
import CommonCrypto
#endif
extension String {
func hmac256(withKey key: String) -> String {
#if os(Linux)
let key = SymmetricKey(data: Data(key.utf8))
let digest = Array(HMAC<SHA256>.authenticationCode(for: Data(utf8), using: key))
#else
let algorithm = HmacAlgorithm.sha256
var digest = [UInt8](repeating: 0, count: algorithm.digestLength)
CCHmac(algorithm.algorithm, key, key.count, self, self.count, &digest)
#endif
let data = Data(digest)
return data.map { String(format: "%02hhx", $0) }.joined()
}
}
//
// Array+Chunks.swift
//
//
// Created by Vladislav Fitc on 29/04/2020.
//
import Foundation
extension Array {
func chunked(into size: Int) -> [[Element]] {
return stride(from: 0, to: count, by: size).map {
Array(self[$0 ..< Swift.min($0 + size, count)])
}
}
}
//
// Dictionary+Merging.swift
//
//
// Created by Vladislav Fitc on 07/04/2020.
//
import Foundation
enum DictionaryMergingStrategy {
case keepInitial
case replaceWithNew
func apply<Value>(firstValue: Value, secondValue: Value) -> Value {
switch self {
case .keepInitial:
return firstValue
case .replaceWithNew:
return secondValue
}
}
}
extension Dictionary {
func merging(_ other: [Key: Value], strategy: DictionaryMergingStrategy = .replaceWithNew) -> [Key: Value] {
return merging(other, uniquingKeysWith: strategy.apply)
}
}
extension Dictionary {
func mapKeys<T>(_ transform: (Key) -> T) -> [T: Value] {
var output: [T: Value] = [:]
for key in keys {
let transformedKey = transform(key)
output[transformedKey] = self[key]
}
return output
}
}
//
// TimeInterval+Minutes.swift
//
//
// Created by Vladislav Fitc on 19/02/2020.
//
import Foundation
public extension TimeInterval {
var milliseconds: Int64 {
return Int64((self * 1000.0).rounded())
}
}
public extension TimeInterval {
static let hour: TimeInterval = minute * 60
static func hours(_ hoursCount: Int) -> TimeInterval {
return TimeInterval(hoursCount) * hour
}
}
public extension TimeInterval {
static let minute: TimeInterval = 60
static func minutes(_ minutesCount: Int) -> TimeInterval {
return TimeInterval(minutesCount) * minute
}
}
public extension TimeInterval {
static let second: TimeInterval = 1
static func seconds(_ secondsCount: Int) -> TimeInterval {
return TimeInterval(secondsCount)
}
}
public extension TimeInterval {
static let day: TimeInterval = hour * 24
static func days(_ daysCount: Int) -> TimeInterval {
return TimeInterval(daysCount) * day
}
}
//
// Logging.swift
//
//
// Created by Vladislav Fitc on 20/02/2020.
//
import Foundation
import Logging
typealias SwiftLog = Logging.Logger
public struct Logger {
static var loggingService: Loggable = {
var swiftLog = SwiftLog(label: "com.algolia.searchClientSwift")
print("Algolia Search Client Swift: Default minimal log severity level is info. Change Logger.minLogServerityLevel value if you want to change it.")
swiftLog.logLevel = .info
return swiftLog
}()
public static var minSeverityLevel: LogLevel {
get {
return loggingService.minSeverityLevel
}
set {
loggingService.minSeverityLevel = newValue
}
}
private init() {}
static func trace(_ message: String) {
loggingService.log(level: .trace, message: message)
}
static func debug(_ message: String) {
loggingService.log(level: .debug, message: message)
}
static func info(_ message: String) {
loggingService.log(level: .info, message: message)
}
static func notice(_ message: String) {
loggingService.log(level: .notice, message: message)
}
static func warning(_ message: String) {
loggingService.log(level: .warning, message: message)
}
static func error(_ message: String) {
loggingService.log(level: .error, message: message)
}
static func critical(_ message: String) {
loggingService.log(level: .critical, message: message)
}
}
public enum LogLevel {
case trace, debug, info, notice, warning, error, critical
}
extension Logger {
static func error(prefix: String = "", _ error: Error) {
let errorMessage: String
if let decodingError = error as? DecodingError {
errorMessage = decodingError.prettyDescription
} else {
errorMessage = "\(error)"
}
self.error("\(prefix) \(errorMessage)")
}
}
extension LogLevel {
init(swiftLogLevel: SwiftLog.Level) {
switch swiftLogLevel {
case .trace: self = .trace
case .debug: self = .debug
case .info: self = .info
case .notice: self = .notice
case .warning: self = .warning
case .error: self = .error
case .critical: self = .critical
}
}
var swiftLogLevel: SwiftLog.Level {
switch self {
case .trace: return .trace
case .debug: return .debug
case .info: return .info
case .notice: return .notice
case .warning: return .warning
case .error: return .error
case .critical: return .critical
}
}
}
protocol Loggable {
var minSeverityLevel: LogLevel { get set }
func log(level: LogLevel, message: String)
}
extension SwiftLog: Loggable {
var minSeverityLevel: LogLevel {
get {
return LogLevel(swiftLogLevel: logLevel)
}
set {
self.logLevel = newValue.swiftLogLevel
}
}
func log(level: LogLevel, message: String) {
self.log(level: level.swiftLogLevel, SwiftLog.Message(stringLiteral: message), metadata: .none)
}
}
//
// ObjectIDChecker.swift
//
//
// Created by Vladislav Fitc on 28/04/2020.
//
import Foundation
struct ObjectIDChecker {
static func checkObjectID<T: Encodable>(_ object: T) throws {
let data = try JSONEncoder().encode(object)
do {
_ = try JSONDecoder().decode(ObjectWrapper<Empty>.self, from: data)
} catch _ {
throw Error.missingObjectIDProperty
}
}
static func assertObjectID<T: Encodable>(_ object: T) {
do {
try checkObjectID(object)
} catch let error {
assertionFailure("\(error.localizedDescription)")
}
}
enum Error: Swift.Error {
case missingObjectIDProperty
var localizedDescription: String {
switch self {
case .missingObjectIDProperty:
return "Object must contain encoded `objectID` field if autoGenerationObjectID is set to false"
}
}
}
}
//
// Builder.swift
//
//
// Created by Vladislav Fitc on 31/03/2020.
//
import Foundation
public protocol Builder {}
public extension Builder {
func set<T>(_ keyPath: WritableKeyPath<Self, T>, to newValue: T) -> Self {
var copy = self
copy[keyPath: keyPath] = newValue
return copy
}
func setIfNotNil<T>(_ keyPath: WritableKeyPath<Self, T>, to newValue: T?) -> Self {
guard let value = newValue else { return self }
var copy = self
copy[keyPath: keyPath] = value
return copy
}
}
//
// Cancellable.swift
//
//
// Created by Vladislav Fitc on 02/04/2020.
//
import Foundation
public protocol Cancellable {
func cancel()
}
//
// ResultContainer.swift
//
//
// Created by Vladislav Fitc on 10.03.2020.
//
import Foundation
public protocol ResultContainer {
associatedtype ResultValue
var result: Result<ResultValue, Swift.Error> { get }
}
//
// URLEncodable.swift
//
//
// Created by Vladislav Fitc on 21/04/2020.
//
import Foundation
public protocol URLEncodable {
var urlEncodedString: String { get }
}
extension RawRepresentable where Self: URLEncodable, RawValue: URLEncodable {
public var urlEncodedString: String {
return rawValue.urlEncodedString
}
}
extension String: URLEncodable {
public var urlEncodedString: String {
return self
}
}
extension Bool: URLEncodable {
public var urlEncodedString: String {
return String(self)
}
}
extension Int: URLEncodable {
public var urlEncodedString: String {
return String(self)
}
}
extension UInt: URLEncodable {
public var urlEncodedString: String {
return String(self)
}
}
extension Double: URLEncodable {
public var urlEncodedString: String {
return String(self)
}
}
//
// ResultCallback.swift
//
//
// Created by Vladislav Fitc on 03/03/2020.
//
import Foundation
public typealias ResultCallback<T> = (Result<T, Error>) -> Void
public typealias ResultTaskCallback<T: Task & Codable> = (Result<WaitableWrapper<T>, Error>) -> Void
public typealias ResultAppTaskCallback<T: AppTask & Codable> = (Result<WaitableWrapper<T>, Error>) -> Void
public typealias ResultBatchesCallback = (Result<WaitableWrapper<BatchesResponse>, Error>) -> Void
//
// String+Base64.swift
//
//
// Created by Vladislav Fitc on 02/06/2020.
//
import Foundation
extension String {
func fromBase64() -> String? {
guard let data = Data(base64Encoded: self) else {
return nil
}
return String(data: data, encoding: .utf8)
}
func toBase64() -> String {
return Data(self.utf8).base64EncodedString()
}
}
//
// String+Environment.swift
//
//
// Created by Vladislav Fitc on 20/03/2020.
//
import Foundation
extension String {
init?(environmentVariable: String) {
if
let rawValue = getenv(environmentVariable),
let value = String(utf8String: rawValue) {
self = value
} else {
return nil
}
}
}
//
// String+Wrapping.swift
//
//
// Created by Vladislav Fitc on 23/11/2020.
//
import Foundation
extension String {
func wrappedInQuotes() -> String { "\"\(self)\"" }
func wrappedInBrackets() -> String { "[\(self)]" }
}
//
// UserAgent.swift
//
//
// Created by Vladislav Fitc on 14/04/2020.
//
import Foundation
public struct UserAgent: Hashable {
public let title: String
public let version: String
public init(title: String, version: String) {
self.title = title
self.version = version
}
}
extension UserAgent: CustomStringConvertible {
public var description: String {
let versionOutput: String
if version.isEmpty {
versionOutput = version
} else {
versionOutput = "(\(version))"
}
return [title, versionOutput].filter { !$0.isEmpty }.joined(separator: " ")
}
}
extension UserAgent {
static var library: UserAgent {
return UserAgent(title: "Algolia for Swift", version: Version.current.description)
}
}
extension UserAgent {
static var operatingSystem: UserAgent = {
let osVersion = ProcessInfo.processInfo.operatingSystemVersion
var osVersionString = "\(osVersion.majorVersion).\(osVersion.minorVersion)"
if osVersion.patchVersion != 0 {
osVersionString += ".\(osVersion.patchVersion)"
}
if let osName = osName {
return UserAgent(title: osName, version: osVersionString)
} else {
return UserAgent(title: ProcessInfo.processInfo.operatingSystemVersionString, version: "")
}
}()
private static var osName: String? {
#if os(iOS)
return "iOS"
#elseif os(OSX)
return "macOS"
#elseif os(tvOS)
return "tvOS"
#elseif os(watchOS)
return "watchOS"
#elseif os(Linux)
return "Linux"
#else
return nil
#endif
}
}
//
// UserAgentController.swift
//
//
// Created by Vladislav Fitc on 15/06/2020.
//
import Foundation
public struct UserAgentController {
public internal(set) static var userAgents: [UserAgent] = [.operatingSystem, .library]
/// Append user agent info which will be includede in each API call.
public static func append(userAgent: UserAgent) {
userAgents.append(userAgent)
}
}
// This is generated file. Don't modify it manually.
public extension Version { static let current: Version = .init(major: 8, minor: 6, patch: 0, prereleaseIdentifier: nil) }
//
// Version.swift
//
//
// Created by Vladislav Fitc on 14/04/2020.
//
public struct Version {
public let major: Int
public let minor: Int
public let patch: Int
public let prereleaseIdentifier: String?
public init(major: Int, minor: Int, patch: Int = 0, prereleaseIdentifier: String? = nil) {
self.major = major
self.minor = minor
self.patch = patch
self.prereleaseIdentifier = prereleaseIdentifier
}
}
extension Version: CustomStringConvertible {
public var description: String {
let main = [major, minor, patch].map(String.init).joined(separator: ".")
if let prereleaseIdentifier = prereleaseIdentifier {
return main + "-\(prereleaseIdentifier)"
} else {
return main
}
}
}
//
// AnyWaitable.swift
//
//
// Created by Vladislav Fitc on 29/04/2020.
//
import Foundation
public protocol AnyWaitable {
func wait(timeout: TimeInterval?, requestOptions: RequestOptions?) throws
func wait(timeout: TimeInterval?, requestOptions: RequestOptions?, completion: @escaping (Result<Empty, Swift.Error>) -> Void)
}
extension Array where Element == AnyWaitable {
func waitAll(timeout: TimeInterval? = nil, requestOptions: RequestOptions? = nil) throws {
for element in self {
try element.wait(timeout: timeout, requestOptions: requestOptions)
}
}
}
//
// TaskWaitable.swift
//
//
// Created by Vladislav Fitc on 25/01/2021.
//
import Foundation
protocol TaskWaitable {
func waitTask(withID taskID: TaskID, timeout: TimeInterval?, requestOptions: RequestOptions?, completion: @escaping ResultCallback<TaskStatus>) -> Operation
func waitTask(withID taskID: TaskID, timeout: TimeInterval?, requestOptions: RequestOptions?) throws -> TaskStatus
}
//
// Waitable.swift
//
//
// Created by Vladislav Fitc on 01/02/2021.
//
import Foundation
struct Waitable: AnyWaitable {
let asyncCall: (TimeInterval?, RequestOptions?, @escaping ResultCallback<Empty>) -> Void
let syncCall: (TimeInterval?, RequestOptions?) throws -> Void
init(index: Index, taskID: TaskID) {
asyncCall = { timeout, requestOptions, completion in index.waitTask(withID: taskID, timeout: timeout, requestOptions: requestOptions, completion: { result in completion(result.map { _ in .empty }) }) }
syncCall = { try index.waitTask(withID: taskID, timeout: $0, requestOptions: $1) }
}
init(client: Client, task: IndexedTask) {
self.init(index: client.index(withName: task.indexName), taskID: task.taskID)
}
init(client: SearchClient, taskID: AppTaskID) {
asyncCall = { timeout, requestOptions, completion in client.waitTask(withID: taskID, timeout: timeout, requestOptions: requestOptions, completion: { result in completion(result.map { _ in .empty }) }) }
syncCall = { try client.waitTask(withID: taskID, timeout: $0, requestOptions: $1) }
}
public func wait(timeout: TimeInterval? = nil,
requestOptions: RequestOptions? = nil) throws {
try syncCall(timeout, requestOptions)
}
public func wait(timeout: TimeInterval? = nil,
requestOptions: RequestOptions? = nil,
completion: @escaping (Result<Empty, Swift.Error>) -> Void) {
asyncCall(timeout, requestOptions, completion)
}
}
//
// TaskWaitWrapper.swift
//
//
// Created by Vladislav Fitc on 16/04/2020.
//
import Foundation
public struct WaitableWrapper<T> {
public let wrapped: T
let tasksToWait: [Waitable]
init(wrapped: T, tasksToWait: [Waitable]) {
self.wrapped = wrapped
self.tasksToWait = tasksToWait
}
}
extension WaitableWrapper where T: Task {
public var task: T {
return wrapped
}
init(task: T, index: Index) {
self.wrapped = task
self.tasksToWait = [.init(index: index, taskID: task.taskID)]
}
static func wrap(with index: Index) -> (T) -> WaitableWrapper<T> {
return { task in
return WaitableWrapper(task: task, index: index)
}
}
}
extension WaitableWrapper where T == [Task] {
public var tasks: T {
return wrapped
}
init(tasks: [Task], index: Index) {
self.wrapped = tasks
self.tasksToWait = tasks.map { Waitable(index: index, taskID: $0.taskID) }
}
}
extension WaitableWrapper where T: AppTask {
public var task: T {
return wrapped
}
init(task: T, client: SearchClient) {
self.wrapped = task
self.tasksToWait = [.init(client: client, taskID: task.taskID)]
}
static func wrap(with client: SearchClient) -> (T) -> WaitableWrapper<T> {
return { task in
return WaitableWrapper(task: task, client: client)
}
}
}
extension WaitableWrapper where T: Task & IndexNameContainer {
static func wrap(credentials: Credentials) -> (T) -> WaitableWrapper<T> {
return { task in
let index = SearchClient(appID: credentials.applicationID, apiKey: credentials.apiKey).index(withName: task.indexName)
return WaitableWrapper(task: task, index: index)
}
}
}
extension WaitableWrapper where T == BatchesResponse {
public var batchesResponse: BatchesResponse {
return wrapped
}
init(batchesResponse: T, client: SearchClient) {
self.wrapped = batchesResponse
self.tasksToWait = batchesResponse.tasks.map { Waitable(index: client.index(withName: $0.indexName), taskID: $0.taskID) }
}
init(batchesResponse: T, index: Index) {
self.wrapped = batchesResponse
self.tasksToWait = batchesResponse.tasks.map { Waitable(index: index, taskID: $0.taskID) }
}
}
extension WaitableWrapper: AnyWaitable {
public func wait(timeout: TimeInterval? = nil,
requestOptions: RequestOptions? = nil) throws {
for waiter in tasksToWait {
try waiter.wait(timeout: timeout, requestOptions: requestOptions)
}
}
public func wait(timeout: TimeInterval? = nil,
requestOptions: RequestOptions? = nil,
completion: @escaping (Result<Empty, Swift.Error>) -> Void) {
let dispatchGroup = DispatchGroup()
var outputError: Error?
for waiter in tasksToWait {
dispatchGroup.enter()
waiter.wait(timeout: timeout,
requestOptions: requestOptions) { result in
switch result {
case .success:
break
case .failure(let error):
outputError = error
}
dispatchGroup.leave()
}
}
dispatchGroup.notify(queue: .global(qos: .userInteractive)) {
completion(outputError.flatMap { .failure($0) } ?? .success(.empty))
}
}
}
//
// Index+Advanced.swift
//
//
// Created by Vladislav Fitc on 06.03.2020.
//
import Foundation
public extension Index {
// MARK: - Task status
/**
Check the current TaskStatus of a given Task.
- parameter taskID: of the indexing [Task].
- parameter requestOptions: Configure request locally with [RequestOptions]
*/
@discardableResult func taskStatus(for taskID: TaskID,
requestOptions: RequestOptions? = nil,
completion: @escaping ResultCallback<TaskInfo>) -> Operation & TransportTask {
let command = Command.Advanced.TaskStatus(indexName: name, taskID: taskID, requestOptions: requestOptions)
return execute(command, completion: completion)
}
/**
Check the current TaskStatus of a given Task.
- parameter taskID: of the indexing [Task].
- parameter requestOptions: Configure request locally with [RequestOptions]
*/
@discardableResult func taskStatus(for taskID: TaskID,
requestOptions: RequestOptions? = nil) throws -> TaskInfo {
let command = Command.Advanced.TaskStatus(indexName: name, taskID: taskID, requestOptions: requestOptions)
return try execute(command)
}
// MARK: - Wait task
/**
Wait for a Task to complete before executing the next line of code, to synchronize index updates.
All write operations in Algolia are asynchronous by design.
It means that when you add or update an object to your index, our servers will reply to your request with
a TaskID as soon as they understood the write operation.
The actual insert and indexing will be done after replying to your code.
You can wait for a task to complete by using the TaskID and this method.
- parameter taskID: of the indexing task to wait for.
- parameter requestOptions: Configure request locally with RequestOptions
*/
@discardableResult func waitTask(withID taskID: TaskID,
timeout: TimeInterval? = nil,
requestOptions: RequestOptions? = nil,
completion: @escaping ResultCallback<TaskStatus>) -> Operation {
let task = WaitTask(index: self,
taskID: taskID,
timeout: timeout,
requestOptions: requestOptions,
completion: completion)
return launch(task)
}
/**
Wait for a Task to complete before executing the next line of code, to synchronize index updates.
All write operations in Algolia are asynchronous by design.
It means that when you add or update an object to your index, our servers will reply to your request with
a TaskID as soon as they understood the write operation.
The actual insert and indexing will be done after replying to your code.
You can wait for a task to complete by using the TaskID and this method.
- parameter taskID: of the indexing task to wait for.
- parameter requestOptions: Configure request locally with RequestOptions
*/
@discardableResult func waitTask(withID taskID: TaskID,
timeout: TimeInterval? = nil,
requestOptions: RequestOptions? = nil) throws -> TaskStatus {
let task = WaitTask(index: self,
taskID: taskID,
timeout: timeout,
requestOptions: requestOptions,
completion: { _ in })
return try launch(task)
}
}
extension Index: TaskWaitable {}
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment