I am using MacOS 10.14.6 and I see peripheral:didConnect being called twice for the same device every time I call _centralManager?.connect once. Does anyone know why? It seems like I get 2 calls to didConnect depending on what device I am connecting to. Some devices get called once and others get called twice. The devices I am connecting to are different models so all the advertising and GATT database data are different. This is my code:
func Init(){
let centralQueue: DispatchQueue = DispatchQueue(label: "centralMsgQueue", attributes: .concurrent)
let opts = [CBCentralManagerOptionShowPowerAlertKey: false]
_centralManager = CBCentralManager(delegate: self, queue: centralQueue, options: opts)
}
func Open(id: Int) -> Int {
let i = GetDevice(id: id)
if (i != -1)
{
let uuid = UUID(uuidString:_deviceTable[i].uuid!);
let data = _centralManager!.retrievePeripherals(withIdentifiers: [uuid!]) as [CBPeripheral]
if data.count > 0 {
_deviceTable[i]._periph = data[0]
_deviceTable[i]._periph?.delegate = self
_deviceTable[i].conn_status = CONN_STATUS_PENDING
_centralManager?.connect(_deviceTable[i]._periph!, options: nil)
}
}
else
{
return -1
}
return 0
}
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
let i = GetDevicePeriph(per: peripheral)
if (i != -1) {
if (_deviceTable[i].conn_status == CONN_STATUS_PENDING)
{
_deviceTable[i].conn_status = CONN_STATUS_DIDCONN
peripheral.discoverServices(nil)
}
}
else {
_centralManager?.cancelPeripheralConnection(peripheral)
}
} // END func centralManager(... didConnect peripheral
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
let peripheralID = peripheral.identifier.uuidString
let deviceName = (advertisementData as NSDictionary).object(forKey: CBAdvertisementDataLocalNameKey) as? String
if (deviceName == "Test"){
_centralManager?.stopScan()
let dev = device(_periph: nil, uuid: peripheralID, conn_status: CONN_STATUS_DISCOVERED)
_deviceTable.append(dev)
}
}
func Discover() {
self._centralManager?.scanForPeripherals(withServices: nil, options: [CBCentralManagerScanOptionAllowDuplicatesKey:_scanAllowDuplicates])
}
func Test() {
Init()
Discover()
Thread.sleep(forTimeInterval: 5.0)
if (_deviceTable.count > 0) {
Open(0)}
}
Related
I'm having a project where I have to write automation for Apple Watch & iPhone app. I'm forced to use XCUITest with Swift because Appium is not yet supporting WatchOS as a testing target.
In my tests, at some points, I have to communicate with an external device via BLE. I wanted to use the CoreBluetooth framework, but I got no luck with that. I have a working CoreBT code that I've used to create a simple BLE chat app, but for some reason, that same code is not working when called from the UI test.
The main problem is that I'm not able to get CBCentralManager in a powered state:
[CoreBluetooth] API MISUSE: <CBCentralManager: 0x28175ce00> can only accept this command while in the powered on state
Later I checked the BT authorization state inside the test with cbCentralManager.authorization == .notDetermined, and it confirmed that authorization is not confirmed. That might be a problem why CBCentralManager is not powering up, but I'm not sure how to resolve it. The app I'm testing is able to use BT without any issues (has all permissions).
Is it even possible to use CoreBluetooth from UITests??
This is the test class code:
import CoreBluetooth
import XCTest
let peripheralName = "MY-BLE-DEVICE-NAME"
let service_First = CBUUID(string: "0000ffe0-0000-1000-8000-00805f9b34fb")
let readCharacteristic = CBUUID(string: "0000ffe1-0000-1000-8000-00805f9b34fb")
let writeCharacteristic = CBUUID(string: "0000ffe1-0000-1000-8000-00805f9b34fb")
var ble_characteristic: CBCharacteristic?
var ble_perip: CBPeripheral?
class myBLERemoteUITests : XCTestCase {
var cbCentralManager : CBCentralManager!
func wait(timeout: TimeInterval){
let exp = expectation(description: "Wait for X seconds")
let result = XCTWaiter.wait(for: [exp], timeout: timeout)
print("LOG - Timer started for " + String(timeout) + " seconds...")
if result == XCTWaiter.Result.timedOut {
print("LOG - Timer stopped after " + String(timeout) + " seconds...")
}else {
XCTFail("Delay interrupted")
}
}
func test_someBLEtest() throws {
let app = XCUIApplication()
app.launch()
// MARK: Some UI automation steps
app.buttons["Get Started"].tap()
let tablesQuery2 = app.tables
let shoeImage = tablesQuery2.cells["ItemA"].images["item"]
shoeImage.tap()
let shoe2 = tablesQuery2.cells["ItemB"].images["item"]
shoe2.tap()
wait(timeout: 10)
app.buttons["Finish"].tap()
// MARK: Part where I need to communicate with BLE device
// Initialize CBCentralManager
cbCentralManager = CBCentralManager.init(delegate: self, queue: nil) // --> getting API MISUSE mentioned above
wait(timeout: 10)
cbCentralManager = CBCentralManager.init(delegate: self, queue: nil) // --> getting API MISUSE mentioned above
wait(timeout: 10)
cbCentralManager = CBCentralManager.init() // --> getting [CoreBluetooth] XPC connection invalid
// MARK: I check CBCentral Manager Authorization state
if (cbCentralManager.authorization == .allowedAlways){
print("allowed")
}else if (cbCentralManager.authorization == .notDetermined){
print("not determined") // --> I get that authorization is not determined
}
}
}
// MARK: Over here I have all needed CoreBluetooth callback functions:
extension myBLERemoteUITests : CBPeripheralDelegate {
func peripheral(_ peripheral: CBPeripheral, didDiscoverServices error: Error?) {
if let services = peripheral.services {
//discover characteristics of services
for service in services {
peripheral.discoverCharacteristics(nil, for: service)
}
}
}
func peripheral(_ peripheral: CBPeripheral, didDiscoverCharacteristicsFor service: CBService, error: Error?) {
if let charac = service.characteristics {
for characteristic in charac {
peripheral.setNotifyValue(true, for: characteristic)
if characteristic.uuid == writeCharacteristic {
ble_characteristic = characteristic
}
for newChar: CBCharacteristic in service.characteristics!{
peripheral.readValue(for: newChar)
}
}
}
}
func peripheral(_ peripheral: CBPeripheral, didUpdateValueFor characteristic: CBCharacteristic, error: Error?) {
if characteristic.uuid == readCharacteristic {
print("Read Value : \(characteristic)")
}
}
func peripheral(_ peripheral: CBPeripheral, didWriteValueFor characteristic: CBCharacteristic, error: Error?) {
if characteristic.uuid == writeCharacteristic {
print("WRITE VALUE : \(characteristic)")
}
}
}
extension myBLERemoteUITests : CBCentralManagerDelegate {
func centralManagerDidUpdateState(_ central: CBCentralManager) {
if central.state == .poweredOn {
central.scanForPeripherals(withServices: nil, options: nil)
print("Scanning...")
}
}
func centralManager(_ central: CBCentralManager, didDiscover peripheral: CBPeripheral, advertisementData: [String : Any], rssi RSSI: NSNumber) {
guard peripheral.name != nil else {return}
if peripheral.name! == peripheralName {
print("BLE Device Found!")
//stopScan
cbCentralManager.stopScan()
//connect
cbCentralManager.connect(peripheral, options: nil)
ble_perip = peripheral
}
}
func centralManager(_ central: CBCentralManager, didConnect peripheral: CBPeripheral) {
print("Connected : \(peripheral.name ?? "No Name")")
peripheral.discoverServices([service_First])
//discover all service
//peripheral.discoverServices(nil)
peripheral.delegate = self
}
func centralManager(_ central: CBCentralManager, didDisconnectPeripheral peripheral: CBPeripheral, error: Error?) {
print("Disconnected : \(peripheral.name ?? "No Name")")
cbCentralManager.scanForPeripherals(withServices: nil, options: nil)
}
}
I'm adding a custom delegate to my app and, for some reason, it is not working.
My app has a map where I show several markers of different company types. There is also a button that, once pressed, takes me to another viewController where the user can input some filters. The user then presses "Apply" which would pass the filtering data to the map viewController.
The issue here is that no data is being passed.
As reference I followed the guideline https://medium.com/#jamesrochabrun/implementing-delegates-in-swift-step-by-step-d3211cbac3ef which works perfectly fine.
Here is the full project code https://github.com/afernandes0001/Custom-Delegate
I use Firebase but code below just shows pieces related to the delegate.
mapViewController - you will notice that I added a print to the prepareForSegue. When first loading the app and clicking "Search" button it shows nav1 as nil (which is expected) but, if I click Search and Apply (in filterVC), that print is never done.
import UIKit
import MapKit
class MapViewController: UIViewController, MKMapViewDelegate, CLLocationManagerDelegate, FilterVCDelegate {
#IBOutlet weak var map: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
map.register(MyAnnotationView.self, forAnnotationViewWithReuseIdentifier: MKMapViewDefaultAnnotationViewReuseIdentifier)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "clinicDetailsSegue" {
let clinicsDetailsViewController = segue.destination as! ClinicsDetailsViewController
clinicsDetailsViewController.id = self.note.mapId
} else if segue.identifier == "searchSegue" {
print("segue call")
let nav1 = segue.destination as? UINavigationController
print("nav1 \(nav1)")
if let nav = segue.destination as? UINavigationController, let filterVC = nav.topViewController as? FilterViewController {
filterVC.delegate = self
}
}
}
func chosenData(clinicNameFilter: String, stateFilter: String, cityFilter: String, esp1Filter: String, esp2Filter: String) {
print("Received data \(clinicNameFilter), \(stateFilter), \(cityFilter), \(esp1Filter), \(esp2Filter)")
}
}
FilterViewController
import UIKit
protocol FilterVCDelegate: class {
func chosenData(clinicNameFilter: String, stateFilter: String, cityFilter: String, esp1Filter: String, esp2Filter: String)
}
class FilterViewController: UIViewController, UIPickerViewDelegate, UIPickerViewDataSource {
weak var delegate: FilterVCDelegate?
var selectedName = ""
var statesJSON = [Estado]()
var cities = [Cidade]()
var state : Estate? // Selected State identifier
var city : City? // Selected City identifier
var selectedState = "" // Used to retrieve info from Firebase
var selectedCity = "" // Used to retrieve info from Firebase
var specialtiesJSON = [Specialty]()
var specialties2 = [Specialty2]()
var specialty1 : Specialty? // Selected Specialty1 identifier
var specialty2 : Specialty2? // Selected Specialty2 identifier
var selectedSpecialty1 = ""
var selectedSpecialty2 = ""
#IBOutlet weak var clinicName: UITextField!
#IBOutlet weak var statePicker: UIPickerView!
#IBOutlet weak var esp1Picker: UIPickerView!
#IBOutlet weak var esp2Picker: UIPickerView!
override func viewDidLoad() {
readJsonStates()
readJsonSpecialties()
super.viewDidLoad()
clinicName.text = ""
}
#IBAction func applyFilter(_ sender: Any) {
if clinicName.text == nil {
clinicName.text = ""
}
if selectedState != "" {
if selectedCity != "" {
if selectedSpecialty1 != ""{
if selectedSpecialty2 != "" {
delegate?.chosenData(clinicNameFilter: clinicName.text!, stateFilter: selectedState, cityFilter: selectedCity, esp1Filter: selectedSpecialty1, esp2Filter: selectedSpecialty2)
let viewControllers: [UIViewController] = self.navigationController!.viewControllers as [UIViewController]
self.navigationController?.popToViewController(viewControllers[viewControllers.count - 2], animated: true)
} else {
print("Fill in all filter data")
}
} else {
print("Fill in all filter data")
}
} else {
print("Fill in all filter data")
}
} else {
print("Fill in all filter data")
}
}
func pickerView(_ pickerView: UIPickerView, didSelectRow row: Int, inComponent component: Int) {
esp1Picker.reloadComponent(0)
esp2Picker.reloadComponent(0)
statePicker.reloadAllComponents()
if pickerView == statePicker {
if component == 0 {
self.state = self.statesJSON[row]
self.coties = self.statesJSON[row].cities
statePicker.reloadComponent(1)
statePicker.selectRow(0, inComponent: 1, animated: true)
} else {
self.city = self.cities[row]
statePicker.reloadAllComponents()
}
} else if pickerView == esp1Picker {
self.specialty1 = self.specialtiesJSON[row]
self.specialties2 = self.specialtiesJSON[row].specialty2
esp1Picker.reloadComponent(0)
esp2Picker.reloadComponent(0)
esp2Picker.selectRow(0, inComponent: 0, animated: true)
} else if pickerView == esp2Picker {
self.specialty2 = self.specialties2[row]
esp1Picker.reloadComponent(0)
esp2Picker.reloadComponent(0)
}
let indexSelectedState = statePicker.selectedRow(inComponent: 0)
let indexSelectedCity = statePicker.selectedRow(inComponent: 1)
let indexSelectedEsp1 = esp1Picker.selectedRow(inComponent: 0)
let indexSelectedEsp2 = esp2Picker.selectedRow(inComponent: 0)
if indexSelectedState >= 0 {
if indexSelectedCity >= 0 {
selectedState = estadosJSON[indexSelectedState].name
selectedCity = cidades[indexSelectedCity].name
}
}
if indexSelectedEsp1 >= 0 {
if indexSelectedEsp2 >= 0 {
selectedSpecialty1 = specialtiesJSON[indexSelectedEsp1].name
selectedSpecialty2 = specialtiesJSON[indexSelectedEsp1].specialty2[indexSelectedEsp2].name
}
}
}
func numberOfComponents(in pickerView: UIPickerView) -> Int {
if pickerView == statePicker {
return 2
} else if pickerView == esp1Picker {
return 1
} else if pickerView == esp2Picker {
return 1
}
return 1
}
func pickerView(_ pickerView: UIPickerView, numberOfRowsInComponent component: Int) -> Int {
if pickerView == statePicker {
if component == 0 {
return statesJSON.count
} else {
return cities.count
}
} else if pickerView == esp1Picker {
return self.specialtiesJSON.count
} else if pickerView == esp2Picker {
return specialties2.count
}
return 1
}
func pickerView(_ pickerView: UIPickerView, viewForRow row: Int, forComponent component: Int, reusing view: UIView?) -> UIView {
var rowTitle = ""
let pickerLabel = UILabel()
pickerLabel.textColor = UIColor.black
if pickerView == statePicker {
if component == 0 {
rowTitle = statesJSON[row].name
} else {
rowTitle = cities[row].name
}
} else if pickerView == esp1Picker {
rowTitle = specialtiesJSON[row].name
} else if pickerView == esp2Picker {
rowTitle = specialties2[row].name
}
pickerLabel.text = rowTitle
pickerLabel.font = UIFont(name: fontName, size: 16.0)
pickerLabel.textAlignment = .center
return pickerLabel
}
func pickerView(_ pickerView: UIPickerView, widthForComponent component: Int) -> CGFloat {
if pickerView == statePicker {
if component == 0 {
return 50
} else {
return 300
}
}
return 300
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
view.endEditing(true)
}
func readJsonStates() {
let url = Bundle.main.url(forResource: "StatesAndCities", withExtension: "json")!
do {
let data = try Data(contentsOf: url)
let jsonResult = try JSONDecoder().decode(RootState.self, from: data)
//handles the array of countries on your json file.
self.statesJSON = jsonResult.state
self.cities = self.statesJSON.first!.cities
} catch {
}
}
func readJsonSpecialties() {
let url = Bundle.main.url(forResource: "Specialties", withExtension: "json")!
do {
let data = try Data(contentsOf: url)
let jsonResult = try JSONDecoder().decode(RootEsp.self, from: data)
//handles the array of specialties on your json file.
self.specialtiesJSON = jsonResult.specialty
self.specialties2 = self.specialtiesJSON.first!.specialty2
} catch {
}
}
}
Any idea why, when I click ApplyFilter, delegate is not updated in the MapViewController?
Thanks
I found the error in my project.
The issue was with my Navigation Controller.
When I posted the error above, my Storyboard looked like the below
To make it work, I added the Navigation Controller to the Filter View Controller as below
That did the work and protocol is working as expected.
I have a core data / document-driven macOS app, using Swift and I struggle on combining the out-of-the-box spell checking API with an NSDocument (NSPersistentDocument in my case)
It took me more time than it should take, but this is what I got, mostly guided by this great answer:
class VTDocument: NSPersistentDocument, NSChangeSpelling {
[...]
private let spellchecker = SpellChecker()
#IBAction func showGuessPanel(_ sender: Any?){
spellchecker.startSpellCheck(nodes: Array(db.nodes), tag: 0)
}
#IBAction #objc func changeSpelling(_ sender: Any?){
spellchecker.replace(with: "Test")
}
This is leading me to see the NSSpellChecker.spellingPanel, correctly showing the word to correct. However, the changeSpelling function should be "called" by the panel but is never called. The above spellChecker is a simple wrapper around the NSSpellChecker that keeps the status between function calls.
The SpellChecker class looks like this.
import Cocoa
class SpellChecker {
let checker = NSSpellChecker.shared
let count: UnsafeMutablePointer<Int> = UnsafeMutablePointer<Int>.allocate(capacity: 1)
var nodes = Array<Node> ()
var nodeNr = 0
var stringPos = 0
var range: NSRange = NSRange()
func startSpellCheck(nodes: [Node], tag: Int ) {
self.nodes = nodes
nodeNr = 0
stringPos = 0
continueChecking()
}
func continueChecking(){
if nodes.count == 0 {
return
}
if nodeNr >= nodes.count {
checker.updateSpellingPanel(withMisspelledWord: "")
checker.spellingPanel.orderFront(self)
return
}
if let nodeText = nodes[nodeNr].label {
range = checker.checkSpelling(of: nodeText, startingAt: stringPos, language: nil, wrap: false, inSpellDocumentWithTag: 0, wordCount: count)
if count.pointee > 0 {
stringPos = range.lowerBound
checker.updateSpellingPanel(withMisspelledWord: nodeText[range])
checker.spellingPanel.orderFront(self)
return
}
}
nodeNr = nodeNr + 1
continueChecking()
}
func replace(with: String){
if let nodeText = nodes[nodeNr].label {
let text = nodeText as NSString
text.replacingCharacters(in: range, with: with)
nodes[nodeNr].label = text as String
}
}
}
Basically I am working on a sleep monitoring application that monitors heart rate as well. So, I don't want to start any workout activity but I think that's the way apple works!
Here's the heart rate only code I am using:
#IBOutlet private weak var label: WKInterfaceLabel!
#IBOutlet private weak var deviceLabel : WKInterfaceLabel!
#IBOutlet private weak var heart: WKInterfaceImage!
#IBOutlet private weak var startStopButton : WKInterfaceButton!
let healthStore = HKHealthStore()
//State of the app - is the workout activated
var workoutActive = false
// define the activity type and location
var workoutSession : HKWorkoutSession?
let heartRateUnit = HKUnit(fromString: "count/min")
var anchor = HKQueryAnchor(fromValue: Int(HKAnchoredObjectQueryNoAnchor))
override func awakeWithContext(context: AnyObject?) {
super.awakeWithContext(context)
}
override func willActivate() {
super.willActivate()
guard HKHealthStore.isHealthDataAvailable() == true else {
label.setText("not available")
return
}
guard let quantityType = HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierHeartRate) else {
displayNotAllowed()
return
}
let dataTypes = Set(arrayLiteral: quantityType)
healthStore.requestAuthorizationToShareTypes(nil, readTypes: dataTypes) { (success, error) -> Void in
if success == false {
self.displayNotAllowed()
}
}
}
func displayNotAllowed() {
label.setText("not allowed")
}
func workoutSession(workoutSession: HKWorkoutSession, didChangeToState toState: HKWorkoutSessionState, fromState: HKWorkoutSessionState, date: NSDate) {
switch toState {
case .Running:
workoutDidStart(date)
case .Ended:
workoutDidEnd(date)
default:
print("Unexpected state \(toState)")
}
}
func workoutSession(workoutSession: HKWorkoutSession, didFailWithError error: NSError) {
// Do nothing for now
NSLog("Workout error: \(error.userInfo)")
}
func workoutDidStart(date : NSDate) {
if let query = createHeartRateStreamingQuery(date) {
healthStore.executeQuery(query)
} else {
label.setText("cannot start")
}
}
func workoutDidEnd(date : NSDate) {
if let query = createHeartRateStreamingQuery(date) {
healthStore.stopQuery(query)
label.setText("---")
} else {
label.setText("cannot stop")
}
}
// MARK: - Actions
#IBAction func startBtnTapped() {
if (self.workoutActive) {
//finish the current workout
self.workoutActive = false
self.startStopButton.setTitle("Start")
if let workout = self.workoutSession {
healthStore.endWorkoutSession(workout)
}
} else {
//start a new workout
self.workoutActive = true
self.startStopButton.setTitle("Stop")
startWorkout()
}
}
func startWorkout() {
self.workoutSession = HKWorkoutSession(activityType: HKWorkoutActivityType.CrossTraining, locationType: HKWorkoutSessionLocationType.Indoor)
self.workoutSession?.delegate = self
healthStore.startWorkoutSession(self.workoutSession!)
}
func createHeartRateStreamingQuery(workoutStartDate: NSDate) -> HKQuery? {
// adding predicate will not work
// let predicate = HKQuery.predicateForSamplesWithStartDate(workoutStartDate, endDate: nil, options: HKQueryOptions.None)
guard let quantityType = HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierHeartRate) else { return nil }
let heartRateQuery = HKAnchoredObjectQuery(type: quantityType, predicate: nil, anchor: anchor, limit: Int(HKObjectQueryNoLimit)) { (query, sampleObjects, deletedObjects, newAnchor, error) -> Void in
guard let newAnchor = newAnchor else {return}
self.anchor = newAnchor
self.updateHeartRate(sampleObjects)
}
heartRateQuery.updateHandler = {(query, samples, deleteObjects, newAnchor, error) -> Void in
self.anchor = newAnchor!
self.updateHeartRate(samples)
}
return heartRateQuery
}
func updateHeartRate(samples: [HKSample]?) {
guard let heartRateSamples = samples as? [HKQuantitySample] else {return}
dispatch_async(dispatch_get_main_queue()) {
guard let sample = heartRateSamples.first else{return}
let value = sample.quantity.doubleValueForUnit(self.heartRateUnit)
self.label.setText(String(UInt16(value)))
// retrieve source from sample
let name = sample.sourceRevision.source.name
self.updateDeviceName(name)
self.animateHeart()
}
}
func updateDeviceName(deviceName: String) {
deviceLabel.setText(deviceName)
}
func animateHeart() {
self.animateWithDuration(0.5) {
self.heart.setWidth(60)
self.heart.setHeight(90)
}
let when = dispatch_time(DISPATCH_TIME_NOW, Int64(0.5 * double_t(NSEC_PER_SEC)))
let queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
dispatch_after(when, queue) {
dispatch_async(dispatch_get_main_queue(), {
self.animateWithDuration(0.5, animations: {
self.heart.setWidth(50)
self.heart.setHeight(80)
})
})
}
} }
To summarize, the unexpected observations are:
1. The time I monitor the heart rate contributes to the green ring in the activity app.
2. Unexpected high amount of calories are being recorded i.e. when the person is on bed or asleep!
Can you please help with the correct code that helps me to monitor and display a person's heart beat at regular interval during his sleep without contributing to the green ring or contributing extra cals. ?
Thanks a lot in advance!
Starting a workout and running the heart rate monitor will drain the apple watch's battery after about 6 hours (if it has a full charge), so having it run continuously while sleeping is probably not realistic at this time.
From what I can tell, starting a workout using workoutSession does 2 things for your app. It keeps your app in the foreground, and it starts taking heart rate sample every few seconds. Have you considered not starting it? Your health kit queries will still work as is and the heart rate monitor still records the users heart rate every 15 minutes or so. The main thing you loose is keeping your app in the foreground, and I am wondering if you need to do that (since the user will be asleep).
To retrieve the last heart rate sample from healthkit:
func getLatestHeartRate() {
let quantityType = HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierHeartRate)!
let sortDescriptor = NSSortDescriptor(key:HKSampleSortIdentifierStartDate, ascending: false)
let sampleQuery = HKSampleQuery(sampleType: quantityType, predicate: nil, limit: 1, sortDescriptors: [sortDescriptor])
{ (sampleQuery, results, error ) -> Void in
}
self.healthStore.executeQuery(sampleQuery)
}
I'm running below function in my iPad app to get an Item by it's hash key (IdName).
This table only contains an Hashkey (No Range key available) but when I run this, It returns an result object which contains only the HashKey Value (The same which I pass). The other property (IdValue) is not there in the result. What am I doing wrong here?
func getCurrentFinalReportNumber()->Int
{
let dynamoDBObjectMapper = AWSDynamoDBObjectMapper.defaultDynamoDBObjectMapper()
var currentId :Int = -1
dynamoDBObjectMapper .load(DDBIDStoreTableRow.self, hashKey: "FinalReportNumber", rangeKey: nil) .continueWithExecutor(AWSExecutor.mainThreadExecutor(), withBlock: { (task:AWSTask!) -> AnyObject! in
if (task.error == nil) {
if (task.result != nil) {
let resultRow :DDBIDStoreTableRow = task.result as! DDBIDStoreTableRow
print(resultRow.IdValue)
currentId = Int(resultRow.IdValue!)
}
} else {
print("Error: \(task.error)")
let alert = SCLAlertView()
alert.addButton("Close", colorButtonBackground: Constants.InspectionTypes.colorClose) {}
alert.showCloseButton = false
alert.showError("", subTitle: (task.error?.description)!)
}
return nil
}).waitUntilFinished()
return currentId
}
class DDBIDStoreTableRow :AWSDynamoDBObjectModel ,AWSDynamoDBModeling {
var IdName:String? //HK
var IdValue:Int? = -1
class func dynamoDBTableName() -> String! {
return AWSIDStoreDynamoDBTableName
}
class func hashKeyAttribute() -> String! {
return "IdName"
}
//MARK: NSObjectProtocol hack
override func isEqual(object: AnyObject?) -> Bool {
return super.isEqual(object)
}
override func `self`() -> Self {
return self
}
}
Just found the mistake. Problem is in the data type of the mapping class. Earlier it was
var IdValue:Int? = -1
But once I change the Int to NSNumber as below. It started to work fine
var IdValue:NSNumber? = -1