"An error occurred with the `activeEnergyQuery`. The error was: Authorization not determined." - swift

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:.

Related

Is it possible to use the beginBackgroundTask() API within SwiftUI lifecycle?

I need to run some code when the app is closed to remove the client from a game. To do this I'm wanting to execute a Google Cloud Function for the server to do the cleanup - the function works, I guess similar to this question I just do not have enough time, and I'm running a completion handler so it's not like iOS thinks the function is finished straight away.
I have seen multiple questions on this, many of which are rather old and do not include answers for the SwiftUI Lifecycle. I have seen this exact issue and a potential answer here, however I'm not using the Realtime Database, I'm using Firestore so there is no equivalents for the onDisconnect methods.
I have seen that you can increase the time you need when the application finishes through beginBackgroundTask(expirationHandler:), I just can't find anywhere to state this can be done through SwiftUI Lifecycle, what I have so far:
.onReceive(NotificationCenter.default.publisher(for: UIApplication.willTerminateNotification), perform: { output in
Backend().removeFromGame(gameCode: otp, playerName: "name", completion: { res, error in
if error != nil{
print(error)
}
})
})
The function called is as follows:
func removeFromGame(gameCode: String, playerName: String, completion: #escaping (Bool?, Error?) -> Void){
Functions.functions().httpsCallable("removeFromGame").call(["gameCode": gameCode, "playerName": playerName]){ result, error in
if let error = error as NSError? {
if error.domain == FunctionsErrorDomain{
_ = FunctionsErrorCode(rawValue: error.code)
let errorDesc = error.localizedDescription
_ = error.userInfo[FunctionsErrorDetailsKey]
print(errorDesc)
}
}else{
print("Removed successfully")
}
}
}
I have seen in this Apple doc how to use the API:
func sendDataToServer( data : NSData ) {
// Perform the task on a background queue.
DispatchQueue.global().async {
// Request the task assertion and save the ID.
self.backgroundTaskID = UIApplication.shared.
beginBackgroundTask (withName: "Finish Network Tasks") {
// End the task if time expires.
UIApplication.shared.endBackgroundTask(self.backgroundTaskID!)
self.backgroundTaskID = UIBackgroundTaskInvalid
}
// Send the data synchronously.
self.sendAppDataToServer( data: data)
// End the task assertion.
UIApplication.shared.endBackgroundTask(self.backgroundTaskID!)
self.backgroundTaskID = UIBackgroundTaskInvalid
}
}
Just cannot seem to implement it correctly within the new way of getting these system notifications?

Cannot receive results from Square Point of Sale in a small Swift app

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

Swift. Can't receive information from personal google drive using google drive api

Hi I'm trying to se what files I have stored on my google drive. The sign in process works without problems, but as soon as I try to request the list of files on my google drive I get this error:
Error Domain=com.google.GTLRErrorObjectDomain Code=403 "Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup."
I follow google drives documentation for installing it, but no matter what I do I get that error over and over again.
Here is what my viewDidLoad looks like:
var error: NSError?
GGLContext.sharedInstance().configureWithError(&error)
if error != nil {
print(error!)
return
}
GIDSignIn.sharedInstance().uiDelegate = self
GIDSignIn.sharedInstance().delegate = self
GIDSignIn.sharedInstance().scopes = scopes
let googleSignIn = GIDSignInButton(frame: CGRect(x: 0, y: 0, width: 100, height: 50))
googleSignIn.center = view.center
view.addSubview(googleSignIn)
This is the function for when the user succesfully signed in:
func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error!) {
if error != nil {
print(error)
return
}
print("\n")
print(user.profile.email)
print("\n")
let query = GTLRDriveQuery_FilesList.query()
query.pageSize = 10
service.executeQuery(query, delegate: self, didFinish: #selector(displayResultWithTicket(ticket:finishedWithObject:error:))
)
}
I does print out my email correctly, so I know it is logged in, and right after if prints my email the error appears.
This is the displayResultWithTicket function:
#objc func displayResultWithTicket(ticket: GTLRServiceTicket,
finishedWithObject result : GTLRDrive_FileList,
error : NSError?) {
if let error = error {
print(error)
return
}
if let files = result.files, !files.isEmpty {
print("Files:\n")
for file in files {
print(file.name!)
print(file.identifier!)
}
} else {
print("No files found")
}
}
Any help is appreciated!
To explain about your error, this documentation state that you have reached Google Drive APIs max request rate. Each limits varies on the kind of request you use. It was suggested to Batch the request.
Also, make sure that the status of Google+ API was turned ON. Wait for a few minutes after turning ON ensure to get a fresh token before trying again.
For reference on this error, you can check out this SO post.
Keep getting a “Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup” when attempting to google plus login on my web app
Daily Limit for Unauthenticated Use Exceeded. Continued use requires signup error

