Read Blood group, Age, Gender etc from HealthKit in Swift - ios

I am trying to read Personal Details (Blood group, Age, Gender) of Healthkit but unable to request for that.
As per Apple Doc here:
HealthKit provides five characteristic types: biological sex, blood
type, birthdate, Fitzpatrick skin type, and wheelchair use. These
types are used only when asking for permission to read data from the
HealthKit store.
But i can't make add HKCharacteristicType in authorisation request.
I have run Apple Sample Project which requests for:
HKQuantityTypeIdentifier.stepCount.rawValue,
HKQuantityTypeIdentifier.distanceWalkingRunning.rawValue,
HKQuantityTypeIdentifier.sixMinuteWalkTestDistance.rawValue
But when I add
HKCharacteristicTypeIdentifier.bloodType.rawValue
HKCharacteristicTypeIdentifier.dateOfBirth.rawValue
The permission screen does not asks for DOB and Blood Type. See Image:
Configuration: Simulator iOS 15.4 and Xcode 13.3
Anyone knows that if we can access Personal Data of HealthKit or not. Please help me out.

This is happening because bloodType and dateOfBirth are of type HKCharacteristicType
when you call this, the compactMap operation will not include your types
private static var allHealthDataTypes: [HKSampleType] {
let typeIdentifiers: [String] = [
HKQuantityTypeIdentifier.stepCount.rawValue,
HKQuantityTypeIdentifier.distanceWalkingRunning.rawValue,
HKQuantityTypeIdentifier.sixMinuteWalkTestDistance.rawValue,
HKCharacteristicTypeIdentifier.bloodType.rawValue,
HKCharacteristicTypeIdentifier.dateOfBirth.rawValue
]
return typeIdentifiers.compactMap { getSampleType(for: $0) }
}
check getSampleType:
func getSampleType(for identifier: String) -> HKSampleType? {
if let quantityType = HKQuantityType.quantityType(forIdentifier: HKQuantityTypeIdentifier(rawValue: identifier)) {
return quantityType
}
if let categoryType = HKCategoryType.categoryType(forIdentifier: HKCategoryTypeIdentifier(rawValue: identifier)) {
return categoryType
}
return nil
}
your types won't fall into any of these if let, so this function will return nil. You must change the code so you are able to use HKCharacteristicTypeIdentifier as well.
EDIT: An easy way to do this is changing the readDataTypes in HealthData class to:
static var readDataTypes: [HKObjectType] {
return allHealthDataTypes + [
HKObjectType.characteristicType(forIdentifier: .dateOfBirth)!,
HKObjectType.characteristicType(forIdentifier: .bloodType)!
]
}

You can only request read authorization for the HKCharacteristicTypes, not share authorization. Update your code to add these 2 data types only to the readDataTypes variable. Right now you are requesting both read & share for the characteristic types, which is why they are not appearing on the authorization sheet.

Related

Apple Pay without Stripe iOS SDK

