Run Modbus Queries while the app is in background state - ios

I'm working with Modbus protocol and after successfully setup TCP connection, I am able to connect with the hardware and start performing continuous reading operations and everything working fine while the application is in foreground.
As soon as the application moves to background state, all my queries stop working. To verify the same, I added logs but logs were also not showing in application background state.
I am stuck with this issue now and not able to identify the actual root cause. Can anyone help me in this. Below is the code which I'm using:
class DemoFile: NSObject {
var mb: OpaquePointer?
var modbusQueue: DispatchQueue?
var ipAddress: NSString?
init(ipAddress: NSString, port: Int32, device: Int32) {
super.init()
modbusQueue = DispatchQueue(label: "com.iModbus.modbusQueue")
let _ = self.setupTCP(ipAddress: ipAddress, port: port, device: device)
}
func setupTCP(ipAddress: NSString, port: Int32, device: Int32) -> Bool {
self.ipAddress = ipAddress
mb = modbus_new_tcp(ipAddress.cString(using: String.Encoding.ascii.rawValue) , port)
var modbusErrorRecoveryMode = modbus_error_recovery_mode(0)
modbusErrorRecoveryMode = modbus_error_recovery_mode(rawValue: MODBUS_ERROR_RECOVERY_LINK.rawValue | MODBUS_ERROR_RECOVERY_PROTOCOL.rawValue)
modbus_set_error_recovery(mb!, modbusErrorRecoveryMode)
modbus_set_slave(mb!, device)
return true
}
private func buildNSError(errno: Int32, errorString: NSString) -> NSError {
var details: [String: Any] = [:]
details[NSLocalizedDescriptionKey] = errorString
let error = NSError(domain: "Modbus", code: Int(errno), userInfo: details)
return error
}
private func buildNSError(errno: Int32) -> NSError {
let errorString = NSString(utf8String: modbus_strerror(errno))
return self.buildNSError(errno: errno, errorString: errorString!)
}
//Step 1 First I make a connection with my modbus RTU
/**
Function to establish modbus TCP connection with success and error handled
*/
func connect(success: #escaping () -> Void, failure: #escaping (NSError) -> Void) {
modbusQueue?.async {
let ret = modbus_connect(self.mb!)
if ret == -1 {
let error = self.buildNSError(errno: errno)
DispatchQueue.main.async {
failure(error)
}
}
else {
DispatchQueue.main.async {
success()
}
}
}
}
//Step 2 I start perform read operations with my connected Modbus RTU
/**
Function to read register from the start of the address and till (according to the count)
- parameter startaddress: the address of the starting register
- parameter count: the number of consecutive registers to be read
*/
func readRegistersFrom(startAddress: Int32, count: Int32, success: #escaping ([AnyObject]) -> Void, failure: #escaping (NSError) -> Void) {
modbusQueue?.async {
let tab_reg: UnsafeMutablePointer<UInt16> = UnsafeMutablePointer<UInt16>.allocate(capacity: Int(count))
if modbus_read_registers(self.mb!, startAddress, count, tab_reg) >= 0 {
let returnArray: NSMutableArray = NSMutableArray(capacity: Int(count))
for i in 0..<Int(count) {
returnArray.add(Int(tab_reg[i]))
}
DispatchQueue.main.async {
success(returnArray as [AnyObject])
}
}
else {
let error = self.buildNSError(errno: errno)
DispatchQueue.main.async {
failure(error)
}
}
}
}
//Step 3 It will gives me response
//Step 4 With the help of for Loop I am continue reading the values
////Calling the readInput Function for getting continue response
func readModbusData() {
for _ in 0..<2000 {
self.readRegistersFrom(startAddress: 51, count: 1) { (result) in
print("Success: \(result)")
} failure: { (error) in
print("Error: \(error.localizedDescription)")
}
}
}
/*
Problem Statement: When my app goes to active state to background state It's stop the execution of modbus query and also, I am not able to print my communication logs also
*/
}

Related

Can't get weight samples using anchored query

