Skip to main content
PESDK/iOS/Guides/Stickers

External Sticker Provider

PhotoEditor SDK for iOS allows integration with external APIs as a data source for custom sticker categories

External sticker provider#

External APIs (image search engines) can be used as a data source for custom sticker categories. The provider object must conform to StickerProvider, by implementing a general trending endpoint for the initial load and a search endpoint.

Pagination is handled via the query, offset, and StickerProviderResult.hasMore parameters. If your service does not support pagination, you can ignore the query, offset arguments and pass false as StickerProviderResult.hasMore.

class ExternalStickerProvider: StickerProvider {
func trending(offset: Int, limit: Int, completion: @escaping (Result<StickerProviderResult, Error>) -> Void) {
// Load stickers from local/remote resource and call completion handler
}
func search(query: String, offset: Int, limit: Int, completion: @escaping (Result<StickerProviderResult, Error>) -> Void) {
// Load stickers from local/remote resource and call completion handler
}
}

A custom category can be added to the existing categories in a familiar way. Instead of passing a stickers array, you should provide a stickerProvider object.

let previewURL = Bundle.main.url(forResource: "external_provider", withExtension: "png")!
let customStickerCategory = StickerProviderCategory(title: "ExternalProvider", imageURL: previewURL, stickerProvider: ExternalStickerProvider())
let configuration = Configuration { builder in
let assetCatalog = AssetCatalog.defaultItems
assetCatalog.stickers.append(customStickerCategory)
builder.assetCatalog = assetCatalog
}

Example sticker provider#

The example below shows an implementation of a StickerProvider for an arbitrary web service.

class WebServiceStickerProvider: StickerProvider {
private static let baseURL = "https://web.service/api/stickers"
private enum Parameters: String {
case apiKey = "api_key"
case query = "q"
}
let apiKey: String
init(apiKey: String) {
self.apiKey = apiKey
}
func trending(offset: Int, limit: Int, completion: @escaping (Result<StickerProviderResult, Error>) -> Void) {
let task = URLSession.shared.dataTask(with: request(path: "trending", query: nil)) { data, response, error in
self.parse(data: data, response: response, error: error, completion: completion)
}
task.resume()
}
func search(query: String, offset: Int, limit: Int, completion: @escaping (Result<StickerProviderResult, Error>) -> Void) {
let task = URLSession.shared.dataTask(with: request(path: "search", query: query)) { data, response, error in
self.parse(data: data, response: response, error: error, completion: completion)
}
task.resume()
}
private func request(path: String, query: String?) -> URLRequest {
var urlComponents = URLComponents(string: WebServiceStickerProvider.baseURL)!
urlComponents.queryItems = [
URLQueryItem(name: Parameters.apiKey.rawValue, value: apiKey)
]
if let query = query {
urlComponents.queryItems?.append(URLQueryItem(name: Parameters.query.rawValue, value: query))
}
let request = URLRequest(url: urlComponents.url!.appendingPathComponent(path))
return request
}
private func parse(data: Data?, response: URLResponse?, error: Error?, completion: @escaping (Result<StickerProviderResult, Error>) -> Void) {
if let error = error {
completion(.failure(error))
return
}
guard let data = data else {
completion(.success(StickerProviderResult(stickers: [])))
return
}
do {
let response = try JSONDecoder().decode(WebServiceResponse.self, from: data)
let result = StickerProviderResult(response: response)
completion(.success(result))
} catch {
completion(.failure(error))
}
}
}
struct WebServiceImage: Decodable {
let id: String
let url, thumbnail: URL
}
struct WebServiceResponse: Decodable {
let images: [WebServiceImage]
}
extension Sticker {
convenience init(image: WebServiceImage) {
self.init(imageURL: image.url, thumbnailURL: image.thumbnail, identifier: image.id)
}
}
extension StickerProviderResult {
convenience init(response: WebServiceResponse) {
let stickers = response.images.map(Sticker.init)
self.init(stickers: stickers)
}
}