iOS添加自定义日历报错EKErrorDomain Code=14(Calendar has no source)求助:为何无Local/iCloud日历源?
Hi there, let's break down exactly why you're hitting this error and walk through how to fix it.
First, Why No Local/iCloud Calendar Sources?
The core problem here is your device only has read-only or restricted calendar sources right now—none of the sources you listed support creating new custom calendars. Let's unpack this:
- Missing iCloud Source: This happens if you haven't signed into an Apple ID with iCloud Calendar enabled, or if you toggled off the Calendar switch in
Settings → [Your Name] → iCloud. iCloud sources only appear when iCloud Calendar is active for your account. - Missing Local Source: iOS doesn't auto-create a Local calendar source by default. It only shows up if you've manually made a local calendar in the stock Calendar app before, or if an app has successfully written to a local calendar (which requires the source to exist first—classic catch-22!).
- Your Current Sources Are Unusable:
- Subscribed calendars (type 3) are read-only—they sync from external URLs, so you can't create new calendars under this source.
- The "Birthdays" calendar (type 4) is system-managed and locked down, so it can't be used as a parent for custom calendars either.
How to Fix the Error
We need to address two parts: getting a valid writable source, and updating your code to handle edge cases more safely.
Step 1: Get a Valid Writable Source (Manual User Fix)
First, you need to add a source that allows creating new calendars:
- Option 1: Enable iCloud Calendar: Go to
Settings → [Your Name] → iCloudand toggle on the Calendar switch. This will add an iCloud source to your device, which fully supports custom calendar creation. - Option 2: Create a Local Calendar: Open the stock Calendar app → Tap "Calendars" at the bottom → Scroll to the very end and tap "Add Calendar" → Select "Local", name it, and save. This will generate a Local source your code can use.
Step 2: Update Your Code to Handle Edge Cases
Your current code falls back to defaultCalendarForNewEvents.source, but that source might be read-only (like your subscribed holiday calendars). Let's adjust the code to properly find writable sources, and fail gracefully if none exist:
EKCalendar *customCalendar = [EKCalendar calendarForEntityType:EKEntityTypeEvent eventStore:eventStore]; customCalendar.CGColor = UIColor.blueColor.CGColor; EKSource *selectedSource = nil; NSArray<EKSource *> *sources = eventStore.sources; // 1. First search for writable Local or iCloud sources for (EKSource *source in sources) { BOOL isSupportedType = (source.sourceType == EKSourceTypeLocal || (source.sourceType == EKSourceTypeCalDAV && [source.title.lowercaseString containsString:@"icloud"])); BOOL canWrite = [source allowsContentModifications]; if (isSupportedType && canWrite) { selectedSource = source; break; } } // 2. If no Local/iCloud, look for any other writable source as a backup if (!selectedSource) { for (EKSource *source in sources) { if ([source allowsContentModifications]) { selectedSource = source; break; } } } // 3. If still no valid source, prompt the user to fix their settings if (!selectedSource) { UIAlertController *alert = [UIAlertController alertControllerWithTitle:@"无法创建日历" message:@"请前往「设置」开启iCloud日历,或在系统日历App中创建一个本地日历后重试。" preferredStyle:UIAlertControllerStyleAlert]; [alert addAction:[UIAlertAction actionWithTitle:@"知道了" style:UIAlertActionStyleDefault handler:nil]]; // Make sure to present this alert from a valid view controller context [self presentViewController:alert animated:YES completion:nil]; return; } // Assign the valid source and attempt to save customCalendar.source = selectedSource; NSError *error = nil; BOOL saveSuccess = [eventStore saveCalendar:customCalendar commit:YES error:&error]; if (!saveSuccess) { NSLog(@"保存日历失败: %@", error.localizedDescription); // Add user-facing error feedback here, like another alert }
Key Notes to Remember
- Always check
allowsContentModificationsbefore using a source—this filters out read-only sources like subscriptions or system calendars. - Relying on
defaultCalendarForNewEventsis risky because it might point to a read-only source (like your subscribed holidays), which will still cause a save failure. - Double-check that you've added the
NSCalendarsUsageDescriptionpermission to your Info.plist (required for iOS 10+), though this isn't the root issue here since you can already access the source list.
Let me know if you need help troubleshooting any of these steps further! 😊