CKContainer.discoverAllIdentities always fails

The CKContainer.discoverAllIdentities request always fails in my CloudKit app. It has continually failed over the course of several days.
A simplified version of the code that is failing (which results in the same error) is:
private func getContacts(completion: (([CKUserIdentity]?) -> Void)?) {
container.status(forApplicationPermission: .userDiscoverability) { [weak self] status, error in
if let error = error {
print(error)
}
switch status {
case .granted:
self?.discover(completion: completion)
default:
print("status not granted")
}
}
}
private func discover(completion: (([CKUserIdentity]?) -> Void)?) {
let op = CKDiscoverAllUserIdentitiesOperation()
op.qualityOfService = .userInitiated
op.discoverAllUserIdentitiesCompletionBlock = { error in
if let error = error {
print(error)
}
}
op.userIdentityDiscoveredBlock = { identity in
print(identity)
}
op.start()
}
It results in an error being passed to the op.discoverAllUserIdentitiesCompletionBlock. The description of the error in the log is:
<CKError 0x1c4a51a60: "Server Rejected Request" (15/2000); server message = "Internal server error"; uuid = F67453B9-712D-4E5E-9335-929123E3C978; container ID = "iCloud.com.huntermaximillionmonk.topdraw">
Previously, this operation would work, but only for certain iCloud users. Now it's not for both of my test users.
Problem:
This was a problem in iOS 11.0
Based on my testing:
This works ok in Xcode 9.2 / iOS 11.2.1 on the device (not simulator)
After resetting the simulator works for the first time, doesn't work subsequently, however on the device it works repeatedly.
Code:
let queue = OperationQueue()
func requestPermissions(for permissions: CKApplicationPermissions,
completionHandler: #escaping (CKApplicationPermissionStatus, Error?) -> ()) {
CKContainer.default().requestApplicationPermission(permissions) { status, error in
if let error = error {
print("Error for requesting \(permissions) - \(error)")
}
let statusMessage : String
switch status {
case .granted:
statusMessage = "Granted"
case .denied:
statusMessage = "Denied"
case .couldNotComplete:
statusMessage = "Could not complete"
case .initialState:
statusMessage = "Initial state"
}
print("Permission - \(statusMessage)")
completionHandler(status, error)
}
}
private func discoverAllUsers() {
let operation = CKDiscoverAllUserIdentitiesOperation()
operation.userIdentityDiscoveredBlock = { userIdentity in
print("userIdentity = \(userIdentity)")
}
operation.discoverAllUserIdentitiesCompletionBlock = { error in
if let error = error {
print("Discover all users Error: \(error) ")
}
else {
print("Discover all users completed successfully")
}
}
queue.addOperation(operation)
}
Edit:
Apple fixed this issue day after this answer was posted, coincidence?! I don't think so :)
This is not actually the answer to the question, but a fix that helped me to cross over this error. It will require you to change your app UI interaction and add ContactsUI framework to your project, moreover your user will be responsible for selecting a contact with iCloud related email.
Good news is that the method discoverUserIdentity is still works. So, you can use it to get CKUserIdentity from manually selected contact.
func addContact(_ contact:CNContact) {
var lookUpEmails = [CKUserIdentityLookupInfo]()
for email in contact.emailAddresses {
lookUpEmails.append(CKUserIdentityLookupInfo(emailAddress: (email.value as String)))
}
let checkUserOperation = CKDiscoverUserIdentitiesOperation()
checkUserOperation.userIdentityLookupInfos = lookUpEmails
checkUserOperation.userIdentityDiscoveredBlock = { [unowned self] (identity, info) -> Void in
if identity.hasiCloudAccount {
if let recordID = identity.userRecordID {
//do something with discovered user
}
checkUserOperation.cancel()
}
}
checkUserOperation.queuePriority = Operation.QueuePriority.high
CKContainer.default().add(checkUserOperation)
}
It might sound useless, but in my case, it helped me to solve the Server Rejected Request" (15/2000) error, to fix one of the features of my app and continue to use the other feature related code with less efforts than I thought.
I hope someone will find this helpful.
Just another data point on this that might help with the overall picture. I was still seeing this error on 11.2.5 when I used my own iCloud AppleID (with hundreds of contacts) while running a Test App that called discoverAllIdentitiesWithCompletionHandler. I'd get the dreaded
CKError 0x1c0051730: "Server Rejected Request" (15/2000); server message = "Internal server error".
When I switched to run the exact same code on my daughters iOS11.2.5 device (with just a handful of contacts) the code worked fine.
Leads me to believe there is some rate limiting going on when there are a lot of contacts with iOS11.
(P.S. No errors at all running on iOS10)

