This Swift library is meant to facilitate writing iOS apps for a Pryv.io platform, it follows the Pryv.io App Guidelines.
You will find a sample iOS app at https://github.com/pryv/app-swift-example. This video tutorial explains you how to get started with the app and how to use its basic features (create a simple event, track your position).
iOS 10.0 is required to use this library.
- Import
- Obtaining a Connection
- API calls
- Advanced usage of API calls with optional individual result
- Get Events Streamed
- Events with Attachments
- High Frequency Events
- Connection with websockets
- Service Information
PryvSwiftKit is available through CocoaPods. To install it, simply add the following line to your Podfile:
source 'https://github.com/pryv/lib-swift.git'
source 'https://github.com/CocoaPods/Specs.git'
pod 'PryvSwiftKit', :git => 'https://github.com/pryv/lib-swift.git', :branch => 'master'
A connection is an authenticated link to a Pryv.io account.
The format of the API endpoint can be found in your platform's service information under the api
property. The most frequent one has the following format: https://{token}@{api-endpoint}
let apiEndpoint = "https://[email protected]"
let connection = Connection(apiEndpoint: apiEndpoint)
let service = Service(pryvServiceInfoUrl: "https://reg.pryv.me/service/info")
service.apiEndpointFor(username: username, token: token).then { apiEndpoint in
let connection = Connection(apiEndpoint: apiEndpoint)
}
The following code is an implementation of the Pryv.io Authentication process.
{
let service = Service(pryvServiceInfoUrl: "https://reg.pryv.me/service/info")
let authPayload: Json = [ // See: https://api.pryv.com/reference/#auth-request
"requestingAppId": "lib-swift-test",
"requestedPermissions": [
[
"streamId": "test",
"level": "manage"
]
],
"languageCode": "fr" // optional (default english)
]
service.setUpAuth(authSettings: authPayload, stateChangedCallback: stateChangedCallback).then { authUrl in
// open a webview with URL(string: authUrl)!
}
}
// event Listener for Authentication steps, called each time the authentication state changed
func stateChangedCallback(authResult: AuthResult) {
switch authResult.state {
case .need_signin: // do nothing if still needs to sign in
return
case .accepted:
// close webview
let connection = Connection(apiEndpoint: authResult.apiEndpoint)
print("Successfully authenticated: \(connection.getApiEndpoint())")
case .refused:
// close webview
case .timeout:
// close webview
print("Authentication timed out")
}
}
import Promises
let pryvServiceInfoUrl = "https://reg.pryv.me/service/info"
let appId = "lib-swift-sample"
let service = Service(pryvServiceInfoUrl: pryvServiceInfoUrl)
service.login(username: username, password: password, appId: appId).then { connection in
// handle connection object
}
Api calls are based on the batch
call specifications: Call batch API reference
let apiCalls: [APICall] = [
[
"method": "streams.create",
"params": [
"id": "heart",
"name": "Heart"
]
],
[
"method": "events.create",
"params": [
"time": 1385046854.282,
"streamId": "heart",
"type": "frequency/bpm",
"content": 90
]
],
[
"method": "events.create",
"params": [
"time": 1385046854.283,
"streamId": "heart",
"type": "frequency/bpm",
"content": 120
]
]
]
connection.api(APICalls: apiCalls).then { result in
// handle the result
}
var count = 0
// the following will be called on each API method result it was provided for
let handleResult: (Event) -> () = { result in
print("Got result \(count): \(String(describing: result))")
count += 1
}
let apiCalls: [APICall] = [
[
"method": streams.create,
"params": [
"id": "heart",
"name": "Heart"
]
],
[
"method": "events.create",
"params": [
"time": 1385046854.282,
"streamId": "heart",
"type": "frequency/bpm",
"content": 90
]
],
[
"method": "events.create",
"params": [
"time": 1385046854.283,
"streamId": "heart",
"type": "frequency/bpm",
"content": 120
]
]
]
let handleResults: [Int: (Event) -> ()] = [
1: handleResult,
2: handleResult
]
connection.api(APICalls: apiCalls, handleResults: handleResults).catch { error in
// handle error
}
When events.get
will provide a large result set, it is recommended to use a method that streams the result instead of the batch API call.
Connection.getEventsStreamed()
parses the response JSON as soon as data is available and calls the forEachEvent()
callback on each event object.
The callback is meant to store the events data, as the function does not return the API call result, which could overflow memory in case of JSON deserialization of a very large data set. Instead, the function returns an events count and eventually event deletions count as well as the common metadata.
let now = Date().timeIntervalSince1970
let queryParams: Json = ["fromTime": 0, "toTime": now, "limit": 10000]
var events = [Event]()
let forEachEvent: (Event) -> () = { event in
events.append(event)
}
connection.getEventsStreamed(queryParams: queryParams, forEachEvent: forEachEvent).then { result in
// handle the result
}
[
"eventsCount": 10000,
"meta": [
"apiVersion": "1.4.26",
"serverTime": 1580728336.864,
"serial": 2019061301
]
]
let now = Date().timeIntervalSince1970
let queryParams: Json = ["fromTime": 0, "toTime": now, "includeDeletions": true, "modifiedSince": 0]
let events = []
var events = [Event]()
let forEachEvent: (Event) -> () = { event in
events.append(event)
// events with .deleted or/and .trashed properties can be tracked here
}
connection.getEventsStreamed(queryParams: queryParams, forEachEvent: forEachEvent).then { result in
// handle the result
}
[
"eventDeletionsCount": 150,
"eventsCount": 10000,
meta: [
"apiVersion": "1.4.26",
"serverTime": 1580728336.864,
"serial": 2019061301
]
]
This shortcut allows to create an event with an attachment in a single API call.
let payload: Event = ["streamId": "data", "type": "picture/attached"]
let filePath = "./test/my_image.png"
let mimeType = "image/png"
connection.createEventWithFile(event: payload, filePath: filePath, mimeType: mimeType).then { result in
// handle the result
}
let filePath = "./test/my_image.png"
let mimeType = "image/png"
if let eventId = event["id"] as? String {
connection.addFileToEvent(eventId: eventId, filePath: filePath, mimeType: mimeType).then { result in
// handle the result
}
}
This function allows to get raw data corresponding to a preview of the image attached to the event.
if let eventId = event["id"] as? String {
let data = connection.getImagePreview(eventId: eventId)
}
Reference: https://api.pryv.com/reference/#hf-events
func generateSerie() -> [[Double]] {
var serie = [[Double]]()
for t in 0..<100000 { // t will be the deltatime in seconds
serie.append([Double(t), sin(Double(t)/1000.0)])
}
return serie
}
{
let pointsA = generateSerie()
let pointsB = generateSerie()
let postHFData: ([[Double]]) -> ((Event) -> ()) = { points in
let internalFunction: (Event) -> () = { event in // will be called each time an HF event is created
let eventId = event["id"] as! String
connection.addPointsToHFEvent(eventId: eventId, fields: ["deltaTime", "value"], points: points).catch { error in
print("add point to hf event error: \(error.localizedDescription)")
}
}
return internalFunction
}
let pointsA = generateSerie()
let pointsB = generateSerie()
let apiCalls: [APICall] = [
[
"method": "streams.create",
"params": [
"id": "signal1",
"name": "Signal1"
]
],
[
"method": "streams.create",
"params": [
"id": "signal2",
"name": "Signal2"
]
],
[
"method": "hfs.create",
"params": [
"streamId": "signal1",
"type": "serie:frequency/bpm"
]
],
[
"method": "hfs.create",
"params": [
"streamId": "signal2",
"type": "serie:frequency/bpm"
]
]
]
let handleResults: [Int: (Event) -> ()] = [
0: postHFData(pointsA),
1: postHFData(pointsB)
]
connection.api(APICalls: apiCalls, handleResults: handleResults).catch { error in
// handle error
}
}
Pryv.io API supports real-time interaction by accepting websocket connections via Socket.io 2.0.
Reference: https://api.pryv.com/reference/#call-with-websockets
In an iOS app
To get an authenticated link to a Pryv.io account, supporting Socket.io,
- First, load and import a Swift Socket.IO client library, e.g. socket.io-client-swift.
- Then initialize the connection with the URL (see https://api.pryv.com/reference/#connecting for more details):
Pryv.me:
let manager = SocketManager(socketURL: URL(string: "https://{username}.pryv.me/")!, config: [.log(true), .connectParams(["auth": "{accessToken}"])])
let socket = manager.socket(forNamespace: "/{username}")
Own domain:
let manager = SocketManager(socketURL: URL(string: "https://{username}.{domain}/")!, config: [.log(true), .connectParams(["auth": "{accessToken}"])])
let socket = manager.socket(forNamespace: "/{username}")
DNS-less:
let manager = SocketManager(socketURL: URL(string: "https://host.your-domain.io/")!, config: [.log(true), .connectParams(["auth": "{accessToken}"])])
let socket = manager.socket(forNamespace: "/{username}/{username}")
This library offers connection, subscribtion to changes and call to methods using Socket.io for your iOS applications.
let url = "https://chuangzi.pryv.me/chuangzi?auth=ckc1s4rxj00037cpv3tt39ceb"
let connection = ConnectionWebSocket(url: url) // initialize the connection
connection.subscribe(message: .eventsChanged) { _, _ in // upon a notification that the events changed ...
connection.emit(methodId: "events.get", params: ["sortAscending": true]) { result in // get the events changed
let dataArray = result as NSArray
let dictionary = dataArray[1] as! Json
let events = (dictionary["events"] as! [Event])
events.forEach. { event in
print(String(describing: event))
}
}
}
connection.connect()
A Pryv.io deployment is a unique "Service", as an example Pryv Lab is a service, deployed on the pryv.me domain name.
It relies on the content of a service information configuration, See: Service Information API reference
Exposes tools to interact with Pryv.io at a "Platform" level.
let service = Service(pryvServiceInfoUrl: "https://reg.pryv.me/service/info")
Service information properties can be overriden with specific values. This might be useful to test new designs on production platforms.
let serviceInfoUrl = "https://reg.pryv.me/service/info"
let serviceCustomizations: Json = ["name": "Pryv Lab 2"]
let service = Service(pryvServiceInfoUrl: serviceInfoUrl, serviceCustomization: serviceCustomizations)
See: Pryv.Service for more details
-
service.info()
- returns the content of the serviceInfo in a Promise// example: get the name of the platform service.info().then { serviceInfo in let serviceName = serviceInfo.name }
-
service.infoSync()
: returns the cached content of the serviceInfo, requiresservice.info()
to be called first. -
service.apiEndpointFor(username, token)
Will return the corresponding API endpoint for the provided credentials,token
can be omitted.