Skip to content

Commit

Permalink
chore: Telemetry (#209)
Browse files Browse the repository at this point in the history
  • Loading branch information
VladislavFitz committed Dec 15, 2021
1 parent 1341c42 commit 7c83f8a
Show file tree
Hide file tree
Showing 56 changed files with 1,336 additions and 70 deletions.
3 changes: 2 additions & 1 deletion Cartfile
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
github "algolia/algoliasearch-client-swift" ~> 8.10
github "algolia/algoliasearch-client-swift" ~> 8.13
github "algolia/instantsearch-telemetry-native" ~> 0.1.0-beta1
github "apple/swift-log" ~> 1.4
5 changes: 3 additions & 2 deletions InstantSearch.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Pod::Spec.new do |s|

s.subspec "Insights" do |ss|
ss.source_files = 'Sources/InstantSearchInsights/**/*.{swift}'
ss.dependency 'AlgoliaSearchClient', '~> 8.12'
ss.dependency 'AlgoliaSearchClient', '~> 8.13'
ss.ios.deployment_target = '9.0'
ss.osx.deployment_target = '10.10'
ss.watchos.deployment_target = '3.0'
Expand All @@ -24,8 +24,9 @@ Pod::Spec.new do |s|

s.subspec "Core" do |ss|
ss.source_files = 'Sources/InstantSearchCore/**/*.{swift}'
ss.dependency 'AlgoliaSearchClient', '~> 8.12'
ss.dependency 'AlgoliaSearchClient', '~> 8.13'
ss.dependency 'InstantSearch/Insights'
ss.dependency 'InstantSearchTelemetry', '~> 0.1.0'
ss.ios.deployment_target = '9.0'
ss.osx.deployment_target = '10.10'
ss.watchos.deployment_target = '3.0'
Expand Down
11 changes: 8 additions & 3 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// swift-tools-version:5.2
// swift-tools-version:5.5
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription
Expand Down Expand Up @@ -26,7 +26,12 @@ let package = Package(
targets: ["InstantSearchSwiftUI"])
],
dependencies: [
.package(name: "AlgoliaSearchClient", url: "https://github.com/algolia/algoliasearch-client-swift", from: "8.12.0")
.package(name: "AlgoliaSearchClient",
url: "https://github.com/algolia/algoliasearch-client-swift",
from: "8.13.0"),
.package(name: "InstantSearchTelemetry",
url: "https://github.com/algolia/instantsearch-telemetry-native",
from: "0.1.0-beta1")
],
targets: [
.target(
Expand All @@ -37,7 +42,7 @@ let package = Package(
dependencies: ["InstantSearchInsights", "AlgoliaSearchClient"]),
.target(
name: "InstantSearchCore",
dependencies: ["AlgoliaSearchClient", "InstantSearchInsights"]),
dependencies: ["AlgoliaSearchClient", "InstantSearchInsights", .product(name: "InstantSearchTelemetry", package: "InstantSearchTelemetry")]),
.testTarget(
name: "InstantSearchCoreTests",
dependencies: ["InstantSearchCore", "AlgoliaSearchClient", "InstantSearchInsights"]),
Expand Down
86 changes: 52 additions & 34 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,56 +100,65 @@ github "algolia/instantsearch-ios" ~> 7.14
Learn more about instantSearch iOS in the [dedicated documentation website](https://www.algolia.com/doc/api-reference/widgets/ios/).

## Basic Usage

In your `ViewController.swift`:

```swift
import InstantSearch

struct BestBuyItem: Codable {
struct Item: Codable {
let name: String
}

struct BestBuyTableViewCellConfigurator: TableViewCellConfigurable {

let model: BestBuyItem
class SearchResultsViewController: UITableViewController, HitsController {

init(model: BestBuyItem, indexPath: IndexPath) {
self.model = model
var hitsSource: HitsInteractor<Item>?

override func viewDidLoad() {
super.viewDidLoad()
tableView.register(UITableViewCell.self, forCellReuseIdentifier: "cell")
}

override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
hitsSource?.numberOfHits() ?? 0
}

func configure(_ cell: UITableViewCell) {
cell.textLabel?.text = model.name
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath)
cell.textLabel?.text = hitsSource?.hit(atIndex: indexPath.row)?.name
return cell
}


override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
if let _ = hitsSource?.hit(atIndex: indexPath.row) {
// Handle hit selection
}
}

}

typealias BestBuyHitsViewController = HitsTableViewController<BestBuyTableViewCellConfigurator>

class ViewController: UIViewController {

let searcher = SingleIndexSearcher(appID: "latency",
apiKey: "1f6fd3a6fb973cb08419fe7d288fa4db",
indexName: "bestbuy")
lazy var searchController: UISearchController = .init(searchResultsController: hitsTableViewController)
lazy var searchConnector: SingleIndexSearchConnector<BestBuyItem> = .init(searcher: searcher,
searchController: searchController,
hitsController: hitsTableViewController)
let hitsTableViewController: BestBuyHitsViewController = .init()
let statsInteractor: StatsInteractor = .init()
lazy var searchController = UISearchController(searchResultsController: hitsViewController)
let hitsViewController = SearchResultsViewController()

let searcher = HitsSearcher(appID: "latency",
apiKey: "1f6fd3a6fb973cb08419fe7d288fa4db",
indexName: "bestbuy")
lazy var searchConnector = SearchConnector<Item>(searcher: searcher,
searchController: searchController,
hitsInteractor: .init(),
hitsController: hitsViewController)

override func viewDidLoad() {
super.viewDidLoad()
searchConnector.connect()
statsInteractor.connectSearcher(searcher)
statsInteractor.connectController(self)
searcher.search()
setupUI()
}

override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
searchController.searchBar.becomeFirstResponder()
searchController.isActive = true
}

func setupUI() {
Expand All @@ -160,24 +169,33 @@ class ViewController: UIViewController {
searchController.automaticallyShowsCancelButton = false
}

}

extension ViewController: StatsTextController {

func setItem(_ item: String?) {
title = item
}

}
```

Run your app and you will the most basic search experience: a `UISearchBar` with the number of results each time you write a query.
You can now build and run your application to see the basic search experience in action.
You should see that the results are changing on each key stroke.

To get a more meaningful search experience, please follow our [Getting Started Guide](https://www.algolia.com/doc/guides/building-search-ui/getting-started/ios/).
To get a more meaningful search experience, please follow the [Getting Started Guide](https://www.algolia.com/doc/guides/building-search-ui/getting-started/ios/).
If you build a SwiftUI application, please check out the Getting [Started with SwiftUI guide](https://www.algolia.com/doc/guides/building-search-ui/getting-started/how-to/declarative/ios/)

If you only require business logic modules in your project and use `InstantSearchCore` framework, add `import InstantSearchCore` to your source files.


## Telemetry

InstantSearch iOS collects data points at runtime. This helps the InstantSearch team improve and prioritize future development.

Here's an exhaustive list of the collected data:

- InstantSearch version
- The name of the instantiated InstantSearch components, for example, `HitsSearcher`, `FilterState`
- The name of the components with custom parameters (overridden defaults). InstantSearch doesn't collect the values of those parameters. For example, the default of the `facets` value in `FacetListInteractor` is an empty list. If you instantiate it with a list of facets, then the telemetry tracks that the `facets` parameter received a custom value, but not the value itself.

InstantSearch doesn't collect any sensitive or personal data. However, you can still opt out of the telemetry collection with the following code:
```swift
InstantSearchTelemetry.shared.isEnabled = false
```

## Getting Help

- **Need help**? Ask a question to the [Algolia Community](https://discourse.algolia.com/) or on [Stack Overflow](http://stackoverflow.com/questions/tagged/algolia).
Expand Down
12 changes: 12 additions & 0 deletions Sources/InstantSearchCore/AdvancedConnectors/SearchConnector.swift
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,10 @@ public struct SearchConnector<Record: Codable>: Connection {
filterStateSearcherConnection = nil
}

Telemetry.shared.traceConnector(type: .hitsSearcher,
parameters: [
filterState == nil ? .none : .filterState
])
}

/**
Expand Down Expand Up @@ -103,6 +107,14 @@ public struct SearchConnector<Record: Codable>: Connection {
hitsInteractor: hitsInteractor,
hitsController: hitsController,
filterState: filterState)

Telemetry.shared.traceConnector(type: .hitsSearcher,
parameters: [
.appID,
.apiKey,
.indexName,
filterState == nil ? .none : .filterStateParameter
])
}

public func connect() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ public extension CurrentFiltersConnector {
interactor: CurrentFiltersInteractor = .init(),
controller: Controller? = nil,
presenter: @escaping Presenter<Filter, String> = DefaultPresenter.Filter.present) where Controller.Item == FilterAndID {
self.init(filterState: filterState, groupIDs: groupIDs, interactor: interactor)
self.init(filterState: filterState,
groupIDs: groupIDs,
interactor: interactor)
if let controller = controller {
connectController(controller, presenter: presenter)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ public class CurrentFiltersConnector {
self.interactor = interactor
self.filterStateConnection = interactor.connectFilterState(filterState, filterGroupIDs: groupIDs)
self.controllerConnections = []
Telemetry.shared.traceConnector(type: .currentFilters,
parameters: [
groupIDs?.isEmpty ?? true ? .none : .groupIds
])
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,17 @@
import Foundation

/// Business logic for Current Refinements component
public typealias CurrentFiltersInteractor = ItemsListInteractor<FilterAndID>
public class CurrentFiltersInteractor: ItemsListInteractor<FilterAndID> {

public override init(items: Set<FilterAndID> = []) {
Telemetry.shared.trace(type: .currentFilters,
parameters: [
items.isEmpty ? .none : .items
])
super.init(items: items)
}

}

/// Union of a filter and its group in a filter state
public struct FilterAndID: Hashable {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,8 +51,8 @@ public extension DynamicFacetListConnector {
filterGroupForAttribute: [Attribute: FilterGroupDescriptor] = [:],
controller: Controller) {
let interactor = DynamicFacetListInteractor(orderedFacets: orderedFacets,
selections: selections,
selectionModeForAttribute: selectionModeForAttribute)
selections: selections,
selectionModeForAttribute: selectionModeForAttribute)
self.init(searcher: searcher,
filterState: filterState,
interactor: interactor,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,10 @@ public class DynamicFacetListConnector<Searcher: SearchResultObservable> where S
searcherConnection = interactor.connectSearcher(searcher)
filterStateConnection = interactor.connectFilterState(filterState,
filterGroupForAttribute: filterGroupForAttribute)
Telemetry.shared.traceConnector(type: .dynamicFacets,
parameters: [
filterGroupForAttribute.isEmpty ? .none : .filterGroupForAttribute
])
}

/**
Expand All @@ -69,12 +73,19 @@ public class DynamicFacetListConnector<Searcher: SearchResultObservable> where S
selectionModeForAttribute: [Attribute: SelectionMode] = [:],
filterGroupForAttribute: [Attribute: FilterGroupDescriptor] = [:]) {
let interactor = DynamicFacetListInteractor(orderedFacets: orderedFacets,
selections: selections,
selectionModeForAttribute: selectionModeForAttribute)
selections: selections,
selectionModeForAttribute: selectionModeForAttribute)
self.init(searcher: searcher,
filterState: filterState,
interactor: interactor,
filterGroupForAttribute: filterGroupForAttribute)
Telemetry.shared.traceConnector(type: .dynamicFacets,
parameters: [
orderedFacets.isEmpty ? .none : .orderedFacets,
selections.isEmpty ? .none : .selections,
selectionModeForAttribute.isEmpty ? .none : .selectionModeForAttribute,
filterGroupForAttribute.isEmpty ? .none : .filterGroupForAttribute
])
}

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,12 @@ public class DynamicFacetListInteractor {
onFacetOrderChanged.fire(orderedFacets)
onSelectionsChanged.fire(selections)
updateInteractors()
Telemetry.shared.trace(type: .dynamicFacets,
parameters: [
orderedFacets.isEmpty ? nil : .orderedFacets,
selections.isEmpty ? nil : .selections,
selectionModeForAttribute.isEmpty ? nil : .selectionModeForAttribute
])
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,10 @@ public extension FacetListConnector {
- Parameters:
- controller: Controller interfacing with a concrete facet list view
- presenter: Presenter defining how a facet appears in the controller
- externalReload: Defines if controller will be updated automatically by the events or manually
- Returns: Established connection
*/
@discardableResult func connectController<Controller: FacetListController>(_ controller: Controller,
with presenter: SelectableListPresentable? = nil,
externalReload: Bool = false) -> FacetListConnector.ControllerConnection<Controller> {
with presenter: SelectableListPresentable? = nil) -> FacetListConnector.ControllerConnection<Controller> {
let connection = interactor.connectController(controller, with: presenter)
controllerConnections.append(connection)
return connection
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ public extension FacetListConnector {
interactor: FacetListInteractor = .init(),
operator: RefinementOperator,
groupName: String? = nil) {
Telemetry.shared.traceConnector(type: .facetList,
parameters: [
.facetSearcherParameter,
groupName == nil ? .none : .groupName
])
self.init(searcher: .facet(searcher),
filterState: filterState,
interactor: interactor,
Expand Down Expand Up @@ -55,6 +60,11 @@ public extension FacetListConnector {
groupName: String? = nil,
controller: Controller,
presenter: SelectableListPresentable? = nil) {
Telemetry.shared.traceConnector(type: .facetList,
parameters: [
.facetSearcherParameter,
groupName == nil ? .none : .groupName
])
self.init(searcher: .facet(searcher),
filterState: filterState,
interactor: interactor,
Expand All @@ -81,9 +91,14 @@ public extension FacetListConnector {
attribute: Attribute,
selectionMode: SelectionMode,
facets: [Facet] = [],
persistentSelection: Bool = false,
operator: RefinementOperator,
groupName: String? = nil) {
Telemetry.shared.traceConnector(type: .facetList,
parameters: [
.facetSearcherParameter,
facets.isEmpty ? .none : .facets,
groupName == nil ? .none : .groupName
])
self.init(searcher: .facet(searcher),
filterState: filterState,
interactor: .init(facets: facets, selectionMode: selectionMode),
Expand All @@ -110,11 +125,16 @@ public extension FacetListConnector {
attribute: Attribute,
selectionMode: SelectionMode,
facets: [Facet] = [],
persistentSelection: Bool = false,
operator: RefinementOperator,
groupName: String? = nil,
controller: Controller,
presenter: SelectableListPresentable? = nil) {
Telemetry.shared.traceConnector(type: .facetList,
parameters: [
.facetSearcherParameter,
facets.isEmpty ? .none : .facets,
groupName == nil ? .none : .groupName
])
self.init(searcher: .facet(searcher),
filterState: filterState,
interactor: .init(facets: facets, selectionMode: selectionMode),
Expand Down
Loading

0 comments on commit 7c83f8a

Please sign in to comment.