CKReferences not appearing in CloudKit dashboard - cloudkit

Why am I not able to see references in the CloudKit dashboard?
I set up CKReferences like this:
let ouS = CKRecord(recordType: "OU")
let userR = CKReference(recordID: userRecordID, action: .None)
organizationUserS.setObject(userR, forKey:"user")
publicDatabase.saveRecord(organizationUserS, completionHandler: { ouS2, error in

Meantime, until today morning references panel appeared for that certain record on dashboard, it is an iOS8 beta side effect.
Checking first the new Record Type what you started to use and then Default Zone, afterwards you will able to force to plot reference panel.

Related

UserDefaults/NSUserDefaults removeObject(forKey:) mysteriously failing in app and between apps

I am working on an "uninstaller" for an macOS app we've had for several years now. The purpose for the uninstaller is to allow us to put a given system into a nascent state as if the original app had never been installed so that we can more reliably test the install process.
The original app has an extensive array of preferences stored in UserDefaults. In the original app there is a resetToDefaults() method which works just fine resetting all the defaults however for the uninstaller we'd wanted to remove the values completely. It looks to be straight-forward and this is what I came up with...
func flushPreferences() {
let defaults = getDefaultPreferences()
for preferenceName in defaults.keys.sorted() {
UserDefaults.standard.removeObject(forKey: preferenceName)
}
UserDefaults.standard.synchronize()
}
... which does not work at all.
I read in the documentation
Removing a default has no effect on the value returned by the objectForKey: method if the same key exists in a domain that precedes the standard application domain in the search list.
and I don't really understand what "domain" relates to and thought it might be app so tried the code as a test in the original app and that does nothing either.
Someone else suggested this, which also does nothing
let appDomain = Bundle.main.bundleIdentifier!
UserDefaults.standard.removePersistentDomain(forName: appDomain)
I also found some test code which works absolutely fine... which looks to be nearly identical to what I'm doing. I even tried using it with hard-coding one of our pref keys and that fails as well.
func testRemoveObject() {
let myKey:String = "myKey"
UserDefaults.standard.set(true, forKey: myKey)
let beforeVal = UserDefaults.standard.value(forKey: myKey)
print("before: \(beforeVal ?? "nil")")
UserDefaults.standard.removeObject(forKey: myKey) // Note: This is the only line needed, others are debugging
let afterVal = UserDefaults.standard.value(forKey: myKey)
print("after: \(afterVal ?? "nil")")
}
What am I missing? It looks like this one (based on what I've been able to find on the web) can be somewhat mysterious but I'm thinking it must be something obvious that I'm not seeing.
Well, thanks to red_menace's suggestion I found one article that led to another that suggested that the following command will reset the user preferences cache:
killall -u #USER cfprefsd
which seemed to work (yay) but upon further investigation it appears that simply closing the app is what updates the actual preference in the .plist and that changing it in the app will not show up until you exit.
This makes sense as it explains why you can create a preference on the fly save it, confirm it saved, delete it and confirm it deleted but cannot delete a previously saved preference — as similar to the persistent prefs perhaps the new preference is not added to the cache until the application exits.
This could also explain the various odd behaviors that other posters were finding (only worked every other time, had to do it asynchronously, etc.). As for NSUserDefaults.synchronize() has been depreciated and developer.apple.com indicates that it is unneeded and does nothing.
So one problem solved...
As it turns out my initial instinct was accurate as well and you cannot access prefs from one app in another using the removeObject(forKey: preferenceName)
// Will not work cross-application, though will work locally (inter-app)
UserDefaults.standard.removeObject(forKey: preferenceName)
To get it to work cross applications you have to use CFPreferencesSetAppValue(_ key:, value:, applicationID:) which is part of the "Preferences Utilities" section of the Core Library which requires that you know the appDomain of the initial app. So, the final solution is:
In the source app:
let appDomain = Bundle.main.bundleIdentifier! // Note, needed by uninstaller
will give you the domain for the stored preference in the source app.
And in the app doing the changing — the final working code:
func flushPreferences() {
let defaults = getDefaultPreferences()
let sourceAppDomain = "{THE_BUNDLE_ID_FROM_SOURCE_APP}"
for preferenceName in defaults.keys {
print("Preference name: \(preferenceName)")
CFPreferencesSetAppValue(preferenceName as CFString,
nil,
sourceAppDomain as CFString)
}
}
Hope this helps someone save some time at some point - thanks to everyone who contributed. This one was a BEAR!

Random EKEventStore sync issues with Google calendar

I spent hours to track this bug.
My app is supposed to remove some events, then to add some other events. This works perfectly with iCloud and local calendars. However with Google calendars, this changes.
What I noticed so far:
Sometimes all works perfectly. All events which should be removed are removed and all events that should be added are added.
Sometimes, initially, all seems to be synced properly, but after 2 minutes, events get removed.
.remove works better (95 %) than .save (50-80%).
there are no catch errors. Neither for .remove, nor for .save.
It seems (?!) to be that Google calendars with few events (inside or outside the predicate - doesn't matter) gets synced correctly. Google calendars with many events (inside or outside the predicate) produce mentioned sync issues.
Here excerpts of the code (really nothing fancy!):
let evenStore = EventStore()
let predicate = eventStore.predicateForEvents(withStart: predicateStartDate!,
end: predicateEndDate!,
calendars: selectedCalendarArray)
let events = eventStore.events(matching: predicate)
for event in events {
do {
try eventStore.remove(event, span: .thisEvent, commit: false)
} catch {
print("event remove catch error")
}
}
... (new events are created and stored in eventsSave)
for event in eventsSave {
do {
try eventStore.save(event, span: .thisEvent, commit: false)
} catch {
print("event save catch error")
}
}
do {
try eventStore.commit()
} catch {
print("commit catch error")
}
I tried to avoid batch .remove or .save (true i.s.o. false - no commit()), No success.
I added eventStore.reset(). No success.
I added eventStore.refreshSourcesIfNecesary(). No success.
What I really don't get is, that iCloud works 100%. Google calendar is also a cloud calendar. What makes it behave so differently? There are some posts on SO, but they are mainly around 2013-2016. After that, virtually nothing anymore. Also never any solid solution, at the very best some workarounds.
Missing EKEvents when saving multiple Google calendar events
Batch saving EKEvents to Google calendar causing loss of random events
I am under the impression something basic and very simple is just missing, but no clue what this could be ...
Any help would be much appreciated!
UPDATE (06.08.2020)
Some events have geo location using the X-APPLE-STRUCTURED-LOCATION property. These events throw upon .save.
Error Domain=EKErrorDomain Code=19 "Locations with geo information are not supported by this account." UserInfo={NSLocalizedDescription=Locations with geo information are not supported by this account.}
Could this error be related to subject sync issue?
I believe I found the solution.
I use the calendar chooser to select a calendar. I store a default calendar in myStoredCalendar (String) which should be preselected upon calendar chooser presentation.
let selectedCalendar = myStoredCalendar
if let selectedCalendarIdentifier = eventStore.calendar(withIdentifier: selectedCalendar) {
calendarChooser.selectedCalendars = Set<EKCalendar>([selectedCalendarIdentifier])
}
Above works, but generates the following log message (apart from subject Google event issues):
Error getting shared calendar invitations for entity types 3 from daemon: Error Domain=EKCADErrorDomain Code=1014"(null)
The following code doesn't produce error 1014 anymore, and all Google event issues are gone.
let selectedCalendar = myStoredCalendar
let calendars = eventStore.calendars(for: .event)
for calendar in calendars {
if calendar.calendarIdentifier == selectedCalendar {
calendarChooser.selectedCalendars = Set<EKCalendar>([calendar])
}
}
}
UPDATE (08.08.2021)
Above wasn't the solution. The error is totally random. Sometimes it works like charm for 20 times in a row, sometimes not a single run works properly.

How to load NSUserDefaults data on a Today Extension in Swift?

I've already searched a lot but any answer fixed the problem.
I've created an app group, selected it on both app and today extension, they're linked to the same group now. I'm trying to load data from the app and display it on a table view on the today extension, the thing is that I'm getting a crash saying that my array is nil, and it cannot be nil. I don't know why tho, cause I'm putting the data into the array. Here is my code:
func handleData() {
let defaults = NSUserDefaults(suiteName: "MY APP GROUP HERE")
if let descriptionsArr = defaults?.valueForKey("descriptions") as? [String] {
descriptions = descriptionsArr
}
defaults?.synchronize()
}
So, I'm pretty sure the app group is spelled right, and the value key. What could it be that is making my array nil? Really need help!
Thanks.

Forcing update to placeholder complication on Apple Watch

Here a simple issue I am facing while starting to experiment with WatchKit and complications.
I created a simple app which is showing a complication with a public string “Y” and by clicking it, the Apple Watch app is shown with a simple switch.
I wrote my code in getPlaceholderTemplateForComplication in ComplicationController.swift and added a switch IBAction in InterfaceController.swift.
By changing the value of the switch, the public string cycles between “N” and “Y”. I would like to have it changed in the complication as well. However I am noticing that the complication stays as it is initially at “Y”.
I found a similar question about forcing complication updates, but it was related to a TimeLine complication func and not placeHolder.
func updateComplication() {
let complicationServer = CLKComplicationServer.sharedInstance()
for complication in complicationServer.activeComplications {
complicationServer.reloadTimelineForComplication(complication)
}
}
It is not clear to me on where and how to use this in my case.
As suggested I worked on getCurrentTimelineEntryForComplication .
In order to test a ModularComplication only, I used:
switch complication.family {
case .ModularSmall:
let modularSmallTemplate =
CLKComplicationTemplateModularSmallRingText()
modularSmallTemplate.textProvider =
CLKSimpleTextProvider(text: stringa)
modularSmallTemplate.fillFraction = 0.95
modularSmallTemplate.ringStyle = CLKComplicationRingStyle.Closed
let template = CLKComplicationTimelineEntry(
date: NSDate(), complicationTemplate: modularSmallTemplate)
handler(template)
default:
handler(nil)
}
I have the switch IBAction in InterfaceController.swift.
I am encountering issues in using:
func updateComplication() {
let complicationServer = CLKComplicationServer.sharedInstance()
for complication in complicationServer.activeComplications {
complicationServer.reloadTimelineForComplication(complication)
}
}
Where do I have to write the above mentioned func, in order to be able to call it from inside the IBAction?
If I write it in ComplicationController.swift, by calling it from the IBAction in InterfaceController.swift as:
ComplicationController.updateComplication()
I get the error “Missing argument for parameter #1 in call”,
while if I write it in InterfaceController and call it in the IBAction, although the build is successful, when running the App and changing the value of the switch I get the following error: “fatal error: unexpectedly found nil while unwrapping an Optional value” on the line:
for complication in complicationServer.activeComplications
of func updateComplication.
It's not the placeholder text that you want to update. The placeholder template is a static template that's displayed in the selection screen for your complication while you are customizing your watch face. It's only called once to get the placeholder text, and doesn't get called when a timeline is reloaded.
The complication's current (actual, live) timeline entry is provided by getCurrentTimelineEntryForComplication. This function is where you should use your model's on state of the switch to create a CLKTextProvider containing a "N" or "Y" for the active complication family.
Finally, you should call updateComplication() from your switch IBAction to reload your complication timeline, which would update the current entry to show the new state of the switch on the watch face.
Since your complication is updated manually, you should return a nil updateDate in getNextRequestedUpdateDateWithHandler to avoid scheduling your complication for any regular updates.
Update for your edited question:
You should include the updateComplication function in your interface controller, as your complication controller isn't meant to be instantiated by you.
In regard to the "Unexpectedly found nil while unwrapping an Optional value" error, this is an issue that has been mentioned on the Apple Developer Forums.
If you are already running watchOS 2.1, you could try the latest watchOS beta to see if the issue has been fixed yet for 2.2. You should also file a bug report and dupe radar 22947535.

Trouble with iOS 8 CoreData iCloud sync consistency

I have a universal app written in Swift using xCode 6.3.2. It is currently very simple in that when I push a button a random number is generated and then stored using CoreData. This works perfectly until I implement iCloud. With iCloud enabled storing a new random number doesn't always propagate onto additional devices. It does most of the time, but not always.
I am testing using an iPad Air, iPhone 6 Plus and iPhone 4s
I am using the following three notification observers:
NSNotificationCenter.defaultCenter().addObserver(self, selector: "persistentStoreDidChange", name: NSPersistentStoreCoordinatorStoresDidChangeNotification, object: nil)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "persistentStoreWillChange:", name: NSPersistentStoreCoordinatorStoresWillChangeNotification, object: managedContext.persistentStoreCoordinator)
NSNotificationCenter.defaultCenter().addObserver(self, selector: "receiveiCloudChanges:", name: NSPersistentStoreDidImportUbiquitousContentChangesNotification, object: managedContext.persistentStoreCoordinator)
and here is the function for the third one:
func receiveiCloudChanges(notification: NSNotification)
{
println("iCloud changes have occured")
dispatch_async(dispatch_get_main_queue())
{
self.activityIndicator.startAnimating()
self.updateLabel.text = "iCloud changes have occured"
self.managedContext.performBlockAndWait
{ () -> Void in
self.managedContext.mergeChangesFromContextDidSaveNotification(notification)
}
self.reloadTextViewAndTableView()
self.activityIndicator.stopAnimating()
}
}
I am not attempting to update the UI until the managedContext is finished with the merge, and I am performing everything on the main thread. I am really at a loss why the changes on one device are only displayed on the second or third one about 90-95% of the time.
As part of my trial and error, when I went to delete the app from my test devices and reinstall there is sometimes a message that an iCloud operation is pending, but it doesn't matter how long I wait, once the devices are out of sync they stay that way. Even when they are out of sync if I add another number or two those will still propagate to the other devices, but then I will invariably lose more data. It seems to work about 90% of the time.
I use the following to update the UI:
func reloadTextViewAndTableView()
{
let allPeopleFetch = NSFetchRequest(entityName: "Person")
var error : NSError?
let result = managedContext.executeFetchRequest(allPeopleFetch, error: &error) as! [Person]?
//Now reload the textView by grabbing every person in the DB and appending it to the textView
textView.text = ""
allPeople = managedContext.executeFetchRequest(allPeopleFetch, error: &error) as! [Person]
for dude in allPeople
{
textView.text = textView.text.stringByAppendingString(dude.name)
}
tableView.reloadData()
println("allPeople.count = \(allPeople.count)")
}
I am really at a stand still here. I am just not sure why it "usually" works...
So I am still not sure how or why CoreData is sometimes getting out of sync as described above. I have found though that if I enter one new number after another very rapidly is when it usually occurs.
As a workaround I have added a button to the UI that allows the user to force a resync with iCloud by rebuilding the NSPersistentStore using the following option.
NSPersistentStoreRebuildFromUbiquitousContentOption: true
I would much rather the store stayed in sync with all other devices all the time, but at least this way the user will never lose data. If they notice that they are missing a record they know they entered on another device, then all they have to do is hit the resync button.