Can I add Patients health records like lab reports etc. to apple health app using health kit in swift? So far what i understand is that you can only read the health record data but you can not write it from your own health app. So what will I have to do to be able to add data in health section in apple's health app.
I have tried to look on the apple's documentation but there is no way to add health records.
I might be misunderstanding what you want to do, but you want to add data using HealthKit right?
Here's a bit of sample code I found for step data.
let healthStore = HKHealthStore()
// Request authorization to write step count data
let stepCountType = HKQuantityType.quantityType(forIdentifier: .stepCount)!
healthStore.requestAuthorization(toShare: [stepCountType]) { (success, error) in
if success {
// Create a new HKQuantitySample with the step count data
let quantity = HKQuantity(unit: HKUnit.count(), doubleValue: 1000)
let sample = HKQuantitySample(type: stepCountType, quantity: quantity, start: Date(), end: Date())
// Save the new HKQuantitySample to the Health app
healthStore.save(sample) { (success, error) in
if success {
print("Step count saved to Health app.")
} else {
print("Error saving step count to Health app: \(error!.localizedDescription)")
}
}
} else {
print("Error requesting authorization to write step count data: \(error!.localizedDescription)")
}
}
More docs at Apple.
The Health Records feature is read-only from a 3rd party API perspective. This data can only be added as a direct download from supported health systems. See here for more information: https://www.apple.com/healthcare/health-records/
Related
Today I worked for the first time with Apple Health Kit and successfully saved a workout in Health with the basic informations (activityType, start and end).
My app is in basic function an interval timer where you can create your own workout. Now in the next step I want to add the calories burned in the workout. This information is stored in the attribute 'totalEnergyBurned'.
Do I need to calculate this value myself or can I query this value directly if the user is wearing an Apple Watch? Or maybe the value is even automatically added to the workout if there is the corresponding record? (So far I have only tested the app in the simulator, which is why I can't answer this possibility).
My current code:
func saveToHealthKit(entryID: String){
if HKHealthStore.isHealthDataAvailable() {
let healthStore = HKHealthStore()
if(healthStore.authorizationStatus(for: HKObjectType.workoutType()) == .sharingAuthorized && healthStore.authorizationStatus(for: HKObjectType.quantityType(forIdentifier: HKQuantityTypeIdentifier.activeEnergyBurned)!) == .sharingAuthorized){
let newWorkout = HKWorkout(activityType: HKWorkoutActivityType.highIntensityIntervalTraining, start: entry.date!, end: Date())
healthStore.save(newWorkout) { success, error in
guard success else {
// Perform proper error handling here.
return
}
// Add detail samples here.
}
}
}
}
}
Thanks :)
I am not sure I understood your case correctly, but if the user is using an Apple Watch and does a workout you can query HealthKit for an HKWorkout object, which holds a totalEnergyBurned attribute.
If you are not querying this HKWorkout object, then I believe you can query for the activeEnergyBurned quantity type using the start and end date-time of the user workout in the predicate.
I have written a small app in Swift using Xcode 12.5 by following the information and code samples provided here ... https://github.com/square/SquarePointOfSaleSDK-iOS
The app polls a server to see if there is a charge to be made. The output from the server is in JSON format. When a charge comes in, the JSON results are providing a customer id, amount to be charged, and a note to the Square Point of Sale SDK.
Using the SCCAPIRequest example from the GitHub page ...
// Replace with your app's URL scheme.
let callbackURL = URL(string: "<#T##Your URL Scheme##String#>://")!
// Your client ID is the same as your Square Application ID.
// Note: You only need to set your client ID once, before creating your first request.
SCCAPIRequest.setApplicationID(<#T##Application ID##String#>)
do {
// Specify the amount of money to charge.
let money = try SCCMoney(amountCents: 100, currencyCode: "USD")
// Create the request.
let apiRequest =
try SCCAPIRequest(
callbackURL: callbackURL,
amount: money,
userInfoString: nil,
locationID: nil,
notes: "Coffee",
customerID: nil,
supportedTenderTypes: .all,
clearsDefaultFees: false,
returnsAutomaticallyAfterPayment: false,
disablesKeyedInCardEntry: false,
skipsReceipt: false
)
// Open Point of Sale to complete the payment.
try SCCAPIConnection.perform(apiRequest)
} catch let error as NSError {
print(error.localizedDescription)
}
The app successfully switches to Square POS, displays the amount due, and knows which customer I am wanting to charge (via customer id). I can process the payment and Square POS switches back to my app just fine.
This is where I am running in to trouble. I am also using the UIApplication delegate method example on that same page. Under the comment "Handle a successful request" ...
func application(_ app: UIApplication, open url: URL, options: [UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
guard SCCAPIResponse.isSquareResponse(url) else {
return
}
do {
let response = try SCCAPIResponse(responseURL: url)
if let error = response.error {
// Handle a failed request.
print(error.localizedDescription)
} else {
// Handle a successful request.
}
} catch let error as NSError {
// Handle unexpected errors.
print(error.localizedDescription)
}
return true
}
I have added the following ...
print("Transaction successful: \(response)")
From what I understand, the response should include the transaction id, and anything that was passed along in the userInfoString. It appears that this code example isn't even firing when Square POS returns to my app. I cannot see anything in the Xcode console.
I have assigned a callback URL within Xcode using the documentation on the link above, and it's also added in the Square Developer Portal under the Point of Sale API.
What am I missing? Where should the UIApplication delegate method be placed, in AppDelegate.swift or should it reside in ViewController.swift, or somewhere else? Any insight would be greatly appreciated.
#ElTomato provided me with the hint that I needed to solve the problem I was having. I needed to delete SceneDelegate.swift, remove Application Scene Manifest from Info.plist, and remove some code from AppDelegate.swift
I found detailed instructions on THIS site ...
iOS 13: Swift - 'Set application root view controller programmatically' does not work
Thank you kindly for the fantastic help #ElTomato
I'm testing out the Kentico Cloud Swift SDK to return some 'article' content types (I have created two of them and they are published).
I am using the Boilerplate code as described here:
The result I get is : [Kentico Cloud] Getting items action has succeeded. Received nil items.
My code:
let client = DeliveryClient.init(projectId: <project id>, previewApiKey: <preview key>, secureApiKey: <secure key>, enableDebugLogging: true)
func getArticles(){
// Note: Using "items" as custom query returns all content items,
// but to map them to a single model, a filter is needed.
let customQuery = "items?system.type=article"
// More about strongly-typed models https://github.com/Kentico/cloud-sdk-swift#using-strongly-typed-models
client.getItems(modelType: Article.self, customQuery: customQuery) { (isSuccess, itemsResponse, error) in
if isSuccess {
// We get here and itemsResponse != nil but items == nil
if let articles = itemsResponse?.items {
for article in articles {
}
}
} else {
if let error = error {
print(error)
}
}
}
}
I believe this error message would appear before ObjectMapper is triggered to convert the JSON into Article objects. I could be wrong though.
Anyone have any ideas?
UPDATE
Interestingly, if I request a single article object like so ...
client.getItem(modelType: Article.self, itemName: <codename>) { (isSuccess, itemResponse, error) in
if isSuccess {
if let article = itemResponse?.item {
// Use your item here
}
} else {
if let error = error {
print(error)
}
}
}
... then it works. I get the Article object. It's just asking for all of the articles that fails.
I'm going to investigate the issue later today, however, from your description, it might be caused by the Delivery API item readiness delay - the project was not fully synced with the delivery API yet. After the publishing/unpublishing item or creating/generating the project, there might be a small delay in processing messages by Delivery API which could cause unavailability of item. This delay might be variable - from my experience, it may vary from a couple of seconds to 2-3 minutes. Nevertheless, I'm going to check it just to be sure. I'll keep you updated.
Edit: I'm pretty sure the project was not synced and processed on the Delivery API at the time you were requested the items. The API returned 200, which caused isSuccess in the callback to be true, however, there might have been none or just a subset of items available - I've reproduced this behavior (screenshot below), although it's by design (the content/messages in Event Hub must be processed asynchronously).
I've also suggested the improvement for Kentico Cloud's documentation to mention/explain the possible delay caused by processing events queue messages from Event Hubs.
Just to be sure - could you try it again with your getArticles custom query?
Edit2: Back to your question about the ObjectMapper. This is not an error just a debug message, however, there shouldn't be probably nil but 0 (zero) in the debug message. This message came from:
private func sendGetItemsRequest<T>(url: String, completionHandler: #escaping (Bool, ItemsResponse<T>?, Error?) -> ()) where T: Mappable {
sessionManager.request(url, headers: self.headers).responseObject { (response: DataResponse<ItemsResponse<T>>) in
switch response.result {
case .success:
if let value = response.result.value {
let deliveryItems = value
if self.isDebugLoggingEnabled {
print("[Kentico Cloud] Getting items action has succeeded. Received \(String(describing: deliveryItems.items?.count)) items.")
}
completionHandler(true, deliveryItems, nil)
}
case .failure(let error):
if self.isDebugLoggingEnabled {
print("[Kentico Cloud] Getting items action has failed. Check requested URL: \(url)")
}
completionHandler(false, nil, error)
}
}
}
Ok. This is very weird. After checking the API by requesting an individual item (see the update in the post above), and getting a result (woot). It now seems the original code (unchanged) now works.
I'm wondering if it takes a while for the data to propagate and be available in the API?
Who knows. Weird.
Pre-Condition : I have 10 Dogs stored in a Realm Server
Is there a way to know when the results are ready?
let usernameCredentials = SyncCredentials.usernamePassword(username: email, password: pass)
SyncUser.logIn(with: usernameCredentials,server: Utils.sharedInstance.serverURL) { user, error in
if error != nil {
// handle error
} else {
let config = Realm.Configuration(syncConfiguration: SyncConfiguration(user: user!, realmURL: Utils.sharedInstance.syncServerURL))
let realm = try! Realm(configuration : config)
let dogs = realm.objects(Dog.self)
print("I have : \(dogs.count) dogs")
// dogs count is 0 here
// ..............
// some time later i have the 10 Dogs
}
}
Unfortunately not at the moment. Realm's sync APIs and semantics right now are best suited for incremental sync use cases, but we're working on two features that should help address the use case you've shared.
The first is a "Download Realm" API which only makes the Realm available once its entire contents have been downloaded.
The second is "sync progress notifications", where you can register a progress update block to fire with information about how much 1) local data needs to be synced up and 2) remote data needs to be synced down.
I'm using Apple's demo HealthKit app called ActivityRings. I have set up the bundle identifiers and entitlements correctly. The iOS app and Watch Extension are working and it's recording data seemingly ok. It should be ok as I haven't touched any code.
However console log says, "An error occurred with the activeEnergyQuery. The error was: Authorization not determined."
As you can see in the reporting query and handler assignment Apple has written to print for this error.
I'd like to know what this is for. Is there broken functionality?
// Create a query to report new Active Energy Burned samples to our app.
let activeEnergyQuery = HKAnchoredObjectQuery(type: activeEnergyType, predicate: predicate, anchor: nil, limit: Int(HKObjectQueryNoLimit)) { query, samples, deletedObjects, anchor, error in
if let error = error {
print("An error occurred with the `activeEnergyQuery`. The error was: \(error.localizedDescription)")
return
}
// NOTE: `deletedObjects` are not considered in the handler as there is no way to delete samples from the watch during a workout.
guard let activeEnergySamples = samples as? [HKQuantitySample] else { return }
sampleHandler(activeEnergySamples)
}
// Assign the same handler to process future samples generated while the query is still active.
activeEnergyQuery.updateHandler = { query, samples, deletedObjects, anchor, error in
if let error = error {
print("An error occurred with the `activeEnergyQuery`. The error was: \(error.localizedDescription)")
return
}
// NOTE: `deletedObjects` are not considered in the handler as there is no way to delete samples from the watch during a workout.
guard let activeEnergySamples = samples as? [HKQuantitySample] else { return }
sampleHandler(activeEnergySamples)
}
currentQuery = activeEnergyQuery
healthStore.executeQuery(activeEnergyQuery)
}
func endWorkoutOnDate(endDate: NSDate) {
workoutEndDate = endDate
workoutButton.setTitle("Begin Workout")
activeEnergyBurnedLabel.setText("0.0")
if let query = currentQuery {
healthStore.stopQuery(query)
}
saveWorkout()
}
requestAuthorizationToShareTypes function
override func willActivate() {
// This method is called when watch view controller is about to be visible to user.
super.willActivate()
// Only proceed if health data is available.
guard HKHealthStore.isHealthDataAvailable() else { return }
// We need to be able to write workouts, so they display as a standalone workout in the Activity app on iPhone.
// We also need to be able to write Active Energy Burned to write samples to HealthKit to later associating with our app.
let typesToShare = Set([
HKObjectType.workoutType(),
HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierActiveEnergyBurned)!])
let typesToRead = Set([
HKObjectType.quantityTypeForIdentifier(HKQuantityTypeIdentifierActiveEnergyBurned)!])
healthStore.requestAuthorizationToShareTypes(typesToShare, readTypes: typesToRead) { success, error in
if let error = error where !success {
print("You didn't allow HealthKit to access these read/write data types. In your app, try to handle this error gracefully when a user decides not to provide access. The error was: \(error.localizedDescription). If you're using a simulator, try it on a device.")
}
}
}
AppDelegate.swift
import UIKit
import HealthKit
#UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?
let healthStore: HKHealthStore = HKHealthStore()
func applicationShouldRequestHealthAuthorization(application: UIApplication) {
healthStore.handleAuthorizationForExtensionWithCompletion { success, error in
if let error = error where !success {
print("You didn't allow HealthKit to access these read/write data types. In your app, try to handle this error gracefully when a user decides not to provide access. The error was: \(error.localizedDescription). If you're using a simulator, try it on a device.")
}
}
}
}
Have you setup your iOS app to handle the healthkit authorization from your watch app? When you request permission to use healthkit types from your Apple Watch, a permission dialog shows up on your iOS app. But, you need to tell your iOS app that you are expecting your apple watch to request it. You do this with the following code in your AppDelegate file:
func applicationShouldRequestHealthAuthorization(application: UIApplication) {
let healthStore = HKHealthStore()
healthStore.handleAuthorizationForExtensionWithCompletion { (success, error) -> Void in
//...
}
}
Note that data can get sent directly from the watch's sensors (like heart rate and calories burned) to healthkit without needing permission from your app. It sounds like your permission errors are because you are trying to read the data (which you don't have permission to do yet).
Your app needs to request authorization to read and write active energy samples. Until the user has chosen whether to authorize your app, authorization will be "not determined". See the HKHealthStore documentation for more information about requesting authorization with requestAuthorizationToShareTypes:readTypes:completion:.