Here are the files I wrote in Swift, and some examples of how to use the methods.
The User.swift file,
import Foundation
protocol ParticleUserDelegate {
func tokenDeletedResult(result: String)
}
class User {
static var userName: String!
static var password: String!
static var accessToken: String!
static var session: NSURLSession!
static var delegate: ParticleUserDelegate!
static func login(userEmail: String, password: String) {
self.userName = userEmail
self.password = password
}
static func listAccessTokens() {
let url = NSURL(string: "https://api.particle.io/v1/access_tokens")
let request = NSMutableURLRequest(URL: url!, cachePolicy: .ReloadIgnoringCacheData, timeoutInterval: 10)
request.HTTPMethod = "GET"
let userAndPassword = userName + ":" + password
let plainData = userAndPassword.dataUsingEncoding(NSUTF8StringEncoding)
let base64String = plainData!.base64EncodedStringWithOptions(.Encoding64CharacterLineLength)
request.setValue("Basic " + base64String, forHTTPHeaderField: "Authorization")
session = NSURLSession.sharedSession()
let dataTask = session.dataTaskWithRequest(request) { (data, response, taskError) -> Void in
if (taskError == nil) {
do {
if let tokenArray = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.AllowFragments) as? NSArray {
print(tokenArray)
}
} catch let jsonError as NSError {
print(jsonError.localizedDescription)
}
}else{
print("taskError from executeFunction func is: \(taskError!.localizedDescription)")
}
}
dataTask.resume()
}
static func deleteAccessToken(token: String) {
let url = NSURL(string: "https://api.particle.io/v1/access_tokens/\(token)")
let request = NSMutableURLRequest(URL: url!, cachePolicy: .ReloadIgnoringCacheData, timeoutInterval: 10)
request.HTTPMethod = "DELETE"
let userAndPassword = userName + ":" + password
let plainData = userAndPassword.dataUsingEncoding(NSUTF8StringEncoding)
let base64String = plainData!.base64EncodedStringWithOptions(.Encoding64CharacterLineLength)
request.setValue("Basic " + base64String, forHTTPHeaderField: "Authorization")
session = NSURLSession.sharedSession()
let dataTask = session.dataTaskWithRequest(request) { (data, response, taskError) -> Void in
if (taskError != nil) {
print("taskError from deleteAccessToken func is: \(taskError!.localizedDescription)")
}
}
dataTask.resume()
}
}
Here’s the Device.swift file,
import Foundation
protocol ParticleDeviceDelegate {
func deviceIDWasSet()
}
class Device {
var deviceID: String!
var accessToken: String!
var newAccessToken: String!
var session: NSURLSession!
var delegate: ParticleDeviceDelegate!
init(device_ID deviceID: String, accessToken: String) {
self.deviceID = deviceID
self.accessToken = accessToken
}
init(newTokenForDeviceNamed deviceName: String) {
let url = NSURL(string: "https://api.particle.io/oauth/token")
let postRequest = NSMutableURLRequest(URL: url!, cachePolicy: .ReloadIgnoringCacheData, timeoutInterval: 10)
postRequest.HTTPMethod = "POST"
let userAndPassword = "particle:particle"
let plainData = userAndPassword.dataUsingEncoding(NSUTF8StringEncoding)
let base64String = plainData!.base64EncodedStringWithOptions(.Encoding64CharacterLineLength)
postRequest.setValue("Basic " + base64String, forHTTPHeaderField: "Authorization")
let bodyData = ("grant_type=password&username=\(User.userName)&password=\(User.password)")
postRequest.HTTPBody = bodyData.dataUsingEncoding(NSUTF8StringEncoding)
session = NSURLSession.sharedSession()
let dataTask = session.dataTaskWithRequest(postRequest) { (data, response, taskError) -> Void in
if (taskError == nil) {
do {
if let dict = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.AllowFragments) as? NSDictionary {
self.newAccessToken = dict["access_token"] as? String
let defaults = NSUserDefaults.standardUserDefaults()
if let prevToken = defaults.objectForKey("newKey") {
User.deleteAccessToken(prevToken as! String)
}
defaults.setObject(self.newAccessToken, forKey: "newKey")
defaults.synchronize()
}
} catch let jsonError as NSError {
print(jsonError.localizedDescription)
}
}else{
print("taskError from deviceNamedWithNewToken:userName:password: is: \(taskError!.localizedDescription)")
}
self.downloadDeviceIDForDevice(deviceName, newToken: true)
}
dataTask.resume()
}
init(deviceNamed deviceName: String) {
let url = NSURL(string: "https://api.particle.io/v1/access_tokens")
let request = NSMutableURLRequest(URL: url!, cachePolicy: .ReloadIgnoringCacheData, timeoutInterval: 10)
request.HTTPMethod = "GET"
let userAndPassword = User.userName + ":" + User.password
let plainData = userAndPassword.dataUsingEncoding(NSUTF8StringEncoding)
let base64String = plainData!.base64EncodedStringWithOptions(.Encoding64CharacterLineLength)
request.setValue("Basic " + base64String, forHTTPHeaderField: "Authorization")
session = NSURLSession.sharedSession()
let dataTask = session.dataTaskWithRequest(request) { (data, response, taskError) -> Void in
if (taskError == nil) {
do {
if let tokenArray = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.AllowFragments) as? NSArray {
if tokenArray.count > 1 {
for aToken in tokenArray {
if aToken["client"] as? String == "user" {
self.accessToken = aToken["token"] as! String
print("token is: \(self.accessToken)")
break
}
}
}else{
self.accessToken = (tokenArray.firstObject as! NSDictionary)["token"] as! String
}
print("access token from init method is: \(self.accessToken)")
}
} catch let jsonError as NSError {
print(jsonError.localizedDescription)
}
}
self.downloadDeviceIDForDevice(deviceName, newToken: false)
}
dataTask.resume()
}
private func downloadDeviceIDForDevice(deviceName: String, newToken: Bool) {
let token = self.newAccessToken != nil ? self.newAccessToken : self.accessToken
let urlString = "https://api.spark.io/v1/devices?access_token=\(token)"
let url = NSURL(string: urlString)
let getRequest = NSMutableURLRequest(URL: url!, cachePolicy: .ReloadIgnoringCacheData, timeoutInterval: 10)
let dataTask = session.dataTaskWithRequest(getRequest) { (data, response, taskError) -> Void in
if taskError == nil {
do {
if let array = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.AllowFragments) as? NSArray {
if deviceName.characters.count == 0 {
self.deviceID = (array[0] as! NSDictionary)["id"] as! String
self.delegate.deviceIDWasSet()
}else{
let indx = array.indexOfObjectPassingTest({ (obj, idx, stop) -> Bool in
return obj["name"] as! String == deviceName
})
if indx != NSIntegerMax {
self.deviceID = (array[indx] as! NSDictionary)["id"] as! String
self.delegate.deviceIDWasSet()
}else{
print("ERROR: that device name was not found")
}
}
}
} catch let jsonError as NSError {
print(jsonError.localizedDescription)
}
}
}
dataTask.resume()
}
func executeFunctionNamed(functionName: String, argument: String, completionHandler: (AnyObject) -> Void) {
self.executeFunctionNamed(functionName, argument: argument, nameLengthPairs: [], completionHandler: completionHandler)
}
func executeFunctionNamed(functionName: String, argument: String, nameLengthPairs: [(String, Int)], completionHandler: (AnyObject) -> Void) {
let token = self.newAccessToken != nil ? self.newAccessToken : self.accessToken
let url = NSURL(string: "https://api.particle.io/v1/devices/\(self.deviceID)/\(functionName)")
let postRequest = NSMutableURLRequest(URL: url!, cachePolicy: .ReloadIgnoringCacheData, timeoutInterval: 10.0)
postRequest.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
postRequest.HTTPMethod = "Post"
let bodyData = "access_token=\(token)¶ms=\(argument)"
postRequest.HTTPBody = bodyData.dataUsingEncoding(NSUTF8StringEncoding)
let dataTask = session.dataTaskWithRequest(postRequest) { (data, response, taskError) -> Void in
if (taskError == nil) {
do {
if let dict = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.AllowFragments) as? NSDictionary {
if let returnedInt = dict["return_value"]?.integerValue {
if nameLengthPairs.count == 0 {
completionHandler(returnedInt)
}else{
let resultDict = self.parseInt(returnedInt, fromArray: nameLengthPairs)
completionHandler(resultDict)
}
}else{
let dictError = self.dictionaryErrorParser(dict)
completionHandler(dictError)
}
}
} catch let jsonError as NSError {
print(jsonError.localizedDescription)
}
}else{
print("taskError from executeFunction func is: \(taskError!.localizedDescription)")
}
}
dataTask.resume()
}
private func parseInt(returnedInt: Int, fromArray array: [(String, Int)]) -> [String:Int] {
var outputs = [String:Int]()
var input = returnedInt
let reversedArray = array.reverse()
for (name, num) in reversedArray {
let mask = Int(pow(Double(2), Double(num))) - 1
let andedNum = input & mask
outputs["\(name)"] = andedNum
input = input >> num
}
return outputs
}
func readVariable(varName: String, completionHandler: (AnyObject) -> Void) {
let token = self.newAccessToken != nil ? self.newAccessToken : self.accessToken
let url = NSURL(string: "https://api.spark.io/v1/devices/\(self.deviceID)/\(varName)?access_token=\(token)")
let request = NSMutableURLRequest(URL: url!, cachePolicy: .ReloadIgnoringCacheData, timeoutInterval: 20)
let dataTask = session.dataTaskWithRequest(request) { (data, response, taskError) -> Void in
if (taskError == nil) {
do {
if let dict = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.AllowFragments) as? NSDictionary {
if let readResult = dict["result"]{
if readResult is String{
print("got a string")
}else if readResult is Int {
print("got an Int whose value is: \(readResult)")
}else if readResult is Double{
print("got a Double")
}else{
print("got something we didn't expect")
}
completionHandler(readResult)
}else{
let dictError = self.dictionaryErrorParser(dict) // this is a String
completionHandler(dictError)
}
}
} catch let jsonError as NSError {
print("jsonError from readVariable is: \(jsonError.localizedDescription)")
}
}else{
print("taskError from readVariable func is: \(taskError!.localizedDescription)")
}
}
dataTask.resume()
}
private func dictionaryErrorParser(dict: NSDictionary) -> String {
guard let errorString = dict["error"] as! String! else {
return "Error string not returned from Particle"
}
guard let infoString = dict["info"] as! String! else {
return errorString
}
return errorString + " " + infoString
}
}
To control your Particle device with the iOS app, you first need to put in your username and password, and then instantiate an instance of your device,
var catDoorPhoton: Device!
override func viewDidLoad() {
super.viewDidLoad()
functionReturnLabel.text = "Connecting"
User.login("<YOUR USERNAME HERE>", password: "<YOUR PASSWORD HERE>") // user name and password for your Particle account
catDoorPhoton = Device(deviceNamed: "CatDoor")
catDoorPhoton.delegate = self
}
User.login doesn’t actually log in to Particle, it just gets your name and password into the app so it can be used when you instantiate your device.
You call a function like so,
@IBAction func overrideTimes(sender: UIButton) {
catDoorPhoton.executeFunctionNamed("manual", argument: "override") {result in
if result as! Int == 3 { // 3 is what I'm returning from the "manual" method when "override" is the command
self.functionReturnLabel.display("Times have been Overridden")
}else{
self.functionReturnLabel.display("\(result)")
}
}
}
Here are examples of how to read an int and a String variable,
@IBAction func startTime(sender: UIButton) {
catDoor.readVariable("startTime") { (result) in
let returnedInt = (result as? Int)!
self.formatAndSetTitle(returnedInt, button: sender)
}
}
@IBAction func sentTimes(sender: UIButton) {
catDoor.readVariable("sentTimes") { (result) in
let returnedString = (result as? String)!
sender.displayTitle(returnedString)
}
}