I've been using Stripe iOS SDK for a while now and everything is clear regarding the implementation. Since our app is going to support App Clips on iOS 14, we are reducing the binary size and therefore decided to remove Stripe iOS SDK as well.
So my question here is if I can somehow send payment requests via the API and omitting the Stripe SDK altogether?
p.s.: It looks like I need to implement the /tokens endpoint passing the card data. Is there any example of the request to be made?
I've managed to solve this situation and here is the solution if anyone is interested. Here are the steps to make this happen:
Prepare a request model
import Foundation
import PassKit
struct StripeTokenRequest: Encodable {
let pkToken: String
let card: Card
let pkTokenInstrumentName: String?
let pkTokenPaymentNetwork: String?
let pkTokenTransactionId: String?
init?(payment: PKPayment) {
guard let paymentString = String(data: payment.token.paymentData, encoding: .utf8) else { return nil }
pkToken = paymentString
card = .init(contact: payment.billingContact)
pkTokenInstrumentName = payment.token.paymentMethod.displayName
pkTokenPaymentNetwork = payment.token.paymentMethod.network.map { $0.rawValue }
pkTokenTransactionId = payment.token.transactionIdentifier
}
}
extension StripeTokenRequest {
struct Card: Encodable {
let name: String?
let addressLine1: String?
let addressCity: String?
let addressState: String?
let addressZip: String?
let addressCountry: String?
init(contact: PKContact?) {
name = contact?.name.map { PersonNameComponentsFormatter.localizedString(from: $0, style: .default, options: []) }
addressLine1 = contact?.postalAddress?.street
addressCity = contact?.postalAddress?.city
addressState = contact?.postalAddress?.state
addressZip = contact?.postalAddress?.postalCode
addressCountry = contact?.postalAddress?.isoCountryCode.uppercased()
}
}
}
Use JSONEncoder and set keyEncodingStrategy to .convertToSnakeCase.
Create a POST request against https://api.stripe.com/v1/tokens endpoint where you need to url encode parameters. If you are using Alamofire, you need to set encoding to URLEncoding.default.
Parse response. I use JSONDecoder with the following model:
import Foundation
struct StripeTokenResponse: Decodable {
let id: String
}
Create a payment
StripeTokenResponse.id is the thing you need to pass to the backend where the payment will be processed. This is the same step as you'll do when using the SDK.
You can check Strip checkout, it allows you to present a payment page in web format without any Stripe SDK on the client side.

Using DynamoDB -> Resource not found

In order to set up a system on AWS where one can create and use user accounts from an iOS app, I recently followed this tutorial which uses AWSMobileClient, AWSAuthCore and AWSAuthUI.
I got up something working where I can create accounts and log in and out.
Now I would like to make use of DynamoDB to allow the user to store something. For that, I have tried to integrate DynamoDB code that I have working in another app. But obviously the two apps environment are not quite the same, so it does not work as I would like it to.
Here is the code for the DynamoDB data that I want to handle:
import Foundation
import AWSDynamoDB
#objcMembers
class DynamoDBData: AWSDynamoDBObjectModel, AWSDynamoDBModeling {
var _message,_timeStamp,_user: String?
class func dynamoDBTableName() -> String {
return "DynamoDBData"
}
class func hashKeyAttribute() -> String {
return "_timeStamp"
}
class func rangeKeyAttribute() -> String {
return "_user"
}
override class func jsonKeyPathsByPropertyKey() -> [AnyHashable: Any] {
return [
"_message" : "message",
"_timeStamp" : "timeStamp",
"_user" : "user"
]
}
}
And here is the code for where I try to save something to the DB and fail:
#objc func handleTap() {
print(#function)
let dynamoDbObjectMapper = AWSDynamoDBObjectMapper.default()
// Create data object using the data model:
let dataBlock = DynamoDBData()
dataBlock?._message = "message-TEST"
dataBlock?._timeStamp = "timeStamp-TEST"
dataBlock?._user = "user-TEST"
// Save the new item:
dynamoDbObjectMapper.save(dataBlock!, completionHandler: {
(error: Error?) -> Void in
if let error = error {
print("Amazon DynamoDB Save Error: \(error)")
return
}
print("An item was saved.")
})
}
Finally, this is the message I get showing a problem when trying to save data:
Amazon DynamoDB Save Error: Error Domain=com.amazonaws.AWSDynamoDBErrorDomain Code=19 "(null)"
UserInfo={__type=com.amazonaws.dynamodb.v20120810#ResourceNotFoundException,
message=Requested resource not found}
Since I have created a table DynamoDBData on the AWS console, I do not really understand why it says "Requested resource not found", but I guess I must have done something wrong at some point.
In case this can be useful, here is what appears in the AWS console for the table DynamoDBData:
Table name DynamoDBData
Primary partition key _timeStamp (String)
Primary sort key _user (String)
Point-in-time recovery DISABLED Enable
Encryption Type DEFAULT Manage Encryption
KMS Master Key ARN Not Applicable
Time to live attribute DISABLED Manage TTL
Table status Active
Creation date May 15, 2019 at 10:13:43 AM UTC+9
UTC: May 15, 2019 at 1:13:43 AM UTC
Local: May 15, 2019 at 10:13:43 AM UTC+9
Region (N. Virginia): May 14, 2019 at 8:13:43 PM UTC-5
Read/write capacity mode Provisioned
Last change to on-demand mode -
Provisioned read capacity units 5 (Auto Scaling Disabled)
Provisioned write capacity units 5 (Auto Scaling Disabled)
Last decrease time -
Last increase time -Storage size (in bytes) 0 bytes
Item count 0 Manage live count
Region US East (N. Virginia)
Amazon Resource Name (ARN) arn:aws:dynamodb:us-east-1:123456789012:table/DynamoDBData
Some guidance (even partial) on how to solve this issue would be very helpful.
One thing to double check: is the table's region the same region you're trying to access it in?
I noticed from a comment in your other question that the code was attempting to reach arn:aws:dynamodb:ap-northeast-1 but the above shows the table is actually in us-east-1.

