I'm having trouble getting my app approved, and to be honest, I'm having trouble understanding what Apple are on about in this case.
My app only uses HealthKit to create, pause, and finish a workout. It does not read any data.
From Apple:
"Regarding the 2.1 issue, your app displays a window on iOS shortly after launching the watch app to start the process to allow your app to integrate with the Health app. No action takes place after tapping on “Open [AppName].” It would be appropriate to ensure your app displays the Health UI to allow permission to access the Health app."
The problem appears to be with my watchOS app not asking for permission to use the Health app. This is the code I'm using to do so:
In my ExtensionDelegate:
WorkoutManager.sharedManager.requestAuthorisation()
The requestAuthorisation function in my WorkoutManager:
//request workout authorisation only
func requestAuthorisation() {
let writeTypes: Set<HKSampleType> = Set([ HKObjectType.workoutType() ]) //to write a workout
healthStore.requestAuthorization(toShare: writeTypes, read: nil) { (success, error) in
if error != nil {
self.debug.log(tag: "WorkoutManager", content: "Error requesting HealthStore authorisation: \(error!.localizedDescription)")
}
}
}
Also, my iOS app does not use HealthKit (at least at the moment), so I'm not doing anything with HealthKit in the iOS app. Only the watchOS app uses HealthKit.
When you first run the app on the watch, it prompts you to allow authorisation to access the Health app. An accept or deny pop up appears on the iPhone, where you can choose. I thought this is what they were wanting? Surely they don't want me to custom write some "do you want to allow access to the Health app" view controller?
Apple are very strict about their apps, so you will need to have as many details as you can , when the pop up opens it needs to ask the user explicitly if they want to allow it to communicate with the health app
Related
This is my code for Requesting Review :
if #available(iOS 10.3, *) {
SKStoreReviewController.requestReview()
}
else{
print("Review is not available with in the app")
}
In Development Mode it is working properly & I am able to get PopUp like this:
But In Live app downloaded from appstore, App isnot showing this ratings Popup and nothing happens if user taps out on Ratings Button.
From the documentation:
Although you should call this method when it makes sense in the user experience flow of your app, the actual display of a rating/review request view is governed by App Store policy. Because this method may or may not present an alert, it's not appropriate to call it in response to a button tap or other user action.
(Highlight mine)
If you have a Ratings Button like you said in your question, you should not expect it to show the prompt.
The prompt will only show up if:
The user hasn't disabled Review Prompts in Settings.
The prompt has been shown to the user 3 times or less in a year.
If you must request a review upon user interaction, you must direct your users to the App Store page of your app instead, using code like this (taken from Requesting App Store Reviews Sample Code):
#IBAction func requestReviewManually() {
// Note: Replace the XXXXXXXXXX below with the App Store ID for your app
// You can find the App Store ID in your app's product URL
guard let writeReviewURL = URL(string: "https://itunes.apple.com/app/idXXXXXXXXXX?action=write-review")
else { fatalError("Expected a valid URL") }
UIApplication.shared.open(writeReviewURL, options: [:], completionHandler: nil)
}
I want to develop an app that detecting the user's moving way (walking, cycling, driving etc...) and send a specific UILocalNotification for each activity type.
My question is: is it possible to detect it on the background (when the app is completely closed) without draining the device's battery? What will be the best way to do it?
Thank you!
There is coprocessor m7(+) in iPhones upper 5s.
It gives you possibility to get device motion.
Just
import CoreMotion
in your file.
Create a CMMotionActivityManager object:
let motionActivityManager = CMMotionActivityManager()
Check if it`s available on your device:
motionActivityManager.isActivityAvailable()
Use this method:
motionActivityManager.startActivityUpdates(to: OperationQueue.main) { (activity) in
if (activity?.automotive)! {
print("User using car")
}
if (activity?.cycling)! {
print("User is cycling")
}
if (activity?.running)! {
print("User is running")
}
if (activity?.walking)! {
print("User is walking")
}
if (activity?.stationary)! {
print("User is standing")
}
if (activity?.unknown)! {
print("Unknown activity")
}
}
It would return you types of user activity.
Regarding the user activity which can be handled in background tasks are the below once which does not mention about (walking, cycling,driving etc...)
Implementing Long-Running Background Tasks
For tasks that require more execution time to implement, you must request specific permissions to run them in the background without their being suspended. In iOS, only specific app types are allowed to run in the background:
Apps that play audible content to the user while in the background,
such as a music player app
Apps that record audio content while in the background.
Apps that keep users informed of their location at all times, such as
a navigation app Apps that support Voice over Internet Protocol
(VoIP)
Apps that need to download and process new content regularly
Apps that receive regular updates from external accessories
Yes it´s possible to do that!
If your iOS app must keep monitoring location even while it’s in the
background, use the standard location service and specify the location
value of the UIBackgroundModes key to continue running in the
background and receiving location updates. (In this situation, you
should also make sure the location manager’s
pausesLocationUpdatesAutomatically property is set to YES to help
conserve power.) Examples of apps that might need this type of
location updating are fitness or turn-by-turn navigation apps.
Read more here.
Here is a question that I know has an answer since I see apps that do this functionality. I have tried (writing directly, using background fetch) but nothing works. I found an app currently on the app store with the functionality that I am looking for. With Background Fetch set to OFF and main app NOT running in background. I go to the Widget and add an item. I open HealthKit and I see the data there as expected.
I would like to do the same for my app. I would like my today extension (widget) and/or WatchKit extension to write to the HealthKit store even when app is not running in background.
Like I said I have tested an app that does this functionality even though in Apple documentation it says this:
The HealthKit store can only be accessed by an authorized app. You
cannot access HealthKit from extensions (like the Today view) or from
a WatchKit app.
Because the HealthKit store is encrypted, your app cannot read data
from the store when the phone is locked. This means your app may not
be able to access the store when it is launched in the background.
However, apps can still write data to the store, even when the phone
is locked. The store temporarily caches the data and saves it to the
encrypted store as soon as the phone is unlocked.
Any answers or insights are appreciated. Thanks everybody.
The Health Data Store is indeed encrypted while the device is locked. Locked is defined as requiring a passcode on the device and the screen was turned off (so a passcode or touch id is required before you can get back to the main screen). While the store is encrypted it is not possible to read any data from it, no matter if the app is running in the background or not. Even setting up observer queries while the app is running will not allow it to continue to be read from. I imagine this level of protection is done simply using the Data Protection capability with the NSFileProtectionComplete option.
What HealthKit functionality have you observed in this other app? If it was displaying step and distance data, then they are likely getting this data directly from the pedometer (CMPedometer), which is not restricted when the device is locked.
Lehn0058's comment about authorization was correct. I had to request authorization explicitly from the WatchKit and Today Extension even though authorization was already given in the app. Afterwards both are able to write to the Health Store. The comment from Apple above only has to do with Reading from the Health Store and NOT writing to the Health Store. Here is some sample code for anybody else who gets in to the same problem. Thanks again.
In WatchKit InterfaceController.m
- (void)awakeWithContext:(id)context {
[super awakeWithContext:context];
// Configure interface objects here.
[[HealthKitManager sharedManager] requestHealthKitAccess];
}
In Today Extension TodayViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
[[HealthKitManager sharedManager] requestHealthKitAccess];
}
We are using Google Analytics, and want to know how many of our users are in possession of an AppleWatch. I have searched Stack for answers, and the recurring answer is to use this:
if WCSession.isSupported() { // check if the device support to handle an Apple Watch
let session = WCSession.defaultSession()
session.delegate = self
session.activateSession() // activate the session
if session.paired { // Check if the iPhone is paired with the Apple Watch
// Do stuff
}
}
The problem with this is that it prompts the user to 'accept' the app on the AppleWatch. Granted, the if-statement is true wether or not the user accepts, but I don't want the user to get their hopes up, thinking the app supports AppleWatch. I simply want to know if the user has an AppleWatch, I don't want to use it. Yet.
Is there a property on the iOS-device that can be accessed to show if the user has or ever had an AppleWatch connected, without prompting the user through the Watch?
Probably you can use (Push)Notifications, since there is no need to develop a native applewatch app to receive notifications on the watch. For example i receive "whats app messages" on my watch, but it does not have any native app on the watch either.
In your watchkit extension ExtensionDelegate.m you can handle provided answers to the push message separately. Link to Apple
This would be a(nother) approach, where you have to be creative!
I'm creating a cloudkit app, and have been trying multiple ways to get the NSUbiquityIdentityDidChangeNotification, but I never am able to get this notification.
I've tried both of these code versions under the delegate didFinish and the viewDidLoad methods. And I tried calling it from another notification - UIApplicationDidBecomeActiveNotification. I also put import Foundation at top of files.
Here's the basic code I've tried:
NSNotificationCenter.defaultCenter().addObserver(self,
selector: "handleIdentityChanged:",
name: NSUbiquityIdentityDidChangeNotification,
object: nil)
// And this one I tried too from another post here on SO:
var localeChangeObserver = NSNotificationCenter.defaultCenter().addObserverForName(NSUbiquityIdentityDidChangeNotification, object: nil, queue: NSOperationQueue.mainQueue()) { _ in
println("The user’s iCloud login changed: should refresh all user data.")
}
Does anyone know how to get this notification to work for only a cloudkit app in swift? I really just want to detect the iCloud status change and then initiate fetching the userID if there's been a change.
Not that I need to access the ubiquityIdentityToken, but I was wondering why not store the token and every-time the app starts compare the current token with the one in local storage to see if it's a different account or nil? Therefore, why is getting the notification necessary?
Also, the code for getting the token only seems to work if I turn on "iCloud Documents", which I don't need. Does anyone know the implications of having that turned on for a social app that doesn't need it? And is there another way to get the token without enabling iCloud Documents?
This is the code I used to get token and placed in the delegate didFinish method, but only works if iCloud documents is turned on:
var token = NSFileManager.defaultManager().ubiquityIdentityToken
println("token is \(token!)")
On iOS, when I sign out of iCloud, my app is killed. So there seems not really to be a need to receive a NSUbiquityIdentityDidChangeNotification. Like you have said, it seems to be sufficient to compare the current token to the saved token.
On the Apple TV though, my app was not killed when I logged out of iCloud. Here I had noticed the notification was not fired, like you described. Since the app is not killed, a notification would be in order. (Did Apple forget to kill apps on Apple TV when iCloud account is changed?)
With Apple TV there is no iCloud documents container available (unless I explicitly share one from an iOS app). I found that on the dev center website, for the app identifier, iCloud was shown as "Configurable" and not "Enabled" if no document container was selected. I wonder if this has an effect on receiving notifications.
Both on the Apple TV and iOS, I can also confirm that the iCloud token is nil when not using documents (here: key-value-store only). Now that makes it difficult for Apple TV apps (because the app is not killed on iCloud account change, like on iOS) to detect account changes.
I have just noticed that my Apple TV app does received several NSUbiquitousKeyValueStoreDidChangeExternallyNotification when I log into another iCloud account, to reflect the changes. I guess this is as good as it gets. These notifications come with the NSUbiquitousKeyValueStoreChangeReasonKey key in userInfo, and a value of NSUbiquitousKeyValueStoreAccountChange indicates the account has changed.
Sorry for not being able to provide a direct solution, maybe it helped to share my experience.
To be notified in iOS when a user logs in or out of iCloud while using your app, use CKAccountChangedNotification instead of NSUbiquityIdentityChanged notification.
(Longer explanation: https://stackoverflow.com/a/38689094/54423.)