iOS DeviceCheck API - GenerateToken gives error Code 0 - ios

I'm trying to implement the new iOS 11 DeviceCheck API (https://developer.apple.com/documentation/devicecheck), but token generation always fails. I've tried on simulator and iPhone SE, with wifi and mobile data. Apple ID in settings is my normal, non-sandbox account.
This is an existing app for an organization - from the docs it sounds like the only configuration requirement is to make sure App ID is set up in the apple developer portal.
Anyone else having this issue?
The exact error message is:
The operation couldn’t be completed. (com.apple.devicecheck.error error 0.)
This is the code I'm using, nothing fancy.
if #available(iOS 11.0, *) {
let device = DCDevice.current
if (device.isSupported) {
device.generateToken(completionHandler: { (data, error) in
if let token = data{
print("token: \(token)")
}else if let error = error{
print("error: \(error.localizedDescription)")
}
})
} else {
print("devicecheck not supported")
}
}

In my case, this error was caused by my iPhone's time being horribly out of sync. I manually changed the device's time to be the actual, current time (Settings → General → Date & Time). After that, the error went away and I was able to generate tokens.

Your code seems fine to me. The problem is simulator. It won't pass device.isSupported. You need to run it on real device.

Related

HealthKit request authorization failing

I continue to receive this error when I request authorization.
Error Domain=com.apple.healthkit Code=5 "Transaction block failed without an error." UserInfo={NSLocalizedDescription=Transaction block failed without an error.}
I have tried re-adding and removing:
Privacy - Health Update Usage Description, Privacy - Health Records Usage Description, Privacy - Health Share Usage Description
I have tried removing the HealthKit entitlement and adding it again. This flow worked previously, so I have no idea what is going on. Deleting and reinstalling app does not fix the problem either.
The HealthKit UI just never shows. Checking the privacy settings doesn't show I've ever requested either.
final class HealthStore {
private let healthStore = HKHealthStore()
func requestAuthorization() {
let objectTypes: Set<HKObjectType> = [
.activitySummaryType()
]
healthStore.requestAuthorization(toShare: nil, read: objectTypes) { (success, error) in
print("success = \(String(describing: success))")
print("error = \(String(describing: error))")
}
}
}
In a sample application this code works just fine. I think HealthKit has gotten into a bad state.
I don't know the underlying problem, but restarting my Mac and my iPhone solved the problem.
You change your IP and the network.

Getting error of "Publisher identifiers not found" when I enable "debugGeography" in AdMob

