Поиск в мобильном приложении
В приложении нет трекера и виджета, поэтому всё делается через API. Ключ не нужен: запросы можно отправлять прямо с устройства.
-
ID пользователя. При первом запуске сгенерируйте UUID и сохраните (Keychain / SharedPreferences). Передавайте его как
userIdво все запросы и какuser_idв события. Если пользователь авторизован и на сайте вы передаёте свой ID — используйте тот же, чтобы история связалась. -
Стартовый экран поиска.
GET /api/v2/{uuid}/completions?query=&userId=…— история и популярные запросы;GET /api/{uuid}/popular?limit=10— популярные товары. -
Подсказки при вводе. Debounce 150 мс, отмена предыдущего запроса:
GET /api/v2/{uuid}/completions?query=сам&userId=…&client=mobile_app_ios -
Выдача и фильтры.
GET /api/{uuid}/search?query=самоед&limit=20&skip=0&userId=…&client=mobile_app_ios&queryId=<uuid>Панель фильтров рисуйте из
paramsответа, выбранные фильтры передавайте вparams(формат). Бесконечная лента —skip += limitс тем жеqueryId. -
Поиск по фото. Сожмите снимок до 512 px и отправьте
POST /searchс{"image":"data:image/jpeg;base64,…"}— код для iOS и Android. -
Карточка товара.
GET /find-by-id?id=…®ionId=…— актуальные цены и наличие; под ней —GET /widgets/{id}?id=…&userId=…для похожих. -
События — без них не будет персонализации и статистики:
- клик в выдаче →
GET /event/click.json?type=productClick&entityId=…&queryId=…¶ms={"pos":N}; - просмотр карточки, корзина, покупка →
POST /event/datalayer.jsonсclient: "mobile_app_ios".
- клик в выдаче →
struct SearchBooster { static let base = URL(string: "https://api.searchbooster.net/api/\(UUID_PROJECT)")! static let userId: String = { if let id = UserDefaults.standard.string(forKey: "sb_user_id") { return id } let id = UUID().uuidString UserDefaults.standard.set(id, forKey: "sb_user_id") return id }()
static func search(_ query: String, skip: Int = 0) async throws -> SearchResponse { var c = URLComponents(url: base.appendingPathComponent("search"), resolvingAgainstBaseURL: false)! c.queryItems = [ .init(name: "query", value: query), .init(name: "limit", value: "20"), .init(name: "skip", value: String(skip)), .init(name: "userId", value: userId), .init(name: "client", value: "mobile_app_ios"), ] let (data, _) = try await URLSession.shared.data(from: c.url!) return try JSONDecoder().decode(SearchResponse.self, from: data) }}object SearchBooster { private const val BASE = "https://api.searchbooster.net/api/$UUID_PROJECT" private val http = OkHttpClient()
fun search(query: String, userId: String, skip: Int = 0): String { val url = "$BASE/search".toHttpUrl().newBuilder() .addQueryParameter("query", query) .addQueryParameter("limit", "20") .addQueryParameter("skip", skip.toString()) .addQueryParameter("userId", userId) .addQueryParameter("client", "mobile_app_android") .build() http.newCall(Request.Builder().url(url).build()).execute().use { return it.body!!.string() } }}