I'm working on a watchOS App as my first Swift/iOS project ever. I want to fetch the latest body weight sample and use it for some calculation. The result is presented to the user. As soon as a new sample is added, I want to update my UI as well. It works in a completely fresh simulator installation. As soon as I add a sample in the iOS simulator, the app updates its UI in the watchOS simulator. However, it doesn't work on my real device or after resetting the watchOS simulator. And I just don't know why. The HKAnchoredObjectQuery just returns 0 samples but I definitely have some samples stored in health. I can even see them under Settings > Health on my watch. I can't imagine this is related to my code, but here it is:
class WeightProvider: ObservableObject {
private static let weightSampleType = HKSampleType.quantityType(forIdentifier: .bodyMass)!
private static let healthStore: HKHealthStore = .init()
private var previousAnchor: HKQueryAnchor?
private var runningQuery: HKAnchoredObjectQuery?
#Published var bodyWeight: Measurement<UnitMass>?
func getBodyWeight(longRunning: Bool = false) {
let query = HKAnchoredObjectQuery(type: Self.weightSampleType, predicate: nil, anchor: previousAnchor, limit: longRunning ? HKObjectQueryNoLimit : 1, resultsHandler: processQueryResult)
if longRunning {
query.updateHandler = processQueryResult
runningQuery = query
}
Self.healthStore.execute(query)
}
func stopLongRunningQuery() {
if let runningQuery = runningQuery {
Self.healthStore.stop(runningQuery)
self.runningQuery = nil
}
}
private func processQueryResult(_: HKAnchoredObjectQuery, samples: [HKSample]?, _: [HKDeletedObject]?, newAnchor: HKQueryAnchor?, error: Error?) {
guard let samples = samples as? [HKQuantitySample], error == nil else {
fatalError(error?.localizedDescription ?? "Failed to cast [HKSample] to [HKQuantitySample]")
}
previousAnchor = newAnchor
guard let sample = samples.last else {
return
}
DispatchQueue.main.async {
if Locale.current.usesMetricSystem {
let weight = sample.quantity.doubleValue(for: .gramUnit(with: .kilo))
self.bodyWeight = .init(value: weight, unit: UnitMass.kilograms)
} else {
let weight = sample.quantity.doubleValue(for: .pound())
self.bodyWeight = .init(value: weight, unit: UnitMass.pounds)
}
}
}
}
// MARK: - HealthKit Authorization
extension WeightProvider {
private static let typesToRead: Set<HKObjectType> = [
weightSampleType,
]
func authorize(completion: #escaping (Bool, Error?) -> Swift.Void) {
Self.healthStore.requestAuthorization(toShare: nil, read: Self.typesToRead) { success, error in
completion(success, error)
}
}
}
In my Views onAppear I call this function:
private func authorizeHealthKit() {
guard firstRun else {
return
}
firstRun = false
weightProvider.authorize { success, error in
guard success, error == nil else {
return
}
weightProvider.getBodyWeight(longRunning: true)
}
}
HealthKit is properly authorized as I can see in the Settings of my Watch. Any ideas? Any tips for my code in general?
Wow, after all this time I found the issue: The line previousAnchor = newAnchor needs to be after the guard statement. That's it.

Swift Multipeer Connectivity - InputStream receiving empty data

