Cannot delete EKCalendar, get Error Domain=EKErrorDomain Code=72 - swift

I would like to delete an EKCalendar. This works fine on iOS devices and simulators, however trying to get this working on Catalina is problematic. Whatever I try I get this:
Error Domain=EKErrorDomain Code=72 "Can't delete a calendar if doing
so would leave the account with no calendars which are eligible to be
the default scheduling calendar."
UserInfo={NSLocalizedDescription=Can't delete a calendar if doing so
would leave the account with no calendars which are eligible to be the
default scheduling calendar.}
Any pointers? I have been chasing this for weeks! Thanks!
I have been granted permission for both Calendars and Reminders:
import UIKit
import EventKit
class ViewController: UIViewController {
let eventStore = EKEventStore()
func deleteCal (eventStoreToUse: EKEventStore) {
let calsArray = eventStoreToUse.calendars(for: .event)
for cals in calsArray {
print (cals.title)
if cals.title == "Gaps2" || cals.title == "Done" {
do { try _ = eventStoreToUse.removeCalendar(cals, commit: true)
} catch {
print ("Error \(error)")
}
} else {
print ("Did not delete \(cals.title)")
}
}
}
func askAccess() {
switch EKEventStore.authorizationStatus(for: .event) {
case .authorized:
print ("Calendars Access Granted")
case .denied:
print("Access denied")
case .notDetermined:
eventStore.requestAccess(to: .event, completion:
{[weak self] (granted: Bool, error: Error?) -> Void in
if granted {
print("Granted")
self?.deleteCal(eventStoreToUse: (self?.eventStore)!)
} else {
print("Access denied")
}
})
default:
print("Case default")
}
switch EKEventStore.authorizationStatus(for: .reminder) {
case .authorized:
print ("Reminders Access Granted")
case .denied:
print("Access denied")
case .notDetermined:
eventStore.requestAccess(to: .event, completion:
{[weak self] (granted: Bool, error: Error?) -> Void in
if granted {
print("Granted")
self?.deleteCal(eventStoreToUse: (self?.eventStore)!)
} else {
print("Access denied")
}
})
default:
print("Case default")
}
}
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
askAccess()
deleteCal(eventStoreToUse: eventStore)
}
}

Related

didFinishLaunchingWithOptions for watchOS

I am trying to implement push messages in my apple watch app. So I have to use the WKExtensionDelegate. Only problem there is that it seems like there is no "didFinishLaunchingWithOptions" method in it. Is there something equivalent?
This is the code for my AppDelegate
class AppDelegate: NSObject, WKExtensionDelegate {
func applicationDidFinishLaunching() {
print("test function")
registerForPushNotifications()
return
}
func registerForPushNotifications() {
UNUserNotificationCenter.current()
.requestAuthorization(
options: [.alert, .sound, .badge]) { [weak self] granted, _ in
print("Permission granted: \(granted)")
guard granted else { return }
self?.getNotificationSettings()
}
}
func getNotificationSettings() {
UNUserNotificationCenter.current().getNotificationSettings { settings in
print("Notification settings: \(settings)")
guard settings.authorizationStatus == .authorized else { return }
DispatchQueue.main.async {
WKExtension.shared().registerForRemoteNotifications()
}
}
}
func application(
_ application: WKExtension,
didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data
) {
let tokenParts = deviceToken.map { data in String(format: "%02.2hhx", data) }
let token = tokenParts.joined()
print("Device Token: \(token)")
}
func application(
_ application: WKExtension,
didFailToRegisterForRemoteNotificationsWithError error: Error
) {
print("Failed to register: \(error)")
}
}
How do I handle received push Messages?? Thanks in advance

How can I get my app to wait for a permission request to complete?

