├── .github └── workflows │ └── tests.yml ├── .gitignore ├── .swiftpm └── xcode │ └── xcshareddata │ └── xcschemes │ └── Typesense.xcscheme ├── LICENSE ├── Package.resolved ├── Package.swift ├── README.md ├── Sources └── Typesense │ ├── Alias.swift │ ├── Analytics.swift │ ├── AnalyticsEvents.swift │ ├── AnalyticsRule.swift │ ├── AnalyticsRules.swift │ ├── ApiCall.swift │ ├── ApiKeys.swift │ ├── Client.swift │ ├── Collection.swift │ ├── Collections.swift │ ├── Configuration.swift │ ├── ConversationModel.swift │ ├── ConversationModels.swift │ ├── Conversations.swift │ ├── Document.swift │ ├── Documents.swift │ ├── Errors.swift │ ├── Models │ ├── AnalyticsEventCreateResponse.swift │ ├── AnalyticsEventCreateSchema.swift │ ├── AnalyticsRuleDeleteResponse.swift │ ├── AnalyticsRuleParameters.swift │ ├── AnalyticsRuleParametersDestination.swift │ ├── AnalyticsRuleParametersSource.swift │ ├── AnalyticsRuleParametersSourceEvents.swift │ ├── AnalyticsRuleSchema.swift │ ├── AnalyticsRuleUpsertSchema.swift │ ├── AnalyticsRulesRetrieveSchema.swift │ ├── ApiKey.swift │ ├── ApiKeySchema.swift │ ├── ApiKeysResponse.swift │ ├── ApiResponse.swift │ ├── CollectionAlias.swift │ ├── CollectionAliasSchema.swift │ ├── CollectionAliasesResponse.swift │ ├── CollectionResponse.swift │ ├── CollectionSchema.swift │ ├── CollectionUpdateSchema.swift │ ├── ConversationModelCreateSchema.swift │ ├── ConversationModelSchema.swift │ ├── ConversationModelUpdateSchema.swift │ ├── DebugRetrieveSchema.swift │ ├── DeleteDocumentsParameters.swift │ ├── DeleteDocumentsResponse.swift │ ├── DocumentIndexParameters.swift │ ├── ExportDocumentsParameters.swift │ ├── FacetCounts.swift │ ├── FacetCountsCounts.swift │ ├── FacetCountsStats.swift │ ├── Field.swift │ ├── FieldEmbed.swift │ ├── FieldEmbedModelConfig.swift │ ├── HealthStatus.swift │ ├── ImportDocumentsParameters.swift │ ├── InlineResponse2002.swift │ ├── ModelErrorResponse.swift │ ├── MultiSearchCollectionParameters.swift │ ├── MultiSearchParameters.swift │ ├── MultiSearchResult.swift │ ├── MultiSearchSearchesParameter.swift │ ├── PresetDeleteSchema.swift │ ├── PresetSchema.swift │ ├── PresetUpsertSchema.swift │ ├── PresetValue.swift │ ├── PresetsRetrieveSchema.swift │ ├── ScopedKeyParameters.swift │ ├── SearchGroupedHit.swift │ ├── SearchHighlight.swift │ ├── SearchOverride.swift │ ├── SearchOverrideDeleteResponse.swift │ ├── SearchOverrideExclude.swift │ ├── SearchOverrideInclude.swift │ ├── SearchOverrideRule.swift │ ├── SearchOverrideSchema.swift │ ├── SearchOverridesResponse.swift │ ├── SearchParameters.swift │ ├── SearchResult.swift │ ├── SearchResultConversation.swift │ ├── SearchResultHit.swift │ ├── SearchResultRequestParams.swift │ ├── SearchResultRequestParamsVoiceQuery.swift │ ├── SearchSynonym.swift │ ├── SearchSynonymSchema.swift │ ├── SearchSynonymsResponse.swift │ ├── SnapshotParameters.swift │ ├── StopwordsSetDeleteSchema.swift │ ├── StopwordsSetRetrieveSchema.swift │ ├── StopwordsSetSchema.swift │ ├── StopwordsSetUpsertSchema.swift │ ├── StopwordsSetsRetrieveAllSchema.swift │ ├── SuccessStatus.swift │ ├── UpdateByFilterResponse.swift │ ├── UpdateDocumentsByFilterParameters.swift │ └── VoiceQueryModelCollectionConfig.swift │ ├── MultiSearch.swift │ ├── Node.swift │ ├── Operations.swift │ ├── Override.swift │ ├── Overrides.swift │ ├── Preset.swift │ ├── Presets.swift │ ├── RequestTypes.swift │ ├── Shared │ ├── Coders.swift │ ├── Enums.swift │ ├── Logger.swift │ └── RequestNumber.swift │ ├── Stopword.swift │ ├── Stopwords.swift │ ├── Synonyms.swift │ └── utils │ ├── CreateURLQueryParams.swift │ └── Extensions.swift ├── Tests └── TypesenseTests │ ├── AnalyticsTests.swift │ ├── ApiCallTests.swift │ ├── ApiKeyTests.swift │ ├── CollectionAliasTests.swift │ ├── CollectionTests.swift │ ├── ConfigurationTests.swift │ ├── ConversationModelTests.swift │ ├── CreateURLQueryParamsTests.swift │ ├── DocumentTests.swift │ ├── MultiSearchTests.swift │ ├── OperationTests.swift │ ├── OverrideTests.swift │ ├── OverridesTests.swift │ ├── PresetTests.swift │ ├── PresetsTests.swift │ ├── StopwordTests.swift │ ├── StopwordsTests.swift │ ├── SynonymTests.swift │ └── TestUtils.swift ├── example ├── README.md ├── SwiftUI │ └── RecipeSearchSwiftUI │ │ ├── README.md │ │ ├── RecipeSearch.gif │ │ ├── RecipeSearchSwiftUI.xcodeproj │ │ ├── project.pbxproj │ │ └── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ └── xcshareddata │ │ │ ├── IDEWorkspaceChecks.plist │ │ │ └── swiftpm │ │ │ └── Package.resolved │ │ └── RecipeSearchSwiftUI │ │ ├── Assets.xcassets │ │ ├── AccentColor.colorset │ │ │ └── Contents.json │ │ ├── AppIcon.appiconset │ │ │ ├── Contents.json │ │ │ ├── appstore1024.png │ │ │ ├── ipad152.png │ │ │ ├── ipad76.png │ │ │ ├── ipadNotification20.png │ │ │ ├── ipadNotification40.png │ │ │ ├── ipadPro167.png │ │ │ ├── ipadSettings29.png │ │ │ ├── ipadSettings58.png │ │ │ ├── ipadSpotlight40.png │ │ │ ├── ipadSpotlight80.png │ │ │ ├── iphone120.png │ │ │ ├── iphone180.png │ │ │ ├── mac1024.png │ │ │ ├── mac128.png │ │ │ ├── mac16.png │ │ │ ├── mac256.png │ │ │ ├── mac32.png │ │ │ ├── mac512.png │ │ │ ├── mac64.png │ │ │ ├── notification40.png │ │ │ ├── notification60.png │ │ │ ├── settings58.png │ │ │ ├── settings87.png │ │ │ ├── spotlight120.png │ │ │ └── spotlight80.png │ │ └── Contents.json │ │ ├── ContentView.swift │ │ ├── Preview Content │ │ └── Preview Assets.xcassets │ │ │ └── Contents.json │ │ ├── Recipe.swift │ │ ├── RecipeDetailView.swift │ │ └── RecipeSearchSwiftUIApp.swift └── UIKit │ └── RecipeSearchUIKit │ ├── README.md │ ├── RecipeSearch.gif │ ├── RecipeSearchUIKit.xcodeproj │ ├── project.pbxproj │ └── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── swiftpm │ │ └── Package.resolved │ └── RecipeSearchUIKit │ ├── AppDelegate.swift │ ├── Assets.xcassets │ ├── AccentColor.colorset │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Info.plist │ ├── Recipe.swift │ ├── SceneDelegate.swift │ └── ViewController.swift └── get-models.sh /.github/workflows/tests.yml: -------------------------------------------------------------------------------- 1 | name: tests 2 | 3 | on: 4 | push: 5 | paths-ignore: 6 | - '**/*.md' 7 | pull_request: 8 | paths-ignore: 9 | - '**/*.md' 10 | 11 | # Cancel previous running if a new push is made 12 | # Source: https://stackoverflow.com/a/72408109/123545 13 | concurrency: 14 | group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }} 15 | cancel-in-progress: true 16 | 17 | jobs: 18 | test: 19 | runs-on: macos-latest 20 | steps: 21 | - uses: actions/checkout@v4 22 | - name: Install and start dependencies 23 | run: | 24 | brew install docker 25 | brew install qemu 26 | brew install colima 27 | # https://github.com/abiosoft/colima/issues/424#issuecomment-1335912905 28 | colima delete 29 | colima start --arch x86_64 30 | 31 | - name: Run Typesense 32 | run: | 33 | mkdir $(pwd)/typesense-data 34 | docker run -p 8108:8108 \ 35 | -d \ 36 | -v$(pwd)/typesense-data:/data typesense/typesense:28.0 \ 37 | --data-dir /data \ 38 | --api-key=xyz \ 39 | --enable-cors 40 | shell: bash 41 | 42 | - name: Set up Xcode 43 | uses: maxim-lobanov/setup-xcode@v1.2.1 44 | with: 45 | xcode-version: latest-stable 46 | 47 | - name: Run tests 48 | run: swift test 49 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | /*.xcodeproj 5 | xcuserdata/ 6 | DerivedData/ 7 | .swiftpm 8 | -------------------------------------------------------------------------------- /.swiftpm/xcode/xcshareddata/xcschemes/Typesense.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 43 | 49 | 50 | 51 | 52 | 53 | 58 | 59 | 61 | 67 | 68 | 69 | 71 | 77 | 78 | 79 | 80 | 81 | 91 | 92 | 98 | 99 | 105 | 106 | 107 | 108 | 110 | 111 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "object": { 3 | "pins": [ 4 | { 5 | "package": "AnyCodable", 6 | "repositoryURL": "https://github.com/Flight-School/AnyCodable", 7 | "state": { 8 | "branch": null, 9 | "revision": "862808b2070cd908cb04f9aafe7de83d35f81b05", 10 | "version": "0.6.7" 11 | } 12 | } 13 | ] 14 | }, 15 | "version": 1 16 | } 17 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:5.5.0 2 | // The swift-tools-version declares the minimum version of Swift required to build this package. 3 | 4 | import PackageDescription 5 | 6 | let package = Package( 7 | name: "Typesense", 8 | platforms: [ 9 | .iOS(.v13), .macOS(.v10_15) 10 | ], 11 | products: [ 12 | // Products define the executables and libraries a package produces, and make them visible to other packages. 13 | .library( 14 | name: "Typesense", 15 | targets: ["Typesense"]), 16 | ], 17 | dependencies: [ 18 | // Dependencies declare other packages that this package depends on. 19 | // .package(url: /* package url */, from: "1.0.0"), 20 | .package( 21 | url: "https://github.com/Flight-School/AnyCodable", 22 | from: "0.6.0" 23 | ), 24 | ], 25 | targets: [ 26 | // Targets are the basic building blocks of a package. A target can define a module or a test suite. 27 | // Targets can depend on other targets in this package, and on products in packages this package depends on. 28 | .target( 29 | name: "Typesense", 30 | dependencies: ["AnyCodable"]), 31 | .testTarget( 32 | name: "TypesenseTests", 33 | dependencies: ["Typesense"]), 34 | ] 35 | ) 36 | -------------------------------------------------------------------------------- /Sources/Typesense/Alias.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | #if canImport(FoundationNetworking) 3 | import FoundationNetworking 4 | #endif 5 | 6 | public struct Alias { 7 | var apiCall: ApiCall 8 | let RESOURCEPATH = "aliases" 9 | 10 | init(apiCall: ApiCall) { 11 | self.apiCall = apiCall 12 | } 13 | 14 | public func upsert(name: String, collection: CollectionAliasSchema) async throws -> (CollectionAlias?, URLResponse?) { 15 | let schemaData = try encoder.encode(collection) 16 | let (data, response) = try await apiCall.put(endPoint: endpointPath(name), body: schemaData) 17 | if let result = data { 18 | let alias = try decoder.decode(CollectionAlias.self, from: result) 19 | return (alias, response) 20 | } 21 | return (nil, response) 22 | } 23 | 24 | public func retrieve(name: String) async throws -> (CollectionAlias?, URLResponse?) { 25 | let (data, response) = try await apiCall.get(endPoint: endpointPath(name)) 26 | if let result = data { 27 | let alias = try decoder.decode(CollectionAlias.self, from: result) 28 | return (alias, response) 29 | } 30 | return (nil, response) 31 | } 32 | 33 | public func retrieve() async throws -> (CollectionAliasesResponse?, URLResponse?) { 34 | let (data, response) = try await apiCall.get(endPoint: endpointPath()) 35 | if let result = data { 36 | let aliases = try decoder.decode(CollectionAliasesResponse.self, from: result) 37 | return (aliases, response) 38 | } 39 | return (nil, response) 40 | } 41 | 42 | public func delete(name: String) async throws -> (CollectionAlias?, URLResponse?) { 43 | let (data, response) = try await apiCall.delete(endPoint: endpointPath(name)) 44 | if let result = data { 45 | let alias = try decoder.decode(CollectionAlias.self, from: result) 46 | return (alias, response) 47 | } 48 | return (nil, response) 49 | } 50 | 51 | private func endpointPath(_ operation: String? = nil) throws -> String { 52 | if let operation: String = operation { 53 | return try "\(RESOURCEPATH)/\(operation.encodeURL())" 54 | } else { 55 | return RESOURCEPATH 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Sources/Typesense/Analytics.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public struct Analytics { 4 | static let resourcePath: String = "/analytics" 5 | 6 | private var analyticsRules: AnalyticsRules 7 | var apiCall: ApiCall 8 | 9 | init(apiCall: ApiCall) { 10 | self.apiCall = apiCall 11 | self.analyticsRules = AnalyticsRules(apiCall: apiCall) 12 | } 13 | 14 | public func events() -> AnalyticsEvents { 15 | return AnalyticsEvents(apiCall: self.apiCall) 16 | } 17 | 18 | public func rule(id: String) -> AnalyticsRule { 19 | return AnalyticsRule(name: id, apiCall: self.apiCall) 20 | } 21 | 22 | public func rules() -> AnalyticsRules { 23 | return AnalyticsRules(apiCall: self.apiCall) 24 | } 25 | } 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /Sources/Typesense/AnalyticsEvents.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | #if canImport(FoundationNetworking) 3 | import FoundationNetworking 4 | #endif 5 | 6 | public struct AnalyticsEvents { 7 | static var resourcePath: String = "\(Analytics.resourcePath)/events" 8 | var apiCall: ApiCall 9 | 10 | init(apiCall: ApiCall) { 11 | self.apiCall = apiCall 12 | } 13 | 14 | public func create(params: AnalyticsEventCreateSchema) async throws -> (AnalyticsEventCreateResponse?, URLResponse?) { 15 | let json = try encoder.encode(params) 16 | let (data, response) = try await self.apiCall.post(endPoint: AnalyticsEvents.resourcePath, body: json) 17 | if let result = data { 18 | let validData = try decoder.decode(AnalyticsEventCreateResponse.self, from: result) 19 | return (validData, response) 20 | } 21 | return (nil, response) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Sources/Typesense/AnalyticsRule.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | #if canImport(FoundationNetworking) 3 | import FoundationNetworking 4 | #endif 5 | 6 | public struct AnalyticsRule { 7 | var name: String 8 | private var apiCall: ApiCall 9 | init(name: String, apiCall: ApiCall) { 10 | self.name = name 11 | self.apiCall = apiCall 12 | } 13 | 14 | public func retrieve() async throws -> (AnalyticsRuleSchema?, URLResponse?) { 15 | let (data, response) = try await self.apiCall.get(endPoint: endpointPath()) 16 | if let result = data { 17 | let fetchedRule = try decoder.decode(AnalyticsRuleSchema.self, from: result) 18 | return (fetchedRule, response) 19 | } 20 | return (nil, response) 21 | } 22 | 23 | public func delete() async throws -> (AnalyticsRuleDeleteResponse?, URLResponse?) { 24 | let (data, response) = try await self.apiCall.delete(endPoint: endpointPath()) 25 | if let result = data { 26 | let deletedRule = try decoder.decode(AnalyticsRuleDeleteResponse.self, from: result) 27 | return (deletedRule, response) 28 | } 29 | return (nil, response) 30 | } 31 | 32 | private func endpointPath() throws -> String { 33 | return try "\(AnalyticsRules.resourcePath)/\(name.encodeURL())" 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Sources/Typesense/AnalyticsRules.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | #if canImport(FoundationNetworking) 3 | import FoundationNetworking 4 | #endif 5 | 6 | 7 | public struct AnalyticsRules { 8 | private var apiCall: ApiCall 9 | static var resourcePath: String = "\(Analytics.resourcePath)/rules" 10 | 11 | init(apiCall: ApiCall) { 12 | self.apiCall = apiCall 13 | } 14 | 15 | public func upsert(params: AnalyticsRuleSchema) async throws -> (AnalyticsRuleSchema?, URLResponse?) { 16 | let ruleData = try encoder.encode(params) 17 | let (data, response) = try await self.apiCall.put(endPoint: endpointPath(params.name), body: ruleData) 18 | if let result = data { 19 | let ruleResult = try decoder.decode(AnalyticsRuleSchema.self, from: result) 20 | return (ruleResult, response) 21 | } 22 | 23 | return (nil, response) 24 | } 25 | 26 | public func retrieveAll() async throws -> (AnalyticsRulesRetrieveSchema?, URLResponse?) { 27 | let (data, response) = try await self.apiCall.get(endPoint: endpointPath()) 28 | if let result = data { 29 | let rules = try decoder.decode(AnalyticsRulesRetrieveSchema.self, from: result) 30 | return (rules, response) 31 | } 32 | 33 | return (nil, response) 34 | } 35 | 36 | private func endpointPath(_ operation: String? = nil) throws -> String { 37 | if let operation = operation { 38 | return try "\(AnalyticsRules.resourcePath)/\(operation.encodeURL())" 39 | } else { 40 | return AnalyticsRules.resourcePath 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Sources/Typesense/ApiKeys.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | #if canImport(FoundationNetworking) 3 | import FoundationNetworking 4 | #endif 5 | 6 | 7 | public struct ApiKeys { 8 | var apiCall: ApiCall 9 | let RESOURCEPATH = "keys" 10 | 11 | init(apiCall: ApiCall) { 12 | self.apiCall = apiCall 13 | } 14 | 15 | public func create(_ keySchema: ApiKeySchema) async throws -> (ApiKey?, URLResponse?) { 16 | var schemaData: Data? = nil 17 | 18 | schemaData = try encoder.encode(keySchema) 19 | 20 | if let validSchema = schemaData { 21 | let (data, response) = try await apiCall.post(endPoint: "\(RESOURCEPATH)", body: validSchema) 22 | if let result = data { 23 | let keyResponse = try decoder.decode(ApiKey.self, from: result) 24 | return (keyResponse, response) 25 | } 26 | } 27 | 28 | return (nil, nil) 29 | } 30 | 31 | public func retrieve(id: Int) async throws -> (ApiKey?, URLResponse?) { 32 | 33 | let (data, response) = try await apiCall.get(endPoint: "\(RESOURCEPATH)/\(id)") 34 | if let result = data { 35 | let keyResponse = try decoder.decode(ApiKey.self, from: result) 36 | return (keyResponse, response) 37 | } 38 | 39 | return (nil, nil) 40 | } 41 | 42 | public func retrieve() async throws -> (ApiKeysResponse?, URLResponse?) { 43 | 44 | let (data, response) = try await apiCall.get(endPoint: "\(RESOURCEPATH)") 45 | if let result = data { 46 | let keyResponse = try decoder.decode(ApiKeysResponse.self, from: result) 47 | return (keyResponse, response) 48 | } 49 | 50 | return (nil, nil) 51 | } 52 | 53 | public func delete(id: Int) async throws -> (Data?, URLResponse?) { 54 | 55 | let (data, response) = try await apiCall.delete(endPoint: "\(RESOURCEPATH)/\(id)") 56 | return (data, response) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Sources/Typesense/Client.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public struct Client { 4 | 5 | var configuration: Configuration 6 | var apiCall: ApiCall 7 | public var collections: Collections 8 | 9 | public init(config: Configuration) { 10 | self.configuration = config 11 | self.apiCall = ApiCall(config: config) 12 | self.collections = Collections(apiCall: apiCall) 13 | } 14 | 15 | public func collection(name: String) -> Collection { 16 | return Collection(apiCall: apiCall, collectionName: name) 17 | } 18 | 19 | public func conversations() -> Conversations { 20 | return Conversations(apiCall: apiCall) 21 | } 22 | 23 | public func keys() -> ApiKeys { 24 | return ApiKeys(apiCall: apiCall) 25 | } 26 | 27 | public func aliases() -> Alias { 28 | return Alias(apiCall: apiCall) 29 | } 30 | 31 | public func operations() -> Operations { 32 | return Operations(apiCall: apiCall) 33 | } 34 | 35 | public func multiSearch() -> MultiSearch { 36 | return MultiSearch(apiCall: apiCall) 37 | } 38 | 39 | public func analytics() -> Analytics { 40 | return Analytics(apiCall: apiCall) 41 | } 42 | 43 | public func presets() -> Presets { 44 | return Presets(apiCall: apiCall) 45 | } 46 | 47 | public func preset(_ presetName: String) -> Preset { 48 | return Preset(apiCall: apiCall, presetName: presetName) 49 | } 50 | 51 | public func stopwords() -> Stopwords { 52 | return Stopwords(apiCall: apiCall) 53 | } 54 | 55 | public func stopword(_ stopwordsSetId: String) -> Stopword { 56 | return Stopword(apiCall: apiCall, stopwordsSetId: stopwordsSetId) 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Sources/Typesense/Collection.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | #if canImport(FoundationNetworking) 3 | import FoundationNetworking 4 | #endif 5 | 6 | 7 | public struct Collection { 8 | var apiCall: ApiCall 9 | var collectionName: String 10 | 11 | init(apiCall: ApiCall, collectionName: String) { 12 | self.apiCall = apiCall 13 | self.collectionName = collectionName 14 | } 15 | 16 | public func documents() -> Documents { 17 | return Documents(apiCall: apiCall, collectionName: self.collectionName) 18 | } 19 | 20 | public func document(id: String) -> Document { 21 | return Document(apiCall: apiCall, collectionName: self.collectionName, id: id) 22 | } 23 | 24 | public func delete() async throws -> (CollectionResponse?, URLResponse?) { 25 | let (data, response) = try await apiCall.delete(endPoint: endpointPath()) 26 | if let result = data { 27 | let fetchedCollection = try decoder.decode(CollectionResponse.self, from: result) 28 | return (fetchedCollection, response) 29 | } 30 | return (nil, response) 31 | } 32 | 33 | public func retrieve() async throws -> (CollectionResponse?, URLResponse?) { 34 | let (data, response) = try await apiCall.get(endPoint: endpointPath()) 35 | if let result = data { 36 | let fetchedCollection = try decoder.decode(CollectionResponse.self, from: result) 37 | return (fetchedCollection, response) 38 | } 39 | return (nil, response) 40 | } 41 | 42 | public func synonyms() -> Synonyms { 43 | return Synonyms(apiCall: apiCall, collectionName: self.collectionName) 44 | } 45 | 46 | public func overrides() -> Overrides{ 47 | return Overrides(apiCall: self.apiCall, collectionName: self.collectionName) 48 | } 49 | 50 | public func override(_ overrideId: String) -> Override{ 51 | return Override(apiCall: self.apiCall, collectionName: self.collectionName, overrideId: overrideId) 52 | } 53 | 54 | private func endpointPath() throws -> String { 55 | return "\(Collections.RESOURCEPATH)/\(try collectionName.encodeURL())" 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Sources/Typesense/Collections.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | #if canImport(FoundationNetworking) 3 | import FoundationNetworking 4 | #endif 5 | 6 | 7 | public struct Collections { 8 | var apiCall: ApiCall 9 | static let RESOURCEPATH = "collections" 10 | 11 | init(apiCall: ApiCall) { 12 | self.apiCall = apiCall 13 | } 14 | 15 | public func create(schema: CollectionSchema) async throws -> (CollectionResponse?, URLResponse?) { 16 | var schemaData: Data? = nil 17 | 18 | schemaData = try encoder.encode(schema) 19 | 20 | if let validSchema = schemaData { 21 | let (data, response) = try await apiCall.post(endPoint: Collections.RESOURCEPATH, body: validSchema) 22 | if let result = data { 23 | let fetchedCollection = try decoder.decode(CollectionResponse.self, from: result) 24 | return (fetchedCollection, response) 25 | } 26 | } 27 | return (nil, nil) 28 | } 29 | 30 | public func retrieveAll() async throws -> ([CollectionResponse]?, URLResponse?) { 31 | let (data, response) = try await apiCall.get(endPoint: Collections.RESOURCEPATH) 32 | 33 | if let result = data { 34 | let fetchedCollections = try decoder.decode([CollectionResponse].self, from: result) 35 | return (fetchedCollections, response) 36 | } 37 | return (nil, response) 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Sources/Typesense/Configuration.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public struct Configuration { 4 | 5 | //Required Config 6 | var nodes: [Node] 7 | var apiKey: String 8 | 9 | //Optional Config 10 | var nearestNode: Node? = nil 11 | var connectionTimeoutSeconds: Int = 10 12 | var healthcheckIntervalSeconds: Int = 15 13 | var numRetries: Int = 3 14 | var retryIntervalSeconds: Float = 0.1 15 | var sendApiKeyAsQueryParam: Bool = false 16 | var cacheSearchResultsForSeconds: Int = 0 17 | var useServerSideSearchCache: Bool = false 18 | var logger: Logger = Logger(debugMode: false) 19 | 20 | //Quick configurations 21 | public init(nodes: [Node], apiKey: String) { 22 | self.nodes = nodes 23 | self.apiKey = apiKey 24 | } 25 | 26 | public init(nodes: [Node], apiKey: String, connectionTimeoutSeconds: Int = 10, nearestNode: Node?) { 27 | self.nodes = nodes 28 | self.apiKey = apiKey 29 | self.connectionTimeoutSeconds = connectionTimeoutSeconds 30 | self.nearestNode = nearestNode 31 | } 32 | 33 | //Advanced configurations 34 | public init(nodes: [Node], apiKey: String, connectionTimeoutSeconds: Int = 10, nearestNode: Node? = nil, healthcheckIntervalSeconds: Int = 15, numRetries: Int = 3, retryIntervalSeconds: Float = 0.1, sendApiKeyAsQueryParam: Bool = false, cacheSearchResultsForSeconds: Int = 0, useServerSideSearchCache: Bool = false, logger: Logger = Logger(debugMode: false)) { 35 | self.nodes = nodes 36 | self.apiKey = apiKey 37 | self.connectionTimeoutSeconds = connectionTimeoutSeconds 38 | self.nearestNode = nearestNode 39 | self.healthcheckIntervalSeconds = healthcheckIntervalSeconds 40 | self.numRetries = numRetries 41 | self.retryIntervalSeconds = retryIntervalSeconds 42 | self.sendApiKeyAsQueryParam = sendApiKeyAsQueryParam 43 | self.cacheSearchResultsForSeconds = cacheSearchResultsForSeconds 44 | self.useServerSideSearchCache = useServerSideSearchCache 45 | self.logger = logger 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /Sources/Typesense/ConversationModel.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | #if canImport(FoundationNetworking) 3 | import FoundationNetworking 4 | #endif 5 | 6 | public struct ConversationModel { 7 | private var apiCall: ApiCall 8 | var modelId: String 9 | 10 | init(apiCall: ApiCall, modelId: String) { 11 | self.apiCall = apiCall 12 | self.modelId = modelId 13 | } 14 | 15 | public func update(params: ConversationModelUpdateSchema) async throws -> (ConversationModelSchema?, URLResponse?) { 16 | let schemaData = try encoder.encode(params) 17 | let (data, response) = try await self.apiCall.put(endPoint: endpointPath(), body: schemaData) 18 | if let result = data { 19 | let decodedData = try decoder.decode(ConversationModelSchema.self, from: result) 20 | return (decodedData, response) 21 | } 22 | return (nil, response) 23 | } 24 | 25 | public func retrieve() async throws -> (ConversationModelSchema?, URLResponse?) { 26 | let (data, response) = try await self.apiCall.get(endPoint: endpointPath()) 27 | if let result = data { 28 | let decodedData = try decoder.decode(ConversationModelSchema.self, from: result) 29 | return (decodedData, response) 30 | } 31 | return (nil, response) 32 | } 33 | 34 | public func delete() async throws -> (ConversationModelSchema?, URLResponse?) { 35 | let (data, response) = try await self.apiCall.delete(endPoint: endpointPath()) 36 | if let result = data { 37 | let decodedData = try decoder.decode(ConversationModelSchema.self, from: result) 38 | return (decodedData, response) 39 | } 40 | return (nil, response) 41 | } 42 | 43 | private func endpointPath() throws -> String { 44 | return try "\(Conversations.RESOURCE_PATH)/\(ConversationModels.RESOURCE_PATH)/\(modelId.encodeURL())" 45 | } 46 | 47 | 48 | } 49 | -------------------------------------------------------------------------------- /Sources/Typesense/ConversationModels.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | #if canImport(FoundationNetworking) 3 | import FoundationNetworking 4 | #endif 5 | 6 | public struct ConversationModels { 7 | static let RESOURCE_PATH = "models" 8 | private var apiCall: ApiCall 9 | 10 | 11 | init(apiCall: ApiCall) { 12 | self.apiCall = apiCall 13 | } 14 | 15 | public func create(params: ConversationModelCreateSchema) async throws -> (ConversationModelSchema?, URLResponse?) { 16 | let schemaData = try encoder.encode(params) 17 | let (data, response) = try await self.apiCall.post(endPoint: endpointPath(), body: schemaData) 18 | if let result = data { 19 | let decodedData = try decoder.decode(ConversationModelSchema.self, from: result) 20 | return (decodedData, response) 21 | } 22 | return (nil, response) 23 | } 24 | 25 | public func retrieve() async throws -> ([ConversationModelSchema]?, URLResponse?) { 26 | let (data, response) = try await self.apiCall.get(endPoint: endpointPath()) 27 | if let result = data { 28 | let decodedData = try decoder.decode([ConversationModelSchema].self, from: result) 29 | return (decodedData, response) 30 | } 31 | return (nil, response) 32 | } 33 | 34 | private func endpointPath() throws -> String { 35 | return "\(Conversations.RESOURCE_PATH)/\(ConversationModels.RESOURCE_PATH)" 36 | } 37 | 38 | 39 | } 40 | -------------------------------------------------------------------------------- /Sources/Typesense/Conversations.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public struct Conversations { 4 | static let RESOURCE_PATH = "conversations" 5 | private var apiCall: ApiCall 6 | 7 | 8 | init(apiCall: ApiCall) { 9 | self.apiCall = apiCall 10 | } 11 | 12 | public func models() -> ConversationModels { 13 | return ConversationModels(apiCall: apiCall) 14 | } 15 | 16 | public func model(modelId: String) -> ConversationModel { 17 | return ConversationModel(apiCall: apiCall, modelId: modelId) 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /Sources/Typesense/Document.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | #if canImport(FoundationNetworking) 3 | import FoundationNetworking 4 | #endif 5 | 6 | 7 | public struct Document { 8 | var apiCall: ApiCall 9 | var collectionName: String 10 | var id: String 11 | 12 | init(apiCall: ApiCall, collectionName: String, id: String) { 13 | self.apiCall = apiCall 14 | self.collectionName = collectionName 15 | self.id = id 16 | } 17 | 18 | public func delete() async throws -> (Data?, URLResponse?) { 19 | let (data, response) = try await apiCall.delete(endPoint: endpointPath()) 20 | return (data, response) 21 | } 22 | 23 | public func retrieve() async throws -> (Data?, URLResponse?) { 24 | let (data, response) = try await apiCall.get(endPoint: endpointPath()) 25 | return (data, response) 26 | } 27 | 28 | public func update(newDocument: Data, options: DocumentIndexParameters? = nil) async throws -> (Data?, URLResponse?) { 29 | let queryParams = try createURLQuery(forSchema: options) 30 | let (data, response) = try await apiCall.patch(endPoint: endpointPath(), body: newDocument, queryParameters: queryParams) 31 | return (data, response) 32 | } 33 | 34 | private func endpointPath() throws -> String { 35 | return try "\(Collections.RESOURCEPATH)/\(collectionName.encodeURL())/documents/\(id.encodeURL())" 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Sources/Typesense/Errors.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public enum HTTPError: Error { 4 | case serverError(code: Int, desc: String) 5 | case clientError(code: Int, desc: String) 6 | } 7 | 8 | extension HTTPError: LocalizedError { 9 | public var errorDescription: String? { 10 | func errorMessage(_ code: Int, _ desc: String) -> String{ 11 | return NSLocalizedString("Request to server failed with code \(code). Message: \(desc)", comment: "Typesense error") 12 | } 13 | switch self { 14 | case .serverError(let code, let desc): 15 | return errorMessage(code, desc) 16 | case .clientError(let code, let desc): 17 | return errorMessage(code, desc) 18 | } 19 | } 20 | } 21 | 22 | public enum URLError: Error { 23 | case invalidURL 24 | case encodingError(message: String) 25 | } 26 | 27 | public enum DataError: Error { 28 | case unableToParse(message: String) 29 | case dataNotFound 30 | } 31 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/AnalyticsEventCreateResponse.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AnalyticsEventCreateResponse.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | public struct AnalyticsEventCreateResponse: Codable { 13 | 14 | public var ok: Bool 15 | 16 | public init(ok: Bool) { 17 | self.ok = ok 18 | } 19 | 20 | 21 | } 22 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/AnalyticsEventCreateSchema.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | 4 | 5 | public struct AnalyticsEventCreateSchema: Encodable { 6 | 7 | public var type: String 8 | public var name: String 9 | public var data: T 10 | 11 | public init(type: String, name: String, data: T) { 12 | self.type = type 13 | self.name = name 14 | self.data = data 15 | } 16 | 17 | 18 | } 19 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/AnalyticsRuleDeleteResponse.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AnalyticsRuleDeleteResponse.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | public struct AnalyticsRuleDeleteResponse: Codable { 13 | 14 | public var name: String 15 | 16 | public init(name: String) { 17 | self.name = name 18 | } 19 | 20 | 21 | } 22 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/AnalyticsRuleParameters.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AnalyticsRuleParameters.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | public struct AnalyticsRuleParameters: Codable { 13 | 14 | public var source: AnalyticsRuleParametersSource 15 | public var destination: AnalyticsRuleParametersDestination 16 | public var limit: Int? 17 | public var expandQuery: Bool? 18 | 19 | public init(source: AnalyticsRuleParametersSource, destination: AnalyticsRuleParametersDestination, limit: Int? = nil, expandQuery: Bool? = nil) { 20 | self.source = source 21 | self.destination = destination 22 | self.limit = limit 23 | self.expandQuery = expandQuery 24 | } 25 | 26 | public enum CodingKeys: String, CodingKey { 27 | case source 28 | case destination 29 | case limit 30 | case expandQuery = "expand_query" 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/AnalyticsRuleParametersDestination.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AnalyticsRuleParametersDestination.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | public struct AnalyticsRuleParametersDestination: Codable { 13 | 14 | public var collection: String 15 | public var counterField: String? 16 | 17 | public init(collection: String, counterField: String? = nil) { 18 | self.collection = collection 19 | self.counterField = counterField 20 | } 21 | 22 | public enum CodingKeys: String, CodingKey { 23 | case collection 24 | case counterField = "counter_field" 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/AnalyticsRuleParametersSource.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AnalyticsRuleParametersSource.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | public struct AnalyticsRuleParametersSource: Codable { 13 | 14 | public var collections: [String] 15 | public var events: [AnalyticsRuleParametersSourceEvents]? 16 | 17 | public init(collections: [String], events: [AnalyticsRuleParametersSourceEvents]? = nil) { 18 | self.collections = collections 19 | self.events = events 20 | } 21 | 22 | 23 | } 24 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/AnalyticsRuleParametersSourceEvents.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AnalyticsRuleParametersSourceEvents.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | public struct AnalyticsRuleParametersSourceEvents: Codable { 13 | 14 | public var type: String 15 | public var weight: Float 16 | public var name: String 17 | 18 | public init(type: String, weight: Float, name: String) { 19 | self.type = type 20 | self.weight = weight 21 | self.name = name 22 | } 23 | 24 | 25 | } 26 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/AnalyticsRuleSchema.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | 4 | 5 | public struct AnalyticsRuleSchema: Codable { 6 | 7 | public enum RuleType: String, Codable { 8 | case popularQueries = "popular_queries" 9 | case nohitsQueries = "nohits_queries" 10 | case counter = "counter" 11 | } 12 | public var name: String 13 | public var type: RuleType 14 | public var params: AnalyticsRuleParameters 15 | 16 | public init(name: String, type: RuleType, params: AnalyticsRuleParameters) { 17 | self.name = name 18 | self.type = type 19 | self.params = params 20 | } 21 | 22 | 23 | } 24 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/AnalyticsRuleUpsertSchema.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AnalyticsRuleUpsertSchema.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | public struct AnalyticsRuleUpsertSchema: Codable { 13 | 14 | public enum ModelType: String, Codable { 15 | case popularQueries = "popular_queries" 16 | case nohitsQueries = "nohits_queries" 17 | case counter = "counter" 18 | } 19 | public var type: ModelType 20 | public var params: AnalyticsRuleParameters 21 | 22 | public init(type: ModelType, params: AnalyticsRuleParameters) { 23 | self.type = type 24 | self.params = params 25 | } 26 | 27 | 28 | } 29 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/AnalyticsRulesRetrieveSchema.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AnalyticsRulesRetrieveSchema.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | public struct AnalyticsRulesRetrieveSchema: Codable { 13 | 14 | public var rules: [AnalyticsRuleSchema]? 15 | 16 | public init(rules: [AnalyticsRuleSchema]? = nil) { 17 | self.rules = rules 18 | } 19 | 20 | 21 | } 22 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/ApiKey.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ApiKey.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | public struct ApiKey: Codable { 13 | 14 | public var value: String? 15 | public var _description: String 16 | public var actions: [String] 17 | public var collections: [String] 18 | public var expiresAt: Int64? 19 | public var _id: Int 20 | public var valuePrefix: String? 21 | 22 | public init(value: String? = nil, _description: String, actions: [String], collections: [String], expiresAt: Int64? = nil, _id: Int, valuePrefix: String? = nil) { 23 | self.value = value 24 | self._description = _description 25 | self.actions = actions 26 | self.collections = collections 27 | self.expiresAt = expiresAt 28 | self._id = _id 29 | self.valuePrefix = valuePrefix 30 | } 31 | 32 | public enum CodingKeys: String, CodingKey { 33 | case value 34 | case _description = "description" 35 | case actions 36 | case collections 37 | case expiresAt = "expires_at" 38 | case _id = "id" 39 | case valuePrefix = "value_prefix" 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/ApiKeySchema.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ApiKeySchema.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | public struct ApiKeySchema: Codable { 13 | 14 | public var value: String? 15 | public var _description: String 16 | public var actions: [String] 17 | public var collections: [String] 18 | public var expiresAt: Int64? 19 | 20 | public init(value: String? = nil, _description: String, actions: [String], collections: [String], expiresAt: Int64? = nil) { 21 | self.value = value 22 | self._description = _description 23 | self.actions = actions 24 | self.collections = collections 25 | self.expiresAt = expiresAt 26 | } 27 | 28 | public enum CodingKeys: String, CodingKey { 29 | case value 30 | case _description = "description" 31 | case actions 32 | case collections 33 | case expiresAt = "expires_at" 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/ApiKeysResponse.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ApiKeysResponse.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | public struct ApiKeysResponse: Codable { 13 | 14 | public var keys: [ApiKey] 15 | 16 | public init(keys: [ApiKey]) { 17 | self.keys = keys 18 | } 19 | 20 | 21 | } 22 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/ApiResponse.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ApiResponse.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | public struct ApiResponse: Codable { 13 | 14 | public var message: String 15 | 16 | public init(message: String) { 17 | self.message = message 18 | } 19 | 20 | 21 | } 22 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/CollectionAlias.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CollectionAlias.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | public struct CollectionAlias: Codable { 13 | 14 | /** Name of the collection alias */ 15 | public var name: String 16 | /** Name of the collection the alias mapped to */ 17 | public var collectionName: String 18 | 19 | public init(name: String, collectionName: String) { 20 | self.name = name 21 | self.collectionName = collectionName 22 | } 23 | 24 | public enum CodingKeys: String, CodingKey { 25 | case name 26 | case collectionName = "collection_name" 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/CollectionAliasSchema.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CollectionAliasSchema.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | public struct CollectionAliasSchema: Codable { 13 | 14 | /** Name of the collection you wish to map the alias to */ 15 | public var collectionName: String 16 | 17 | public init(collectionName: String) { 18 | self.collectionName = collectionName 19 | } 20 | 21 | public enum CodingKeys: String, CodingKey { 22 | case collectionName = "collection_name" 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/CollectionAliasesResponse.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CollectionAliasesResponse.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | public struct CollectionAliasesResponse: Codable { 13 | 14 | public var aliases: [CollectionAlias] 15 | 16 | public init(aliases: [CollectionAlias]) { 17 | self.aliases = aliases 18 | } 19 | 20 | 21 | } 22 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/CollectionResponse.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CollectionResponse.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | public struct CollectionResponse: Codable { 13 | 14 | /** Name of the collection */ 15 | public var name: String 16 | /** A list of fields for querying, filtering and faceting */ 17 | public var fields: [Field] 18 | /** The name of an int32 / float field that determines the order in which the search results are ranked when a sort_by clause is not provided during searching. This field must indicate some kind of popularity. */ 19 | public var defaultSortingField: String? 20 | /** List of symbols or special characters to be used for splitting the text into individual words in addition to space and new-line characters. */ 21 | public var tokenSeparators: [String]? 22 | /** Enables experimental support at a collection level for nested object or object array fields. This field is only available if the Typesense server is version `0.24.0.rcn34` or later. */ 23 | public var enableNestedFields: Bool? 24 | /** List of symbols or special characters to be indexed. */ 25 | public var symbolsToIndex: [String]? 26 | /** Number of documents in the collection */ 27 | public var numDocuments: Int64 28 | /** Timestamp of when the collection was created (Unix epoch in seconds) */ 29 | public var createdAt: Int64 30 | public var voiceQueryModel: VoiceQueryModelCollectionConfig? 31 | 32 | 33 | public init(name: String, fields: [Field], defaultSortingField: String? = nil, tokenSeparators: [String]? = nil, enableNestedFields: Bool? = nil, symbolsToIndex: [String]? = nil, numDocuments: Int64, createdAt: Int64, voiceQueryModel: VoiceQueryModelCollectionConfig? = nil) { 34 | self.name = name 35 | self.fields = fields 36 | self.defaultSortingField = defaultSortingField 37 | self.tokenSeparators = tokenSeparators 38 | self.enableNestedFields = enableNestedFields 39 | self.symbolsToIndex = symbolsToIndex 40 | self.numDocuments = numDocuments 41 | self.createdAt = createdAt 42 | self.voiceQueryModel = voiceQueryModel 43 | } 44 | 45 | public enum CodingKeys: String, CodingKey { 46 | case name 47 | case fields 48 | case defaultSortingField = "default_sorting_field" 49 | case tokenSeparators = "token_separators" 50 | case enableNestedFields = "enable_nested_fields" 51 | case symbolsToIndex = "symbols_to_index" 52 | case numDocuments = "num_documents" 53 | case createdAt = "created_at" 54 | case voiceQueryModel = "voice_query_model" 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/CollectionSchema.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CollectionSchema.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | public struct CollectionSchema: Codable { 13 | 14 | /** Name of the collection */ 15 | public var name: String 16 | /** A list of fields for querying, filtering and faceting */ 17 | public var fields: [Field] 18 | /** The name of an int32 / float field that determines the order in which the search results are ranked when a sort_by clause is not provided during searching. This field must indicate some kind of popularity. */ 19 | public var defaultSortingField: String? 20 | /** List of symbols or special characters to be used for splitting the text into individual words in addition to space and new-line characters. */ 21 | public var tokenSeparators: [String]? 22 | /** Enables experimental support at a collection level for nested object or object array fields. This field is only available if the Typesense server is version `0.24.0.rcn34` or later. */ 23 | public var enableNestedFields: Bool? 24 | /** List of symbols or special characters to be indexed. */ 25 | public var symbolsToIndex: [String]? 26 | public var voiceQueryModel: VoiceQueryModelCollectionConfig? 27 | 28 | public init(name: String, fields: [Field], defaultSortingField: String? = nil, tokenSeparators: [String]? = nil, enableNestedFields: Bool? = nil, symbolsToIndex: [String]? = nil, voiceQueryModel: VoiceQueryModelCollectionConfig? = nil) { 29 | self.name = name 30 | self.fields = fields 31 | self.defaultSortingField = defaultSortingField 32 | self.tokenSeparators = tokenSeparators 33 | self.enableNestedFields = enableNestedFields 34 | self.symbolsToIndex = symbolsToIndex 35 | self.voiceQueryModel = voiceQueryModel 36 | } 37 | 38 | public enum CodingKeys: String, CodingKey { 39 | case name 40 | case fields 41 | case defaultSortingField = "default_sorting_field" 42 | case tokenSeparators = "token_separators" 43 | case enableNestedFields = "enable_nested_fields" 44 | case symbolsToIndex = "symbols_to_index" 45 | case voiceQueryModel = "voice_query_model" 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/CollectionUpdateSchema.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CollectionUpdateSchema.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | public struct CollectionUpdateSchema: Codable { 13 | 14 | /** A list of fields for querying, filtering and faceting */ 15 | public var fields: [Field] 16 | 17 | public init(fields: [Field]) { 18 | self.fields = fields 19 | } 20 | 21 | 22 | } 23 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/ConversationModelCreateSchema.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ConversationModelCreateSchema.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | public struct ConversationModelCreateSchema: Codable { 13 | 14 | /** An explicit id for the model, otherwise the API will return a response with an auto-generated conversation model id. */ 15 | public var _id: String? 16 | /** Name of the LLM model offered by OpenAI, Cloudflare or vLLM */ 17 | public var modelName: String 18 | /** The LLM service's API Key */ 19 | public var apiKey: String? 20 | /** Typesense collection that stores the historical conversations */ 21 | public var historyCollection: String? 22 | /** LLM service's account ID (only applicable for Cloudflare) */ 23 | public var accountId: String? 24 | /** The system prompt that contains special instructions to the LLM */ 25 | public var systemPrompt: String? 26 | /** Time interval in seconds after which the messages would be deleted. Default: 86400 (24 hours) */ 27 | public var ttl: Int? 28 | /** The maximum number of bytes to send to the LLM in every API call. Consult the LLM's documentation on the number of bytes supported in the context window. */ 29 | public var maxBytes: Int 30 | /** URL of vLLM service */ 31 | public var vllmUrl: String? 32 | 33 | public init(_id: String? = nil, modelName: String, apiKey: String? = nil, historyCollection: String? = nil, accountId: String? = nil, systemPrompt: String? = nil, ttl: Int? = nil, maxBytes: Int, vllmUrl: String? = nil) { 34 | self._id = _id 35 | self.modelName = modelName 36 | self.apiKey = apiKey 37 | self.historyCollection = historyCollection 38 | self.accountId = accountId 39 | self.systemPrompt = systemPrompt 40 | self.ttl = ttl 41 | self.maxBytes = maxBytes 42 | self.vllmUrl = vllmUrl 43 | } 44 | 45 | public enum CodingKeys: String, CodingKey { 46 | case _id = "id" 47 | case modelName = "model_name" 48 | case apiKey = "api_key" 49 | case historyCollection = "history_collection" 50 | case accountId = "account_id" 51 | case systemPrompt = "system_prompt" 52 | case ttl 53 | case maxBytes = "max_bytes" 54 | case vllmUrl = "vllm_url" 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/ConversationModelSchema.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ConversationModelSchema.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | public struct ConversationModelSchema: Codable { 13 | 14 | /** An explicit id for the model, otherwise the API will return a response with an auto-generated conversation model id. */ 15 | public var _id: String 16 | /** Name of the LLM model offered by OpenAI, Cloudflare or vLLM */ 17 | public var modelName: String? 18 | /** The LLM service's API Key */ 19 | public var apiKey: String? 20 | /** Typesense collection that stores the historical conversations */ 21 | public var historyCollection: String? 22 | /** LLM service's account ID (only applicable for Cloudflare) */ 23 | public var accountId: String? 24 | /** The system prompt that contains special instructions to the LLM */ 25 | public var systemPrompt: String? 26 | /** Time interval in seconds after which the messages would be deleted. Default: 86400 (24 hours) */ 27 | public var ttl: Int? 28 | /** The maximum number of bytes to send to the LLM in every API call. Consult the LLM's documentation on the number of bytes supported in the context window. */ 29 | public var maxBytes: Int? 30 | /** URL of vLLM service */ 31 | public var vllmUrl: String? 32 | 33 | public init(_id: String, modelName: String? = nil, apiKey: String? = nil, historyCollection: String? = nil, accountId: String? = nil, systemPrompt: String? = nil, ttl: Int? = nil, maxBytes: Int? = nil, vllmUrl: String? = nil) { 34 | self._id = _id 35 | self.modelName = modelName 36 | self.apiKey = apiKey 37 | self.historyCollection = historyCollection 38 | self.accountId = accountId 39 | self.systemPrompt = systemPrompt 40 | self.ttl = ttl 41 | self.maxBytes = maxBytes 42 | self.vllmUrl = vllmUrl 43 | } 44 | 45 | public enum CodingKeys: String, CodingKey { 46 | case _id = "id" 47 | case modelName = "model_name" 48 | case apiKey = "api_key" 49 | case historyCollection = "history_collection" 50 | case accountId = "account_id" 51 | case systemPrompt = "system_prompt" 52 | case ttl 53 | case maxBytes = "max_bytes" 54 | case vllmUrl = "vllm_url" 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/ConversationModelUpdateSchema.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ConversationModelUpdateSchema.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | public struct ConversationModelUpdateSchema: Codable { 13 | 14 | /** An explicit id for the model, otherwise the API will return a response with an auto-generated conversation model id. */ 15 | public var _id: String? 16 | /** Name of the LLM model offered by OpenAI, Cloudflare or vLLM */ 17 | public var modelName: String? 18 | /** The LLM service's API Key */ 19 | public var apiKey: String? 20 | /** Typesense collection that stores the historical conversations */ 21 | public var historyCollection: String? 22 | /** LLM service's account ID (only applicable for Cloudflare) */ 23 | public var accountId: String? 24 | /** The system prompt that contains special instructions to the LLM */ 25 | public var systemPrompt: String? 26 | /** Time interval in seconds after which the messages would be deleted. Default: 86400 (24 hours) */ 27 | public var ttl: Int? 28 | /** The maximum number of bytes to send to the LLM in every API call. Consult the LLM's documentation on the number of bytes supported in the context window. */ 29 | public var maxBytes: Int? 30 | /** URL of vLLM service */ 31 | public var vllmUrl: String? 32 | 33 | public init(_id: String? = nil, modelName: String? = nil, apiKey: String? = nil, historyCollection: String? = nil, accountId: String? = nil, systemPrompt: String? = nil, ttl: Int? = nil, maxBytes: Int? = nil, vllmUrl: String? = nil) { 34 | self._id = _id 35 | self.modelName = modelName 36 | self.apiKey = apiKey 37 | self.historyCollection = historyCollection 38 | self.accountId = accountId 39 | self.systemPrompt = systemPrompt 40 | self.ttl = ttl 41 | self.maxBytes = maxBytes 42 | self.vllmUrl = vllmUrl 43 | } 44 | 45 | public enum CodingKeys: String, CodingKey { 46 | case _id = "id" 47 | case modelName = "model_name" 48 | case apiKey = "api_key" 49 | case historyCollection = "history_collection" 50 | case accountId = "account_id" 51 | case systemPrompt = "system_prompt" 52 | case ttl 53 | case maxBytes = "max_bytes" 54 | case vllmUrl = "vllm_url" 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/DebugRetrieveSchema.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | 4 | 5 | public struct DebugRetrieveSchema: Codable { 6 | 7 | public var state: Int 8 | public var version: String 9 | 10 | public init(state: Int, version: String) { 11 | self.state = state 12 | self.version = version 13 | } 14 | 15 | 16 | } 17 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/DeleteDocumentsParameters.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DeleteDocumentsParameters.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | public struct DeleteDocumentsParameters: Codable { 13 | 14 | public var filterBy: String 15 | /** Batch size parameter controls the number of documents that should be deleted at a time. A larger value will speed up deletions, but will impact performance of other operations running on the server. */ 16 | public var batchSize: Int? 17 | public var ignoreNotFound: Bool? 18 | 19 | public init(filterBy: String, batchSize: Int? = nil, ignoreNotFound: Bool? = nil) { 20 | self.filterBy = filterBy 21 | self.batchSize = batchSize 22 | self.ignoreNotFound = ignoreNotFound 23 | } 24 | 25 | public enum CodingKeys: String, CodingKey { 26 | case filterBy = "filter_by" 27 | case batchSize = "batch_size" 28 | case ignoreNotFound = "ignore_not_found" 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/DeleteDocumentsResponse.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | 4 | 5 | public struct DeleteDocumentsResponse: Codable { 6 | 7 | public var numDeleted: Int 8 | 9 | public init(numDeleted: Int) { 10 | self.numDeleted = numDeleted 11 | } 12 | 13 | public enum CodingKeys: String, CodingKey { 14 | case numDeleted = "num_deleted" 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/DocumentIndexParameters.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | 4 | 5 | public struct DocumentIndexParameters: Codable { 6 | 7 | public var dirtyValues: DirtyValues? 8 | 9 | public init(dirtyValues: DirtyValues? = nil) { 10 | self.dirtyValues = dirtyValues 11 | } 12 | 13 | public enum CodingKeys: String, CodingKey { 14 | case dirtyValues = "dirty_values" 15 | } 16 | 17 | } 18 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/ExportDocumentsParameters.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExportDocumentsParameters.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | public struct ExportDocumentsParameters: Codable { 13 | 14 | /** Filter conditions for refining your search results. Separate multiple conditions with &&. */ 15 | public var filterBy: String? 16 | /** List of fields from the document to include in the search result */ 17 | public var includeFields: String? 18 | /** List of fields from the document to exclude in the search result */ 19 | public var excludeFields: String? 20 | 21 | public init(filterBy: String? = nil, includeFields: String? = nil, excludeFields: String? = nil) { 22 | self.filterBy = filterBy 23 | self.includeFields = includeFields 24 | self.excludeFields = excludeFields 25 | } 26 | 27 | public enum CodingKeys: String, CodingKey { 28 | case filterBy = "filter_by" 29 | case includeFields = "include_fields" 30 | case excludeFields = "exclude_fields" 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/FacetCounts.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FacetCounts.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | public struct FacetCounts: Codable { 13 | 14 | public var counts: [FacetCountsCounts]? 15 | public var fieldName: String? 16 | public var stats: FacetCountsStats? 17 | 18 | public init(counts: [FacetCountsCounts]? = nil, fieldName: String? = nil, stats: FacetCountsStats? = nil) { 19 | self.counts = counts 20 | self.fieldName = fieldName 21 | self.stats = stats 22 | } 23 | 24 | public enum CodingKeys: String, CodingKey { 25 | case counts 26 | case fieldName = "field_name" 27 | case stats 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/FacetCountsCounts.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FacetCountsCounts.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | public struct FacetCountsCounts: Codable { 13 | 14 | public var count: Int? 15 | public var highlighted: String? 16 | public var value: String? 17 | 18 | public init(count: Int? = nil, highlighted: String? = nil, value: String? = nil) { 19 | self.count = count 20 | self.highlighted = highlighted 21 | self.value = value 22 | } 23 | 24 | 25 | } 26 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/FacetCountsStats.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FacetCountsStats.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | public struct FacetCountsStats: Codable { 13 | 14 | public var max: Double? 15 | public var min: Double? 16 | public var sum: Double? 17 | public var totalValues: Int? 18 | public var avg: Double? 19 | 20 | public init(max: Double? = nil, min: Double? = nil, sum: Double? = nil, totalValues: Int? = nil, avg: Double? = nil) { 21 | self.max = max 22 | self.min = min 23 | self.sum = sum 24 | self.totalValues = totalValues 25 | self.avg = avg 26 | } 27 | 28 | public enum CodingKeys: String, CodingKey { 29 | case max 30 | case min 31 | case sum 32 | case totalValues = "total_values" 33 | case avg 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/Field.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Field.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | public struct Field: Codable { 13 | 14 | public var name: String 15 | public var type: String 16 | public var _optional: Bool? 17 | public var facet: Bool? 18 | public var index: Bool? 19 | public var locale: String? 20 | public var sort: Bool? 21 | public var _infix: Bool? 22 | public var reference: String? 23 | public var numDim: Int? 24 | public var drop: Bool? 25 | /** Whether to store the image on disk. */ 26 | public var store: Bool? 27 | public var embed: FieldEmbed? 28 | 29 | public init(name: String, type: String, _optional: Bool? = nil, facet: Bool? = nil, index: Bool? = nil, locale: String? = nil, sort: Bool? = nil, _infix: Bool? = nil, reference: String? = nil, numDim: Int? = nil, drop: Bool? = nil, store: Bool? = nil, embed: FieldEmbed? = nil) { 30 | self.name = name 31 | self.type = type 32 | self._optional = _optional 33 | self.facet = facet 34 | self.index = index 35 | self.locale = locale 36 | self.sort = sort 37 | self._infix = _infix 38 | self.reference = reference 39 | self.numDim = numDim 40 | self.drop = drop 41 | self.store = store 42 | self.embed = embed 43 | } 44 | 45 | public enum CodingKeys: String, CodingKey { 46 | case name 47 | case type 48 | case _optional = "optional" 49 | case facet 50 | case index 51 | case locale 52 | case sort 53 | case _infix = "infix" 54 | case reference 55 | case numDim = "num_dim" 56 | case drop 57 | case store 58 | case embed 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/FieldEmbed.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FieldEmbed.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | public struct FieldEmbed: Codable { 13 | 14 | public var from: [String] 15 | public var modelConfig: FieldEmbedModelConfig 16 | 17 | public init(from: [String], modelConfig: FieldEmbedModelConfig) { 18 | self.from = from 19 | self.modelConfig = modelConfig 20 | } 21 | 22 | public enum CodingKeys: String, CodingKey { 23 | case from 24 | case modelConfig = "model_config" 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/FieldEmbedModelConfig.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FieldEmbedModelConfig.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | public struct FieldEmbedModelConfig: Codable { 13 | 14 | public var modelName: String 15 | public var apiKey: String? 16 | public var accessToken: String? 17 | public var clientId: String? 18 | public var clientSecret: String? 19 | public var projectId: String? 20 | 21 | public init(modelName: String, apiKey: String? = nil, accessToken: String? = nil, clientId: String? = nil, clientSecret: String? = nil, projectId: String? = nil) { 22 | self.modelName = modelName 23 | self.apiKey = apiKey 24 | self.accessToken = accessToken 25 | self.clientId = clientId 26 | self.clientSecret = clientSecret 27 | self.projectId = projectId 28 | } 29 | 30 | public enum CodingKeys: String, CodingKey { 31 | case modelName = "model_name" 32 | case apiKey = "api_key" 33 | case accessToken = "access_token" 34 | case clientId = "client_id" 35 | case clientSecret = "client_secret" 36 | case projectId = "project_id" 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/HealthStatus.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HealthStatus.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | public struct HealthStatus: Codable { 13 | 14 | public var ok: Bool 15 | 16 | public init(ok: Bool) { 17 | self.ok = ok 18 | } 19 | 20 | 21 | } 22 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/ImportDocumentsParameters.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ImportDocumentsParameters.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | public struct ImportDocumentsParameters: Codable { 13 | 14 | public var action: IndexAction? 15 | public var batchSize: Int? 16 | public var dirtyValues: DirtyValues? 17 | public var remoteEmbeddingBatchSize: Int? 18 | public var returnDoc: Bool? 19 | public var returnId: Bool? 20 | 21 | public init(action: IndexAction? = nil, batchSize: Int? = nil, dirtyValues: DirtyValues? = nil, remoteEmbeddingBatchSize: Int? = nil, returnDoc: Bool? = nil, returnId: Bool? = nil) { 22 | self.action = action 23 | self.batchSize = batchSize 24 | self.dirtyValues = dirtyValues 25 | self.remoteEmbeddingBatchSize = remoteEmbeddingBatchSize 26 | self.returnDoc = returnDoc 27 | self.returnId = returnId 28 | } 29 | 30 | public enum CodingKeys: String, CodingKey { 31 | case action 32 | case batchSize = "batch_size" 33 | case dirtyValues = "dirty_values" 34 | case remoteEmbeddingBatchSize = "remote_embedding_batch_size" 35 | case returnDoc = "return_doc" 36 | case returnId = "return_id" 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/InlineResponse2002.swift: -------------------------------------------------------------------------------- 1 | // 2 | // InlineResponse2002.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | public struct InlineResponse2002: Codable { 13 | 14 | public var version: String? 15 | 16 | public init(version: String? = nil) { 17 | self.version = version 18 | } 19 | 20 | 21 | } 22 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/ModelErrorResponse.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ModelErrorResponse.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | public struct ModelErrorResponse: Codable { 13 | 14 | public var message: String? 15 | 16 | public init(message: String? = nil) { 17 | self.message = message 18 | } 19 | 20 | 21 | } 22 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/MultiSearchResult.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MultiSearchResult.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | public struct MultiSearchResult: Decodable { 13 | 14 | public var results: [SearchResult] 15 | public var conversation: SearchResultConversation? 16 | 17 | public init(results: [SearchResult], conversation: SearchResultConversation? = nil) { 18 | self.results = results 19 | self.conversation = conversation 20 | } 21 | 22 | 23 | } 24 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/MultiSearchSearchesParameter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MultiSearchSearchesParameter.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | public struct MultiSearchSearchesParameter: Codable { 11 | public var searches: [MultiSearchCollectionParameters] 12 | 13 | public init(searches: [MultiSearchCollectionParameters]) { 14 | self.searches = searches 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/PresetDeleteSchema.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PresetDeleteSchema.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | public struct PresetDeleteSchema: Codable { 13 | 14 | public var name: String 15 | 16 | public init(name: String) { 17 | self.name = name 18 | } 19 | 20 | 21 | } 22 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/PresetSchema.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | 4 | 5 | public struct PresetSchema: Codable { 6 | public var name: String 7 | public var value: PresetValue 8 | 9 | public init(name: String, value: PresetValue) { 10 | self.name = name 11 | self.value = value 12 | } 13 | 14 | 15 | } 16 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/PresetUpsertSchema.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | 4 | 5 | public struct PresetUpsertSchema: Codable { 6 | 7 | public var value: PresetValue 8 | 9 | public init(value: PresetValue) { 10 | self.value = value 11 | } 12 | 13 | 14 | } 15 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/PresetValue.swift: -------------------------------------------------------------------------------- 1 | public enum PresetValue: Codable { 2 | case multiSearch(MultiSearchSearchesParameter) 3 | case singleCollectionSearch(SearchParameters) 4 | 5 | public init (from decoder: Decoder) throws { 6 | if let multiSearch = try? MultiSearchSearchesParameter(from: decoder) { 7 | self = .multiSearch(multiSearch) 8 | } 9 | else if let singleCollectionSearch = try? SearchParameters(from: decoder) { 10 | self = .singleCollectionSearch(singleCollectionSearch) 11 | } else { 12 | throw DecodingError.dataCorrupted(DecodingError.Context( 13 | codingPath: decoder.codingPath, 14 | debugDescription: "Unable to decode value for preset `value`" 15 | ) 16 | ) 17 | } 18 | } 19 | 20 | public func encode(to encoder: Encoder) throws { 21 | switch self { 22 | case .multiSearch(let multiSearch): 23 | try multiSearch.encode(to: encoder) 24 | case .singleCollectionSearch(let singleCollectionSearch): 25 | try singleCollectionSearch.encode(to: encoder) 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/PresetsRetrieveSchema.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | 4 | 5 | public struct PresetsRetrieveSchema: Codable { 6 | 7 | public var presets: [PresetSchema] 8 | 9 | public init(presets: [PresetSchema]) { 10 | self.presets = presets 11 | } 12 | 13 | 14 | } 15 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/ScopedKeyParameters.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ScopedKeyParameters.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | public struct ScopedKeyParameters: Codable { 13 | 14 | public var filterBy: String? 15 | public var expiresAt: Decimal? 16 | 17 | public init(filterBy: String? = nil, expiresAt: Decimal? = nil) { 18 | self.filterBy = filterBy 19 | self.expiresAt = expiresAt 20 | } 21 | 22 | public enum CodingKeys: String, CodingKey { 23 | case filterBy = "filter_by" 24 | case expiresAt = "expires_at" 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/SearchGroupedHit.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SearchGroupedHit.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | import AnyCodable 10 | 11 | public struct SearchGroupedHit: Decodable { 12 | 13 | public var found: Int? 14 | public var groupKey: [AnyCodable] 15 | /** The documents that matched the search query */ 16 | public var hits: [SearchResultHit] 17 | 18 | public init(found: Int? = nil, groupKey: [AnyCodable], hits: [SearchResultHit]) { 19 | self.found = found 20 | self.groupKey = groupKey 21 | self.hits = hits 22 | } 23 | 24 | public enum CodingKeys: String, CodingKey { 25 | case found 26 | case groupKey = "group_key" 27 | case hits 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/SearchHighlight.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SearchHighlight.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | public struct SearchHighlight: Codable { 13 | 14 | public var field: String? 15 | /** Present only for (non-array) string fields */ 16 | public var snippet: String? 17 | /** Present only for (array) string[] fields */ 18 | public var snippets: [String]? 19 | /** Full field value with highlighting, present only for (non-array) string fields */ 20 | public var value: String? 21 | /** Full field value with highlighting, present only for (array) string[] fields */ 22 | public var values: [String]? 23 | /** The indices property will be present only for string[] fields and will contain the corresponding indices of the snippets in the search field */ 24 | public var indices: [Int]? 25 | public var matchedTokens: StringQuantum? 26 | 27 | public init(field: String? = nil, snippet: String? = nil, snippets: [String]? = nil, value: String? = nil, values: [String]? = nil, indices: [Int]? = nil, matchedTokens: StringQuantum? = nil) { 28 | self.field = field 29 | self.snippet = snippet 30 | self.snippets = snippets 31 | self.value = value 32 | self.values = values 33 | self.indices = indices 34 | self.matchedTokens = matchedTokens 35 | } 36 | 37 | public enum CodingKeys: String, CodingKey { 38 | case field 39 | case snippet 40 | case snippets 41 | case value 42 | case values 43 | case indices 44 | case matchedTokens = "matched_tokens" 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/SearchOverride.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SearchOverride.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | public struct SearchOverride: Codable { 13 | 14 | public var _id: String 15 | public var rule: SearchOverrideRule 16 | /** List of document `id`s that should be included in the search results with their corresponding `position`s. */ 17 | public var includes: [SearchOverrideInclude]? 18 | /** List of document `id`s that should be excluded from the search results. */ 19 | public var excludes: [SearchOverrideExclude]? 20 | /** A filter by clause that is applied to any search query that matches the override rule. */ 21 | public var filterBy: String? 22 | /** Indicates whether search query tokens that exist in the override's rule should be removed from the search query. */ 23 | public var removeMatchedTokens: Bool? 24 | /** Return a custom JSON object in the Search API response, when this rule is triggered. This can can be used to display a pre-defined message (eg: a promotion banner) on the front-end when a particular rule is triggered. */ 25 | public var metadata: T? 26 | /** A sort by clause that is applied to any search query that matches the override rule. */ 27 | public var sortBy: String? 28 | /** Replaces the current search query with this value, when the search query matches the override rule. */ 29 | public var replaceQuery: String? 30 | /** When set to true, the filter conditions of the query is applied to the curated records as well. Default: false. */ 31 | public var filterCuratedHits: Bool? 32 | /** A Unix timestamp that indicates the date/time from which the override will be active. You can use this to create override rules that start applying from a future point in time. */ 33 | public var effectiveFromTs: Int? 34 | /** A Unix timestamp that indicates the date/time until which the override will be active. You can use this to create override rules that stop applying after a period of time. */ 35 | public var effectiveToTs: Int? 36 | /** When set to true, override processing will stop at the first matching rule. When set to false override processing will continue and multiple override actions will be triggered in sequence. Overrides are processed in the lexical sort order of their id field. Default: true. */ 37 | public var stopProcessing: Bool? 38 | 39 | public init(_id: String, rule: SearchOverrideRule, includes: [SearchOverrideInclude]? = nil, excludes: [SearchOverrideExclude]? = nil, filterBy: String? = nil, removeMatchedTokens: Bool? = nil, metadata: T? = nil, sortBy: String? = nil, replaceQuery: String? = nil, filterCuratedHits: Bool? = nil, effectiveFromTs: Int? = nil, effectiveToTs: Int? = nil, stopProcessing: Bool? = nil) { 40 | self._id = _id 41 | self.rule = rule 42 | self.includes = includes 43 | self.excludes = excludes 44 | self.filterBy = filterBy 45 | self.removeMatchedTokens = removeMatchedTokens 46 | self.metadata = metadata 47 | self.sortBy = sortBy 48 | self.replaceQuery = replaceQuery 49 | self.filterCuratedHits = filterCuratedHits 50 | self.effectiveFromTs = effectiveFromTs 51 | self.effectiveToTs = effectiveToTs 52 | self.stopProcessing = stopProcessing 53 | } 54 | 55 | public enum CodingKeys: String, CodingKey { 56 | case _id = "id" 57 | case rule 58 | case includes 59 | case excludes 60 | case filterBy = "filter_by" 61 | case removeMatchedTokens = "remove_matched_tokens" 62 | case metadata 63 | case sortBy = "sort_by" 64 | case replaceQuery = "replace_query" 65 | case filterCuratedHits = "filter_curated_hits" 66 | case effectiveFromTs = "effective_from_ts" 67 | case effectiveToTs = "effective_to_ts" 68 | case stopProcessing = "stop_processing" 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/SearchOverrideDeleteResponse.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SearchOverrideDeleteResponse.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | public struct SearchOverrideDeleteResponse: Codable { 13 | 14 | /** The id of a deleted override. */ 15 | public var _id: String 16 | 17 | public init(_id: String) { 18 | self._id = _id 19 | } 20 | 21 | public enum CodingKeys: String, CodingKey { 22 | case _id = "id" 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/SearchOverrideExclude.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SearchOverrideExclude.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | public struct SearchOverrideExclude: Codable { 13 | 14 | /** document id that should be excluded from the search results. */ 15 | public var _id: String 16 | 17 | public init(_id: String) { 18 | self._id = _id 19 | } 20 | 21 | public enum CodingKeys: String, CodingKey { 22 | case _id = "id" 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/SearchOverrideInclude.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SearchOverrideInclude.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | public struct SearchOverrideInclude: Codable { 13 | 14 | /** document id that should be included */ 15 | public var _id: String 16 | /** position number where document should be included in the search results */ 17 | public var position: Int 18 | 19 | public init(_id: String, position: Int) { 20 | self._id = _id 21 | self.position = position 22 | } 23 | 24 | public enum CodingKeys: String, CodingKey { 25 | case _id = "id" 26 | case position 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/SearchOverrideRule.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SearchOverrideRule.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | public struct SearchOverrideRule: Codable { 13 | 14 | public enum Match: String, Codable { 15 | case exact = "exact" 16 | case contains = "contains" 17 | } 18 | /** List of tag values to associate with this override rule. */ 19 | public var tags: [String]? 20 | /** Indicates what search queries should be overridden */ 21 | public var query: String? 22 | /** Indicates whether the match on the query term should be `exact` or `contains`. If we want to match all queries that contained the word `apple`, we will use the `contains` match instead. */ 23 | public var match: Match? 24 | /** Indicates that the override should apply when the filter_by parameter in a search query exactly matches the string specified here (including backticks, spaces, brackets, etc). */ 25 | public var filterBy: String? 26 | 27 | public init(tags: [String]? = nil, query: String? = nil, match: Match? = nil, filterBy: String? = nil) { 28 | self.tags = tags 29 | self.query = query 30 | self.match = match 31 | self.filterBy = filterBy 32 | } 33 | 34 | public enum CodingKeys: String, CodingKey { 35 | case tags 36 | case query 37 | case match 38 | case filterBy = "filter_by" 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/SearchOverrideSchema.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SearchOverrideSchema.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | public struct SearchOverrideSchema: Codable { 13 | 14 | public var rule: SearchOverrideRule 15 | /** List of document `id`s that should be included in the search results with their corresponding `position`s. */ 16 | public var includes: [SearchOverrideInclude]? 17 | /** List of document `id`s that should be excluded from the search results. */ 18 | public var excludes: [SearchOverrideExclude]? 19 | /** A filter by clause that is applied to any search query that matches the override rule. */ 20 | public var filterBy: String? 21 | /** Indicates whether search query tokens that exist in the override's rule should be removed from the search query. */ 22 | public var removeMatchedTokens: Bool? 23 | /** Return a custom JSON object in the Search API response, when this rule is triggered. This can can be used to display a pre-defined message (eg: a promotion banner) on the front-end when a particular rule is triggered. */ 24 | public var metadata: T? 25 | /** A sort by clause that is applied to any search query that matches the override rule. */ 26 | public var sortBy: String? 27 | /** Replaces the current search query with this value, when the search query matches the override rule. */ 28 | public var replaceQuery: String? 29 | /** When set to true, the filter conditions of the query is applied to the curated records as well. Default: false. */ 30 | public var filterCuratedHits: Bool? 31 | /** A Unix timestamp that indicates the date/time from which the override will be active. You can use this to create override rules that start applying from a future point in time. */ 32 | public var effectiveFromTs: Int? 33 | /** A Unix timestamp that indicates the date/time until which the override will be active. You can use this to create override rules that stop applying after a period of time. */ 34 | public var effectiveToTs: Int? 35 | /** When set to true, override processing will stop at the first matching rule. When set to false override processing will continue and multiple override actions will be triggered in sequence. Overrides are processed in the lexical sort order of their id field. Default: true. */ 36 | public var stopProcessing: Bool? 37 | 38 | public init(rule: SearchOverrideRule, includes: [SearchOverrideInclude]? = nil, excludes: [SearchOverrideExclude]? = nil, filterBy: String? = nil, removeMatchedTokens: Bool? = nil, metadata: T? = nil, sortBy: String? = nil, replaceQuery: String? = nil, filterCuratedHits: Bool? = nil, effectiveFromTs: Int? = nil, effectiveToTs: Int? = nil, stopProcessing: Bool? = nil) { 39 | self.rule = rule 40 | self.includes = includes 41 | self.excludes = excludes 42 | self.filterBy = filterBy 43 | self.removeMatchedTokens = removeMatchedTokens 44 | self.metadata = metadata 45 | self.sortBy = sortBy 46 | self.replaceQuery = replaceQuery 47 | self.filterCuratedHits = filterCuratedHits 48 | self.effectiveFromTs = effectiveFromTs 49 | self.effectiveToTs = effectiveToTs 50 | self.stopProcessing = stopProcessing 51 | } 52 | 53 | public enum CodingKeys: String, CodingKey { 54 | case rule 55 | case includes 56 | case excludes 57 | case filterBy = "filter_by" 58 | case removeMatchedTokens = "remove_matched_tokens" 59 | case metadata 60 | case sortBy = "sort_by" 61 | case replaceQuery = "replace_query" 62 | case filterCuratedHits = "filter_curated_hits" 63 | case effectiveFromTs = "effective_from_ts" 64 | case effectiveToTs = "effective_to_ts" 65 | case stopProcessing = "stop_processing" 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/SearchOverridesResponse.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SearchOverridesResponse.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | public struct SearchOverridesResponse: Codable { 13 | 14 | public var overrides: [SearchOverride] 15 | 16 | public init(overrides: [SearchOverride]) { 17 | self.overrides = overrides 18 | } 19 | 20 | 21 | } 22 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/SearchResult.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SearchResult.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | public struct SearchResult: Decodable { 13 | 14 | public var facetCounts: [FacetCounts]? 15 | /** The number of documents found */ 16 | public var found: Int? 17 | /** The number of milliseconds the search took */ 18 | public var searchTimeMs: Int? 19 | /** The total number of documents in the collection */ 20 | public var outOf: Int? 21 | /** Whether the search was cut off */ 22 | public var searchCutoff: Bool? 23 | /** The search result page number */ 24 | public var page: Int? 25 | public var groupedHits: [SearchGroupedHit]? 26 | /** The documents that matched the search query */ 27 | public var hits: [SearchResultHit]? 28 | public var requestParams: SearchResultRequestParams? 29 | public var conversation: SearchResultConversation? 30 | 31 | public init(facetCounts: [FacetCounts]? = nil, found: Int? = nil, searchTimeMs: Int? = nil, outOf: Int? = nil, searchCutoff: Bool? = nil, page: Int? = nil, groupedHits: [SearchGroupedHit]? = nil, hits: [SearchResultHit]? = nil, requestParams: SearchResultRequestParams? = nil, conversation: SearchResultConversation? = nil) { 32 | self.facetCounts = facetCounts 33 | self.found = found 34 | self.searchTimeMs = searchTimeMs 35 | self.outOf = outOf 36 | self.searchCutoff = searchCutoff 37 | self.page = page 38 | self.groupedHits = groupedHits 39 | self.hits = hits 40 | self.requestParams = requestParams 41 | self.conversation = conversation 42 | } 43 | 44 | public enum CodingKeys: String, CodingKey { 45 | case facetCounts = "facet_counts" 46 | case found 47 | case searchTimeMs = "search_time_ms" 48 | case outOf = "out_of" 49 | case searchCutoff = "search_cutoff" 50 | case page 51 | case groupedHits = "grouped_hits" 52 | case hits 53 | case requestParams = "request_params" 54 | case conversation 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/SearchResultConversation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SearchResultConversation.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | public struct SearchResultConversation: Codable { 13 | 14 | public var answer: String 15 | public var conversationHistory: [[String: String]] 16 | public var conversationId: String 17 | public var query: String 18 | 19 | public init(answer: String, conversationHistory: [[String: String]], conversationId: String, query: String) { 20 | self.answer = answer 21 | self.conversationHistory = conversationHistory 22 | self.conversationId = conversationId 23 | self.query = query 24 | } 25 | 26 | public enum CodingKeys: String, CodingKey { 27 | case answer 28 | case conversationHistory = "conversation_history" 29 | case conversationId = "conversation_id" 30 | case query 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/SearchResultHit.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SearchResultHit.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | public struct SearchResultHit: Decodable { 13 | 14 | /** (Deprecated) Contains highlighted portions of the search fields */ 15 | public var highlights: [SearchHighlight]? 16 | public var document: T? 17 | public var textMatch: Int64? 18 | public var geoDistanceMeters: [String:Int]? 19 | /** Distance between the query vector and matching document's vector value */ 20 | public var vectorDistance: Float? 21 | 22 | public init(highlights: [SearchHighlight]? = nil, document: T? = nil, textMatch: Int64? = nil, geoDistanceMeters: [String:Int]? = nil, vectorDistance: Float? = nil) { 23 | self.highlights = highlights 24 | // self.highlightData = highlight 25 | self.document = document 26 | self.textMatch = textMatch 27 | self.geoDistanceMeters = geoDistanceMeters 28 | self.vectorDistance = vectorDistance 29 | } 30 | 31 | public init(from decoder: Decoder) throws { 32 | let container: KeyedDecodingContainer.CodingKeys> = try decoder.container(keyedBy: SearchResultHit.CodingKeys.self) 33 | self.highlights = try container.decodeIfPresent([SearchHighlight].self, forKey: SearchResultHit.CodingKeys.highlights) 34 | self.document = try container.decodeIfPresent(T.self, forKey: SearchResultHit.CodingKeys.document) 35 | self.textMatch = try container.decodeIfPresent(Int64.self, forKey: SearchResultHit.CodingKeys.textMatch) 36 | self.geoDistanceMeters = try container.decodeIfPresent([String : Int].self, forKey: SearchResultHit.CodingKeys.geoDistanceMeters) 37 | self.vectorDistance = try container.decodeIfPresent(Float.self, forKey: SearchResultHit.CodingKeys.vectorDistance) 38 | } 39 | 40 | public enum CodingKeys: String, CodingKey { 41 | case highlights 42 | case highlight 43 | case document 44 | case textMatch = "text_match" 45 | case geoDistanceMeters = "geo_distance_meters" 46 | case vectorDistance = "vector_distance" 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/SearchResultRequestParams.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SearchResultRequestParams.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | public struct SearchResultRequestParams: Codable { 13 | 14 | public var collectionName: String 15 | public var q: String 16 | public var perPage: Int 17 | public var voiceQuery: SearchResultRequestParamsVoiceQuery? 18 | 19 | public init(collectionName: String, q: String, perPage: Int, voiceQuery: SearchResultRequestParamsVoiceQuery? = nil) { 20 | self.collectionName = collectionName 21 | self.q = q 22 | self.perPage = perPage 23 | self.voiceQuery = voiceQuery 24 | } 25 | 26 | public enum CodingKeys: String, CodingKey { 27 | case collectionName = "collection_name" 28 | case q 29 | case perPage = "per_page" 30 | case voiceQuery = "voice_query" 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/SearchResultRequestParamsVoiceQuery.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SearchResultRequestParamsVoiceQuery.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | public struct SearchResultRequestParamsVoiceQuery: Codable { 13 | 14 | public var transcribedQuery: String? 15 | 16 | public init(transcribedQuery: String? = nil) { 17 | self.transcribedQuery = transcribedQuery 18 | } 19 | 20 | public enum CodingKeys: String, CodingKey { 21 | case transcribedQuery = "transcribed_query" 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/SearchSynonym.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SearchSynonym.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | public struct SearchSynonym: Codable { 13 | 14 | /** For 1-way synonyms, indicates the root word that words in the `synonyms` parameter map to. */ 15 | public var root: String? 16 | /** Array of words that should be considered as synonyms. */ 17 | public var synonyms: [String] 18 | public var _id: String 19 | /** Locale for the synonym, leave blank to use the standard tokenizer. */ 20 | public var locale: String? 21 | /** By default, special characters are dropped from synonyms. Use this attribute to specify which special characters should be indexed as is. */ 22 | public var symbolsToIndex: [String]? 23 | 24 | public init(root: String? = nil, synonyms: [String], _id: String, locale: String? = nil, symbolsToIndex: [String]? = nil) { 25 | self.root = root 26 | self.synonyms = synonyms 27 | self._id = _id 28 | self.locale = locale 29 | self.symbolsToIndex = symbolsToIndex 30 | } 31 | 32 | public enum CodingKeys: String, CodingKey { 33 | case _id = "id" 34 | case root 35 | case synonyms 36 | case locale 37 | case symbolsToIndex = "symbols_to_index" 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/SearchSynonymSchema.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SearchSynonymSchema.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | public struct SearchSynonymSchema: Codable { 13 | 14 | /** For 1-way synonyms, indicates the root word that words in the `synonyms` parameter map to. */ 15 | public var root: String? 16 | /** Array of words that should be considered as synonyms. */ 17 | public var synonyms: [String] 18 | /** Locale for the synonym, leave blank to use the standard tokenizer. */ 19 | public var locale: String? 20 | /** By default, special characters are dropped from synonyms. Use this attribute to specify which special characters should be indexed as is. */ 21 | public var symbolsToIndex: [String]? 22 | 23 | public init(root: String? = nil, synonyms: [String], locale: String? = nil, symbolsToIndex: [String]? = nil) { 24 | self.root = root 25 | self.synonyms = synonyms 26 | self.locale = locale 27 | self.symbolsToIndex = symbolsToIndex 28 | } 29 | 30 | public enum CodingKeys: String, CodingKey { 31 | case root 32 | case synonyms 33 | case locale 34 | case symbolsToIndex = "symbols_to_index" 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/SearchSynonymsResponse.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SearchSynonymsResponse.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | public struct SearchSynonymsResponse: Codable { 13 | 14 | public var synonyms: [SearchSynonym] 15 | 16 | public init(synonyms: [SearchSynonym]) { 17 | self.synonyms = synonyms 18 | } 19 | 20 | 21 | } 22 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/SnapshotParameters.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SnapshotParameters.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | public struct SnapshotParameters: Codable { 13 | 14 | public var snapshotPath: String? 15 | 16 | public init(snapshotPath: String? = nil) { 17 | self.snapshotPath = snapshotPath 18 | } 19 | 20 | public enum CodingKeys: String, CodingKey { 21 | case snapshotPath = "snapshot_path" 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/StopwordsSetDeleteSchema.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | 4 | 5 | public struct StopwordsSetDeleteSchema: Codable { 6 | public var _id: String 7 | 8 | public init(_id: String) { 9 | self._id = _id 10 | } 11 | 12 | public enum CodingKeys: String, CodingKey { 13 | case _id = "id" 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/StopwordsSetRetrieveSchema.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StopwordsSetRetrieveSchema.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | public struct StopwordsSetRetrieveSchema: Codable { 13 | 14 | public var stopwords: StopwordsSetSchema 15 | 16 | public init(stopwords: StopwordsSetSchema) { 17 | self.stopwords = stopwords 18 | } 19 | 20 | 21 | } 22 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/StopwordsSetSchema.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StopwordsSetSchema.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | public struct StopwordsSetSchema: Codable { 13 | 14 | public var _id: String 15 | public var stopwords: [String] 16 | public var locale: String? 17 | 18 | public init(_id: String, stopwords: [String], locale: String? = nil) { 19 | self._id = _id 20 | self.stopwords = stopwords 21 | self.locale = locale 22 | } 23 | 24 | public enum CodingKeys: String, CodingKey { 25 | case _id = "id" 26 | case stopwords 27 | case locale 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/StopwordsSetUpsertSchema.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StopwordsSetUpsertSchema.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | public struct StopwordsSetUpsertSchema: Codable { 13 | 14 | public var stopwords: [String] 15 | public var locale: String? 16 | 17 | public init(stopwords: [String], locale: String? = nil) { 18 | self.stopwords = stopwords 19 | self.locale = locale 20 | } 21 | 22 | 23 | } 24 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/StopwordsSetsRetrieveAllSchema.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StopwordsSetsRetrieveAllSchema.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | public struct StopwordsSetsRetrieveAllSchema: Codable { 13 | 14 | public var stopwords: [StopwordsSetSchema] 15 | 16 | public init(stopwords: [StopwordsSetSchema]) { 17 | self.stopwords = stopwords 18 | } 19 | 20 | 21 | } 22 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/SuccessStatus.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SuccessStatus.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | public struct SuccessStatus: Codable { 13 | 14 | public var success: Bool 15 | 16 | public init(success: Bool) { 17 | self.success = success 18 | } 19 | 20 | 21 | } 22 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/UpdateByFilterResponse.swift: -------------------------------------------------------------------------------- 1 | // 2 | // InlineResponse2001.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | public struct UpdateByFilterResponse: Codable { 13 | 14 | /** The number of documents that have been updated */ 15 | public var numUpdated: Int 16 | 17 | public init(numUpdated: Int) { 18 | self.numUpdated = numUpdated 19 | } 20 | 21 | public enum CodingKeys: String, CodingKey { 22 | case numUpdated = "num_updated" 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/UpdateDocumentsByFilterParameters.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UpdateDocumentsParameters.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | 12 | public struct UpdateDocumentsByFilterParameters: Codable { 13 | 14 | public var filterBy: String? 15 | 16 | public init(filterBy: String? = nil) { 17 | self.filterBy = filterBy 18 | } 19 | 20 | public enum CodingKeys: String, CodingKey { 21 | case filterBy = "filter_by" 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /Sources/Typesense/Models/VoiceQueryModelCollectionConfig.swift: -------------------------------------------------------------------------------- 1 | // 2 | // VoiceQueryModelCollectionConfig.swift 3 | // 4 | // Generated by swagger-codegen 5 | // https://github.com/swagger-api/swagger-codegen 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | /** Configuration for the voice query model */ 12 | 13 | public struct VoiceQueryModelCollectionConfig: Codable { 14 | 15 | public var modelName: String? 16 | 17 | public init(modelName: String? = nil) { 18 | self.modelName = modelName 19 | } 20 | 21 | public enum CodingKeys: String, CodingKey { 22 | case modelName = "model_name" 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /Sources/Typesense/Node.swift: -------------------------------------------------------------------------------- 1 | public struct Node: CustomStringConvertible { 2 | var host: String? 3 | var port: String? 4 | var nodeProtocol: String? 5 | var url: String? 6 | var isHealthy: Bool = false 7 | var lastAccessTimeStamp: Int64 = 0 8 | 9 | public init(host: String? = nil, port: String? = nil, nodeProtocol: String? = nil, url: String? = nil) { 10 | if url == nil && (host == nil || port == nil || nodeProtocol == nil) { 11 | fatalError("Node `url` or `nodeProtocol` and `host` and `port` must be set!") 12 | } 13 | self.host = host 14 | self.port = port 15 | self.nodeProtocol = nodeProtocol 16 | self.url = url 17 | } 18 | 19 | public var description: String { 20 | if let url = self.url { 21 | return "Node: \(url)" 22 | } 23 | return "Node: \(nodeProtocol!)://\(host!):\(port!)" 24 | } 25 | 26 | public var healthStatus: String { 27 | return isHealthy ? "Healthy" : "Unhealthy" 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Sources/Typesense/Operations.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | #if canImport(FoundationNetworking) 3 | import FoundationNetworking 4 | #endif 5 | 6 | 7 | public struct Operations { 8 | var apiCall: ApiCall 9 | var RESOURCEPATH = "operations" 10 | 11 | init(apiCall: ApiCall) { 12 | self.apiCall = apiCall 13 | } 14 | 15 | public func getHealth() async throws -> (HealthStatus?, URLResponse?) { 16 | let (data, response) = try await apiCall.get(endPoint: "health") 17 | if let result = data { 18 | let health = try decoder.decode(HealthStatus.self, from: result) 19 | return (health, response) 20 | } 21 | return (nil, nil) 22 | } 23 | 24 | public func getStats() async throws -> (Data?, URLResponse?) { 25 | let (data, response) = try await apiCall.get(endPoint: "stats.json") 26 | return (data, response) 27 | } 28 | 29 | public func getMetrics() async throws -> (Data?, URLResponse?) { 30 | let (data, response) = try await apiCall.get(endPoint: "metrics.json") 31 | return (data, response) 32 | } 33 | 34 | public func getDebug() async throws -> (DebugRetrieveSchema?, URLResponse?) { 35 | let (data, response) = try await apiCall.get(endPoint: "debug") 36 | if let result = data { 37 | let decodedData = try decoder.decode(DebugRetrieveSchema.self, from: result) 38 | return (decodedData, response) 39 | } 40 | return (nil, response) 41 | } 42 | 43 | public func vote() async throws -> (SuccessStatus?, URLResponse?) { 44 | let (data, response) = try await apiCall.post(endPoint: "\(RESOURCEPATH)/vote", body: Data()) 45 | if let result = data { 46 | let success = try decoder.decode(SuccessStatus.self, from: result) 47 | return (success, response) 48 | } 49 | return (nil, nil) 50 | } 51 | 52 | public func snapshot(path: String? = "/tmp/typesense-data-snapshot") async throws -> (SuccessStatus?, URLResponse?) { 53 | let snapshotQueryParam = URLQueryItem(name: "snapshot_path", value: path) 54 | let (data, response) = try await apiCall.post(endPoint: "\(RESOURCEPATH)/snapshot", body: Data(), queryParameters: [snapshotQueryParam]) 55 | if let result = data { 56 | let success = try decoder.decode(SuccessStatus.self, from: result) 57 | return (success, response) 58 | } 59 | return (nil, nil) 60 | } 61 | 62 | public func toggleSlowRequestLog(seconds: Float) async throws -> (SuccessStatus?, URLResponse?) { 63 | let durationInMs = seconds * 1000 64 | let slowReq = SlowRequest(durationInMs) 65 | let slowReqData = try encoder.encode(slowReq) 66 | let (data, response) = try await apiCall.post(endPoint: "/config", body: slowReqData) 67 | if let result = data { 68 | let success = try decoder.decode(SuccessStatus.self, from: result) 69 | return (success, response) 70 | } 71 | return (nil, nil) 72 | } 73 | 74 | public func clearCache() async throws -> (SuccessStatus?, URLResponse?) { 75 | let (data, response) = try await apiCall.post(endPoint: "\(RESOURCEPATH)/cache/clear", body: Data()) 76 | if let result = data { 77 | let success = try decoder.decode(SuccessStatus.self, from: result) 78 | return (success, response) 79 | } 80 | return (nil, response) 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /Sources/Typesense/Override.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | #if canImport(FoundationNetworking) 3 | import FoundationNetworking 4 | #endif 5 | 6 | public struct Override { 7 | private var apiCall: ApiCall 8 | private var collectionName: String 9 | private var overrideId: String 10 | 11 | 12 | init(apiCall: ApiCall, collectionName: String, overrideId: String) { 13 | self.apiCall = apiCall 14 | self.collectionName = collectionName 15 | self.overrideId = overrideId 16 | } 17 | 18 | public func retrieve(metadataType: T.Type) async throws -> (SearchOverride?, URLResponse?) { 19 | let (data, response) = try await apiCall.get(endPoint: endpointPath()) 20 | if let result = data { 21 | let override = try decoder.decode(SearchOverride.self, from: result) 22 | return (override, response) 23 | } 24 | 25 | return (nil, response) 26 | } 27 | 28 | public func delete() async throws -> (SearchOverrideDeleteResponse?, URLResponse?) { 29 | let (data, response) = try await apiCall.delete(endPoint: endpointPath()) 30 | if let result = data { 31 | let decodedData = try decoder.decode(SearchOverrideDeleteResponse.self, from: result) 32 | return (decodedData, response) 33 | } 34 | return (nil, response) 35 | } 36 | 37 | private func endpointPath() throws -> String { 38 | return try "\(Collections.RESOURCEPATH)/\(collectionName.encodeURL())/\(Overrides.RESOURCEPATH)/\(overrideId.encodeURL())" 39 | } 40 | 41 | 42 | } 43 | -------------------------------------------------------------------------------- /Sources/Typesense/Overrides.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | #if canImport(FoundationNetworking) 3 | import FoundationNetworking 4 | #endif 5 | 6 | public struct Overrides { 7 | static let RESOURCEPATH = "overrides" 8 | private var apiCall: ApiCall 9 | private var collectionName: String 10 | 11 | 12 | init(apiCall: ApiCall, collectionName: String) { 13 | self.apiCall = apiCall 14 | self.collectionName = collectionName 15 | } 16 | 17 | public func upsert(overrideId: String, params: SearchOverrideSchema) async throws -> (SearchOverride?, URLResponse?) { 18 | let schemaData = try encoder.encode(params) 19 | let (data, response) = try await self.apiCall.put(endPoint: endpointPath(overrideId), body: schemaData) 20 | 21 | if let result = data { 22 | let override = try decoder.decode(SearchOverride.self, from: result) 23 | return (override, response) 24 | } 25 | 26 | return (nil, response) 27 | } 28 | 29 | public func retrieve(metadataType: T.Type) async throws -> (SearchOverridesResponse?, URLResponse?) { 30 | let (data, response) = try await self.apiCall.get(endPoint: endpointPath()) 31 | if let result = data { 32 | let overrides = try decoder.decode(SearchOverridesResponse.self, from: result) 33 | return (overrides, response) 34 | } 35 | return (nil, nil) 36 | } 37 | 38 | private func endpointPath(_ operation: String? = nil) throws -> String { 39 | let baseEndpoint = try "\(Collections.RESOURCEPATH)/\(collectionName.encodeURL())/\(Overrides.RESOURCEPATH)" 40 | if let operation = operation { 41 | return try "\(baseEndpoint)/\(operation.encodeURL())" 42 | } else { 43 | return baseEndpoint 44 | } 45 | } 46 | 47 | 48 | } 49 | -------------------------------------------------------------------------------- /Sources/Typesense/Preset.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | #if canImport(FoundationNetworking) 3 | import FoundationNetworking 4 | #endif 5 | 6 | public struct Preset { 7 | private var apiCall: ApiCall 8 | private var presetName: String 9 | 10 | 11 | init(apiCall: ApiCall, presetName: String) { 12 | self.apiCall = apiCall 13 | self.presetName = presetName 14 | } 15 | 16 | public func retrieve() async throws -> (PresetSchema?, URLResponse?) { 17 | let (data, response) = try await apiCall.get(endPoint: endpointPath()) 18 | if let result = data { 19 | let preset = try decoder.decode(PresetSchema.self, from: result) 20 | return (preset, response) 21 | } 22 | return (nil, response) 23 | } 24 | 25 | public func delete() async throws -> (PresetDeleteSchema?, URLResponse?) { 26 | let (data, response) = try await apiCall.delete(endPoint: endpointPath()) 27 | if let result = data { 28 | let decodedData = try decoder.decode(PresetDeleteSchema.self, from: result) 29 | return (decodedData, response) 30 | } 31 | return (nil, response) 32 | } 33 | 34 | private func endpointPath() throws -> String { 35 | return try "\(Presets.RESOURCEPATH)/\(presetName.encodeURL())" 36 | } 37 | 38 | 39 | } 40 | -------------------------------------------------------------------------------- /Sources/Typesense/Presets.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | #if canImport(FoundationNetworking) 3 | import FoundationNetworking 4 | #endif 5 | 6 | public struct Presets { 7 | static let RESOURCEPATH = "presets" 8 | private var apiCall: ApiCall 9 | 10 | 11 | init(apiCall: ApiCall) { 12 | self.apiCall = apiCall 13 | } 14 | 15 | public func upsert(presetName: String, params: PresetUpsertSchema) async throws -> (PresetSchema?, URLResponse?) { 16 | let schemaData = try encoder.encode(params) 17 | let (data, response) = try await self.apiCall.put(endPoint: endpointPath(presetName), body: schemaData) 18 | if let result = data { 19 | let decodedData = try decoder.decode(PresetSchema.self, from: result) 20 | return (decodedData, response) 21 | } 22 | return (nil, response) 23 | } 24 | 25 | public func retrieve() async throws -> (PresetsRetrieveSchema?, URLResponse?) { 26 | let (data, response) = try await self.apiCall.get(endPoint: endpointPath()) 27 | if let result = data { 28 | let decodedData = try decoder.decode(PresetsRetrieveSchema.self, from: result) 29 | return (decodedData, response) 30 | } 31 | return (nil, response) 32 | } 33 | 34 | private func endpointPath(_ operation: String? = nil) throws -> String { 35 | let baseEndpoint = "\(Presets.RESOURCEPATH)" 36 | if let operation = operation { 37 | return try "\(baseEndpoint)/\(operation.encodeURL())" 38 | } else { 39 | return baseEndpoint 40 | } 41 | } 42 | 43 | 44 | } 45 | -------------------------------------------------------------------------------- /Sources/Typesense/RequestTypes.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | enum RequestType: String { 4 | case get = "GET" 5 | case post = "POST" 6 | case delete = "DELETE" 7 | case put = "PUT" 8 | case patch = "PATCH" 9 | } 10 | -------------------------------------------------------------------------------- /Sources/Typesense/Shared/Coders.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | #if canImport(FoundationNetworking) 3 | import FoundationNetworking 4 | #endif 5 | 6 | public let encoder = JSONEncoder() 7 | public let decoder = JSONDecoder() 8 | 9 | public enum StringQuantum: Codable { 10 | 11 | public var arrStr : [String]? { 12 | guard case .arrOfStrings(let arrOfStrings) = self else { return nil } 13 | return arrOfStrings 14 | } 15 | 16 | public var arrArrStr : [[String]]? { 17 | guard case .arrOfArrOfStrings(let arrOfArrOfStrings) = self else { return nil } 18 | return arrOfArrOfStrings 19 | } 20 | 21 | case arrOfStrings([String]), arrOfArrOfStrings([[String]]) 22 | 23 | public init(from decoder: Decoder) throws { 24 | if let arrS = try? decoder.singleValueContainer().decode([String].self) { 25 | self = .arrOfStrings(arrS) 26 | return 27 | } 28 | 29 | if let arrArrS = try? decoder.singleValueContainer().decode([[String]].self) { 30 | self = .arrOfArrOfStrings(arrArrS) 31 | return 32 | } 33 | 34 | throw QuantumError.missingValue 35 | } 36 | 37 | public enum QuantumError:Error { 38 | case missingValue 39 | } 40 | } 41 | 42 | public enum ActionModes: String { 43 | case create = "create" 44 | case upsert = "upsert" 45 | case update = "update" 46 | } 47 | 48 | public struct SlowRequest: Codable { 49 | public var logSlowRequestsTimeMs: Float? 50 | 51 | public init(_ timeInMS: Float? = nil) { 52 | self.logSlowRequestsTimeMs = timeInMS 53 | } 54 | 55 | public enum CodingKeys: String, CodingKey { 56 | case logSlowRequestsTimeMs = "log-slow-requests-time-ms" 57 | } 58 | } 59 | 60 | @available(iOS, deprecated: 15.0, message: "Use the built-in API instead") 61 | extension URLSession { 62 | func data(for req: URLRequest) async throws -> (Data, URLResponse) { 63 | try await withCheckedThrowingContinuation { continuation in 64 | let task = self.dataTask(with: req) { data, response, error in 65 | guard let data = data, let response = response else { 66 | let error = error ?? URLError.invalidURL 67 | return continuation.resume(throwing: error) 68 | } 69 | 70 | continuation.resume(returning: (data, response)) 71 | } 72 | 73 | task.resume() 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /Sources/Typesense/Shared/Enums.swift: -------------------------------------------------------------------------------- 1 | public enum IndexAction: String, Codable { 2 | case create = "create" 3 | case update = "update" 4 | case upsert = "upsert" 5 | case emplace = "emplace" 6 | } 7 | 8 | public enum DirtyValues: String, Codable { 9 | case coerceOrReject = "coerce_or_reject" 10 | case coerceOrDrop = "coerce_or_drop" 11 | case drop = "drop" 12 | case reject = "reject" 13 | } -------------------------------------------------------------------------------- /Sources/Typesense/Shared/Logger.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | public struct Logger { 4 | var isDebug: Bool 5 | 6 | public init(debugMode: Bool = false) { 7 | isDebug = debugMode 8 | } 9 | 10 | func log(_ content: String) { 11 | if(isDebug) { 12 | print(content) 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Sources/Typesense/Shared/RequestNumber.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | extension Date { 4 | var millisecondsSince1970:Int64 { 5 | return Int64((self.timeIntervalSince1970 * 1000.0).rounded()) 6 | } 7 | 8 | init(milliseconds:Int64) { 9 | self = Date(timeIntervalSince1970: TimeInterval(milliseconds) / 1000) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Sources/Typesense/Stopword.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | #if canImport(FoundationNetworking) 3 | import FoundationNetworking 4 | #endif 5 | 6 | public struct Stopword { 7 | private var apiCall: ApiCall 8 | private var stopwordsSetId: String 9 | 10 | 11 | init(apiCall: ApiCall, stopwordsSetId: String) { 12 | self.apiCall = apiCall 13 | self.stopwordsSetId = stopwordsSetId 14 | } 15 | 16 | public func retrieve() async throws -> (StopwordsSetSchema?, URLResponse?) { 17 | let (data, response) = try await apiCall.get(endPoint: endpointPath()) 18 | if let result = data { 19 | let decodedData = try decoder.decode(StopwordsSetRetrieveSchema.self, from: result) 20 | return (decodedData.stopwords, response) 21 | } 22 | return (nil, response) 23 | } 24 | 25 | public func delete() async throws -> (StopwordsSetDeleteSchema?, URLResponse?) { 26 | let (data, response) = try await apiCall.delete(endPoint: endpointPath()) 27 | if let result = data { 28 | let decodedData = try decoder.decode(StopwordsSetDeleteSchema.self, from: result) 29 | return (decodedData, response) 30 | } 31 | return (nil, response) 32 | } 33 | 34 | private func endpointPath() throws -> String { 35 | return try "\(Stopwords.RESOURCEPATH)/\(stopwordsSetId.encodeURL())" 36 | } 37 | 38 | 39 | } 40 | -------------------------------------------------------------------------------- /Sources/Typesense/Stopwords.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | #if canImport(FoundationNetworking) 3 | import FoundationNetworking 4 | #endif 5 | 6 | public struct Stopwords { 7 | static let RESOURCEPATH = "stopwords" 8 | private var apiCall: ApiCall 9 | 10 | 11 | init(apiCall: ApiCall) { 12 | self.apiCall = apiCall 13 | } 14 | 15 | public func upsert(stopwordsSetId: String, params: StopwordsSetUpsertSchema) async throws -> (StopwordsSetSchema?, URLResponse?) { 16 | let schemaData = try encoder.encode(params) 17 | let (data, response) = try await self.apiCall.put(endPoint: endpointPath(stopwordsSetId), body: schemaData) 18 | if let result = data { 19 | let decodedData = try decoder.decode(StopwordsSetSchema.self, from: result) 20 | return (decodedData, response) 21 | } 22 | return (nil, response) 23 | } 24 | 25 | public func retrieve() async throws -> ([StopwordsSetSchema]?, URLResponse?) { 26 | let (data, response) = try await self.apiCall.get(endPoint: endpointPath()) 27 | if let result = data { 28 | let decodedData = try decoder.decode(StopwordsSetsRetrieveAllSchema.self, from: result) 29 | return (decodedData.stopwords, response) 30 | } 31 | return (nil, response) 32 | } 33 | 34 | private func endpointPath(_ operation: String? = nil) throws -> String { 35 | let baseEndpoint = "\(Stopwords.RESOURCEPATH)" 36 | if let operation = operation { 37 | return try "\(baseEndpoint)/\(operation.encodeURL())" 38 | } else { 39 | return baseEndpoint 40 | } 41 | } 42 | 43 | 44 | } 45 | -------------------------------------------------------------------------------- /Sources/Typesense/Synonyms.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | #if canImport(FoundationNetworking) 3 | import FoundationNetworking 4 | #endif 5 | 6 | public struct Synonyms { 7 | var apiCall: ApiCall 8 | var collectionName: String 9 | 10 | init(apiCall: ApiCall, collectionName: String) { 11 | self.apiCall = apiCall 12 | self.collectionName = collectionName 13 | } 14 | 15 | public func upsert(id: String, _ searchSynonym: SearchSynonymSchema) async throws -> (SearchSynonym?, URLResponse?) { 16 | var schemaData: Data? = nil 17 | schemaData = try encoder.encode(searchSynonym) 18 | 19 | if let validSchema = schemaData { 20 | let (data, response) = try await apiCall.put(endPoint: endpointPath(id), body: validSchema) 21 | if let result = data { 22 | let synonym = try decoder.decode(SearchSynonym.self, from: result) 23 | return (synonym, response) 24 | } 25 | 26 | } 27 | return (nil, nil) 28 | } 29 | 30 | public func retrieve(id: String) async throws -> (SearchSynonym?, URLResponse?) { 31 | let (data, response) = try await apiCall.get(endPoint: endpointPath(id)) 32 | if let result = data { 33 | let synonym = try decoder.decode(SearchSynonym.self, from: result) 34 | return (synonym, response) 35 | } 36 | return (nil, nil) 37 | } 38 | 39 | public func retrieve() async throws -> (SearchSynonymsResponse?, URLResponse?) { 40 | let (data, response) = try await apiCall.get(endPoint: endpointPath()) 41 | if let result = data { 42 | let synonym = try decoder.decode(SearchSynonymsResponse.self, from: result) 43 | return (synonym, response) 44 | } 45 | return (nil, nil) 46 | } 47 | 48 | public func delete(id: String) async throws -> (Data?, URLResponse?) { 49 | let (data, response) = try await apiCall.get(endPoint: endpointPath()) 50 | return (data, response) 51 | } 52 | 53 | private func endpointPath(_ operation: String? = nil) throws -> String { 54 | let baseEndpoint = try "\(Collections.RESOURCEPATH)/\(collectionName.encodeURL())/synonyms" 55 | if let operation: String = operation { 56 | return "\(baseEndpoint)/\(try operation.encodeURL())" 57 | } else { 58 | return baseEndpoint 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /Sources/Typesense/utils/CreateURLQueryParams.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | #if canImport(FoundationNetworking) 3 | import FoundationNetworking 4 | #endif 5 | 6 | func createURLQuery(forSchema: T?) throws -> [URLQueryItem]{ 7 | var searchQueryParams: [URLQueryItem] = [] 8 | 9 | guard let schema = forSchema else{ 10 | return searchQueryParams 11 | } 12 | 13 | let jsonData = try encoder.encode(schema) 14 | let json = try JSONSerialization.jsonObject(with: jsonData, options: []) 15 | 16 | guard let dictionary = json as? [String : Any] else { 17 | throw DataError.unableToParse(message: "Unable to create query params for \(type(of: schema))") 18 | } 19 | 20 | for (key, val) in dictionary { 21 | var stringVal = "" 22 | if let str = val as? String { 23 | stringVal = str 24 | } else if type(of: val) == type(of: NSNumber(booleanLiteral: true)) || type(of: val) == type(of: NSNumber(booleanLiteral: false)), let bool = val as? Bool{ 25 | stringVal = String(bool) 26 | } else if let int = val as? Int { 27 | stringVal = String(int) 28 | } else if let double = val as? Double { 29 | stringVal = String(double) 30 | } else{ 31 | throw DataError.unableToParse(message: "Unknown data type for key: `\(key)`, type: `\(type(of: val))` of `\(type(of: schema))`") 32 | } 33 | searchQueryParams.append(URLQueryItem(name: key, value: stringVal)) 34 | } 35 | return searchQueryParams 36 | } -------------------------------------------------------------------------------- /Sources/Typesense/utils/Extensions.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | 3 | private let urlAllowed: CharacterSet = .alphanumerics.union(.init(charactersIn: "-._~")) 4 | 5 | internal extension String { 6 | func encodeURL() throws -> String { 7 | let percentEncoded = self.addingPercentEncoding(withAllowedCharacters: urlAllowed) 8 | guard let valid = percentEncoded else{ 9 | throw URLError.encodingError(message: "Failed to encode URL for string `\(self)`") 10 | } 11 | return valid 12 | } 13 | } 14 | 15 | extension MultiSearchSearchesParameter { 16 | public init(searches: [MultiSearchParameters]) { 17 | self.searches = searches.map { params in 18 | var collectionParams = MultiSearchCollectionParameters() 19 | collectionParams.q = params.q 20 | collectionParams.queryBy = params.queryBy 21 | collectionParams.queryByWeights = params.queryByWeights 22 | collectionParams.textMatchType = params.textMatchType 23 | collectionParams._prefix = params._prefix 24 | collectionParams._infix = params._infix 25 | collectionParams.maxExtraPrefix = params.maxExtraPrefix 26 | collectionParams.maxExtraSuffix = params.maxExtraSuffix 27 | collectionParams.filterBy = params.filterBy 28 | collectionParams.sortBy = params.sortBy 29 | collectionParams.facetBy = params.facetBy 30 | collectionParams.maxFacetValues = params.maxFacetValues 31 | collectionParams.facetQuery = params.facetQuery 32 | collectionParams.numTypos = params.numTypos 33 | collectionParams.page = params.page 34 | collectionParams.perPage = params.perPage 35 | collectionParams.limit = params.limit 36 | collectionParams.offset = params.offset 37 | collectionParams.groupBy = params.groupBy 38 | collectionParams.groupLimit = params.groupLimit 39 | collectionParams.groupMissingValues = params.groupMissingValues 40 | collectionParams.includeFields = params.includeFields 41 | collectionParams.excludeFields = params.excludeFields 42 | collectionParams.highlightFullFields = params.highlightFullFields 43 | collectionParams.highlightAffixNumTokens = params.highlightAffixNumTokens 44 | collectionParams.highlightStartTag = params.highlightStartTag 45 | collectionParams.highlightEndTag = params.highlightEndTag 46 | collectionParams.snippetThreshold = params.snippetThreshold 47 | collectionParams.dropTokensThreshold = params.dropTokensThreshold 48 | collectionParams.typoTokensThreshold = params.typoTokensThreshold 49 | collectionParams.pinnedHits = params.pinnedHits 50 | collectionParams.hiddenHits = params.hiddenHits 51 | collectionParams.overrideTags = params.overrideTags 52 | collectionParams.highlightFields = params.highlightFields 53 | collectionParams.preSegmentedQuery = params.preSegmentedQuery 54 | collectionParams.preset = params.preset 55 | collectionParams.enableOverrides = params.enableOverrides 56 | collectionParams.prioritizeExactMatch = params.prioritizeExactMatch 57 | collectionParams.prioritizeTokenPosition = params.prioritizeTokenPosition 58 | collectionParams.prioritizeNumMatchingFields = params.prioritizeNumMatchingFields 59 | collectionParams.enableTyposForNumericalTokens = params.enableTyposForNumericalTokens 60 | collectionParams.exhaustiveSearch = params.exhaustiveSearch 61 | collectionParams.searchCutoffMs = params.searchCutoffMs 62 | collectionParams.useCache = params.useCache 63 | collectionParams.cacheTtl = params.cacheTtl 64 | collectionParams.minLen1typo = params.minLen1typo 65 | collectionParams.minLen2typo = params.minLen2typo 66 | collectionParams.vectorQuery = params.vectorQuery 67 | collectionParams.remoteEmbeddingTimeoutMs = params.remoteEmbeddingTimeoutMs 68 | collectionParams.remoteEmbeddingNumTries = params.remoteEmbeddingNumTries 69 | collectionParams.facetStrategy = params.facetStrategy 70 | collectionParams.stopwords = params.stopwords 71 | collectionParams.facetReturnParent = params.facetReturnParent 72 | collectionParams.voiceQuery = params.voiceQuery 73 | collectionParams.rerankHybridMatches = params.rerankHybridMatches 74 | collectionParams.xTypesenseApiKey = params.xTypesenseApiKey 75 | return collectionParams 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Tests/TypesenseTests/AnalyticsTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Typesense 3 | 4 | final class AnalyticsTests: XCTestCase { 5 | override func setUp() async throws { 6 | try await createCollection() 7 | try await createAnalyticRule() 8 | } 9 | 10 | override func tearDown() async throws { 11 | try await tearDownAnalyticsRules() 12 | try await tearDownCollections() 13 | } 14 | 15 | func testAnalyticsRuleCreate() async { 16 | let destination = AnalyticsRuleParametersDestination(collection: "product_queries") 17 | let source = AnalyticsRuleParametersSource(collections: ["products"]) 18 | let schema = AnalyticsRuleSchema(name: "product_queries_aggregation", type: .popularQueries, params: AnalyticsRuleParameters(source: source, destination: destination, limit: 1000)) 19 | do { 20 | let (rule, _) = try await client.analytics().rules().upsert(params: schema) 21 | XCTAssertNotNil(rule) 22 | guard let validRule = rule else { 23 | throw DataError.dataNotFound 24 | } 25 | print(validRule) 26 | XCTAssertEqual(validRule.name, schema.name) 27 | XCTAssertEqual(validRule.params.limit, schema.params.limit) 28 | XCTAssertEqual(validRule.params.destination.collection, schema.params.destination.collection) 29 | } catch (let error) { 30 | print(error.localizedDescription) 31 | XCTAssertTrue(false) 32 | } 33 | } 34 | 35 | func testAnalyticsRuleRetrieve() async { 36 | do { 37 | let (rule, _) = try await client.analytics().rule(id: "product_queries_aggregation").retrieve() 38 | guard let validRule = rule else { 39 | throw DataError.dataNotFound 40 | } 41 | print(validRule) 42 | XCTAssertEqual(validRule.name, "product_queries_aggregation") 43 | } catch (let error) { 44 | print(error.localizedDescription) 45 | XCTAssertTrue(false) 46 | } 47 | } 48 | 49 | func testAnalyticsRuleRetrieveAll() async { 50 | do { 51 | let (rules, _) = try await client.analytics().rules().retrieveAll() 52 | XCTAssertNotNil(rules) 53 | guard let validRules = rules?.rules else { 54 | throw DataError.dataNotFound 55 | } 56 | print(validRules) 57 | XCTAssertEqual(validRules[0].name, "product_queries_aggregation") 58 | } catch (let error) { 59 | print(error.localizedDescription) 60 | XCTAssertTrue(false) 61 | } 62 | } 63 | 64 | func testAnalyticsRuleDelete() async { 65 | do { 66 | let (deletedRule, _) = try await client.analytics().rule(id: "product_queries_aggregation").delete() 67 | XCTAssertNotNil(deletedRule) 68 | guard let validRule = deletedRule else { 69 | throw DataError.dataNotFound 70 | } 71 | print(validRule) 72 | XCTAssertEqual(validRule.name, "product_queries_aggregation") 73 | } catch (let error) { 74 | print(error.localizedDescription) 75 | XCTAssertTrue(false) 76 | } 77 | } 78 | 79 | func testAnalyticsEventsCreate() async { 80 | do { 81 | let (res, _) = try await client.analytics().events().create(params: AnalyticsEventCreateSchema( 82 | type: "click", 83 | name: "products_click_event", 84 | data: [ 85 | "q": "nike shoes", 86 | "doc_id": "1024", 87 | "user_id": "111112" 88 | ] 89 | )) 90 | guard let validRes = res else { 91 | throw DataError.dataNotFound 92 | } 93 | print(validRes) 94 | XCTAssertTrue(validRes.ok) 95 | } catch (let error) { 96 | print(error.localizedDescription) 97 | XCTAssertTrue(false) 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /Tests/TypesenseTests/ApiKeyTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Typesense 3 | 4 | final class ApiKeyTests: XCTestCase { 5 | override func tearDown() async throws { 6 | try await tearDownAPIKeys() 7 | } 8 | 9 | func testKeyCreate() async { 10 | do { 11 | let adminKey = ApiKeySchema(_description: "Test key with all privileges", actions: ["*"], collections: ["*"]) 12 | let (data, _) = try await client.keys().create(adminKey) 13 | XCTAssertNotNil(data) 14 | guard let validData = data else { 15 | throw DataError.dataNotFound 16 | } 17 | print(validData) 18 | XCTAssertEqual(validData._description, "Test key with all privileges") 19 | XCTAssertEqual(validData.actions, ["*"]) 20 | XCTAssertEqual(validData.collections, ["*"]) 21 | } catch (let error) { 22 | print(error.localizedDescription) 23 | XCTAssertTrue(false) //To prevent this, check availability of Typesense Server and retry 24 | } 25 | } 26 | 27 | func testKeyRetrieve() async { 28 | do { 29 | let key = try await createAPIKey() 30 | let (data, _) = try await client.keys().retrieve(id: key._id) 31 | XCTAssertNotNil(data) 32 | guard let validData = data else { 33 | throw DataError.dataNotFound 34 | } 35 | print(validData) 36 | XCTAssertEqual(validData._description, "Test key with all privileges") 37 | XCTAssertEqual(validData.actions, ["*"]) 38 | XCTAssertEqual(validData.collections, ["*"]) 39 | } catch (let error) { 40 | print(error.localizedDescription) 41 | XCTAssertTrue(false) //To prevent this, check availability of Typesense Server and retry 42 | } 43 | } 44 | 45 | func testKeyRetrieveAll() async { 46 | do { 47 | let key = try await createAPIKey() 48 | let (data, _) = try await client.keys().retrieve() 49 | XCTAssertNotNil(data) 50 | guard let validData = data?.keys else { 51 | throw DataError.dataNotFound 52 | } 53 | print(validData) 54 | XCTAssertEqual(validData[0]._id, key._id) 55 | } catch (let error) { 56 | print(error.localizedDescription) 57 | XCTAssertTrue(false) //To prevent this, check availability of Typesense Server and retry 58 | } 59 | } 60 | 61 | func testKeyDelete() async { 62 | do { 63 | let key = try await createAPIKey() 64 | let (data, _) = try await client.keys().delete(id: key._id) 65 | XCTAssertNotNil(data) 66 | guard let validData = data else { 67 | throw DataError.dataNotFound 68 | } 69 | print(validData) 70 | } catch (let error) { 71 | print(error.localizedDescription) 72 | XCTAssertTrue(false) //To prevent this, check availability of Typesense Server and retry 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /Tests/TypesenseTests/CollectionAliasTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Typesense 3 | 4 | final class CollectionAliasTests: XCTestCase { 5 | override func tearDown() async throws { 6 | try await tearDownAliases() 7 | } 8 | 9 | func testAliasUpsert() async { 10 | do { 11 | let aliasCollection = CollectionAliasSchema(collectionName: "companies_june") 12 | let (data, _) = try await client.aliases().upsert(name: "companies-_-~.?test=&123 + # /h/()", collection: aliasCollection) 13 | XCTAssertNotNil(data) 14 | guard let validData = data else { 15 | throw DataError.dataNotFound 16 | } 17 | print(validData) 18 | XCTAssertEqual(validData.name, "companies-_-~.?test=&123 + # /h/()") 19 | XCTAssertEqual(validData.collectionName, "companies_june") 20 | } catch (let error) { 21 | print(error.localizedDescription) 22 | XCTAssertTrue(false) 23 | } 24 | } 25 | 26 | func testAliasRetrieve() async { 27 | do { 28 | try await createAlias() 29 | let (data, _) = try await client.aliases().retrieve(name: "companies") 30 | XCTAssertNotNil(data) 31 | guard let validData = data else { 32 | throw DataError.dataNotFound 33 | } 34 | print(validData) 35 | XCTAssertEqual(validData.name, "companies") 36 | XCTAssertEqual(validData.collectionName, "companies_june") 37 | } catch (let error) { 38 | print(error.localizedDescription) 39 | XCTAssertTrue(false) 40 | } 41 | } 42 | 43 | func testAliasRetrieveAll() async { 44 | do { 45 | try await createAlias() 46 | let (data, _) = try await client.aliases().retrieve() 47 | XCTAssertNotNil(data) 48 | guard let validData = data?.aliases else { 49 | throw DataError.dataNotFound 50 | } 51 | XCTAssertEqual(validData.count, 1) 52 | XCTAssertEqual(validData[0].collectionName, "companies_june") 53 | XCTAssertEqual(validData[0].name, "companies") 54 | } catch (let error) { 55 | print(error.localizedDescription) 56 | XCTAssertTrue(false) 57 | } 58 | } 59 | 60 | func testAliasDelete() async { 61 | do { 62 | try await createAlias() 63 | let (data, _) = try await client.aliases().delete(name: "companies") 64 | XCTAssertNotNil(data) 65 | guard let validData = data else { 66 | throw DataError.dataNotFound 67 | } 68 | print(validData) 69 | XCTAssertEqual(validData.name, "companies") 70 | XCTAssertEqual(validData.collectionName, "companies_june") 71 | } catch (let error) { 72 | print(error.localizedDescription) 73 | XCTAssertTrue(false) 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /Tests/TypesenseTests/CollectionTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Typesense 3 | 4 | final class CollectionTests: XCTestCase { 5 | override func tearDown() async throws { 6 | try await tearDownCollections() 7 | } 8 | 9 | func testCollectionCreate() async { 10 | let schema = CollectionSchema(name: "companies", fields: [Field(name: "company_name", type: "string"), Field(name: "num_employees", type: "int32"), Field(name: "country", type: "string", facet: true)], defaultSortingField: "num_employees") 11 | do { 12 | let (collResp, _) = try await client.collections.create(schema: schema) 13 | XCTAssertNotNil(collResp) 14 | guard let validData = collResp else { 15 | throw DataError.dataNotFound 16 | } 17 | print(validData) 18 | XCTAssertEqual(validData.name, "companies") 19 | XCTAssertNotNil(validData.fields) 20 | XCTAssertEqual(validData.fields.count, 3) 21 | XCTAssertEqual(validData.defaultSortingField, "num_employees") 22 | } catch (let error) { 23 | print(error.localizedDescription) 24 | XCTAssertTrue(false) //To prevent this, check availability of Typesense Server and retry 25 | } 26 | } 27 | 28 | func testCollectionDelete() async { 29 | do { 30 | try await createCollection() 31 | let (collResp, _) = try await client.collection(name: "companies").delete() 32 | XCTAssertNotNil(collResp) 33 | guard let validData = collResp else { 34 | throw DataError.dataNotFound 35 | } 36 | print(validData) 37 | XCTAssertEqual(validData.name, "companies") 38 | } catch (let error) { 39 | print(error.localizedDescription) 40 | XCTAssertTrue(false) 41 | } 42 | } 43 | 44 | func testCollectionRetrieveAll() async { 45 | do { 46 | try await createCollection() 47 | let (collResp, _) = try await client.collections.retrieveAll() 48 | XCTAssertNotNil(collResp) 49 | guard let validData = collResp else { 50 | throw DataError.dataNotFound 51 | } 52 | print(validData) 53 | XCTAssertEqual(validData.count, 1) 54 | XCTAssertEqual(validData[0].name, "companies") 55 | } catch (let error) { 56 | print(error.localizedDescription) 57 | XCTAssertTrue(false) 58 | } 59 | } 60 | 61 | func testCollectionRetrieveOne() async { 62 | do { 63 | try await createCollection() 64 | let (collResp, _) = try await client.collection(name: "companies").retrieve() 65 | XCTAssertNotNil(collResp) 66 | guard let validData = collResp else { 67 | throw DataError.dataNotFound 68 | } 69 | print(validData) 70 | XCTAssertEqual(validData.name, "companies") 71 | } catch (let error) { 72 | print(error.localizedDescription) 73 | XCTAssertTrue(false) 74 | } 75 | } 76 | 77 | } 78 | -------------------------------------------------------------------------------- /Tests/TypesenseTests/ConfigurationTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Typesense 3 | 4 | final class ConfigurationTests: XCTestCase { 5 | func testQuickConfig() { 6 | let newConfig = Configuration(nodes: [Node(host: "localhost", port: "8108", nodeProtocol: "http")], apiKey: "xyz") 7 | let myClient = Client(config: newConfig) 8 | 9 | XCTAssertFalse(myClient.configuration.nodes.isEmpty) 10 | } 11 | 12 | func testQuickConfigConnectionTimeout() { 13 | let newConfig = Configuration(nodes: [Node(host: "localhost", port: "8108", nodeProtocol: "http")], apiKey: "xyz", connectionTimeoutSeconds: 8) 14 | let myClient = Client(config: newConfig) 15 | 16 | XCTAssertFalse(myClient.configuration.nodes.isEmpty) 17 | XCTAssertEqual(myClient.configuration.connectionTimeoutSeconds, 8) 18 | } 19 | 20 | func testQuickConfigNearestNode() { 21 | let newConfig = Configuration(nodes: [Node(host: "localhost", port: "8108", nodeProtocol: "http")], apiKey: "xyz", nearestNode: Node(host: "localhost", port: "8108", nodeProtocol: "http")) 22 | let myClient = Client(config: newConfig) 23 | 24 | XCTAssertFalse(myClient.configuration.nodes.isEmpty) 25 | XCTAssertNotNil(myClient.configuration.nearestNode) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Tests/TypesenseTests/ConversationModelTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Typesense 3 | 4 | final class ConversationModelTests: XCTestCase { 5 | func testConversationModelsCreate() async { 6 | let schema = ConversationModelCreateSchema( 7 | _id: "conv-model-1", 8 | modelName: "test/gpt-3.5-turbo", 9 | apiKey: "sk", 10 | historyCollection: "conversation_store", 11 | systemPrompt: "You are an assistant for question-answering.", 12 | ttl: 123, 13 | maxBytes: 16384 14 | ) 15 | do { 16 | try await createConversationCollection() 17 | let _ = try await client.conversations().models().create(params: schema) 18 | } catch HTTPError.clientError(let code, let desc){ 19 | print(desc) 20 | XCTAssertEqual(code, 400) 21 | XCTAssertTrue(desc.contains("Model namespace `test` is not supported.")) 22 | } catch (let error) { 23 | print(error.localizedDescription) 24 | XCTAssertTrue(false) 25 | } 26 | try? await tearDownCollections() 27 | } 28 | 29 | func testConversationModelsRetrieve() async { 30 | do { 31 | let (data, _) = try await client.conversations().models().retrieve() 32 | guard let validData = data else { 33 | throw DataError.dataNotFound 34 | } 35 | XCTAssertEqual(0, validData.count) 36 | } catch (let error) { 37 | print(error) 38 | XCTAssertTrue(false) 39 | } 40 | } 41 | 42 | func testConversationModelUpdate() async { 43 | do { 44 | let _ = try await client.conversations().model(modelId: "conv-model-1").update(params: ConversationModelUpdateSchema()) 45 | } catch HTTPError.clientError(let code, let desc){ 46 | print(desc) 47 | XCTAssertEqual(code, 404) 48 | XCTAssertTrue(desc.contains("Model not found")) 49 | } catch (let error) { 50 | print(error) 51 | XCTAssertTrue(false) 52 | } 53 | } 54 | 55 | func testConversationModelRetrieve() async { 56 | do { 57 | let _ = try await client.conversations().model(modelId: "conv-model-1").retrieve() 58 | } catch HTTPError.clientError(let code, let desc){ 59 | print(desc) 60 | XCTAssertEqual(code, 404) 61 | XCTAssertTrue(desc.contains("Model not found")) 62 | } catch (let error) { 63 | print(error) 64 | XCTAssertTrue(false) 65 | } 66 | } 67 | 68 | func testConversationModelDelete() async { 69 | do { 70 | let _ = try await client.conversations().model(modelId: "conv-model-1").delete() 71 | } catch HTTPError.clientError(let code, let desc){ 72 | print(desc) 73 | XCTAssertEqual(code, 404) 74 | XCTAssertTrue(desc.contains("Model not found")) 75 | } catch (let error) { 76 | print(error) 77 | XCTAssertTrue(false) 78 | } 79 | } 80 | 81 | func testConversationSearch() async { 82 | do { 83 | let string = """ 84 | { 85 | "conversation": { 86 | "answer": " context information, I' unable to suggest an is information given about specific context action as,, specific titles If a preference for particular genre length of, please that information and I' try my best to suggestions.", 87 | "conversation_history": [ 88 | { 89 | "user": "can you suggest an action series" 90 | }, 91 | { 92 | "assistant": " context information, I' unable to suggest an is information given about specific context action as,, specific titles If a preference for particular genre length of, please that information and I' try my best to suggestions." 93 | } 94 | ], 95 | "conversation_id": "123", 96 | "query": "can you suggest an action series" 97 | }, 98 | "results": [ 99 | { 100 | "code": 404, 101 | "error": "Not found." 102 | } 103 | ] 104 | } 105 | """ 106 | let data = string.data(using: .utf8)! 107 | let _ = try decoder.decode(MultiSearchResult.self, from: data) 108 | XCTAssertTrue(true) 109 | }catch (let error) { 110 | print(error) 111 | XCTAssertTrue(false) 112 | } 113 | } 114 | 115 | } 116 | -------------------------------------------------------------------------------- /Tests/TypesenseTests/CreateURLQueryParamsTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Typesense 3 | 4 | final class CreateURLQueryParamsTests: XCTestCase { 5 | struct Car: Codable { 6 | var name = "Fast" 7 | var age: Int = 20 8 | var price: Int64 = 2000 9 | var page: Int? = 1 10 | var groupLimit: Int64? = 0 11 | var speed: Double = 120.555 12 | var mileage: Float = 120.5 13 | var isElectric = true 14 | var inStock: Bool = false 15 | var empty: String? = nil 16 | 17 | enum CodingKeys: String, CodingKey { 18 | case name 19 | case age 20 | case price 21 | case page 22 | case groupLimit = "group_limit" 23 | case speed 24 | case mileage 25 | case isElectric = "is_electric" 26 | case inStock = "in_stock" 27 | case empty 28 | } 29 | } 30 | func testCreateURLQueryParams() { 31 | do { 32 | let queryParams = try createURLQuery(forSchema: Car()) 33 | print(queryParams) 34 | XCTAssertEqual(queryParams.count, 9) 35 | XCTAssertTrue(queryParams.contains(URLQueryItem(name: "name", value: "Fast"))) 36 | XCTAssertTrue(queryParams.contains(URLQueryItem(name: "age", value: "20"))) 37 | XCTAssertTrue(queryParams.contains(URLQueryItem(name: "price", value: "2000"))) 38 | XCTAssertTrue(queryParams.contains(URLQueryItem(name: "page", value: "1"))) 39 | XCTAssertTrue(queryParams.contains(URLQueryItem(name: "group_limit", value: "0"))) 40 | XCTAssertTrue(queryParams.contains(URLQueryItem(name: "speed", value: "120.555"))) 41 | XCTAssertTrue(queryParams.contains(URLQueryItem(name: "mileage", value: "120.5"))) 42 | XCTAssertTrue(queryParams.contains(URLQueryItem(name: "is_electric", value: "true"))) 43 | XCTAssertTrue(queryParams.contains(URLQueryItem(name: "in_stock", value: "false"))) 44 | XCTAssertFalse(queryParams.contains(URLQueryItem(name: "empty", value: "null"))) 45 | } catch (let error) { 46 | print(error) 47 | XCTAssertTrue(false) 48 | } 49 | } 50 | 51 | struct People: Codable { 52 | var obj = ["isHooman": true] 53 | } 54 | 55 | func testCreateURLQueryParamsThrowsError() { 56 | do { 57 | let _ = try createURLQuery(forSchema: People()) 58 | } catch DataError.unableToParse(let msg){ 59 | if !msg.contains("Unknown data type"){ 60 | XCTAssertTrue(false) 61 | } 62 | } catch (let error) { 63 | print(error) 64 | XCTAssertTrue(false) 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /Tests/TypesenseTests/OverrideTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Typesense 3 | 4 | final class OverrideTests: XCTestCase { 5 | override func setUp() async throws { 6 | try await createCollection() 7 | try await createAnOverride() 8 | } 9 | 10 | override func tearDown() async throws { 11 | try await tearDownCollections() 12 | } 13 | 14 | func testOverrideRetrieve() async { 15 | do { 16 | let (result, _) = try await client.collection(name: "companies").override("test-id").retrieve(metadataType: SearchOverrideExclude.self ) 17 | guard let validOverride = result else { 18 | throw DataError.dataNotFound 19 | } 20 | print(validOverride) 21 | XCTAssertEqual("test-id", validOverride._id) 22 | XCTAssertEqual("exclude-id", validOverride.metadata?._id) 23 | } catch HTTPError.serverError(let code, let desc) { 24 | print(desc) 25 | print("The response status code is \(code)") 26 | XCTAssertTrue(false) 27 | } catch (let error) { 28 | print(error) 29 | XCTAssertTrue(false) 30 | } 31 | } 32 | 33 | func testOverrideDelete() async { 34 | do { 35 | let (result, _) = try await client.collection(name: "companies").override("test-id").delete() 36 | guard let validOverride = result else { 37 | throw DataError.dataNotFound 38 | } 39 | print(validOverride) 40 | XCTAssertEqual("test-id", validOverride._id) 41 | } catch HTTPError.serverError(let code, let desc) { 42 | print(desc) 43 | print("The response status code is \(code)") 44 | XCTAssertTrue(false) 45 | } catch (let error) { 46 | print(error) 47 | XCTAssertTrue(false) 48 | } 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /Tests/TypesenseTests/OverridesTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Typesense 3 | 4 | final class OverridesTests: XCTestCase { 5 | override func setUp() async throws { 6 | try await createCollection() 7 | } 8 | 9 | override func tearDown() async throws { 10 | try await tearDownCollections() 11 | } 12 | 13 | func testOverridesUpsert() async { 14 | let schema = SearchOverrideSchema( 15 | rule: SearchOverrideRule(tags: ["test"], query: "test", match: SearchOverrideRule.Match.exact, filterBy: "employees:=50"), 16 | includes: [SearchOverrideInclude(_id: "include-id", position: 1)], 17 | excludes: [SearchOverrideExclude(_id: "exclude-id")], 18 | filterBy: "test:=true", 19 | removeMatchedTokens: false, 20 | metadata: SearchOverrideExclude(_id: "test-json"), 21 | sortBy: "asc", 22 | replaceQuery: "test", 23 | filterCuratedHits: false, 24 | effectiveFromTs: 123, 25 | effectiveToTs: 456, 26 | stopProcessing: false 27 | ) 28 | do { 29 | let (result, _) = try await client.collection(name: "companies").overrides().upsert(overrideId: "test-id", params: schema) 30 | XCTAssertNotNil(result) 31 | guard let validOverride = result else { 32 | throw DataError.dataNotFound 33 | } 34 | print(validOverride) 35 | XCTAssertEqual("test-id", validOverride._id) 36 | XCTAssertEqual("test", validOverride.rule.query) 37 | XCTAssertEqual("test-json", validOverride.metadata?._id) 38 | XCTAssertEqual("include-id", validOverride.includes?[0]._id) 39 | XCTAssertEqual("exclude-id", validOverride.excludes?[0]._id) 40 | XCTAssertEqual("test:=true", validOverride.filterBy) 41 | XCTAssertEqual(false, validOverride.removeMatchedTokens) 42 | XCTAssertEqual("asc", validOverride.sortBy) 43 | XCTAssertEqual("test", validOverride.replaceQuery) 44 | XCTAssertEqual(false, validOverride.filterCuratedHits) 45 | XCTAssertEqual(123, validOverride.effectiveFromTs) 46 | XCTAssertEqual(456, validOverride.effectiveToTs) 47 | XCTAssertEqual(false, validOverride.stopProcessing) 48 | } catch (let error) { 49 | print(error.localizedDescription) 50 | XCTAssertTrue(false) 51 | } 52 | } 53 | 54 | func testOverridesRetrieve() async { 55 | do { 56 | try await createAnOverride() 57 | let (overrides, _) = try await client.collection(name: "companies").overrides().retrieve(metadataType: SearchOverrideExclude.self ) 58 | guard let validOverrides = overrides else { 59 | throw DataError.dataNotFound 60 | } 61 | print(validOverrides) 62 | XCTAssertEqual(1, validOverrides.overrides.count) 63 | XCTAssertEqual("test-id", validOverrides.overrides[0]._id) 64 | } catch HTTPError.serverError(let code, let desc) { 65 | print(desc) 66 | print("The response status code is \(code)") 67 | XCTAssertTrue(false) 68 | } catch (let error) { 69 | print(error) 70 | XCTAssertTrue(false) 71 | } 72 | } 73 | 74 | } 75 | -------------------------------------------------------------------------------- /Tests/TypesenseTests/PresetTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Typesense 3 | 4 | final class PresetTests: XCTestCase { 5 | override func setUp() async throws { 6 | try await createSingleCollectionSearchPreset() 7 | } 8 | override func tearDown() async throws { 9 | try await tearDownPresets() 10 | } 11 | 12 | func testPresetRetrieve() async { 13 | do { 14 | let (result, _) = try await client.preset("test-id").retrieve() 15 | XCTAssertNotNil(result) 16 | guard let validResult = result else { 17 | throw DataError.dataNotFound 18 | } 19 | print(validResult) 20 | XCTAssertEqual("test-id", validResult.name) 21 | switch validResult.value { 22 | case .singleCollectionSearch(let value): 23 | XCTAssertEqual("apple", value.q) 24 | default: 25 | XCTAssertTrue(false) 26 | } 27 | } catch (let error) { 28 | print(error.localizedDescription) 29 | XCTAssertTrue(false) 30 | } 31 | } 32 | 33 | func testPresetDelete() async { 34 | do { 35 | let (result, _) = try await client.preset("test-id").delete() 36 | XCTAssertNotNil(result) 37 | guard let validResult = result else { 38 | throw DataError.dataNotFound 39 | } 40 | print(validResult) 41 | XCTAssertEqual("test-id", validResult.name) 42 | } catch (let error) { 43 | print(error.localizedDescription) 44 | XCTAssertTrue(false) 45 | } 46 | 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /Tests/TypesenseTests/PresetsTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Typesense 3 | 4 | final class PresetsTests: XCTestCase { 5 | override func tearDown() async throws { 6 | try await tearDownPresets() 7 | } 8 | 9 | func testPresetsUpsertSearchParameters() async { 10 | let schema = PresetUpsertSchema( 11 | value: PresetValue.singleCollectionSearch(SearchParameters(q: "apple")) 12 | ) 13 | do { 14 | let (result, _) = try await client.presets().upsert(presetName: "test-id", params: schema) 15 | XCTAssertNotNil(result) 16 | guard let validResult = result else { 17 | throw DataError.dataNotFound 18 | } 19 | print(validResult) 20 | XCTAssertEqual("test-id", validResult.name) 21 | switch validResult.value { 22 | case .singleCollectionSearch(let value): 23 | XCTAssertEqual("apple", value.q) 24 | default: 25 | XCTAssertTrue(false) 26 | } 27 | } catch (let error) { 28 | print(error.localizedDescription) 29 | XCTAssertTrue(false) 30 | } 31 | } 32 | 33 | func testPresetsUpsertMultiSearchSearchesParameter() async { 34 | let schema = PresetUpsertSchema( 35 | value: PresetValue.multiSearch(MultiSearchSearchesParameter(searches: [MultiSearchCollectionParameters(q: "apple")])) 36 | ) 37 | do { 38 | let (result, _) = try await client.presets().upsert(presetName: "test-id", params: schema) 39 | XCTAssertNotNil(result) 40 | guard let validResult = result else { 41 | throw DataError.dataNotFound 42 | } 43 | print(validResult) 44 | XCTAssertEqual("test-id", validResult.name) 45 | switch validResult.value { 46 | case .multiSearch(let value): 47 | XCTAssertEqual("apple", value.searches[0].q) 48 | default: 49 | XCTAssertTrue(false) 50 | } 51 | } catch (let error) { 52 | print(error.localizedDescription) 53 | XCTAssertTrue(false) 54 | } 55 | } 56 | 57 | func testPresetsRetrieveAll() async { 58 | do { 59 | try await createSingleCollectionSearchPreset(); 60 | try await createMultiSearchPreset(); 61 | let (result, _) = try await client.presets().retrieve() 62 | guard let validResult = result else { 63 | throw DataError.dataNotFound 64 | } 65 | print(validResult) 66 | XCTAssertEqual(2, validResult.presets.count) 67 | for preset in validResult.presets{ 68 | switch preset.value { 69 | case .singleCollectionSearch(let value): 70 | XCTAssertEqual("test-id", preset.name) 71 | XCTAssertEqual("apple", value.q) 72 | case .multiSearch(let value): 73 | XCTAssertEqual("test-id-preset-multi-search", preset.name) 74 | XCTAssertEqual("banana", value.searches[0].q) 75 | } 76 | } 77 | } catch (let error) { 78 | print(error) 79 | XCTAssertTrue(false) 80 | } 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /Tests/TypesenseTests/StopwordTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Typesense 3 | 4 | final class StopwordTests: XCTestCase { 5 | override func tearDown() async throws { 6 | try await tearDownStopwords() 7 | } 8 | 9 | func testStopwordRetrieve() async { 10 | do { 11 | try await createStopwordSet() 12 | let (result, _) = try await client.stopword("test-id-stopword-set").retrieve() 13 | XCTAssertNotNil(result) 14 | guard let validResult = result else { 15 | throw DataError.dataNotFound 16 | } 17 | print(validResult) 18 | XCTAssertEqual("test-id-stopword-set", validResult._id) 19 | XCTAssertEqual(["states","united"], validResult.stopwords) 20 | XCTAssertEqual("en", validResult.locale) 21 | } catch (let error) { 22 | print(error.localizedDescription) 23 | XCTAssertTrue(false) 24 | } 25 | } 26 | 27 | func testStopwordDelete() async { 28 | do { 29 | try await createStopwordSet() 30 | let (result, _) = try await client.stopword("test-id-stopword-set").delete() 31 | guard let validResult = result else { 32 | throw DataError.dataNotFound 33 | } 34 | print(validResult) 35 | XCTAssertEqual("test-id-stopword-set", validResult._id) 36 | } catch (let error) { 37 | print(error) 38 | XCTAssertTrue(false) 39 | } 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /Tests/TypesenseTests/StopwordsTests.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import Typesense 3 | 4 | final class StopwordsTests: XCTestCase { 5 | override func tearDown() async throws { 6 | try await tearDownStopwords() 7 | } 8 | 9 | func testStopwordsUpsert() async { 10 | let schema = StopwordsSetUpsertSchema( 11 | stopwords: ["states","united"], 12 | locale: "en" 13 | ) 14 | do { 15 | let (result, _) = try await client.stopwords().upsert(stopwordsSetId: "test-id", params: schema) 16 | XCTAssertNotNil(result) 17 | guard let validResult = result else { 18 | throw DataError.dataNotFound 19 | } 20 | print(validResult) 21 | XCTAssertEqual("test-id", validResult._id) 22 | XCTAssertEqual(["states","united"], validResult.stopwords) 23 | XCTAssertEqual("en", validResult.locale) 24 | } catch (let error) { 25 | print(error.localizedDescription) 26 | XCTAssertTrue(false) 27 | } 28 | } 29 | 30 | func testStopwordsRetrieveAll() async { 31 | do { 32 | try await createStopwordSet() 33 | let (result, _) = try await client.stopwords().retrieve() 34 | guard let validResult = result else { 35 | throw DataError.dataNotFound 36 | } 37 | print(validResult) 38 | XCTAssertEqual(1, validResult.count) 39 | XCTAssertEqual("test-id-stopword-set", validResult[0]._id) 40 | } catch (let error) { 41 | print(error) 42 | XCTAssertTrue(false) 43 | } 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /example/README.md: -------------------------------------------------------------------------------- 1 | # Typesense swift example 2 | 3 | I have created two simple apps to demonstrate how typesense-swift works. There's a SwifUI version and a UIKit version. The SwiftUI Version is a much more elaborate application than the UIKit version but both illustrate the intended usage of the typesense-swift package. 4 | 5 | The application is a Recipe Search 🥘 app and typesense manages to search through over 2 Million records in no time ⚡️ You can checkout the web variant of typesense recipe search [here](https://recipe-search.typesense.org) 6 | -------------------------------------------------------------------------------- /example/SwiftUI/RecipeSearchSwiftUI/README.md: -------------------------------------------------------------------------------- 1 | # RecipeSearch (SwiftUI) 2 | 3 | This is a tiny demo-app made with SwiftUI, implementing the typesense-swift client. This demonstrates how typesense can easily breeze through around 2 Million records to find out the most relevant recipes for you and your search query. 4 | 5 | ![Demo Gif](RecipeSearch.gif) 6 | -------------------------------------------------------------------------------- /example/SwiftUI/RecipeSearchSwiftUI/RecipeSearch.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typesense/typesense-swift/8057f2ca81471be207ca1d4e6e46ad0336faffe0/example/SwiftUI/RecipeSearchSwiftUI/RecipeSearch.gif -------------------------------------------------------------------------------- /example/SwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUI.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/SwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUI.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/SwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUI.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "pins" : [ 3 | { 4 | "identity" : "typesense-swift", 5 | "kind" : "remoteSourceControl", 6 | "location" : "https://github.com/typesense/typesense-swift", 7 | "state" : { 8 | "revision" : "9454327b9a8c19f7e37a8f27865439add0cb5618", 9 | "version" : "0.1.2" 10 | } 11 | } 12 | ], 13 | "version" : 2 14 | } 15 | -------------------------------------------------------------------------------- /example/SwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUI/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /example/SwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUI/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "notification40.png", 5 | "idiom" : "iphone", 6 | "scale" : "2x", 7 | "size" : "20x20" 8 | }, 9 | { 10 | "filename" : "notification60.png", 11 | "idiom" : "iphone", 12 | "scale" : "3x", 13 | "size" : "20x20" 14 | }, 15 | { 16 | "filename" : "settings58.png", 17 | "idiom" : "iphone", 18 | "scale" : "2x", 19 | "size" : "29x29" 20 | }, 21 | { 22 | "filename" : "settings87.png", 23 | "idiom" : "iphone", 24 | "scale" : "3x", 25 | "size" : "29x29" 26 | }, 27 | { 28 | "filename" : "spotlight80.png", 29 | "idiom" : "iphone", 30 | "scale" : "2x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "filename" : "spotlight120.png", 35 | "idiom" : "iphone", 36 | "scale" : "3x", 37 | "size" : "40x40" 38 | }, 39 | { 40 | "filename" : "iphone120.png", 41 | "idiom" : "iphone", 42 | "scale" : "2x", 43 | "size" : "60x60" 44 | }, 45 | { 46 | "filename" : "iphone180.png", 47 | "idiom" : "iphone", 48 | "scale" : "3x", 49 | "size" : "60x60" 50 | }, 51 | { 52 | "filename" : "ipadNotification20.png", 53 | "idiom" : "ipad", 54 | "scale" : "1x", 55 | "size" : "20x20" 56 | }, 57 | { 58 | "filename" : "ipadNotification40.png", 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "20x20" 62 | }, 63 | { 64 | "filename" : "ipadSettings29.png", 65 | "idiom" : "ipad", 66 | "scale" : "1x", 67 | "size" : "29x29" 68 | }, 69 | { 70 | "filename" : "ipadSettings58.png", 71 | "idiom" : "ipad", 72 | "scale" : "2x", 73 | "size" : "29x29" 74 | }, 75 | { 76 | "filename" : "ipadSpotlight40.png", 77 | "idiom" : "ipad", 78 | "scale" : "1x", 79 | "size" : "40x40" 80 | }, 81 | { 82 | "filename" : "ipadSpotlight80.png", 83 | "idiom" : "ipad", 84 | "scale" : "2x", 85 | "size" : "40x40" 86 | }, 87 | { 88 | "filename" : "ipad76.png", 89 | "idiom" : "ipad", 90 | "scale" : "1x", 91 | "size" : "76x76" 92 | }, 93 | { 94 | "filename" : "ipad152.png", 95 | "idiom" : "ipad", 96 | "scale" : "2x", 97 | "size" : "76x76" 98 | }, 99 | { 100 | "filename" : "ipadPro167.png", 101 | "idiom" : "ipad", 102 | "scale" : "2x", 103 | "size" : "83.5x83.5" 104 | }, 105 | { 106 | "filename" : "appstore1024.png", 107 | "idiom" : "ios-marketing", 108 | "scale" : "1x", 109 | "size" : "1024x1024" 110 | }, 111 | { 112 | "filename" : "mac16.png", 113 | "idiom" : "mac", 114 | "scale" : "1x", 115 | "size" : "16x16" 116 | }, 117 | { 118 | "filename" : "mac32.png", 119 | "idiom" : "mac", 120 | "scale" : "2x", 121 | "size" : "16x16" 122 | }, 123 | { 124 | "filename" : "mac32.png", 125 | "idiom" : "mac", 126 | "scale" : "1x", 127 | "size" : "32x32" 128 | }, 129 | { 130 | "filename" : "mac64.png", 131 | "idiom" : "mac", 132 | "scale" : "2x", 133 | "size" : "32x32" 134 | }, 135 | { 136 | "filename" : "mac128.png", 137 | "idiom" : "mac", 138 | "scale" : "1x", 139 | "size" : "128x128" 140 | }, 141 | { 142 | "filename" : "mac256.png", 143 | "idiom" : "mac", 144 | "scale" : "2x", 145 | "size" : "128x128" 146 | }, 147 | { 148 | "filename" : "mac256.png", 149 | "idiom" : "mac", 150 | "scale" : "1x", 151 | "size" : "256x256" 152 | }, 153 | { 154 | "filename" : "mac512.png", 155 | "idiom" : "mac", 156 | "scale" : "2x", 157 | "size" : "256x256" 158 | }, 159 | { 160 | "filename" : "mac512.png", 161 | "idiom" : "mac", 162 | "scale" : "1x", 163 | "size" : "512x512" 164 | }, 165 | { 166 | "filename" : "mac1024.png", 167 | "idiom" : "mac", 168 | "scale" : "2x", 169 | "size" : "512x512" 170 | } 171 | ], 172 | "info" : { 173 | "author" : "xcode", 174 | "version" : 1 175 | } 176 | } 177 | -------------------------------------------------------------------------------- /example/SwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUI/Assets.xcassets/AppIcon.appiconset/appstore1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typesense/typesense-swift/8057f2ca81471be207ca1d4e6e46ad0336faffe0/example/SwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUI/Assets.xcassets/AppIcon.appiconset/appstore1024.png -------------------------------------------------------------------------------- /example/SwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUI/Assets.xcassets/AppIcon.appiconset/ipad152.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typesense/typesense-swift/8057f2ca81471be207ca1d4e6e46ad0336faffe0/example/SwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUI/Assets.xcassets/AppIcon.appiconset/ipad152.png -------------------------------------------------------------------------------- /example/SwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUI/Assets.xcassets/AppIcon.appiconset/ipad76.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typesense/typesense-swift/8057f2ca81471be207ca1d4e6e46ad0336faffe0/example/SwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUI/Assets.xcassets/AppIcon.appiconset/ipad76.png -------------------------------------------------------------------------------- /example/SwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUI/Assets.xcassets/AppIcon.appiconset/ipadNotification20.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typesense/typesense-swift/8057f2ca81471be207ca1d4e6e46ad0336faffe0/example/SwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUI/Assets.xcassets/AppIcon.appiconset/ipadNotification20.png -------------------------------------------------------------------------------- /example/SwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUI/Assets.xcassets/AppIcon.appiconset/ipadNotification40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typesense/typesense-swift/8057f2ca81471be207ca1d4e6e46ad0336faffe0/example/SwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUI/Assets.xcassets/AppIcon.appiconset/ipadNotification40.png -------------------------------------------------------------------------------- /example/SwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUI/Assets.xcassets/AppIcon.appiconset/ipadPro167.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typesense/typesense-swift/8057f2ca81471be207ca1d4e6e46ad0336faffe0/example/SwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUI/Assets.xcassets/AppIcon.appiconset/ipadPro167.png -------------------------------------------------------------------------------- /example/SwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUI/Assets.xcassets/AppIcon.appiconset/ipadSettings29.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typesense/typesense-swift/8057f2ca81471be207ca1d4e6e46ad0336faffe0/example/SwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUI/Assets.xcassets/AppIcon.appiconset/ipadSettings29.png -------------------------------------------------------------------------------- /example/SwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUI/Assets.xcassets/AppIcon.appiconset/ipadSettings58.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typesense/typesense-swift/8057f2ca81471be207ca1d4e6e46ad0336faffe0/example/SwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUI/Assets.xcassets/AppIcon.appiconset/ipadSettings58.png -------------------------------------------------------------------------------- /example/SwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUI/Assets.xcassets/AppIcon.appiconset/ipadSpotlight40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typesense/typesense-swift/8057f2ca81471be207ca1d4e6e46ad0336faffe0/example/SwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUI/Assets.xcassets/AppIcon.appiconset/ipadSpotlight40.png -------------------------------------------------------------------------------- /example/SwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUI/Assets.xcassets/AppIcon.appiconset/ipadSpotlight80.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typesense/typesense-swift/8057f2ca81471be207ca1d4e6e46ad0336faffe0/example/SwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUI/Assets.xcassets/AppIcon.appiconset/ipadSpotlight80.png -------------------------------------------------------------------------------- /example/SwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUI/Assets.xcassets/AppIcon.appiconset/iphone120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typesense/typesense-swift/8057f2ca81471be207ca1d4e6e46ad0336faffe0/example/SwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUI/Assets.xcassets/AppIcon.appiconset/iphone120.png -------------------------------------------------------------------------------- /example/SwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUI/Assets.xcassets/AppIcon.appiconset/iphone180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typesense/typesense-swift/8057f2ca81471be207ca1d4e6e46ad0336faffe0/example/SwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUI/Assets.xcassets/AppIcon.appiconset/iphone180.png -------------------------------------------------------------------------------- /example/SwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUI/Assets.xcassets/AppIcon.appiconset/mac1024.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typesense/typesense-swift/8057f2ca81471be207ca1d4e6e46ad0336faffe0/example/SwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUI/Assets.xcassets/AppIcon.appiconset/mac1024.png -------------------------------------------------------------------------------- /example/SwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUI/Assets.xcassets/AppIcon.appiconset/mac128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typesense/typesense-swift/8057f2ca81471be207ca1d4e6e46ad0336faffe0/example/SwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUI/Assets.xcassets/AppIcon.appiconset/mac128.png -------------------------------------------------------------------------------- /example/SwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUI/Assets.xcassets/AppIcon.appiconset/mac16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typesense/typesense-swift/8057f2ca81471be207ca1d4e6e46ad0336faffe0/example/SwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUI/Assets.xcassets/AppIcon.appiconset/mac16.png -------------------------------------------------------------------------------- /example/SwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUI/Assets.xcassets/AppIcon.appiconset/mac256.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typesense/typesense-swift/8057f2ca81471be207ca1d4e6e46ad0336faffe0/example/SwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUI/Assets.xcassets/AppIcon.appiconset/mac256.png -------------------------------------------------------------------------------- /example/SwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUI/Assets.xcassets/AppIcon.appiconset/mac32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typesense/typesense-swift/8057f2ca81471be207ca1d4e6e46ad0336faffe0/example/SwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUI/Assets.xcassets/AppIcon.appiconset/mac32.png -------------------------------------------------------------------------------- /example/SwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUI/Assets.xcassets/AppIcon.appiconset/mac512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typesense/typesense-swift/8057f2ca81471be207ca1d4e6e46ad0336faffe0/example/SwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUI/Assets.xcassets/AppIcon.appiconset/mac512.png -------------------------------------------------------------------------------- /example/SwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUI/Assets.xcassets/AppIcon.appiconset/mac64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typesense/typesense-swift/8057f2ca81471be207ca1d4e6e46ad0336faffe0/example/SwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUI/Assets.xcassets/AppIcon.appiconset/mac64.png -------------------------------------------------------------------------------- /example/SwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUI/Assets.xcassets/AppIcon.appiconset/notification40.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typesense/typesense-swift/8057f2ca81471be207ca1d4e6e46ad0336faffe0/example/SwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUI/Assets.xcassets/AppIcon.appiconset/notification40.png -------------------------------------------------------------------------------- /example/SwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUI/Assets.xcassets/AppIcon.appiconset/notification60.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typesense/typesense-swift/8057f2ca81471be207ca1d4e6e46ad0336faffe0/example/SwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUI/Assets.xcassets/AppIcon.appiconset/notification60.png -------------------------------------------------------------------------------- /example/SwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUI/Assets.xcassets/AppIcon.appiconset/settings58.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typesense/typesense-swift/8057f2ca81471be207ca1d4e6e46ad0336faffe0/example/SwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUI/Assets.xcassets/AppIcon.appiconset/settings58.png -------------------------------------------------------------------------------- /example/SwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUI/Assets.xcassets/AppIcon.appiconset/settings87.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typesense/typesense-swift/8057f2ca81471be207ca1d4e6e46ad0336faffe0/example/SwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUI/Assets.xcassets/AppIcon.appiconset/settings87.png -------------------------------------------------------------------------------- /example/SwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUI/Assets.xcassets/AppIcon.appiconset/spotlight120.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typesense/typesense-swift/8057f2ca81471be207ca1d4e6e46ad0336faffe0/example/SwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUI/Assets.xcassets/AppIcon.appiconset/spotlight120.png -------------------------------------------------------------------------------- /example/SwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUI/Assets.xcassets/AppIcon.appiconset/spotlight80.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typesense/typesense-swift/8057f2ca81471be207ca1d4e6e46ad0336faffe0/example/SwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUI/Assets.xcassets/AppIcon.appiconset/spotlight80.png -------------------------------------------------------------------------------- /example/SwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUI/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /example/SwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUI/ContentView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentView.swift 3 | // RecipeSearchSwiftUI 4 | // 5 | // Created by Sabesh Bharathi on 16/01/22. 6 | // 7 | 8 | import SwiftUI 9 | import Typesense 10 | 11 | struct ContentView: View { 12 | @State private var queryString = "" 13 | @State private var hits: [SearchResultHit] = [] 14 | @State private var showingSheet = false 15 | @State private var selectedRecipe: Recipe? = nil 16 | @Environment(\.colorScheme) private var colorScheme 17 | 18 | let client: Client 19 | 20 | init() { 21 | let config = Configuration(nodes: [Node(host: "qtg5aekc2iosjh93p.a1.typesense.net", port: "443", nodeProtocol: "https")], apiKey: "8hLCPSQTYcBuK29zY5q6Xhin7ONxHy99") 22 | client = Client(config: config) 23 | } 24 | 25 | var body: some View { 26 | NavigationView { 27 | ScrollView { 28 | ForEach(hits, id: \.document){ hit in 29 | HStack { 30 | Button { 31 | selectedRecipe = hit.document 32 | } label: { 33 | VStack(alignment: .leading) { 34 | Text(hit.document!.title) 35 | .font(.headline) 36 | HStack{ 37 | Text("From " + getFoodProvider(link: hit.document?.link ?? "N/A")) 38 | } 39 | .font(.caption) 40 | 41 | 42 | Text(attachIngredients(recipe: hit.document)) 43 | .padding(.top, 5) 44 | } 45 | .padding(.vertical, 2) 46 | } 47 | .buttonStyle(.plain) 48 | Spacer() 49 | } 50 | .padding() 51 | .background(colorScheme == .dark ? Color.gray.opacity(0.2) : Color.white) 52 | .cornerRadius(20) 53 | .padding(.horizontal, 20) 54 | .padding(.vertical, 10) 55 | .shadow(color: .black.opacity(0.2), radius: 20, x: 0, y: 10) 56 | } 57 | 58 | } 59 | .navigationTitle("Search Recipes 🥘") 60 | } 61 | .background(Color.red) 62 | .sheet(item: $selectedRecipe) { chosenItem in 63 | RecipeDetailView(recipe: chosenItem) 64 | } 65 | .searchable(text: $queryString) 66 | .onChange(of: queryString) { quer in 67 | fetchRecipes(searchQuery: quer, quer == "" ? true : false) 68 | } 69 | .onAppear { 70 | fetchRecipes(searchQuery: "", true) 71 | } 72 | } 73 | 74 | private func fetchRecipes(searchQuery: String, _ initialRecipes: Bool = false) { 75 | let collectionParams = MultiSearchCollectionParameters(q: searchQuery, collection: "r") 76 | let searchParams = MultiSearchParameters(queryBy: "title", perPage: initialRecipes ? 8 : 25) 77 | Task { 78 | do { 79 | let (data, _) = try await client.multiSearch().perform(searchRequests: [collectionParams], commonParameters: searchParams, for: Recipe.self) 80 | hits = (data?.results[0].hits) ?? [] 81 | } catch (let error) { 82 | print(error.localizedDescription) 83 | } 84 | } 85 | } 86 | 87 | private func getFoodProvider(link: String) -> String { 88 | let first = link.split(separator: ".")[0] 89 | if first.contains("www") { 90 | let second = link.split(separator: ".")[1] 91 | return String(second + ".com") 92 | } 93 | return String(first + ".com") 94 | } 95 | 96 | private func attachIngredients(recipe: Recipe?) -> String { 97 | 98 | guard let recipe = recipe else { 99 | return "" 100 | } 101 | 102 | var displayString = "" 103 | let list = recipe.ingredient_names 104 | for index in 0.. 5 { 113 | displayString.append(contentsOf: " and more!") 114 | } 115 | 116 | return displayString 117 | } 118 | } 119 | 120 | struct ContentView_Previews: PreviewProvider { 121 | static var previews: some View { 122 | ContentView() 123 | .previewDevice("iPhone 13 Pro") 124 | } 125 | } 126 | -------------------------------------------------------------------------------- /example/SwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUI/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /example/SwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUI/Recipe.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Recipe.swift 3 | // RecipeSearchSwiftUI 4 | // 5 | // Created by Sabesh Bharathi on 16/01/22. 6 | // 7 | 8 | import Foundation 9 | 10 | public struct Recipe: Codable, Hashable, Identifiable { 11 | var directions: [String] 12 | public var id: String 13 | var ingredient_names: [String] 14 | var ingredients_with_measurements: [String] 15 | var link: String 16 | var recipe_id: Int 17 | var title: String 18 | } 19 | -------------------------------------------------------------------------------- /example/SwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUI/RecipeDetailView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RecipeDetailView.swift 3 | // RecipeSearchSwiftUI 4 | // 5 | // Created by Sabesh Bharathi on 08/05/22. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct RecipeDetailView: View { 11 | @Environment(\.dismiss) var dismiss 12 | 13 | var recipe: Recipe? 14 | var body: some View { 15 | if let recipe = recipe { 16 | List { 17 | 18 | HStack { 19 | Text(recipe.title) 20 | .font(.title) 21 | .bold() 22 | Spacer() 23 | Text("🍴") 24 | .font(.system(size: 60)) 25 | } 26 | 27 | .padding(10) 28 | 29 | Section(header: Text("Ingredients")) { 30 | ForEach(recipe.ingredients_with_measurements, id: \.self) { ingredient in 31 | Text(ingredient) 32 | } 33 | } 34 | 35 | Section(header: Text("Cooking Directions")) { 36 | ForEach(recipe.directions, id: \.self) { direction in 37 | Text("\(recipe.directions.firstIndex(of: direction)! + 1). " + direction) 38 | } 39 | } 40 | 41 | HStack { 42 | Spacer() 43 | Button { 44 | if let url = URL(string: recipe.link) { 45 | if UIApplication.shared.canOpenURL(url) { 46 | UIApplication.shared.open(url, options: [:]) 47 | } 48 | } 49 | } label: { 50 | Text("Explore") 51 | } 52 | Spacer() 53 | } 54 | 55 | } 56 | } 57 | } 58 | } 59 | 60 | struct RecipeDetailView_Previews: PreviewProvider { 61 | static var previews: some View { 62 | RecipeDetailView() 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /example/SwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUI/RecipeSearchSwiftUIApp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RecipeSearchSwiftUIApp.swift 3 | // RecipeSearchSwiftUI 4 | // 5 | // Created by Sabesh Bharathi on 16/01/22. 6 | // 7 | 8 | import SwiftUI 9 | 10 | @main 11 | struct RecipeSearchSwiftUIApp: App { 12 | var body: some Scene { 13 | WindowGroup { 14 | ContentView() 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /example/UIKit/RecipeSearchUIKit/README.md: -------------------------------------------------------------------------------- 1 | # RecipeSearch (UIKit) 2 | 3 | This is a tiny demo-app made with UIKit, implementing the typesense-swift client. This demonstrates how typesense can easily breeze through around 2 Million records to find out the most relevant recipes for you and your search query. 4 | 5 | ![Demo Gif](RecipeSearch.gif) 6 | -------------------------------------------------------------------------------- /example/UIKit/RecipeSearchUIKit/RecipeSearch.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/typesense/typesense-swift/8057f2ca81471be207ca1d4e6e46ad0336faffe0/example/UIKit/RecipeSearchUIKit/RecipeSearch.gif -------------------------------------------------------------------------------- /example/UIKit/RecipeSearchUIKit/RecipeSearchUIKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /example/UIKit/RecipeSearchUIKit/RecipeSearchUIKit.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/UIKit/RecipeSearchUIKit/RecipeSearchUIKit.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "pins" : [ 3 | { 4 | "identity" : "typesense-swift", 5 | "kind" : "remoteSourceControl", 6 | "location" : "https://github.com/typesense/typesense-swift", 7 | "state" : { 8 | "revision" : "9454327b9a8c19f7e37a8f27865439add0cb5618", 9 | "version" : "0.1.2" 10 | } 11 | } 12 | ], 13 | "version" : 2 14 | } 15 | -------------------------------------------------------------------------------- /example/UIKit/RecipeSearchUIKit/RecipeSearchUIKit/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // RecipeSearchUIKit 4 | // 5 | // Created by Sabesh Bharathi on 04/01/22. 6 | // 7 | 8 | import UIKit 9 | 10 | @main 11 | class AppDelegate: UIResponder, UIApplicationDelegate { 12 | 13 | 14 | 15 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 16 | // Override point for customization after application launch. 17 | return true 18 | } 19 | 20 | // MARK: UISceneSession Lifecycle 21 | 22 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { 23 | // Called when a new scene session is being created. 24 | // Use this method to select a configuration to create the new scene with. 25 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) 26 | } 27 | 28 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { 29 | // Called when the user discards a scene session. 30 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. 31 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return. 32 | } 33 | 34 | 35 | } 36 | 37 | -------------------------------------------------------------------------------- /example/UIKit/RecipeSearchUIKit/RecipeSearchUIKit/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /example/UIKit/RecipeSearchUIKit/RecipeSearchUIKit/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /example/UIKit/RecipeSearchUIKit/RecipeSearchUIKit/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /example/UIKit/RecipeSearchUIKit/RecipeSearchUIKit/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /example/UIKit/RecipeSearchUIKit/RecipeSearchUIKit/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | -------------------------------------------------------------------------------- /example/UIKit/RecipeSearchUIKit/RecipeSearchUIKit/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | UIApplicationSceneManifest 6 | 7 | UIApplicationSupportsMultipleScenes 8 | 9 | UISceneConfigurations 10 | 11 | UIWindowSceneSessionRoleApplication 12 | 13 | 14 | UISceneConfigurationName 15 | Default Configuration 16 | UISceneDelegateClassName 17 | $(PRODUCT_MODULE_NAME).SceneDelegate 18 | UISceneStoryboardFile 19 | Main 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /example/UIKit/RecipeSearchUIKit/RecipeSearchUIKit/Recipe.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Recipe.swift 3 | // RecipeSearchUIKit 4 | // 5 | // Created by Sabesh Bharathi on 04/01/22. 6 | // 7 | 8 | import Foundation 9 | 10 | public struct Recipe: Codable, Hashable, Identifiable { 11 | var directions: [String] 12 | public var id: String 13 | var ingredient_names: [String] 14 | var ingredients_with_measurements: [String] 15 | var link: String 16 | var recipe_id: Int 17 | var title: String 18 | } 19 | 20 | -------------------------------------------------------------------------------- /example/UIKit/RecipeSearchUIKit/RecipeSearchUIKit/SceneDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.swift 3 | // RecipeSearchUIKit 4 | // 5 | // Created by Sabesh Bharathi on 04/01/22. 6 | // 7 | 8 | import UIKit 9 | 10 | class SceneDelegate: UIResponder, UIWindowSceneDelegate { 11 | 12 | var window: UIWindow? 13 | 14 | 15 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { 16 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. 17 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. 18 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). 19 | guard let _ = (scene as? UIWindowScene) else { return } 20 | } 21 | 22 | func sceneDidDisconnect(_ scene: UIScene) { 23 | // Called as the scene is being released by the system. 24 | // This occurs shortly after the scene enters the background, or when its session is discarded. 25 | // Release any resources associated with this scene that can be re-created the next time the scene connects. 26 | // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead). 27 | } 28 | 29 | func sceneDidBecomeActive(_ scene: UIScene) { 30 | // Called when the scene has moved from an inactive state to an active state. 31 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. 32 | } 33 | 34 | func sceneWillResignActive(_ scene: UIScene) { 35 | // Called when the scene will move from an active state to an inactive state. 36 | // This may occur due to temporary interruptions (ex. an incoming phone call). 37 | } 38 | 39 | func sceneWillEnterForeground(_ scene: UIScene) { 40 | // Called as the scene transitions from the background to the foreground. 41 | // Use this method to undo the changes made on entering the background. 42 | } 43 | 44 | func sceneDidEnterBackground(_ scene: UIScene) { 45 | // Called as the scene transitions from the foreground to the background. 46 | // Use this method to save data, release shared resources, and store enough scene-specific state information 47 | // to restore the scene back to its current state. 48 | } 49 | 50 | 51 | } 52 | 53 | -------------------------------------------------------------------------------- /example/UIKit/RecipeSearchUIKit/RecipeSearchUIKit/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // RecipeSearchUIKit 4 | // 5 | // Created by Sabesh Bharathi on 04/01/22. 6 | // 7 | 8 | import UIKit 9 | import Typesense 10 | 11 | class ViewController: UIViewController, UISearchResultsUpdating { 12 | 13 | @IBOutlet weak var hitsTableView: UITableView! 14 | 15 | var hits:[SearchResultHit] 16 | 17 | class HitsViewController: UIViewController { 18 | override func viewDidLoad() { 19 | super.viewDidLoad() 20 | } 21 | } 22 | 23 | let searchController = UISearchController(searchResultsController: HitsViewController()) 24 | let client: Client 25 | 26 | required init?(coder aDecoder: NSCoder) { 27 | let config = Configuration(nodes: [Node(host: "qtg5aekc2iosjh93p.a1.typesense.net", port: "443", nodeProtocol: "https")], apiKey: "8hLCPSQTYcBuK29zY5q6Xhin7ONxHy99") 28 | self.client = Client(config: config) 29 | self.hits = [] 30 | super.init(coder: aDecoder) 31 | } 32 | 33 | override func viewDidLoad() { 34 | super.viewDidLoad() 35 | title = "Search Recipes 🥘" 36 | searchController.searchResultsUpdater = self 37 | navigationItem.searchController = searchController 38 | hitsTableView.delegate = self 39 | hitsTableView.dataSource = self 40 | updateSearchResults(for: UISearchController()) 41 | } 42 | 43 | func updateSearchResults(for searchController: UISearchController) { 44 | guard let text = searchController.searchBar.text else { 45 | return 46 | } 47 | 48 | let collectionParams = MultiSearchCollectionParameters(q: text, collection: "r") 49 | let searchParams = MultiSearchParameters(queryBy: "title", perPage: 25) 50 | 51 | Task { 52 | do { 53 | let (data, _) = try await client.multiSearch().perform(searchRequests: [collectionParams], commonParameters: searchParams, for: Recipe.self) 54 | self.hits = (data?.results[0].hits) ?? [] 55 | self.hitsTableView.reloadData() 56 | } catch (let error) { 57 | print(error.localizedDescription) 58 | } 59 | } 60 | 61 | } 62 | } 63 | 64 | extension ViewController: UITableViewDelegate, UITableViewDataSource { 65 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 66 | return hits.count 67 | } 68 | 69 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 70 | let recipe = hits[indexPath.row].document 71 | var cell = hitsTableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) 72 | if #available(iOS 14.0, *) { 73 | var content = cell.defaultContentConfiguration() 74 | content.text = recipe?.title 75 | cell.contentConfiguration = content 76 | } else { 77 | cell = UITableViewCell(style: UITableViewCell.CellStyle.subtitle, 78 | reuseIdentifier: "cell") 79 | cell.textLabel?.text = recipe?.title 80 | } 81 | 82 | return cell 83 | } 84 | } 85 | 86 | 87 | -------------------------------------------------------------------------------- /get-models.sh: -------------------------------------------------------------------------------- 1 | curl https://raw.githubusercontent.com/typesense/typesense-api-spec/master/openapi.yml > openapi.yml 2 | swagger-codegen generate -i openapi.yml -l swift5 -o output 3 | rm -rf Models 4 | cd output/SwaggerClient/Classes/Swaggers 5 | mv ./Models ../../../../ 6 | cd ../../../../ 7 | rm -rf output 8 | 9 | cd Models 10 | 11 | # Delete useless structs generated as a mistake by the code-gen 12 | rm OneOfSearchParametersMaxHits.swift 13 | rm OneOfMultisearchParametersMaxHits.swift 14 | 15 | # Fix the maxHits type by defining it as a String Optional 16 | find . -name "SearchParameters.swift" -exec sed -i '' 's/maxHits: OneOfSearchParametersMaxHits\?/maxHits: String\?/g' {} \; 17 | find . -name "MultiSearchParameters.swift" -exec sed -i '' 's/maxHits: OneOfMultiSearchParametersMaxHits\?/maxHits: String\?/g' {} \; 18 | find . -name "MultiSearchCollectionParameters.swift" -exec sed -i '' 's/maxHits: Any\?/maxHits: String\?/g' {} \; 19 | 20 | # Add Generics to SearchResult 21 | find . -name "SearchResult.swift" -exec sed -i '' 's/SearchResult:/SearchResult:/g' {} \; 22 | find . -name "SearchResult.swift" -exec sed -i '' 's/groupedHits: \[SearchGroupedHit\]\?/groupedHits: \[SearchGroupedHit\]\?/g' {} \; 23 | find . -name "SearchResult.swift" -exec sed -i '' 's/hits: \[SearchResultHit\]\?/hits: \[SearchResultHit\]\?/g' {} \; 24 | 25 | # Add Generics to MultiSearchResult 26 | find . -name "MultiSearchResult.swift" -exec sed -i '' 's/results: \[SearchResult\]/results: \[SearchResult\]/g' {} \; 27 | find . -name "MultiSearchResult.swift" -exec sed -i '' 's/MultiSearchResult:/MultiSearchResult:/g' {} \; 28 | 29 | # Add Generics to SearchResultHit 30 | find . -name "SearchResultHit.swift" -exec sed -i '' 's/SearchResultHit:/SearchResultHit:/g' {} \; 31 | 32 | # Convert document parameter to a generic T 33 | find . -name "SearchResultHit.swift" -exec sed -i '' 's/document: \[String:Any\]\?/document: T?/g' {} \; 34 | 35 | # Add Generics to SearchGroupedHit 36 | find . -name "SearchGroupedHit.swift" -exec sed -i '' 's/SearchGroupedHit:/SearchGroupedHit:/g' {} \; 37 | find . -name "SearchGroupedHit.swift" -exec sed -i '' 's/hits: \[SearchResultHit\]/hits: \[SearchResultHit\]/g' {} \; 38 | 39 | # Convert matchedTokens to custom defined StringQuantum 40 | find . -name "SearchHighlight.swift" -exec sed -i '' 's/matchedTokens: \[Any\]\?/matchedTokens: StringQuantum\?/g' {} \; 41 | 42 | 43 | 44 | 45 | 46 | 47 | --------------------------------------------------------------------------------