I'm doing a multiplayer game based on the MPC framework for the data communication between the devices of the players (and spritekit for the game).
The logic is that one of the device is the master, does all the game logic and calculations, and sends the positions of the various sprites to the slave devices that just have to update the sprites positions.
The master opens streams between him and all the slaves and sends the various data through these streams. Data are stored in dictionaries of type (String, Any).
The issue is twofold :
little by little I see a difference between the number of time the master send data and the number of time a slave received data
the more the master increase the number of data sent the more the slave receive empty/partial data
I have put various checks and controls, I don't have errors thrown by the send or receive functions, just lost data or empty/partial data.
Here is below some relevant parts of the code. Let me know if more is needed
Thx
J.
Stream initialization between master and slaves
Executed by the master
func InitMPCStream()
{
for Peer in Session.connectedPeers
{
if Peer.displayName != gMasterPeerName
{
let IndexPeer = gtListPlayerMulti.index(where: { $0.Name == Peer.displayName } )
try! gtListPlayerMulti[IndexPeer!].Stream = Session.startStream(withName: "Stream " + Peer.displayName, toPeer: Peer)
gtListPlayerMulti[IndexPeer!].Stream!.delegate = self
gtListPlayerMulti[IndexPeer!].Stream!.schedule(in: RunLoop.main, forMode:RunLoopMode.defaultRunLoopMode)
gtListPlayerMulti[IndexPeer!].Stream!.open()
}
}
}
Executed by the slaves
func session(_ session: MCSession, didReceive InStream: InputStream, withName StreamName: String, fromPeer PeerID: MCPeerID)
{
InputStream = InStream
InputStream.delegate = self
InputStream.schedule(in: RunLoop.main, forMode: RunLoopMode.defaultRunLoopMode)
InputStream.open()
}
Function that sends the data to the slaves
This function is executed on the master side in the update loop of the SKScene, several times per update, so very often.
The logic is that for each update cycle several events occur (between 10 to 100). Each event generates a call to this function. The quantity of data sent is not very important : a dictionary of (String, Any) of 3 to 10 lines where Any can be a string, number (Int, CGFloat...) or bool.
func SendStreamData(IDPeer: Int = 0, DataToSend: Dictionary<String, Any>)
{
let DataConverted = NSKeyedArchiver.archivedData(withRootObject: DataToSend)
if IDPeer == 0
{
for Peer in Session.connectedPeers
{
let IndexPeer = gtListPlayerMulti.index(where: { $0.Name == Peer.displayName } )
if gtListPlayerMulti[IndexPeer!].Stream!.hasSpaceAvailable
{
let bytesWritten = DataConverted.withUnsafeBytes { gtListPlayerMulti[IndexPeer!].Stream!.write($0, maxLength: DataConverted.count) }
if bytesWritten == -1 { print("Erreur send stream") } else { gSendingNumber = gSendingNumber + 1 }
} else { print("No space in stream") }
}
}
else
{
let IndexPeer = gtListPlayerMulti.index(where: { $0.ID == IDPeer } )
let bytesWritten = DataConverted.withUnsafeBytes { gtListPlayerMulti[IndexPeer!].Stream!.write($0, maxLength: DataConverted.count) }
}
}
Function called when data is received on the slave side
func stream(_ aStream: Stream, handle eventCode: Stream.Event)
{
switch(eventCode)
{
case Stream.Event.hasBytesAvailable:
gSendingNumber = gSendingNumber + 1
let InputStream = aStream as! InputStream
var Buffer = [UInt8](repeating: 0, count: 1024)
let NumberBytes = InputStream.read(&Buffer, maxLength:1024)
let DataString = NSData(bytes: &Buffer, length: NumberBytes)
if let Message = NSKeyedUnarchiver.unarchiveObject(with: DataString as Data) as? [String:Any] //deserializing the NSData
{ ProcessMPCDataReceived(VCMain: self, PeerID: PeerID, RawData: DataString as Data) }
else { print("Empty Data") }
case Stream.Event.hasSpaceAvailable:
break
case Stream.Event.errorOccurred:
print("ErrorOccurred: \(aStream.streamError?.localizedDescription)")
default:
break
}
}
The issue here is on the NSKeyedUnarchiver.unarchiveObject line which return nil because DataString does not conform to the expected dictionary

Swift 2 - Extra argument in call

I've created a error handling class, which is suppose to make my calls easier, however i keep getting an error Extra limit argument in call. I've double checked that their is 7 arguments in both, how come i get this error?
Class
class GetOrganization {
func request(
lastPage: Int?,
limit: Int,
location: CLLocation,
radius: Int?,
success successCallback: (JSON) -> Void,
error errorCallback: (statusCode: Int) -> Void,
failure failureCallback: (error: Error) -> Void
) {
Provider.request(.Organizations(lastPage, limit,location.coordinate.longitude, location.coordinate.latitude, radius)) { result in
switch result {
case let .Success(response):
do {
try response.filterSuccessfulStatusCodes()
let json = try JSON(response.mapJSON())
successCallback(json)
}
catch {
errorCallback(statusCode: response.statusCode)
}
case let .Failure(error):
failureCallback(error: error)
}
}
}
}
*Where i get the error**
GetOrganization.request(
lastPage: lastPage,
limit: limit,
location: location,
radius: nil,
success: { data in
}, error: { err in
print(err)
}, failure: { faillure in
// oh well, no network apparently
})
It is not a static function, so you can't call it like GetOrganization.request. Either set the func request as static func request or create an object and access it via that object.

Completion handler not working as expected in Swift