How to check if my contact list members are registered with my app just like whatsapp does?

I am creating a chat app, for which I need to check each of my contact list members if they are registered with my app and show the registered members list in my app.
Currently I am calling an API to check every number one by one. In case of 800 contacts it is getting called 800 times. I know this is not best way to do it. So, can some one please help me out and suggest me to do it in better way?
Below is my code:
func createContactNumbersArray() {
for i in 0...self.objects.count-1 {
let contact:CNContact! = self.objects[i]
if contact.phoneNumbers.count > 0 {
for j in 0...contact.phoneNumbers.count-1 {
print((contact.phoneNumbers[j].value).value(forKey: "digits")!)
let tappedContactMobileNumber = (contact.phoneNumbers[j].value).value(forKey: "digits")
let phoneNo = self.separateISDCodeMobileNo(mobileNo:tappedContactMobileNumber as! String)
contactNumbersArray.append(phoneNo.1)
}
}
}
callGetAppUserDetailService(mobileNumber: self.contactNumbersArray[currentIndex] as! String)
}
I am doing this whole process in the background and refreshing the member list on front in current scenario.
I want to make the whole process as fast as Whatsapp.
There is no way to do it without back-end modifications. So you need to work closely with your back-end engineer and build an API for this.
Here is an advise how you can build this API:
To have 2 APIs:
1) Upload the whole user address book to the back-end in a single request, something like:
let addressBook = NSMutableOrderedSet()
let contact1 = AddressBookContact()
contact1.name = "Jony Ive"
contact1.phone = "1-800-300-2000"
addressBook.add(contact1)
let contact2 = AddressBookContact()
contact2.name = "Steve Why"
contact2.phone = "412739123123"
addressBook.add(contact2)
let deviceUDID = nil
Request.uploadAddressBook(withUdid: deviceUDID, addressBook: addressBook, force: false, successBlock: { (updates) in
}) { (error) in
}
2) As a next step - retrieve a list of already registered users with phones from your address book, something like this:
let deviceUDID = nil
Request.registeredUsersFromAddressBook(withUdid: nil, successBlock: { (users) in
}) { (error) in
}
so now you can show it in UI
Found this example here https://developers.connectycube.com/ios/address-book
In fact,you can do little on client.
The normal way is: Server provide an interface that support check a phonenumber array, and return the phonenumbers has registered on server.
I think you need to save user's phone number to your database when user registers your app. Then you can find out contacts which have already used your chat app.

Swift HealthKit update Birthday

