Particle iOS Cloud SDK enables iOS apps to interact with Particle-powered connected products via the Particle Cloud. It’s an easy-to-use wrapper for Particle REST API. The Cloud SDK will allow you to:
- Manage user sessions for the Particle Cloud (access tokens, encrypted session management)
- Claim/Unclaim devices for a user account
- Get a list of instances of user's Particle devices
- Read variables from devices
- Invoke functions on devices
- Publish events from the mobile app and subscribe to events coming from devices (New!)
All cloud operations take place asynchronously and use the well-known completion blocks (closures for swift) design pattern for reporting results allowing you to build beautiful responsive apps for your Particle products and projects. iOS Cloud SDK is implemented as an open-source Cocoapod library. See Installation section for more details. It works well for both Objective-C and Swift projects.
Rebranding notice
Spark has been recently rebranded as Particle.
Code currently refers to SparkCloud
and SparkDevice
, this will soon be replaced with ParticleCloud
and ParticleDevice
. A new Cocoapod library will be published and current one will be depracated and point to the new one. This should not bother or affect your code.
Beta notice
This SDK is still under development and is currently released as Beta, although tested, bugs and issues may be present, some code might require cleanups. In addition, as long as version 1.0 was not released, we cannot gurantee that API calls will not break from one Cloud SDK version to the next. Be sure to consult the Change Log for any breaking changes / additions to the SDK.
- Perform the installation step described under the Installation section below for integrating in your own project
- You can also Download Particle iOS Cloud SDK and try out the included iOS example app
- Be sure to check Usage before you begin for some code examples
Cloud SDK usage involves two basic classes: first is SparkCloud
which is a singleton object that enables all basic cloud operations such as user authentication, device listing, claiming etc. Second class is SparkDevice
which is an instance represnting a claimed device in the current user session. Each object enables device-specific operation such as: getting its info, invoking functions and reading variables from it.
Here are few examples for the most common use cases to get your started:
You don't need to worry about access tokens, SDK takes care of that for you
Objective-C
[[SparkCloud sharedInstance] loginWithUser:@"ido@particle.io" password:@"userpass" completion:^(NSError *error) {
if (!error)
NSLog(@"Logged in to cloud");
else
NSLog(@"Wrong credentials or no internet connectivity, please try again");
}];
Swift
SparkCloud.sharedInstance().loginWithUser("ido@particle.io", password: "userpass") { (error:NSError!) -> Void in
if let e=error {
println("Wrong credentials or no internet connectivity, please try again")
}
else {
println("Logged in")
}
}
List the devices that belong to currently logged in user and find a specific device by name:
Objective-C
__block SparkDevice *myPhoton;
[[SparkCloud sharedInstance] getDevices:^(NSArray *sparkDevices, NSError *error) {
NSLog(@"%@",sparkDevices.description); // print all devices claimed to user
for (SparkDevice *device in sparkDevices)
{
if ([device.name isEqualToString:@"myNewPhotonName"])
myPhoton = device;
}
}];
Swift
var myPhoton : SparkDevice?
SparkCloud.sharedInstance().getDevices { (sparkDevices:[AnyObject]!, error:NSError!) -> Void in
if let e = error {
println("Check your internet connectivity")
}
else {
if let devices = sparkDevices as? [SparkDevice] {
for device in devices {
if device.name == "myNewPhotonName" {
myPhoton = device
}
}
}
}
}
Assuming here that myPhoton
is an active instance of SparkDevice
class which represents a device claimed to current user:
Objective-C
[myPhoton getVariable:@"temperature" completion:^(id result, NSError *error) {
if (!error) {
NSNumber *temperatureReading = (NSNumber *)result;
NSLog(@"Room temperature is %f degrees",temperatureReading.floatValue);
}
else {
NSLog(@"Failed reading temperature from Photon device");
}
}];
Swift
myPhoton!.getVariable("temperature", completion: { (result:AnyObject!, error:NSError!) -> Void in
if let e=error {
println("Failed reading temperature from device")
}
else {
if let temp = result as? Float {
println("Room temperature is \(temp) degrees")
}
}
})
Invoke a function on the device and pass a list of parameters to it, resultCode
on the completion block will represent the returned result code of the function on the device
Objective-C
[myPhoton callFunction:@"digitalwrite" withArguments:@[@"D7",@1] completion:^(NSNumber *resultCode, NSError *error) {
if (!error)
{
NSLog(@"LED on D7 successfully turned on");
}
}];
Swift
let funcArgs = ["D7",1]
myPhoton!.callFunction("digitalwrite", withArguments: funcArgs) { (resultCode : NSNumber!, error : NSError!) -> Void in
if (error == nil) {
println("LED on D7 successfully turned on")
}
}
Functions is just a list of names, variables is a dictionary in which keys are variable names and values are variable types:
Objective-C
NSDictionary *myDeviceVariables = myPhoton.variables;
NSLog(@"MyDevice first Variable is called %@ and is from type %@", myDeviceVariables.allKeys[0], myDeviceVariables.allValues[0]);
NSArray *myDeviceFunctions = myPhoton.functions;
NSLog(@"MyDevice first Function is called %@", myDeviceFunctions[0]);
Swift
let myDeviceVariables : Dictionary? = myPhoton.variables as? Dictionary<String,String>
println("MyDevice first Variable is called \(myDeviceVariables!.keys.first) and is from type \(myDeviceVariables?.values.first)")
let myDeviceFunction = myPhoton.functions
println("MyDevice first function is called \(myDeviceFunction!.first)")
Get a device instance by its ID:
Objective-C
__block SparkDevice *myOtherDevice;
NSString *deviceID = @"53fa73265066544b16208184";
[[SparkCloud sharedInstance] getDevice:deviceID completion:^(SparkDevice *device, NSError *error) {
if (!error)
myOtherDevice = device;
}];
Swift
var myOtherDevice : SparkDevice? = nil
SparkCloud.sharedInstance().getDevice("53fa73265066544b16208184", completion: { (device:SparkDevice!, error:NSError!) -> Void in
if let d = device {
myOtherDevice = d
}
})
you can simply set the .name
property or use -rename() method if you need a completion block to be called (for example updating a UI after renaming was done):
Objective-C
myPhoton.name = @"myNewDeviceName";
or
[myPhoton rename:@"myNewDeviecName" completion:^(NSError *error) {
if (!error)
NSLog(@"Device renamed successfully");
}];
Swift
myPhoton!.name = "myNewDeviceName"
or
myPhoton!.rename("myNewDeviceName", completion: { (error:NSError!) -> Void in
if (error == nil) {
println("Device successfully renamed")
}
})
Also clears user session and access token
Objective-C
[[SparkCloud sharedInstance] logout];
Swift
SparkCloud.sharedInstance().logout()
You can make an API call that will open a stream of Server-Sent Events (SSEs). You will make one API call that opens a connection to the Particle Cloud. That connection will stay open, unlike normal HTTP calls which end quickly. Very little data will come to you across the connection unless your Particle device publishes an event, at which point you will be immediately notified. In each case, the event name filter is eventNamePrefix
and is optional. When specifying an event name filter, published events will be limited to those events with names that begin with the specified string. For example, specifying an event name filter of 'temp' will return events with names 'temp' and 'temperature'.
Subscribe to the firehose of public events, plus private events published by devices one owns:
// The event handler:
SparkEventHandler handler = ^(SparkEvent *event, NSError *error) {
if (!error)
{
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(@"Got Event %@ with data: %@",event.event,event.data);
});
}
else
{
NSLog(@"Error occured: %@",error.localizedDescription);
}
};
// This line actually subscribes to the event stream:
id eventListenerID = [[SparkCloud sharedInstance] subscribeToAllEventsWithPrefix:@"temp" handler:handler];
Note: specifying nil or empty string in the eventNamePrefix parameter will subscribe to ALL events (lots of data!) You can have multiple handlers per event name and/or same handler per multiple events names.
Subscribe to all events, public and private, published by devices the user owns:
id eventListenerID = [[SparkCloud sharedInstance] subscribeToMyDevicesEventsWithPrefix:@"temp" handler:handler];
Subscribe to events from one specific device (by deviceID, second parameter). If the API user owns the device, then he'll receive all events, public and private, published by that device. If the API user does not own the device he will only receive public events.
id eventListenerID = [[SparkCloud sharedInstance] subscribeToDeviceEventsWithPrefix:@"temp" deviceID:@"53ff6c065075535119511687" handler:handler];
other option is calling same method via the SparkDevice
instance:
id eventListenerID = [device subscribeToEventsWithPrefix:@"temp" handler:handler];
this guarantees that private events will be received since having access device instance in your app signifies that the user has this device claimed.
Very straightforward. Keep the id object the subscribe method returned and use it as parameter to call the unsubscribe method:
[[SparkCloud sharedInstance] unsubscribeFromEventWithID:self.eventListenerID];
or via the SparkDevice
instance (if applicable):
[device unsubscribeFromEventWithID:self.eventListenerID];
You can also publish an event from your app to the Particle Cloud:
Objective-C
[[SparkCloud sharedInstance] publishEventWithName:@"event_from_app" data:@"event_payload" isPrivate:NO ttl:60 completion:^(NSError *error) {
if (error)
{
NSLog(@"Error publishing event: %@",error.localizedDescription);
}
}];
Swift
SparkCloud.sharedInstance().publishEventWithName("event_from_app", data: "event_payload", isPrivate: false, ttl: 60, completion: { (error:NSError!) -> Void in
if let e = error
{
println("Error publishing event" + e.localizedDescription)
}
})
If you're creating an app you're required to provide the SparkCloud
class with OAuth clientId and secret.
Those are used to identify users coming from your specific app to the Particle Cloud.
Please follow the procedure decribed in our guide to create those strings,
then in your AppDelegate
class you can supply those credentials by setting the following properties in SparkCloud
singleton:
@property (nonatomic, strong) NSString *OAuthClientId;
@property (nonatomic, strong) NSString *OAuthClientSecret;
Important Those credentials should be kept as secret. We recommend the use of Cocoapods-keys plugin for cocoapods (which you have to use anyways to install the SDK). It is essentially a key value store for enviroment and application keys. It's a good security practice to keep production keys out of developer hands. CocoaPods-keys makes it easy to have per-user config settings stored securely in the developer's keychain, and not in the application source. It is a plugin that once installed will run on every pod install or pod update.
After adding the following additional lines your project Podfile
:
plugin 'cocoapods-keys', {
:project => "YourAppName",
:keys => [
"OAuthClientId",
"OAuthSecret"
]}
go to your project folder in shell and run pod install
- it will now ask you for "OAuthClientId", "OAuthSecret" - you can copy/paste the generated keys there
and from that point on you can feed those keys into SparkCloud
by adding this code to your AppDelegate didFinishLaunchingWithOptions
function which gets called
when your app starts:
Swift example code
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
var keys = YourappnameKeys()
SparkCloud.sharedInstance().OAuthClientId = keys.oAuthClientId()
SparkCloud.sharedInstance().OAuthClientSecret = keys.oAuthSecret()
return true
}
Be sure to replace YourAppName
with your project name.
For additional reference check out the Reference in Cocoadocs website for full coverage of SparkDevice
and SparkCloud
functions and member variables. In addition you can consult the javadoc style comments in SparkCloud.h
and SparkDevice.h
for each public method. If Particle iOS Cloud SDK is integrated in your XCode project you should be able to press Esc
to get an auto-complete hints for each cloud and device method.
Particle iOS Cloud SDK is available through CocoaPods. Cocoapods is an easy to use dependacy manager for iOS. You must have Cocoapods installed, if you don't then be sure to Install Cocoapods before you start: To install the iOS Cloud SDK, simply add the following line to your Podfile on main project folder:
pod "Spark-SDK"
and then run pod update
. A new .xcworkspace
file will be created for you to open by Cocoapods, open that file workspace file in XCode and you can start interacting with Particle cloud and devices by
adding #import "Spark-SDK.h"
. (that is not required for swift projects)
- If you need help, use Our community website, use the
Mobile
category for dicussion/troubleshooting iOS apps using the Particle iOS Cloud SDK. - If you are certain you found a bug, and can provide steps to reliably reproduce it, open an issue, label it as
bug
. - If you have a feature request, open an issue with an
enhancement
label on it - If you want to contribute, submit a pull request, be sure to check out spark.github.io for our contribution guidelines, and please sign the CLA.
To use iOS Cloud SDK from within Swift based projects read here. For a detailed step-by-step help on integrating the Cloud SDK within a Swift project check out this Particle community posting.
The Apple documentation is an important resource on mixing Objective-C and Swift code, be sure to read through that as well.
Notice that we've included the required bridging header file in the SDK, you just need to copy it to your project add it as the active bridging header file in the project settings as described in the links above. There's also an example app, this app also demonstrates the Particle DeviceSetup library usage, as well as several Cloud SDK calls.
Particle iOS Cloud SDK is available under the Apache License 2.0. See the LICENSE file for more info.