I am creating an iOS contact app using swift. I have imported ContactsUI. I am trying to parse CNContact items using CNContactFetchRequest. when I try to build, it shows a white screen instate of home page (contact list in table view).
After bringing the app in background a alert shown for importing contacts. But this alert should show first time.
Why?
My code:
if CNContactStore.authorizationStatusForEntityType(.Contacts) == .NotDetermined {
store.requestAccessForEntityType(.Contacts, completionHandler: { (authorized: Bool, error: NSError?) -> Void in if authorized { self.ContactArr = self.createContactArr(contactCN) } })
}
else if CNContactStore.authorizationStatusForEntityType(.Contacts) == .Authorized {
self.ContactArr = self.createContactArr(contactCN) }
let keysToFetch = [CNContactFormatter.descriptorForRequiredKeysForStyle(.FullName), CNContactImageDataKey,CNContactEmailAddressesKey,CNContactUrlAddressesKey,CNContactNoteKey, CNContactPhoneNumbersKey,CNContactPostalAddressesKey]
let fetchRequest = CNContactFetchRequest(keysToFetch: keysToFetch) var contacts = [CNContact]() do { try store.enumerateContactsWithFetchRequest(fetchRequest, usingBlock: { (let contact, let stop) -> Void in contacts.append(contact) }) }
I need the solution so that I need not to go to the background mode for going to the home page.
Related
I'm developing a SwiftUI app for iOS and iPadOS to control my HomeKit smart devices, especially a RGB led strip. I have downloaded the accessory simulator and I have created two accessories named "Plug" and "Led Strip". I'm following this official documentation and I was able to create and to remove new homes using this code:
class HomeStore: NSObject {
static var shared = HomeStore()
let homeManager = HMHomeManager()
var homeDelegates = Set<NSObject>()
var accessoryDelegates = Set<NSObject>()
}
extension HomeStore: HMHomeManagerDelegate {
func addHome(name: String) {
homeManager.addHome(withName: name, completionHandler: { (home, error) in
if let error = error {
print("Error while adding a new home named \(name): \(error.localizedDescription)")
}
})
}
func removeHome(homeName: String) {
homeManager.homes.forEach({ home in
if (home.name == homeName) {
homeManager.removeHome(home, completionHandler: { error in
if let error = error {
print("Error while removing home named \(homeName): \(error.localizedDescription)")
}
})
}
})
}
I created another function to search new accessories:
func searchAccessories() -> [HMAccessory] {
accessoryBrowser.startSearchingForNewAccessories()
accessoryBrowser.stopSearchingForNewAccessories()
return accessoryBrowser.discoveredAccessories
}
If I run this function above it searches for new accessories and show me them in this SwiftUI View:
var body: some View {
Form {
ForEach(homeStore.searchAccessories()) { accessory in
Section {
Text(accessory.name)
Button("Add", action: {
homeStore.addAccessory(accessory: accessory, to: home)
})
}
}
}
}
When I press on "Add" button it runs the following function:
func addAccessory(home: HMHome, accessory: HMAccessory) {
home.addAccessory(accessory, completionHandler: { error in
if let error = error {
print("Error while adding a new accessory named \(accessory.name) to home \(home.name): \(error.localizedDescription)")
}
})
}
But it gives me the following error:
Error while adding a new accessory named Led Strip to home Casa: Object not found.
I tried also adding manually HomeKit-URL (of the type X-ME://ect..) with the following function:
func addAccessory(name: String, to home: HMHome, to room: HMRoom, url: URL) {
let request = HMAccessorySetupRequest()
request.suggestedAccessoryName = name
request.homeUniqueIdentifier = home.uniqueIdentifier
request.suggestedRoomUniqueIdentifier = room.uniqueIdentifier
request.payload = HMAccessorySetupPayload(url: url)
let setupManager = HMAccessorySetupManager()
setupManager.performAccessorySetup(using: request, completionHandler: { result, error in
if let error = error {
print("Error while adding accessory named \(name) to home \(home.name), room \(room.name): \(error.localizedDescription)")
}
})
}
But it gives me error 17: insufficient privileges for the operation.
so this could be because you haven't downloaded the HomeKit Simulator there's an apple doc here on how to do that. It essentially aids in developing and testing HomeKit Accessories.
I am in the process of implementing UMP SDK into my iOS app. I have setup the GDPR and IDFA messages in the Google AdMob dashboard's Privacy and Messaging section. I am having trouble getting the GDPR message to show up. The IDFA and iOS' ATT messages work perfectly.
Below is the code that I am using. I have tested this on both simulator and physical device. Also, I am located in the EU.
static func trackingConsentFlow(completion: #escaping () -> Void) {
let umpParams = UMPRequestParameters()
let debugSettings = UMPDebugSettings()
debugSettings.geography = UMPDebugGeography.EEA
umpParams.debugSettings = debugSettings
umpParams.tagForUnderAgeOfConsent = false
UMPConsentInformation
.sharedInstance
.requestConsentInfoUpdate(with: umpParams,
completionHandler: { error in
if error != nil {
print("MYERROR #1 \(String(describing: error))")
completion()
} else {
let formStatus = UMPConsentInformation.sharedInstance.formStatus
print("FORM STATUS: \(formStatus)")
if formStatus == .available {
loadForm(completion)
} else {
completion()
}
}
})
}
private static func loadForm(_ completion: #escaping () -> Void) {
UMPConsentForm.load(completionHandler: { form, loadError in
if loadError != nil {
print("MYERROR #2 \(String(describing: loadError))")
completion()
} else {
print("CONSENT STATUS: \(UMPConsentInformation.sharedInstance.consentStatus)")
if UMPConsentInformation
.sharedInstance.consentStatus == .required {
guard let rootViewController = UIApplication.shared.currentUIWindow()?.rootViewController else {
return completion()
}
form?.present(from: rootViewController, completionHandler: { dismissError in
if UMPConsentInformation
.sharedInstance.consentStatus == .obtained {
completion()
}
})
}
}
})
}
Just to be clear:
With this code I am able to show the IDFA message, after which the AppTrackingTransparency alert is shown. But I am expecting to also see the GDPR consent form.
For anyone wondering the same thing. The GDPR message was not appearing because I had not finalised my Admob account setup. I had not added my payment method. After adding it (and sending the app for review in Admob) the GDPR message started to appear.
I have a simple code that request access to contacts
override func viewDidLoad() {
super.viewDidLoad()
fetchContacts()
}
func fetchContacts()
{
let allowedCharset = CharacterSet
.decimalDigits
let store = CNContactStore()
store.requestAccess(for: .contacts) { (granted, err) in
if let error = err
{
print("failed to access",error)
return
}
if (granted)
{
///// after we get access to fetch contacts //// we reload table view data ///
print("access granted")
let keys = [CNContactGivenNameKey,CNContactPhoneNumbersKey,CNContactFamilyNameKey,CNContactMiddleNameKey]
let request = CNContactFetchRequest(keysToFetch: keys as [CNKeyDescriptor])
do {
try store.enumerateContacts(with: request, usingBlock: { (contact, stopPointerIfYouWantToStopEnumerating) in
let array = contact.phoneNumbers
for number in array
{
let fullName = contact.givenName + contact.middleName
let lastName = contact.familyName
let value = number.value.stringValue
let number = String(value.unicodeScalars.filter(allowedCharset.contains))
print (number)
/////////// 4 cases we just need the phone not to be zero ///////
if (fullName != "SPAM")
{
self.firstName.append(fullName)
self.lastName.append(lastName)
self.numberArray.append(number)
}
}
})
//self.table()
}
catch let err2 {
print ("failer to enurmerate",err2)
}
}
}
}
This code works fine on simulator. When I delete app on the simulator and clean then build and run the app again it works fine a popup view appears with permissions request, however on real device it works the permissions pops the first time when I delete the app from the phone and clean then build and run I dont receive the pop permission request again
When you delete an app the iOS keeps the permissions for a day for bundle identifier if you want to remove it in the same date you have a three options
change the iPhone OS (iOS) data by increasing iPhone OS (iOS) date with a day
Wait for a day
Reset the Device settings
Click here the apple docs reference that i take the screenshot form it also you can check it.
I've been stuck on a bug since last Monday, so I'm asking for help now ..
Contacts and Micriohpone request access does not work on iOS 9. I use this piece of code in order to request access to contacts :
let contactsStore = CNContactStore()
func requestAccess(completionHandler: #escaping (Permission) -> ()) {
self.contactsStore.requestAccess(for: .contacts, completionHandler: { (granted, error) in
if granted {
completionHandler(.granted)
} else {
completionHandler(.denied)
}
})
}
This function is called, no problem with that, the problem is it always return .denied and an error set with "Access denied", even though no alert has been shown to the user. The same with microphone.
The key 'Privacy - Contacts Usage Description' is present in my Info.plist
EDIT :
I also know that when the user denied once the usage it is not shown anymore, but the other problem is that there is not "switch" in the settings section of the app. I tried to restore the device (Working on simulator as I don't have a real iOS 9 device), but still the same behaviour.
This code works perfeclty on iOS 10 and iOS 11. But no chance on iOS 9
If you could help me on this issue that would be awesome.
Thanks !
I tried this on 9.3 in the simplest way imaginable, and I did get a prompt:
import UIKit
import Contacts
class ViewController: UIViewController {
let contactsStore = CNContactStore()
override func viewDidAppear(_ animated: Bool) {
DispatchQueue.main.async {
self.requestAccess(completionHandler: { (permission) in
print("The user said \(permission)")
})
}
}
func requestAccess(completionHandler: #escaping (Permission) -> ()) {
self.contactsStore.requestAccess(for: .contacts, completionHandler: { (granted, error) in
if granted {
completionHandler(.granted)
} else {
completionHandler(.denied)
}
})
}
}
enum Permission {
case granted
case denied
}
This works fine. I think the issue is that you already denied it.
The only solutions are:
Change the bundle id, which will make your app act as a different one
Reset your device/simulator (easier if a simulator of course)
Change the privacy setting from Off to On
For end users, I've seen the UI prompt the user to change the setting if they see "denied".
You can do that like this:
self.requestAccess(completionHandler: { (permission) in
print("The user said \(permission)")
if ( permission == .denied ) {
let urlStr = UIApplicationOpenSettingsURLString
if let url = URL(string:urlStr) {
UIApplication.shared.openURL(url)
}
}
})
I am making an iOS application and I would like to use the iCloud feature "Look Me Up By Email" found under iCloud in the Settings of iOS.
I would like to simplify the user experience by identifying users by their iCloud so they don't have to remember a login for my app.
As I understand this, this is a CloudKit feature related to CKDiscoverAllContactsOperation. You can discover people from your contacts with the same app and can be discovered by them. You need to request permission for that ability first, like this:
CKContainer.defaultContainer().requestApplicationPermission(CKApplicationPermissions.UserDiscoverability) { [unowned self] (status, error) -> Void in
//Your code handling error or success...
}
Note
Release notes for iOS 10 mention, that this operation and related functions will be changed. In iOS 10+ you need to use CKDiscoverAllUserIdentitiesOperation
Example of usage
init a container first, note that your container accountStatus must be correct
let container = CKContainer(identifier: "iCloud.com.YourContainerID")
Later, ask a permission
container.requestApplicationPermission(CKApplicationPermissions.userDiscoverability) { [unowned self] (status, error) -> Void in
if let err = error {
print(err)
}
else if status == CKApplicationPermissionStatus.granted{
//success
}
else{
print("Permission not granted")
print(status)
}
}
Later, you can get your current user record id and create a subscription for example (note that you don't need a permission and user record ID to create a subscription, but in my case it was needed to create a predicate):
container.fetchUserRecordID { [weak self] (recordID, error) in
let predicate = NSPredicate(format: "user = %#",userRecordID.recordName)
let subscription = CKSubscription(recordType: "myRecordType", predicate: predicate, options: CKSubscriptionOptions.firesOnRecordCreation)
container.publicCloudDatabase.save(subscription, completionHandler: { (subscription, error) in
//completion stuff here
}
}
Friends discoverability example
let discoverOperation = CKDiscoverAllUserIdentitiesOperation()
var users = [CKUserIdentity]()
discoverOperation.discoverAllUserIdentitiesCompletionBlock = { [weak self] (error: Error?) -> Void in
if let err = error as? NSError{
print("Discover Friends Error: \(err)")
} else {
//do whatever you want with discovered contacts
}
}
discoverOperation.userIdentityDiscoveredBlock = { (userIdentity: CKUserIdentity) -> Void in
users.append(userIdentity)
}
discoverOperation.queuePriority = Operation.QueuePriority.high //this option is up to your tasks
container.add(discoverOperation)