Potentially breaking change in mobile SDKs (both iOS & Android)


#1

Hello everyone,
Just a short notice on a potentially breaking change we introduced in our latest mobile SDK releases. iOS SDK is already live (v0.9.4) and Android will soon follow (will post version number when it will go live).

Our SDK had one weird behavior that caused few problems. Whenever you would call ParticleCloud.getDevice**s**, we would go ahead and call ParticleCloud.getDevice (no ‘s’) for EVERY device that is reported as being online by getDevice**s** method. Originally this seemed like a great idea, that would simplify everything for you guys, but we’ve come to the point where it causes more trouble than it should:

  1. Now this behavior causes 429 HTTP errors (API throttling) for users who have many devices.
  2. Our SDK simply don’t offer any ways to just load device list as fast as possible when it is needed
  3. Since details would only be loaded for online devices, we couldn’t rely on details to be really fetched anyway

All things considered, we decided to decompose these two actions and make getDevices return basic information about every user owned device, and require fetching full device info separately. To do that you have two options:

  1. Call ParticleCloud.getDevice which will return ParticleDevice instance containing full device info
  2. Call ParticleDevice.refresh on ParticleDevice instance returned by call to ParticleCloud.getDevice**s**

Please let us know if this affects your apps significantly.


pinned #2

#3

@Raimis so are you saying that this code (using swift):

ParticleCloud.sharedInstance().getDevices { (particleDevices:[ParticleDevice]?, error:Error?) in
  if error != nil { completion(false)}
  
  guard let devices = particleDevices else { completion(false); return }

  for device in devices {
    print("Device in particle account: \(String(describing: device.name)) with id:\(device.id))")
  }
  completion(true)
}

now becomes something like this:

ParticleCloud.sharedInstance().getDevices { (particleDevicesInfo:[ParticleDeviceInfo]?, error:Error?) in
  if error != nil { completion(false)}
  guard let devicesInfo = particleDevicesInfo else { completion(false); return }

  for deviceInfo in devicesInfo {
    let device = ParticleCloud.sharedInstance().getDevice(deviceInfo)
    print("Device in particle account: \(String(describing: device.name)) with id:\(device.id))")
  }
 
  completion(true)
}

Seems a trivial change on this side of the wall - though I dont like API breakage whereby a method’s function, result and side effects are redefined. If at all an option, I’d rather have the original method return nil and/or error and a new method erected something along the lines of getDevices2(), getDevicesEx() etc.


#4

Your code snippets are partially correct and in general the topic is somewhat more complicated, but let’s try to solve it :). We want to avoid breaking changes as much as possible, but this one in particular feels like it is necessary and we really expect that it will barely affect anyone.

For one thing is that we expect that getDevices provides sufficient information to render any kind of view, where you need list of user devices. Therefore, we expect that there will be no need to get extended info about each device on that list to render those screens.

This is the list of properties returned for each device by calling getDevices:

{
    "id": "e00fce68f6688aa482358318",
    "name": "xenon-2",
    "last_app": null,
    "last_ip_address": "162.244.250.220",
    "last_heard": "2019-02-27T16:44:14.876Z",
    "product_id": 14,
    "connected": true,
    "platform_id": 14,
    "cellular": false,
    "notes": null,
    "status": "normal",
    "serial_number": "XENHAB838P9MJB8",
    "current_build_target": "0.9.0",
    "system_firmware_version": "0.9.0",
    "default_build_target": "0.9.0"
}

This is the list of properties returned for the same device by getDevice:

{
    "id": "e00fce68f6688aa482358318",
    "name": "xenon-2",
    "last_app": null,
    "last_ip_address": "162.244.250.220",
    "last_heard": "2019-02-27T16:44:17.024Z",
    "product_id": 14,
    "connected": true,
    "platform_id": 14,
    "cellular": false,
    "notes": null,
    "status": "normal",
    "serial_number": "XENHAB838P9MJB8",
    "current_build_target": "0.9.0",
    "system_firmware_version": "0.9.0",
    "default_build_target": "0.9.0",
    "variables": {},
    "functions": [
        "digitalread",
        "digitalwrite",
        "analogread",
        "analogwrite"
    ],
    "mobile_secret": "I REMOVED THIS FOR SECURITY REASONS :)"
}

We are open to suggestions to which fields should be added to getDevices list to make everyone’s life easier, but we really don’t expect you to need list of device variables & functions or mobile secret to go together with that call.

As for code snippets, please note that getDevice is async as well, so you would need to have different completion handler to print result of that. Also to completely replicate original behavior, you should only call getDevice for devices that have connected set to true in the list returned by getDevices. Also instead of calling getDevice you could simply call refresh() on ParticleDevice instance.

Does this make sense?