I have these two functions below, using a completion handler. The questions is highlighted in comments of the 2nd function... why is the result part getting executed even before the asynchronous call in function checforViolationStatus() been completed.
func checkViolationStatus(usr: PFUser, completion: (result: Int32) -> Void) {
var violations: Int32 = 0
var query = PFQuery(className: PF_BLOCKEDUSERS_CLASS_NAME)
query.whereKey(PF_BLOCKEDUSERS_USER, equalTo: usr)
query.countObjectsInBackgroundWithBlock {
(count: Int32, error: NSError?) -> Void in
if error == nil {
print("Result = \(count)")
//The result here returned is 4, I can see it but always ZERO(0) gets printed in the main function. Unable to understand why.
violations = count
}
}
completion(result: violations)
}
func goToMainMenu() {
if PFUser.currentUser() != nil {
self.mCould.checkViolationStatus(PFUser.currentUser()!) {
(result: Int32) in
//QUESTION: result is getting returned as ZERO even before the actual asynchronous call in the checkforViolation function has been completed - why????
if result < 4 {
//Go to Main Menu Screen
print("result<=4 so calling segue")
self.performSegueWithIdentifier("segueLoginVCToMainVC", sender: nil)
} else {
print("result >=4, so should not be doing anything")
}
print("Number of Violations Received Back: \(result)")
}
}
}
Try change your function to this,you should call completion in the countObjectsInBackgroundWithBlock,this method is async.
Or this function return before countObjectsInBackgroundWithBlock is finished
func checkViolationStatus(usr: PFUser, completion: (result: Int32) -> Void) {
var violations: Int32 = 0
var query = PFQuery(className: PF_BLOCKEDUSERS_CLASS_NAME)
query.whereKey(PF_BLOCKEDUSERS_USER, equalTo: usr)
query.countObjectsInBackgroundWithBlock {
(count: Int32, error: NSError?) -> Void in
if error == nil {
print("Result = \(count)")
//The result here returned is 4, I can see it but always ZERO(0) gets printed in the main function. Unable to understand why.
violations = count
completion(result: violations)
}
}
}

Why doesn't HKAnchoredObjectQuery with enableBackgroundDeliveryForType always fire when app is in the background?