I want to update the birthday in Apple Health. But I don't know how.
This is my authorization func:
private func requestAuthorisationForHealthStore() {
let dataTypesToWrite = [
HKCharacteristicType.characteristicTypeForIdentifier(HKCharacteristicTypeIdentifierDateOfBirth),
HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierBodyMass),
HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierHeight)
]
let dataTypesToRead = [
HKCharacteristicType.characteristicTypeForIdentifier(HKCharacteristicTypeIdentifierDateOfBirth),
HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierBodyMass),
HKQuantityType.quantityTypeForIdentifier(HKQuantityTypeIdentifierHeight)
]
self.healthStore?.requestAuthorizationToShareTypes(NSSet(array: dataTypesToWrite),
readTypes: NSSet(array: dataTypesToRead), completion: {
(success, error) in
if success { println("User completed authorisation request.") }
else { println("The user cancelled the authorisation request. \(error)") }
})
}
For requesting the birthday I call my function:
func requestAgeAndUpdate() {
var error: NSError?
let dob = self.healthStore?.dateOfBirthWithError(&error)
if error != nil {
println("There was an error requesting the date of birth: \(error)")
return
}
self.ageLabel.text = "\(dob)"
}
But how I can change/update the birthday programmatically?
Thanks for help!
You cannot change these characteristics programatically. The user must enter this data via the Health App.
From the documentation
The HKCharacteristicType class is a concrete subclass of the
HKObjectType class. HealthKit uses characteristic types to represent
data that does not typically change over time. Unlike the other object
types, characteristic types cannot be used to create new HealthKit
objects. Instead, users must enter and edit their characteristic data
using the Health app. Characteristic types are used only when asking
for permission to read data from the HealthKit store.
HealthKit Framework Reference;
https://developer.apple.com/library/ios/documentation/HealthKit/Reference/HealthKit_Framework/index.html#//apple_ref/doc/uid/TP40014707
HealthKit objects can be divided into two main groups: characteristics
and samples. Characteristic objects represent data that typically does
not change. This data includes the user’s birthdate, blood type, and
biological sex. Your application cannot save characteristic data. The
user must enter or modify this data using the Health app.

Access "My Info" from iOS app [duplicate]

Outside of asking the user to input their name, is there any way to get it off the device?
I tried this library, which attempts to extract the name from [UIDevice currentDevice] name], but that doesn't work in a lot of situations:
https://github.com/tiboll/TLLNameFromDevice
Is the user's name present in the phonebook or anywhere else that we have access to in iOS 6?
Well you could go through all the contacts in the AddressBook and see if any of them are marked with the owner flag.
Just be aware that doing this will popup the "this app wants access to the address book" message. Also Apple isn't very keen on these kind of things. In the app review guide it is specified that an app can not use personal information without the user's permission.
You could use Square's solution:
Get the device's name (e.g. "John Smith's iPhone").
Go through the contacts on the phone and look for a contact named "John Smith".
JBDeviceOwner and ABGetMe will both do this for you.
You could use CloudKit. Following a snippet in Swift (ignoring errors):
let container = CKContainer.defaultContainer()
container.fetchUserRecordIDWithCompletionHandler(
{
(recordID, error) in
container.requestApplicationPermission(
.PermissionUserDiscoverability,
{
(status, error2) in
if (status == CKApplicationPermissionStatus.Granted)
{
container.discoverUserInfoWithUserRecordID(
recordID,
completionHandler:
{
(info, error3) in
println("\(info.firstName) \(info.lastName)")
}
)
}
}
)
}
)
The above code was based on the code at http://www.snip2code.com/Snippet/109633/CloudKit-User-Info
to save folks time. in swift4:
let container = CKContainer.default()
container.fetchUserRecordID(
completionHandler: {
(recordID, error) in
guard let recordID = recordID else {
return
}
container.requestApplicationPermission(
.userDiscoverability,
completionHandler: {
(status, error2) in
if (status == CKContainer_Application_PermissionStatus.granted)
{
if #available(iOS 10.0, *) {
container.discoverUserIdentity(withUserRecordID:
recordID,
completionHandler:
{
(info, error3) in
guard let info = info else {
return
}
print("\(info.firstName) \(info.lastName)")
}
)
}
}
}
)
}
)
however: CKUserIdentity no longer exposes either first or last name
So this answer no longer works.
You can use:
NSLog(#"user == %#",[[[NSHost currentHost] names] objectAtIndex:0]);
I did receive compiler warnings that the methods +currentHost and -names were not found. Given the warning, I’m not sure of Apple’s intention to make this available (or not) as a publicly accessible API, however, everything seemed to work as expected without the need to include any additional header files or linking in additional libraries/frameworks.
Edit 1:
You may also take a look at this Link
Edit 2:
If you have integrated your app with Facebook you can easily retrieve the user info, see Facebook Fetch User Data
For SWIFT you can use
NSUserName() returns the logon name of the current user.
func NSUserName() -> String

Resources