I am trying to remove all support for Siri Shortcuts from my app. I have two types of shortcuts, both are made by donating INInteraction's.
func createDocIntent(for template: Document) -> CreateDocumentIntent {
let intent = CreateDocumentIntent()
intent.templateURI = template.URI.absoluteString
intent.templateTitle = template.title.trimmingCharacters(in: .whitespacesAndNewlines)
intent.suggestedInvocationPhrase = String(format: LS("siri-shortcut.create-document-suggested-phrase"), template.title.trimmingCharacters(in: .whitespacesAndNewlines))
return intent
}
func donateCreateDocShortcut(for template: Document) {
let intent = self.createDocIntent(for: template)
INInteraction(intent: intent, response: nil).donate { error in
if let error = error {
dprintln("Failed to donate CreateDocument shortcut to Siri with error: \(error.localizedDescription)")
}
}
}
To remove the shortcuts I have tried removing the Intent subclasses created in my Info.plist, and I have tried calling both INInteraction.deleteAll and NSUserActivity.deleteAllSavedUserActivities, but neither seem to actually delete the shortcuts (see below). Any shortcuts the user has made are still available in the Shorcuts.app, and can still be triggered by using voice commands.
INInteraction.deleteAll { (error) in
guard let error = error else { return }
print(error.localizedDescription)
}
NSUserActivity.deleteAllSavedUserActivities {
// Nothing to do
}
Is there something else I should be doing to remove existing Siri Shortcuts?
Related
I learn the Homekit to integrate my IOT device.
let payload = HMAccessorySetupPayload.init(url: URL.init(string: "X-HM://XXXXXXXXX"))
let request = HMAccessorySetupRequest()
request.payload = payload
let setupManager = HMAccessorySetupManager()
setupManager.performAccessorySetup(using: request) { (result, error) in
if (error != nil) {
print("Error from addAndSetupAccessories:", [error?.localizedDescription])
} else {
print("The accessory is added.")
}
}
The "X-HM" link is provided by OpenHab but it returned a error:
Failed to perform accessory setup using request: Error Domain=HMErrorDomain Code=17 "(null)"
I find other user say you need set the permission but I cannot set this in Entitlements file. It return error not find this.
“com.apple.developer.homekit.allow-setup-payload”
Question 1:
in the Homekit doc mean, the link must join "MFi Program"?
====
Question 2:
also, i have the pin code, Could i skip the input step in app setup?
i use the below code but this has not option to input the pin code, it need user to input using keyboard.
let setupManager = HMAccessorySetupManager()
setupManager.performAccessorySetup(using: HMAccessorySetupRequest()) { (result, error) in
if (error != nil) {
print("Error from addAndSetupAccessories:", [error?.localizedDescription])
} else {
print("The accessory is added.")
}
}
I have configured an always on VPN with a NEOnDemandRuleConnect I retrieve some user data from a backend such as expiration date if the user has paid the subscription. If it expires I'd like to deactivate the VPN without opening the main app, doing it from the Network Extension. I retrieve the data from the backend using a daily timer and then check if the subscription has expired. Then I'd have a function that loads the VPN manager from the system settings app and then deactivate it and finally save it. If I don't deactivate the manager the device will be without connection as it's a VPN that has been configured to connect always with the NEOnDemandRule. The function will be more or less this one
func stopProtection(completion: #escaping (Result<Void>) -> Void) {
NSLog("Called stopProtection")
NETunnelProviderManager.loadAllFromPreferences { (managers, error) in
if let error = error {
NSLog("[SUBS] ERROR \(error)")
}
if let managers = managers {
if managers.count > 0 {
let index = managers.firstIndex(where: { $0.localizedDescription == Constants.vpnBundleId })
guard let index = index else {
completion(.error(ProtectionServiceError.noKidsVpnInstalled))
return
}
let myManager = managers[index]
myManager.loadFromPreferences(completionHandler: { (error) in
guard error == nil else {
completion(.error(ProtectionServiceError.errorStoppingTunnel))
return
}
// Deactivate the VPN and save it
myManager.isEnabled = false
myManager.saveToPreferences(completionHandler: { (error) in
guard error == nil else {
completion(.error(ProtectionServiceError.errorStoppingTunnel))
return
}
completion(.success(()))
})
})
} else {
completion(.error(ProtectionServiceError.errorStoppingTunnel))
}
}
}
}
All this code and logic is being performed in the extension with all the limitations it supposes. Using the previous function I'd only get the first NSLog saying Called stopProtection but it doesn't load any manager. Calling this from the main target it'd work. I don't know if I can load and modify the manager from the extension or it's another way to do it.
Okay, I have debugged the network extension by attaching to the process and looking into the device Console and this error pops up,
NETunnelProviderManager objects cannot be instantiated from NEProvider processes
So nope, there's the answer!
For some reason when I try to load the GDPR Consent form I keep getting the error:
Error: invalid app name.
It happens here :
form.load { [weak self](_ error: Error?) -> Void in
print("Load complete.")
if let error = error {
// *** HERE IS THE ERROR ***
print("Error loading form: \(error.localizedDescription)")
return
}
When I do a global search for invalid app name I get brought to the consentform.html file:
// Set app name.
var appName = formInfo['app_name'] || '';
if (appName.length <= 0) {
formLoadCompleted('Error: invalid app name.');
}
This tells some value from either the googleservice-plist or my info.plist or something else isn't being read correctly but I don't know which value to look at.
Inside my info.plist I have the GADApplicationIdentifier correctly set:
Why is my app's name coming up as nil?
PACConsentInformation.sharedInstance.debugGeography = .EEA
PACConsentInformation
.sharedInstance
.requestConsentInfoUpdate(forPublisherIdentifiers: ["pub-MY_PublisherID"]) { [weak self](error) in
if let error = error { return }
PACConsentInformation.sharedInstance.isRequestLocationInEEAOrUnknown {
if PACConsentInformation.sharedInstance.consentStatus == PACConsentStatus.unknown {
guard let privacyUrl = URL(string: "My_Privacy_URL"),
let form = PACConsentForm(applicationPrivacyPolicyURL: privacyUrl) else {
return
}
form.shouldOfferPersonalizedAds = true
form.shouldOfferNonPersonalizedAds = true
form.shouldOfferAdFree = true
form.load { [weak self](_ error: Error?) -> Void in
print("Load complete.")
if let error = error {
// *** HERE IS THE ERROR ***
print("Error loading form: \(error.localizedDescription)")
return
}
// ...
}
return
}
}
I had to add dig deep to find out the problem but the issue was the CFBundleDisplayName or better yet the Bundle Display Name(red) in the info.plist was blank:
If the Bundle Display Name (red) is blank then the GDPR Consent form will return an error. You can either manually type in your app's name
OR
just DELETE the entire (red)Bundle Display Name k/v field. If you delete it then then the GDPR Consent will resort to use the Bundle Name(yellow).
Whatever you do DON'T make the mistake and delete theBundle Name(yellow) .
Follow these 2 answers for an explanation between the Bundle Display Name and the Bundle Name:
Bundle Display Name explained
Bundle Display Name with pics
i am creating app like as trucaller. Everything is completed, only one issue remaining. issue is that how to reload application extension after add new contact number from server in app. first of all, i enter some static number in the array then after i store it in userdefault. i got this by app-groups functionality. i want that when user synchronize their contact in my application, i want reload contact list.
this is my code
manager.reloadExtension(withIdentifier: extensionIdentifer, completionHandler: { error in
print("error \(error?.localizedDescription)")
if let _ = error{
print("A error \(error?.localizedDescription as String!)");
}
})
this is give me error like below
"sqlite3_step for query 'INSERT INTO PhoneNumberBlockingEntry
(extension_id, phone_number_id) VALUES (?, (SELECT id FROM PhoneNumber
WHERE (number = ?)))' returned 19 (2067) errorMessage 'UNIQUE
constraint failed: PhoneNumberBlockingEntry.extension_id,
PhoneNumberBlockingEntry.phone_number_id'"
Jaydeep: Call your new contact web service or synchronize contacts in application and just reload extension as -
CXCallDirectoryManager.sharedInstance.getEnabledStatusForExtension(withIdentifier: "com.compname.sampleapp", completionHandler: { (enabledStatus,error) ->
Void in if let error = error {
print(error.localizedDescription)
}
CXCallDirectoryManager.sharedInstance.reloadExtension(withIdentifier:"com.compname.sampleapp", completionHandler: {
(error) ->
Void in if let error = error {
print(error.localizedDescription)
}
DispatchQueue.main.async {
self.hud?.hide(animated: true)
}
})
print("No error")
})
Let me know still issue getting. I have done this and working fine.
I'm trying to get the users first name using cloud kit however the following code is not getting the users first name and is leaving firstNameFromFunction variable empty. Does anyone know how to achieve this in iOS 10?
let container = CKContainer.default()
container.fetchUserRecordID { (recordId, error) in
if error != nil {
print("Handle error)")
}else{
self.container.discoverUserInfo(
withUserRecordID: recordId!, completionHandler: { (userInfo, error) in
if error != nil {
print("Handle error")
}else{
if let userInfo = userInfo {
print("givenName = \(userInfo.displayContact?.givenName)")
print("familyName = \(userInfo.displayContact?.familyName)")
firstNameFromFunction = userInfo.displayContact?.givenName
}else{
print("no user info")
}
}
})
}
}
the permission screen that comes up when asking for the first time, IMO, is very poorly worded. They need to change that. It says "Allow people using 'your app' to look you up by email? People who know your email address will be able to see that you use this app." This make NO sense. This has nothing to do with asking the user to get their iCloud first name, last name, email address.
Speaking of email address - this and the phone number from the lookupInfo property is missing - i.e. set to nil, even though those values are legit and correct. Filing a bug tonight.
First, you will need to request permission to access the user's information.
Then, you can use a CKDiscoverUserIdentitiesOperation. This is just like any other CKOperation (eg. the modify record operation). You just need to create a new operation with the useridentitylookupinfo. Then you will also need to create a completion block to handle the results.
Here is an example function I created:
func getUserName(withRecordID recordID: CKRecordID,
completion: #escaping (String) -> ()) {
if #available(iOS 10.0, *) {
let userInfo = CKUserIdentityLookupInfo(userRecordID: recordID)
let discoverOperation = CKDiscoverUserIdentitiesOperation(userIdentityLookupInfos: [userInfo])
discoverOperation.userIdentityDiscoveredBlock = { (userIdentity, userIdentityLookupInfo) in
let userName = "\((userIdentity.nameComponents?.givenName ?? "")) \((userIdentity.nameComponents?.familyName ?? ""))"
completion(userName)
}
discoverOperation.completionBlock = {
completion("")
}
CKContainer.default().add(discoverOperation)
} else {
// iOS 10 and below version of the code above,
// no longer works. So, we just return an empty string.
completion("")
}
}
First you need to ask the user for permission to be discovered.
Use CKContainer.default().requestApplicationPermission method passing .userDiscoverability on applicationPermission parameter.
The CKContainer.default().discoverUserInfo method is deprecated on iOS 10. Instead use CKContainer.default().discoverUserIdentity method.
Do something like:
CKContainer.default().requestApplicationPermission(.userDiscoverability) { (status, error) in
CKContainer.default().fetchUserRecordID { (record, error) in
CKContainer.default().discoverUserIdentity(withUserRecordID: record!, completionHandler: { (userIdentity, error) in
print("\(userIdentity?.nameComponents?.givenName)")
print("\(userIdentity?.nameComponents?.familyName)")
})
}
}