My updated code is below, added a semaphore, and the app still blows through the AVCaptureDevice.authorizationStatus part and keeps running.
However, if I declare the semaphore with 0, then the first semaphore.wait() is successful, and the program freezes because the userAlert permission box never pops up.
So am having a tough time figuring out what the issue is here.
print ("going in...")
let semaphore = DispatchSemaphore(value: 1 )
DispatchQueue.global(qos: .userInitiated).async {
let mediaAuthorizationStatus = AVCaptureDevice.authorizationStatus(for: .audio)
switch mediaAuthorizationStatus {
case .denied:
print (".denied")
case .authorized:
print ("authorized")
case .restricted:
print ("restricted")
case .notDetermined:
print("Need to ask user")
semaphore.wait()
AVCaptureDevice.requestAccess(for: .audio, completionHandler: { (granted: Bool) in
if granted {
semaphore.signal()
} else {
semaphore.signal()
}
})
#unknown default:
print("unknown")
}
print ("\(semaphore.debugDescription)")
}
semaphore.wait()
print ("and we're out")
Misusing DispatchQueue to force an asynchronous task to become synchronous is a very bad practice.
Either use a completion handler
func avAuthorization(completion : #escaping (Bool) -> Void)
{
let mediaType = AVMediaType.audio
let mediaAuthorizationStatus = AVCaptureDevice.authorizationStatus(for: mediaType)
switch mediaAuthorizationStatus {
case .denied, .restricted: completion(false)
case .authorized: completion(true)
case .notDetermined:
AVCaptureDevice.requestAccess(for: .audio) { granted in
completion(granted)
}
}
}
Or in Swift 5.5+ an async function
func avAuthorization() async -> Bool
{
let mediaType = AVMediaType.audio
let mediaAuthorizationStatus = AVCaptureDevice.authorizationStatus(for: mediaType)
switch mediaAuthorizationStatus {
case .denied, .restricted: return false
case .authorized: return true
case .notDetermined: return await AVCaptureDevice.requestAccess(for: .audio)
}
}
I think you are already on the main thread and trying to DispatchQueue.main.sync from the Main thread. Check it like this.
if Thread.isMainThread {
// you are already on the main thread
} else {
DispatchQueue.main.sync {
// do stuff
}
}
without waiting for the iOs permission alert to pop up and complete
Great line, partially describing possible solution (imho).
AFAIK all UIAlertController-related routines are richly flavoured with completions. I'd try to put recording logic trigger inside the completion handler of method that presents the alert itself.

Change view once contact permission is granted

At the moment, I am able to successfully ask the user for their permission to access their Contact information. I am handling this through a switch statement like so:
func requestContactPermissions() {
let store = CNContactStore()
var authStatus = CNContactStore.authorizationStatus(for: .contacts)
switch authStatus {
case .restricted:
print("User cannot grant permission, e.g. parental controls in force.")
exit(1)
case .denied:
print("User has explicitly denied permission.")
print("They have to grant it via Preferences app if they change their mind.")
exit(1)
case .notDetermined:
print("You need to request authorization via the API now.")
store.requestAccess(for: .contacts) { success, error in
if let error = error {
print("Not authorized to access contacts. Error = \(String(describing: error))")
exit(1)
}
if success {
print("Access granted")
}
}
case .authorized:
print("You are already authorized.")
#unknown default:
print("unknown case")
}
}
In the .notDetermined case, this is opening the dialog where I can either click no or yes, granting or denying the application access. This is fine and expected.
What I am looking to do, is change the view if the user clicks yes. Right now, I have the requestContactPermissions function within a button like so:
Button(action: {
withAnimation {
// TODO: Screen should not change until access is successfully given.
requestContactPermissions()
// This is where the view change is occurring.
self.loginSignupScreen = .findFriendsResults
}
})
How might I add in the logic to have the view change once the user has granted the application access to their contacts?
add a completion to the requestContactPermissions function something like this (I trimmed the irrelevant parts for the answer):
func requestContactPermissions(completion: #escaping (Bool) -> ()) {
let store = CNContactStore()
var authStatus = CNContactStore.authorizationStatus(for: .contacts)
switch authStatus {
case .notDetermined:
print("You need to request authorization via the API now.")
store.requestAccess(for: .contacts) { success, error in
if let error = error {
print("Not authorized to access contacts. Error = \(String(describing: error))")
exit(1)
//call completion for failure
completion(false)
}
if success {
//call completion for success
completion(true)
print("Access granted")
}
}
}
}
and then you can determine inside the closure whether the user granted permission or not:
Button(action: {
withAnimation {
// TODO: Screen should not change until access is successfully given.
requestContactPermissions { didGrantPermission in
if didGrantPermission {
//this is the part where you know if the user granted permission:
// This is where the view change is occurring.
self.loginSignupScreen = .findFriendsResults
}
}
}
})

Perform segue after notification access has been granted

I would like to know how to perform a modal segue after the remote notification access has been granted from the dialog box. I have set up my remote notification in the app delegate.
func registerANSForApplication(_ application: UIApplication,withBlock block: #escaping (_ granted:Bool) -> (Void)){
InstanceID.instanceID().instanceID { (result, error) in
if let error = error {
print("Error fetching remote instange ID: \(error)")
} else if let result = result {
print("Remote instance ID token: \(result.token)")
AppDelegate.isToken = result.token
}
}
let current = UNUserNotificationCenter.current()
let options : UNAuthorizationOptions = [.sound, .badge, .alert]
current.requestAuthorization(options: options) { (granted, error) in
guard granted else{
return
}
if error != nil{
print(error?.localizedDescription ?? "")
}else{
Messaging.messaging().delegate = self
current.delegate = self
DispatchQueue.main.async {
application.registerForRemoteNotifications()
}
}
}
}
Then, in my view controller, I have this code:
let appDelegate = UIApplication.shared.delegate as!
appDelegate.registerANSForApplication(UIApplication.shared) { (granted) -> (Void) in
self.performSegue(withIdentifier: "MembershipVC", sender: nil)
}
The problem is whether the user allows or denies the access to notification, the segue is not executed.
Thank you for your help.
You have to call the block parameter
Replace
current.requestAuthorization(options: options) { (granted, error) in
guard granted else{
return
}
if error != nil{
print(error?.localizedDescription ?? "")
}else{
Messaging.messaging().delegate = self
current.delegate = self
DispatchQueue.main.async {
application.registerForRemoteNotifications()
}
}
}
with
current.requestAuthorization(options: options) { (granted, error) in
if error != nil {
print(error?.localizedDescription ?? "")
block(false)
} else {
Messaging.messaging().delegate = self
current.delegate = self
DispatchQueue.main.async {
application.registerForRemoteNotifications()
block(granted)
}
}
}

EKCalendar title in iOS12 is blank

In iOS 12 I'm trying to list the calendars. I can print the calendar ids, but the titles are all blank. What am I doing wrong?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let status = EKEventStore.authorizationStatus(for: EKEntityType.event)
switch (status) {
case EKAuthorizationStatus.notDetermined:
EKEventStore().requestAccess(to: .event, completion: {
(granted: Bool, error: Error?) in
if granted != true {
print("Access not granted")
}
})
case EKAuthorizationStatus.authorized:
print("Access granted")
case EKAuthorizationStatus.restricted, EKAuthorizationStatus.denied:
print("Access restricted or denied")
}
print("Calendars:")
for c in EKEventStore().calendars(for: EKEntityType.event) {
print(" \(c.calendarIdentifier):\(c.title)")
}
}
I'm not sure why, but this code shows event titles in iOS 12 Simulator.
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
let status = EKEventStore.authorizationStatus(for: EKEntityType.event)
let eventStore = EKEventStore() //<-
switch (status) {
case EKAuthorizationStatus.notDetermined:
eventStore.requestAccess(to: .event, completion: {
(granted: Bool, error: Error?) in
if granted != true {
print("Access not granted")
}
})
case EKAuthorizationStatus.authorized:
print("Access granted")
case EKAuthorizationStatus.restricted, EKAuthorizationStatus.denied:
print("Access restricted or denied")
}
print("Calendars:")
for c in eventStore.calendars(for: EKEntityType.event) {
print(" \(c.calendarIdentifier):\(c.title)",c)
}
}
Maybe, you need to keep strong reference to the instance of EKEventStore while accessing EKCalendar properties.