I am trying to test my GDPR compliance code and trying to display the consent form for AdMob but I get this error:
Consent info update failed. Error: Error Domain=Consent Code=1
"Response error. Publisher identifiers not found: ca-app-pub-0123456789012345"
I have triple checked, my publisherID is correct, it is something like "pub-0123456789012345".
When I check my AdMob account status I see that
"Your account is active and in good standing."
I have followed the directions of Google to receive user consent for personalized ads. I am in the USA, so use this line:
PACConsentInformation.sharedInstance.debugGeography = .EEA
When I disable this line, I don't get the error above anymore, but the error below:
Error loading form: Error: request is not in EEA or unknown.
I have tried to use this line instead but nothing changed:
PACConsentInformation.sharedInstance.debugGeography = PACDebugGeography.EEA
My only guess is that for the reason that my AdMob account is USA based, I get "publisherID not found" error when I enable ".EEA". But how can I do the testing if my guess is true?
I am doing the testing both on simulator and on the phone. I could not manage to display the consent form anywhere.
The code I am using is below:
// advertiseIDOfPhone and publisherID are constants defined elsewhere
PACConsentInformation.sharedInstance.debugIdentifiers = [advertiseIDOfPhone]
PACConsentInformation.sharedInstance.debugGeography = .EEA
PACConsentInformation.sharedInstance.requestConsentInfoUpdate(forPublisherIdentifiers: [publisherID])
{(_ error: Error?) -> Void in
if let error = error {
// Consent info update failed.
print("☢️ Consent info update failed. Error: \(error)")
} else {
print("☢️ Consent info updated.")
// Consent info update succeeded. The shared PACConsentInformation
// instance has been updated.
switch PACConsentInformation.sharedInstance.consentStatus {
case .nonPersonalized:
print("☢️ Personalized ads consent NOT given.")
case .personalized:
print("☢️ Personalized ads consent given.")
case .unknown:
print("☢️ Unknown consent.")
guard let privacyUrl = URL(string: "yourWebsiteURL"),
let form = PACConsentForm(applicationPrivacyPolicyURL: privacyUrl) else {
print("incorrect privacy URL.")
return
}
form.shouldOfferPersonalizedAds = true
form.shouldOfferNonPersonalizedAds = true
form.shouldOfferAdFree = true
form.load {(_ error: Error?) -> Void in
print("⚛️ Load complete.")
if let error = error {
// Handle error.
print("⚛️ Error loading form: \(error.localizedDescription)")
} else {
form.present(from: self) { (error, userPrefersAdFree) in
if error != nil {
// Handle error.
} else if userPrefersAdFree {
// User prefers to use a paid version of the app.
//buy the pro Version
}
}
}
}
default:
break
}
print("☢️ isRequestLocationInEEAOrUnknown: \(PACConsentInformation.sharedInstance.isRequestLocationInEEAOrUnknown)")
}
}
Where can be my mistake here? Is the code I am using good?
--
It seems that this is a known bug:
Kindly note that we are aware of this issue, and our Engineering team
is already working on a resolution. Rest assured that we are keeping
an eye out on this, and that we will update you for any news regarding
this issue.
Regards, Ziv Yves Sanchez Mobile Ads SDK Team
I didn't expect a bug from Google in such a fundamental aspect of a big product and unfortunately I have spent hours on this problem trying to find my mistake somewhere...
While they work on a resolution, you can use the Google Test Publisher ID:
pub-3940256099942544
My account gives me the error but I was able to use the test ID to implement and test the Consent SDK. Just make sure you switch back to your ID before you distribute the app.
I got the Same Issue!
This is not our Code's fault. Admob not serving ads.
Go to Admob Console
In Home There is Dialog Box: "Don't serve Until PaymentDetails Entered"
Click the Learn more and create an AdSense account
after that enter payment details
That's all they will serve ads after 24 hours after the account verified
I have the same issue.
Until I updated my settings on https://fundingchoices.google.com.
Go to the webpage, select APP on the left side, go in and activate the app you are using.

How to check if Apple Music is installed on the user's device?