I'm experimenting a bit to familiarize myself with the HKAnchoredObjectQuery and getting results when my app is inactive.
I start the app, switch away to Apple Health, enter a blood glucose result; sometimes the results handler is called right away (as evidenced by the print to the console) but other times the handler isn't called until I switch back to my app. Same is true for deleted results as well as added results. Anybody have any guidance?
Most of this code is from a question from thedigitalsean adapted here to get updates while app is in the background and logging to the console. See: Healthkit HKAnchoredObjectQuery in iOS 9 not returning HKDeletedObject
class HKClient : NSObject {
var isSharingEnabled: Bool = false
let healthKitStore:HKHealthStore? = HKHealthStore()
let glucoseType : HKObjectType = HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierBloodGlucose)!
override init(){
super.init()
}
func requestGlucosePermissions(authorizationCompleted: (success: Bool, error: NSError?)->Void) {
let dataTypesToRead : Set<HKObjectType> = [ glucoseType ]
if(!HKHealthStore.isHealthDataAvailable())
{
// let error = NSError(domain: "com.test.healthkit", code: 2, userInfo: [NSLocalizedDescriptionKey: "Healthkit is not available on this device"])
self.isSharingEnabled = false
return
}
self.healthKitStore?.requestAuthorizationToShareTypes(nil, readTypes: dataTypesToRead){(success, error) -> Void in
self.isSharingEnabled = true
authorizationCompleted(success: success, error: error)
}
}
func getGlucoseSinceAnchor(anchor:HKQueryAnchor?, maxResults:uint, callback: ((source: HKClient, added: [String]?, deleted: [String]?, newAnchor: HKQueryAnchor?, error: NSError?)->Void)!) {
let queryEndDate = NSDate(timeIntervalSinceNow: NSTimeInterval(60.0 * 60.0 * 24))
let queryStartDate = NSDate.distantPast()
let sampleType: HKSampleType = glucoseType as! HKSampleType
let predicate: NSPredicate = HKAnchoredObjectQuery.predicateForSamplesWithStartDate(queryStartDate, endDate: queryEndDate, options: HKQueryOptions.None)
var hkAnchor: HKQueryAnchor
if(anchor != nil){
hkAnchor = anchor!
} else {
hkAnchor = HKQueryAnchor(fromValue: Int(HKAnchoredObjectQueryNoAnchor))
}
let onAnchorQueryResults : ((HKAnchoredObjectQuery, [HKSample]?, [HKDeletedObject]?, HKQueryAnchor?, NSError?) -> Void)! = {
(query:HKAnchoredObjectQuery, addedObjects:[HKSample]?, deletedObjects:[HKDeletedObject]?, newAnchor:HKQueryAnchor?, nsError:NSError?) -> Void in
var added = [String]()
var deleted = [String]()
if (addedObjects?.count > 0){
for obj in addedObjects! {
let quant = obj as? HKQuantitySample
if(quant?.UUID.UUIDString != nil){
let val = Double( (quant?.quantity.doubleValueForUnit(HKUnit(fromString: "mg/dL")))! )
let msg : String = (quant?.UUID.UUIDString)! + " " + String(val)
added.append(msg)
}
}
}
if (deletedObjects?.count > 0){
for del in deletedObjects! {
let value : String = del.UUID.UUIDString
deleted.append(value)
}
}
if(callback != nil){
callback(source:self, added: added, deleted: deleted, newAnchor: newAnchor, error: nsError)
}
}
// remove predicate to see deleted objects
let anchoredQuery = HKAnchoredObjectQuery(type: sampleType, predicate: nil, anchor: hkAnchor, limit: Int(maxResults), resultsHandler: onAnchorQueryResults)
// added - query should be always running
anchoredQuery.updateHandler = onAnchorQueryResults
// added - allow query to pickup updates when app is in backgroun
healthKitStore?.enableBackgroundDeliveryForType(sampleType, frequency: .Immediate) {
(success, error) in
if (!success) {print("enable background error")}
}
healthKitStore?.executeQuery(anchoredQuery)
}
let AnchorKey = "HKClientAnchorKey"
func getAnchor() -> HKQueryAnchor? {
let encoded = NSUserDefaults.standardUserDefaults().dataForKey(AnchorKey)
if(encoded == nil){
return nil
}
let anchor = NSKeyedUnarchiver.unarchiveObjectWithData(encoded!) as? HKQueryAnchor
return anchor
}
func saveAnchor(anchor : HKQueryAnchor) {
let encoded = NSKeyedArchiver.archivedDataWithRootObject(anchor)
NSUserDefaults.standardUserDefaults().setValue(encoded, forKey: AnchorKey)
NSUserDefaults.standardUserDefaults().synchronize()
}
}
class ViewController: UIViewController {
let debugLabel = UILabel(frame: CGRect(x: 10,y: 20,width: 350,height: 600))
override func viewDidLoad() {
super.viewDidLoad()
self.view = UIView();
self.view.backgroundColor = UIColor.whiteColor()
debugLabel.textAlignment = NSTextAlignment.Center
debugLabel.textColor = UIColor.blackColor()
debugLabel.lineBreakMode = NSLineBreakMode.ByWordWrapping
debugLabel.numberOfLines = 0
self.view.addSubview(debugLabel)
let hk = HKClient()
hk.requestGlucosePermissions(){
(success, error) -> Void in
if(success){
let anchor = hk.getAnchor()
hk.getGlucoseSinceAnchor(anchor, maxResults: 0)
{ (source, added, deleted, newAnchor, error) -> Void in
var msg : String = String()
if(deleted?.count > 0){
msg += "Deleted: \n" + (deleted?[0])!
for s in deleted!{
msg += s + "\n"
}
}
if (added?.count > 0) {
msg += "Added: "
for s in added!{
msg += s + "\n"
}
}
if(error != nil) {
msg = "Error = " + (error?.description)!
}
if(msg.isEmpty)
{
msg = "No changes"
}
debugPrint(msg)
if(newAnchor != nil && newAnchor != anchor){
hk.saveAnchor(newAnchor!)
}
dispatch_async(dispatch_get_main_queue(), { () -> Void in
self.debugLabel.text = msg
})
}
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
I also added print()'s at the various application state changes. A sample of the console log (this is running on iPhone 6s device from XCode) shows the handler being called sometimes after I entered background but before reentering foreground and other times only after reentering foreground.
app did become active
"No changes"
app will resign active
app did enter background
app will enter foreground
"Added: E0340084-6D9A-41E4-A9E4-F5780CD2EADA 99.0\n"
app did become active
app will resign active
app did enter background
"Added: CEBFB656-0652-4109-B994-92FAA45E6E55 98.0\n"
app will enter foreground
"Added: E2FA000A-D6D5-45FE-9015-9A3B9EB1672C 97.0\n"
app did become active
app will resign active
app did enter background
"Deleted: \nD3124A07-23A7-4571-93AB-5201F73A4111D3124A07-23A7-4571-93AB-5201F73A4111\n92244E18-941E-4514-853F-D890F4551D76\n"
app will enter foreground
app did become active
app will resign active
app did enter background
app will enter foreground
"Added: 083A9DE4-5EF6-4992-AB82-7CDDD1354C82 96.0\n"
app did become active
app will resign active
app did enter background
app will enter foreground
"Added: C7608F9E-BDCD-4CBC-8F32-94DF81306875 95.0\n"
app did become active
app will resign active
app did enter background
"Deleted: \n15D5DC92-B365-4BB1-A40C-B870A48A70A415D5DC92-B365-4BB1-A40C-B870A48A70A4\n"
"Deleted: \n17FB2A43-0828-4830-A229-7D7DDC6112DB17FB2A43-0828-4830-A229-7D7DDC6112DB\n"
"Deleted: \nCEBFB656-0652-4109-B994-92FAA45E6E55CEBFB656-0652-4109-B994-92FAA45E6E55\n"
app will enter foreground
"Deleted: \nE0340084-6D9A-41E4-A9E4-F5780CD2EADAE0340084-6D9A-41E4-A9E4-F5780CD2EADA\n"
app did become active
I suggest using an HKObserverQuery and setting it up carefully.
There is an algorithm that watches how and when you call the "completion" handler of the HKObserverQuery when you have background delivery enabled. The details of this are vague unfortunately. Someone on the Apple Dev forums called it the "3 strikes" rule but Apple hasn't published any docs that I can find on it's behavior.
https://forums.developer.apple.com/thread/13077
One thing I have noticed is that, if your app is responding to a background delivery with an HKObserverQuery, creating an HKAnchoredObjectQuery, and setting the UpdateHandler in that HKAnchoredObjectQuery, this UpdateHandler will often cause multiple firings of the callback. I suspected that perhaps since these additional callbacks are being executed AFTER you have already told Apple that you have completed you work in response to the background delivery, you are calling the completion handler multiple times and maybe they ding you some "points" and call you less often for bad behavior.
I had the most success with getting consistent callbacks by doing the following:
Using an ObserverQuery and making the sure the call of the "completion" handler gets called once and at the very end of your work.
Not setting an update handler in my HKAnchoredObjectQuery when running in the background (helps achieve 1).
Focusing on making my query handlers, AppDelegate, and ViewController are as fast as possible. I noticed that when I reduced all my callbacks down to just a print statement, the callbacks from HealthKit came immediately and more consistently. So that says Apple is definitely paying attention to execution time. So try to statically declare things where possible and focus on speed.
I have since moved on to my original project which uses Xamarin.iOS, not swift, so I haven't kept up with the code I originally posted. But here is an updated (and untested) version of that code that should take these changes into account (except for the speed improvements):
//
// HKClient.swift
// HKTest
import UIKit
import HealthKit
class HKClient : NSObject {
var isSharingEnabled: Bool = false
let healthKitStore:HKHealthStore? = HKHealthStore()
let glucoseType : HKObjectType = HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierBloodGlucose)!
override init(){
super.init()
}
func requestGlucosePermissions(authorizationCompleted: (success: Bool, error: NSError?)->Void) {
let dataTypesToRead : Set<HKObjectType> = [ glucoseType ]
if(!HKHealthStore.isHealthDataAvailable())
{
// let error = NSError(domain: "com.test.healthkit", code: 2, userInfo: [NSLocalizedDescriptionKey: "Healthkit is not available on this device"])
self.isSharingEnabled = false
return
}
self.healthKitStore?.requestAuthorizationToShareTypes(nil, readTypes: dataTypesToRead){(success, error) -> Void in
self.isSharingEnabled = true
authorizationCompleted(success: success, error: error)
}
}
func startBackgroundGlucoseObserver( maxResultsPerQuery: Int, anchorQueryCallback: ((source: HKClient, added: [String]?, deleted: [String]?, newAnchor: HKQueryAnchor?, error: NSError?)->Void)!)->Void {
let onBackgroundStarted = {(success: Bool, nsError : NSError?)->Void in
if(success){
//Background delivery was successfully created. We could use this time to create our Observer query for the system to call when changes occur. But we do it outside this block so that even when background deliveries don't work,
//we will have the observer query working when are in the foreground at least.
} else {
debugPrint(nsError)
}
let obsQuery = HKObserverQuery(sampleType: self.glucoseType as! HKSampleType, predicate: nil) {
query, completion, obsError in
if(obsError != nil){
//Handle error
debugPrint(obsError)
abort()
}
var hkAnchor = self.getAnchor()
if(hkAnchor == nil) {
hkAnchor = HKQueryAnchor(fromValue: Int(HKAnchoredObjectQueryNoAnchor))
}
self.getGlucoseSinceAnchor(hkAnchor, maxResults: maxResultsPerQuery, callContinuosly:false, callback: { (source, added, deleted, newAnchor, error) -> Void in
anchorQueryCallback(source: self, added: added, deleted: deleted, newAnchor: newAnchor, error: error)
//Tell Apple we are done handling this event. This needs to be done inside this handler
completion()
})
}
self.healthKitStore?.executeQuery(obsQuery)
}
healthKitStore?.enableBackgroundDeliveryForType(glucoseType, frequency: HKUpdateFrequency.Immediate, withCompletion: onBackgroundStarted )
}
func getGlucoseSinceAnchor(anchor:HKQueryAnchor?, maxResults:Int, callContinuosly:Bool, callback: ((source: HKClient, added: [String]?, deleted: [String]?, newAnchor: HKQueryAnchor?, error: NSError?)->Void)!){
let sampleType: HKSampleType = glucoseType as! HKSampleType
var hkAnchor: HKQueryAnchor;
if(anchor != nil){
hkAnchor = anchor!
} else {
hkAnchor = HKQueryAnchor(fromValue: Int(HKAnchoredObjectQueryNoAnchor))
}
let onAnchorQueryResults : ((HKAnchoredObjectQuery, [HKSample]?, [HKDeletedObject]?, HKQueryAnchor?, NSError?) -> Void)! = {
(query:HKAnchoredObjectQuery, addedObjects:[HKSample]?, deletedObjects:[HKDeletedObject]?, newAnchor:HKQueryAnchor?, nsError:NSError?) -> Void in
var added = [String]()
var deleted = [String]()
if (addedObjects?.count > 0){
for obj in addedObjects! {
let quant = obj as? HKQuantitySample
if(quant?.UUID.UUIDString != nil){
let val = Double( (quant?.quantity.doubleValueForUnit(HKUnit(fromString: "mg/dL")))! )
let msg : String = (quant?.UUID.UUIDString)! + " " + String(val)
added.append(msg)
}
}
}
if (deletedObjects?.count > 0){
for del in deletedObjects! {
let value : String = del.UUID.UUIDString
deleted.append(value)
}
}
if(callback != nil){
callback(source:self, added: added, deleted: deleted, newAnchor: newAnchor, error: nsError)
}
}
let anchoredQuery = HKAnchoredObjectQuery(type: sampleType, predicate: nil, anchor: hkAnchor, limit: Int(maxResults), resultsHandler: onAnchorQueryResults)
if(callContinuosly){
//The updatehandler should not be set when responding to background observerqueries since this will cause multiple callbacks
anchoredQuery.updateHandler = onAnchorQueryResults
}
healthKitStore?.executeQuery(anchoredQuery)
}
let AnchorKey = "HKClientAnchorKey"
func getAnchor() -> HKQueryAnchor? {
let encoded = NSUserDefaults.standardUserDefaults().dataForKey(AnchorKey)
if(encoded == nil){
return nil
}
let anchor = NSKeyedUnarchiver.unarchiveObjectWithData(encoded!) as? HKQueryAnchor
return anchor
}
func saveAnchor(anchor : HKQueryAnchor) {
let encoded = NSKeyedArchiver.archivedDataWithRootObject(anchor)
NSUserDefaults.standardUserDefaults().setValue(encoded, forKey: AnchorKey)
NSUserDefaults.standardUserDefaults().synchronize()
}
}

Resources