Skip to main content
PESDK/iOS/Guides/Stickers

Smart Stickers

PhotoEditor SDK for iOS provides predefined smart stickers

There are currently six predefined smart stickers available in SmartSticker.defaultItems:

  1. Weekday
  2. Date
  3. Time
  4. Time Clock
  5. Weather Cloud (Requires a WeatherProvider)
  6. Weather Thermostat (Requires a WeatherProvider)

These smart stickers are part of the first default sticker category with the identifier "imgly_sticker_category_emoticons". To enable all of the smart stickers you need to define a WeatherProvider otherwise the weather stickers are hidden per default when StickerToolControllerOptions.weatherProvider is not set. The following code snippet shows how to configure and use the provided OpenWeatherProvider.

let unit = TemperatureFormat.locale
// Make sure to pass in your API key to display real weather data
// otherwise the sample data API is used!
let weatherProvider = OpenWeatherProvider(apiKey: nil, unit: unit)
weatherProvider.locationAccessRequestClosure = { locationManager in
locationManager.requestWhenInUseAuthorization()
}
let configuration = Configuration { builder in
builder.configureStickerToolController { options in
options.weatherProvider = weatherProvider
}
}

Example Weather Provider#

The following lists the implementation of the above used OpenWeatherProvider that is also part of the SDK. It exemplifies how you could implement your own WeatherProvider for your service of choice.

import CoreLocation
import Foundation
/// A `WeatherProvider` for the https://openweathermap.org service.
@objcMembers @objc(PESDKOpenWeatherProvider) public class OpenWeatherProvider: NSObject, WeatherProvider, CLLocationManagerDelegate {
// MARK: - Properties
/// The used API key. If `nil` or empty the sample API is used.
public let apiKey: String?
/// The minimum update interval to request new data from the service. It defaults to one hour.
public var updateInterval: TimeInterval = 60 * 60
// MARK: - Initializers
/// Create a new `OpenWeatherProvider`.
/// - Parameters:
/// - apiKey: The used API key. If `nil` or empty the sample API is used.
/// - unit: The temperature format that should be used for displaying temperature measurements to the user.
public init(apiKey: String?, unit: TemperatureFormat) {
self.apiKey = apiKey
temperatureFormat = unit
super.init()
locationManager.startUpdatingLocation()
}
deinit {
locationManager.stopUpdatingLocation()
}
// MARK: - WeatherProvider
private var url: URL? {
guard let location = lastLocation else {
return nil
}
let domain, appid: String
if let apiKey = apiKey, !apiKey.isEmpty {
domain = "api.openweathermap.org"
appid = apiKey
} else {
domain = "samples.openweathermap.org"
appid = "_"
}
let lat = location.coordinate.latitude
let lon = location.coordinate.longitude
let url = "https://\(domain)/data/2.5/weather?lat=\(lat)&lon=\(lon)&appid=\(appid)"
return URL(string: url)
}
private var lastTemperature: Temperature?
private var lastUpdate: Date?
private var lastTask: URLSessionTask?
private struct WeatherData: Codable {
let main: Main
}
private struct Main: Codable {
let temp: Double
}
/// The temperature format that should be used for displaying temperature measurements to the user.
public var temperatureFormat: TemperatureFormat
/// The temperature measurement.
public var temperature: Temperature? { lastTemperature }
/// Request to update the weather data.
public func updateData() {
guard let url = url else {
return
}
if let lastTask = lastTask {
switch lastTask.state {
case .running:
return
case .suspended:
lastTask.resume()
return
default:
break
}
}
if let lastUpdate = lastUpdate, lastUpdate.timeIntervalSinceNow < updateInterval {
return
}
let session = URLSession.shared
let task = session.dataTask(with: url) { data, _, _ in
guard let data = data, let weatherData = try? JSONDecoder().decode(WeatherData.self, from: data) else {
return
}
DispatchQueue.main.async {
self.lastUpdate = Date()
self.lastTemperature = Temperature(value: weatherData.main.temp, unit: .kelvin)
}
}
task.resume()
lastTask = task
}
// MARK: - CoreLocation
private var lastLocation: CLLocation?
private lazy var locationManager: CLLocationManager = {
let locationManager = CLLocationManager()
locationManager.delegate = self
locationManager.distanceFilter = 10
return locationManager
}()
/// When this closure is called, the SDK has determined that location access has not been granted
/// to the host app yet. Within this closure you should then request appropriate permissions from
/// the passed in `CLLocationManager` object. Location access is used to request weather data
/// for the current location for weather stickers.
///
/// - Attention: Starting Spring 2019, all apps submitted to the App Store that access user data
/// are required to include a purpose string as soon as location permissions requests appear
/// somewhere in the binary. Since we do not want to force developers integrating the SDK into
/// their app to include a purpose string even with weather stickers disabled, this closure was
/// introduced, so that developers can decide for themselves if it is appropriate to request
/// location access. Simply set this property like this:
/// ````
/// openWeatherProvider.locationAccessRequestClosure = { locationManager in
/// locationManager.requestWhenInUseAuthorization()
/// }
/// ````
public var locationAccessRequestClosure: ((CLLocationManager) -> Void)? {
didSet {
if CLLocationManager.authorizationStatus() == .notDetermined {
locationAccessRequestClosure?(locationManager)
}
}
}
/// :nodoc:
public func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
lastLocation = locations.last
updateData()
}
}