Swiftpack.co - Package - vakoc/particle-swift

particle-swift

Swift 4 Package for interacting with Particle Cloud services. particle-swift is the engine powering the commerical SpeckCode IDE and Particle device manager.

Compatibility: Xcode 9.3+ (Swift 4.1+) or the equivalent open source variant of Swift is required.

This project provides a pure Swift SDK for interacting with the particle.io cloud services. The APIs provide access to the following portions of the Particle Cloud

  • Authentication
  • Devices
  • Webhooks
  • Events
  • Libraries
  • Firmware
  • OAuth Clients

This project has the following long term goals

  • Follow pure swift design and coding styles
  • Be dependency free
  • Work with the swift package manager and provide frameworks for iOS, macOS, tvOS, and watchOS

Some general design guidelines are

  • Delegate secured storage of credentials to the caller. Higher level consumers can store in keychain, etc.
  • Generally be a stateless (outside of authentication) API set for the Particle Cloud services.
  • Be compatible with Linux and other swift ports (long term goal)
  • Multi-user concurrency. Multiple particle cloud accounts may be created and used at the same time.

Intended usages for this library would include server side Swift, iOS/tvOS/macOS/watchOS applications that utilize particle cloud service, or any other Swift based product that wants to use the awesome Particle Cloud.

Using the Library - Xcode Project

particle-swift is delivered with an Xcode project suitable for embedding in other Xcode projects. An example application can be found in Examples/PhoneExample

Download or clone the the particle-swift project. Advanced users can use git submodules to add the project as well.

Drag and drop the Xcode/ParticleSwift.xcodeproj into your main project.

Embed Xcode Project

Add the ParticleSwift framework to your application's embedded binaries. The framework may appear multiple times. Select the top level reference to ParticleSwift.xcodeproj as shown

Embed Framework

Verify the framework was added to both the linked libraries and embedded frameworks

Embed Frameworks

Verify that ParticleSwift is a build dependency of your main application. This should happen automatically.

Dependencies

Import the ParticleSwift framework in sources as needed

Import

Reference the examples for sample usage.

Using the Library - Swift Package Manager

particle-swift can be used by by any platform that is supported by the Swift Package Manager (SPM) or directly by Xcode for Apple based platforms.

SPM based deployments simply need to include the particle-swift github url in the Package.swift as shown below. The particle-swift-cli, for example, utilizes particle-swift by delcaring it in Package.swift as follows

import PackageDescription

let package = Package(
    name: "particle-swift-cli",
    dependencies: [
        .Package(url: "https://github.com/vakoc/particle-swift.git", versions: Version(0,0,0)...Version(1,0,0)),
    ]
)

The SPM supports only macOS targets on the Apple platforms and command line applications on Linux. Traditional iOS, tvOS, watchOS, or macOS applications may also utilize particle-swift by simply cloning the git repository and adding the source managed Xcode/ParticleSwift.xcodeproj Xcode project directly into their sources.

This Xcode project provides Swift frameworks for each of those platforms. Simply add the corresponding framework as an embedded framework to your application. Any source files that want to utilize particle-swift should import the module as shown below

import Foundation
import ParticleSwift

let particleCloud = ParticleCloud(.....)

Getting Started

particle-swift provides the APIs to interact with the Particle Cloud webservices. Authentication is handled by the library but credential storage is not; the caller is reponsible for providing and securely storing sensitive information. Apple platorms provide keychain storage which is suitable for persisting sensitive information.

The following sample provides an example of using particle-swift with basic and insecure credential management. Keychain services is beyond the scope of this example. Note: ParticleCloud instances are not singletons and is fully supported to have multiple instances run concurrently that may utilize separate OAuth realms at the same time. Multi-user concurrency is an essential design goal of this library.

import Foundation
import ParticleSwift

// Illustrative example utilizing insecure UserDefaults for token storage and hard coded
// user names.  Production apps should use more secure mechanisms like the Keychain services
// provided by the OS
class MyParticleCloud {
    
    var token: OAuthToken?
    var particleCloud: ParticleCloud?
    
    init() {
        if let data = UserDefaults.standard.value(forKey: "token") as? Data {
            self.token = try? JSONDecoder().decode(OAuthToken.self, from: data)
        }
        particleCloud = ParticleCloud(secureStorage: self)
    }
    
