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 inlet assetCatalog = AssetCatalog.defaultItemsassetCatalog.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: Stringinit(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 inself.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 inself.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: Stringlet 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)}}