Problems accessing Calendar using EKEventStore on OSX Sierra with Swift 3

This appears very simple, but I've been struggling for several days to get access to the Calendar on OSX. I have switched on the App Sandbox capability, and I've ticked the "Calendar" box in App Data. I have created very simple app with the following view controller class:
import Cocoa
import EventKit
class ViewController: NSViewController {
var eventControl = EKEventStore()
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override var representedObject: Any? {
didSet {
// Update the view, if already loaded.
}
}
}
As you can see, the only lines of code I've added are to import EventKit and to initialise eventControl.
When I run this in debug, I get an error at the eventControl initialisation line
2016-10-28 15:02:00.056521 calendarTest[4105:847101] CoreData: XPC: Unable to load metadata: Error Domain=NSCocoaErrorDomain Code=134070 "An error occurred in the persistent store." UserInfo={Problem=request failed, insufficient permission}
2016-10-28 15:02:00.057742 calendarTest[4105:847101] [error] error: -addPersistentStoreWithType:NSXPCStore configuration:(null) URL:file:///Users/patrickramsden/Library/Calendars/Calendar%20Cache options:{
NSInferMappingModelAutomaticallyOption = 1;
NSMigratePersistentStoresAutomaticallyOption = 1;
agentOrDaemon = 1;
serviceName = "com.apple.CalendarAgent.database";
} ... returned error Error Domain=NSCocoaErrorDomain Code=134070 "An error occurred in the persistent store." UserInfo={Problem=request failed, insufficient permission} with userInfo dictionary {
Problem = "request failed, insufficient permission";
}
I can't work out how to get the right permissions.
I am using Xcode 8.1 and macOS Sierra 10.12.1
You need to add a usage description to your info.plist. This is a small description of why you need access to the services that is presented to the user.
<key>NSCalendarsUsageDescription</key>
<string>Description of why you need access to the Calendar</string>
Has anyone else figured this out?
I am running into the same issue, however trying to access anything in the event Store returns nil. Even though "access is granted". Such as:
let eventStore = EKEventStore()
switch EKEventStore.authorizationStatus(for: .event) {
case .authorized:
print("Access Granted")
break
case .denied:
print("Access denied")
case .notDetermined:
eventStore.requestAccess(to: .event, completion:
{(granted: Bool, error: Error?) -> Void in
if granted {
print("Access Granted")
} else {
print("Access denied")
}
})
break
default:
print("Case Default")
break
}
print(eventStore.calendars(for: .event))
Getting the same messages: How to prevent EventStore access error on first run
However the access is working correctly once you start the application again after the user grants permission! My app is not sandboxed. Have you set the "Privacy - Calendars Usage Description" key in your plist?