    func callFunctionOnAllMyDevices() {
        
        particleCloud?.devices { result in
            switch (result) {
            case .success(let devices):
                devices.forEach { device in
                    self.particleCloud?.callFunction("myFunction", deviceID: device.deviceID, argument: "7") { functionResult in
                        
                        switch (functionResult) {
                        case .success(let retVal):
                            print("Result of myFunction(7) device \(device.name) was \(retVal)")
                        case .failure(let error):
                            print("Error:  Failed to call myFunction(7) on device \(device.name) with error \(error)")
                        }
                    }
                }
            case .failure(let error):
                print("Error:  Unable to enumerate all devices with function \(error)")
            }
        }
    }
}

/// MARK: - SecureStorage
///
/// This is for illustrative purposes only.  Use security coding techniques for real applications
extension MyParticleCloud: SecureStorage {
    
    /// Callback to obtain the user name
    func username(_ realm: String) -> String? {
        return "myuser"
    }
    
    /// Callback to obtain the user's pasword
    func password(_ realm: String) -> String? {
        return "mypassword"
    }
    
    /// The oauth client identifier to use.  Use "particle" for regular particle cloud accounts
    func oauthClientId(_ realm: String) -> String? {
        return "particle"
    }
    
    /// The oauth client secret to use.  Use "particle" for regular particle cloud accounts
    func oauthClientSecret(_ realm: String) -> String? {
        return "particle"
    }
    
    /// Called to obtain a persisted token.  Persisted tokens are strongly preferred to minimize the
    /// number of authentication calls.
    func oauthToken(_ realm: String) -> OAuthToken? {
        
        if let data = UserDefaults.standard.data(forKey: "ParticleToken") {
            do {
                return try JSONDecoder().decode(OAuthToken.self, from: data)
            } catch {
                /// Some sort of decoding error.  Remove the invalid value
                UserDefaults.standard.removeObject(forKey: "ParticleToken")
                return nil
            }
        }
        /// No known or valid token.  Return nil
        return nil
    }

    /// Method is called back to obtain an existing token.  The realm from ParticleSwift will be
    /// "ParticleSwift".  Realm is only relevant for applications that re-use the SecureStorage
    /// protocol for multiple OAuth endpoints.  This would be extremely rare.
    ///
    /// Tokens should be persisted and reused across application launches.  Expired or invalid tokens will be
    /// destroyed and new tokens created automatically
    func updateOAuthToken(_ token: OAuthToken?, forRealm realm: String) {
        
        guard let token = token else {
            /// An invalid or expired will be purged by calling this method with nil as the token.
            /// Remove any previously stored token
            UserDefaults.standard.removeObject(forKey: "ParticleToken")
            return
        }
        
        /// OAuthToken is Codable, so we can persist it to Data for storage in UserDefaults.
        /// Real applications should store in the keychain rather than UserDefaults
        let encoder = JSONEncoder()
        if let data = try? encoder.encode(token) {
            UserDefaults.standard.set(data, forKey: "ParticleToken")
        }
    }    
}


The following example uses the MyParticleCloud class to call a function on all devices assocaited with the user's account.

let cloud = MyParticleCloud()
cloud.callFunctionOnAllMyDevices()

Note: if you are using particle-swift in a command line style application you will need to create a runloop. All particle-swift interactions are asynchronous and utliize background threads for network communications. Create a runloop like

RunLoop.current.run(until: Date.distantFuture)

Refer to the particle-swift-cli sample application, which utilizes every particle-swift capability, for more examples on how to make use of this framework.

Versioning

Swift package manager based projects utilize only tagged releases that match the versions specified in the Package.swift manifest file. As such releases of this library are created often. Version numbers follow the semantic versioning system of MAJOR.MINOR.PATCH. While every attempt is made to prevent source level incompatibilities between patch level versions, at this point, this is not guaranteed.

Linux Support

particle-swift (and particle-swift-cli) currently compile and function on Linux.

The most recent Swift build known to work is swift 4.1 release.

Roadmap

APIs should be relatively stable but are subject to change. Additional Particle Cloud functionality is being added in the following general order

  • Products (In Progress, mostly complete)

Once complete additional functionality will be added.

License

All code is licensed under the Apache 2.0 license. See http://www.vakoc.com/LICENSE.txt for more information.

Github

link
Stars: 8
Help us keep the lights on

Dependencies

Used By

Total: 1

Releases

0.3.6 - Apr 24, 2017

0.3.5 - Apr 24, 2017

0.3.4 - Mar 31, 2017

Progress towards supporting official 3.1 based Swift on Linux

0.3.3 - Mar 22, 2017

Near completion of products cloud API

0.3.2 - Mar 17, 2017

Initial support of products. Fixes a typo in error enums that may break source compatibility with earlier releases.