I am making a music app with swift. The app lets users play music through their Apple Music subscription via their Apple Music app. I am able to check whether the user has an Apple Music subscription via:
SKCloudServiceController().requestCapabilities { (capability:SKCloudServiceCapability, err:Error?) in
guard err == nil else {
print("error in capability check is \(err!)")
return
}
if capability.contains(SKCloudServiceCapability.musicCatalogPlayback) {
print("user has Apple Music subscription")
}
if capability.contains(SKCloudServiceCapability.musicCatalogSubscriptionEligible) {
print("user does not have subscription")
}
}
However: there are scenarios where somebody will, for some reason, have an Apple Music subscription but not have the Apple Music app downloaded on their device. If the user has the subscription but not the device, I want to essentially treat that case as if they do not have a subscription at all, i.e. we cannot play music via Apple Music.
So, I go searching for ways to add a check for if Apple Music is on the user's device. I find this answer: Check whether an app is installed using Swift combined with this resource for finding Apple Music's url scheme and conclude I can check if a user has both an Apple Music subscription and the Apple Music app installed on their device via:
SKCloudServiceController()requestCapabilities { (capability:SKCloudServiceCapability, err:Error?) in
guard err == nil else {
print("error in capability check is \(err!)")
return
}
if capability.contains(SKCloudServiceCapability.musicCatalogPlayback) && UIApplication.shared.canOpenURL(URL(string: "music://")!) {
print("user has Apple Music subscription and has the apple music app installed")
}
if capability.contains(SKCloudServiceCapability.musicCatalogSubscriptionEligible) || !UIApplication.shared.canOpenURL(URL(string: "music://")!) {
print("user does not have subscription or doesn't have apple music installed")
}
}
The issue is, even after deleting Apple Music from my device, the first case, i.e. the one that prints user has Apple Music subscription and has the apple music app installed is still being called. I believe I have the correct url scheme because when changing "music://" to "musi://", the second case, i.e. the one that prints user does not have subscription or doesn't have apple music installed is being called.
When trying to open URL(string: "music://") with Apple Music deleted via UIApplication.shared.open(URL(string: "music://")!), I am hit with the following alert:
So why is the device saying that I can open URL(string: "music://") even after Apple Music is deleted? Is the URL capable of being opened, but the result is simply the presentation of the above alert? Is this the correct way to confirm that the user has Apple Music installed on their device? Is there even a way to confirm the user has Apple Music installed on their device? If Apple gives users the option to delete the Apple Music app, they should also give developers the ability to check if the app is installed.
The best solution I've got, though I expect there is something better out there, is to use MPMusicPlayer.prepareToPlay(completionHandler:) to check if there is an error when trying to play a track:
SKCloudServiceController().requestCapabilities { (capability:SKCloudServiceCapability, err:Error?) in
guard err == nil else {
print("error in capability check is \(err!)")
return
}
if capability.contains(SKCloudServiceCapability.musicCatalogPlayback) {
print("user has Apple Music subscription")
MPMusicPlayerController.systemMusicPlayer.setQueue(with: ["1108845248"])
systemMusicPlayer.prepareToPlay { (error) in
if error != nil && error!.localizedDescription == "The operation couldn’t be completed. (MPCPlayerRequestErrorDomain error 1.)" {
//It would appear that the user does not have the Apple Music App installed
}
}
}
if capability.contains(SKCloudServiceCapability.musicCatalogSubscriptionEligible) {
print("user does not have subscription")
}
}
I am not sure how this could apply to anybody using Apple Music within their app for anything other than playing tracks, but this seems to definitely work as a check when you are about to play a check. Whenever I am hit with that error, I simply create an alert telling the individual they have an Apple Music subscription but doesn't have the app installed.
Still, it would be great to be able to check without some completion handler as that would allow the boolean check to be integrated into conditional statements (via if capability.contains(SKCloudServiceCapability.musicCatalogPlayback) && hasAppleMusicAppInstalled { //do something }).
Luckily Apple provides you a method which returns false if no app installed on the device is registered to handle the URL’s scheme, or if you have not declared the URL’s scheme in your Info.plist file; otherwise, true.
func canOpenURL(_ url: URL) -> Bool
Following i'm posting the url schemes
Open = music://
Open = musics://
Open = audio-player-event://
Add the ones you will further use into your info.plist file.
After this use 'canOpenURL' to check
for more information check Apple docs
https://developer.apple.com/documentation/uikit/uiapplication/1622952-canopenurl
For the lucky souls that need music app installed to display MPMediaPickerController, the easiest way to check is to see if view was presented. If Music app is missing, it will fail silently.
let mediaPickerController = MPMediaPickerController(mediaTypes: MPMediaType.anyAudio)
mediaPickerController.delegate = self
mediaPickerController.prompt = "prompt"
presenter.present(mediaPickerController, animated: true, completion: nil)
DispatchQueue.main.asyncAfter(deadline: .now() + .milliseconds(500)) { [weak self] () -> () in
if self?.presenter.presentedViewController == nil {
self?.callback(.failure(.failedToPresentMusicPickerError))
}
}
A possible solution is doing the following: Setup a developer token through the Apple Music API (Used so you can query Apple Music REST endpoints). Submit a request to the following StoreKit function (Documentation):
requestUserToken(forDeveloperToken:completionHandler:)
If your developer token is valid and the user token value returned is still nil/null then the device user is not a subscriber of the Apple Music service. An error that is generated with the HTTP status code is 401 (Unauthorized). This still requires you checking an error however does not require to try and play a specific track (Especially for some reason if the content track id your checking against becomes invalid or changed).
For the issue of account signed into the device and has a subscription but not the Music app downloaded: Handle the error upon attempting to play specific content and either provide information to the user or use content that does not require an Apple Music subscription as an alternative when an error occurs.
Yes, We can check most of the Applications by following these Steps:
Use the Deep URL or URL Scheme for the particular application you want to open, add that into info.plist
Use the same URL and Call this method
func canOpenURL(_ url: URL) -> Bool
let url = URL(string: "music://")
UIApplication.shared.open(url!) { (result) in
if result {
// The URL was delivered successfully!
}
}
You can use this property to detect if Apple Music is installed.
import MediaPlayer
var isAppleMusicInstalled: Bool {
get async {
await withCheckedContinuation { continuation in
MPMusicPlayerController.systemMusicPlayer.setQueue(with: ["1108845248"])
self.systemMusicPlayer.prepareToPlay { (error) in
if error != nil && error!.localizedDescription.contains("error 6") {
print("Apple Music App not installed")
continuation.resume(returning: false)
} else {
continuation.resume(returning: true)
}
}
}
}
}
Note that it triggers a permission dialog and that you need to add the NSAppleMusicUsageDescription key in your Info.plist with a value like "to check if Apple Music is installed".
Simply do: UIApplication.shared.canOpenUrl(URL(string: “music://”)!)

NEHotspotConfigurationErrorDomain Error on some devices

I've implemented a WiFi auto-join feature in my app -> Click a button and connect to a named SSID. This works on some devices but also fails on others - meaning it won't even show the Apple pop up asking to join the network. The device might be the same device model, same iOS but fails on some and not on others. This is the error that I see being returned when it fails:
Error Domain=NEHotspotConfigurationErrorDomain Code=10 "cannot modify system configuration." UserInfo={NSLocalizedDescription=cannot modify system configuration.
This is the code used to attempt auto-join:
let WiFiConfig = NEHotspotConfiguration(ssid: "MYSSID")
WiFiConfig.joinOnce = true
NEHotspotConfigurationManager.shared.apply(WiFiConfig) { error in
if error == nil {
//success
} else {
//fail
}
}
Any ideas?
Thanks!
Jennie
I figured out this is unique to our devices using an Embedded Event Manager with a SSID prefilled on the register. That SSID cannot be joined to via the Auto-join feature.

fetchTimeEstimate not authorised - but strangely only on some iPhone's

I'm using the Uber Rides iOS SDK and currently only interested only in how long an uber will take to arrive, we just launch the uber app to the app if the user wants to proceed.
Strangely on some iPhones and the simulator it returns a result pretty much always (unless no cars available), but on others it says not authorised as a response.
Here is the code for the call:
func fetchTimeEstimates(from location: CLLocation, completion: #escaping (_ estimate: TimeEstimate?) -> Void) {
ridesClient.fetchTimeEstimates(pickupLocation: location) { (estimates, response) in
if response.statusCode != 200 || estimates.isEmpty {
print("UBER fetchTimeEstimates returned \(response.statusCode)")
completion(nil)
return
}
// Set default to uberX
for i in 0...estimates.count - 1 {
if let id = estimates[i].productID, id == UberRides.uberXProductId {
completion(estimates[i])
return
}
}
completion(estimates[0])
}
}
Im using server token setup in my plist
Although it either consistently works or does not on peoples phones, It does not seem a consistent pattern, i.e. its not that the first 5 devices worked and then it stopped working after that, or type of phone or OS version.
Heres what I tried:
Adding a failing devices user to list of developers in the dashboard authorisation screen, no difference.
Requesting full api access for a 'request', nope
checking if a brand new phone 8 with brand new user credentials both for the phone and uber login, works fine so don't explain why we go two people in the office where its consistently refused.
adding the app ID in the developer console.
Any ideas why I'm getting inconsistent results, its a challenge to debug as it always works on my test phones, but annoyingly it fails consistently on the CEO's phone.

Resources