evermeer / evcloudkitdao Goto Github PK
View Code? Open in Web Editor NEWSimplified access to Apple's CloudKit
License: Other
Simplified access to Apple's CloudKit
License: Other
Hi,
I am trying to modify the record as another user and save the modified record. However, iCloud is giving me this error message:" server message = "WRITE operation not permitted"
Does that mean I cant modify the record created by other user? Thanks
I am trying to use the connect method to create the subscription handler. The insert handler is working fine when there is data being added. However, when I modify the existing data created by another user, and save it the icloud, the original user doesn't receive the update notification. Do you know how should I deal with such situation?
Hey there!
I'm using EVCloudKitDao's query method (I'm accessing mostly static data) and was wondering how the caching mechanism worked? Do I need to call .connect
to enable the caching mechanism? Any time I close the app and reopen the app, the query's are run. Does the cache support what happens via a query and is it persisted between sessions?
Thanks!
I keep getting this error everytime I tried to use the EVCloudData.privateDB.connect method. Do you have any idea what might cause that?
Thanks
In iOS Settings->iCloud->iCloud Drive a setting exists:
"Look Me Up By Email"
Clicking it shows description:
"Apps that have requested permissions to look you up by your Apple ID will appear here People who look you up will see your first and last name".
AppMessage is not listed, nor any other app. How does this feature relate to EVCloudKitDao if at all? When will an app be listed, i.e. what API call would generate an entry here. Documentation on search seems fairly light. When the iCloud Drive switch is turned off, the "Look Me Up By Email" setting persists seeming to indicate iCloud Drive is not a requirement for it's intended capability.
Is this feature related to the call EVCloudKitDao.publicDB.discoverUserInfo?
Also, listed below the "Look Me Up By Email" setting is a list of Apps, including AppMessage. Please confirm this is a selective enable/disable of iCloud Drive itself on a per app basis. Can't seem to locate any documentation describing this feature.
It is currently possible to use EVCloudKitDao's functionality before it has completed initialization, specifically before the completion closure being called by accountStatusWithCompletionHandler() and the user's account status being assigned to the accountStatus property. While the accountStatus property can be checked for a nil value, I don't see an obvious way to receive a callback or notification when initialization has completed without making source changes to the project.
I thought about calling the accountStatusWithCompletionHandler method directly, but I doubt that getting a callback from my own execution will guarantee that EVCloudKitDao's call has also been completed.
I need this due to the new Apple TV's heavy dependence on iCloud for application data storage. The first view displayed by my app depends on the user's logged in status and if they've previously created documents with my app. I need to display an error screen if the user isn't (a) logged in to their iCloud account or (b) they are unable to save documents using the account, or a loading screen until EVCloudKitDao is finished initializing and I've checked for previously created documents.
I am admittedly new to both Swift and (i/tv)OS(x) development, so I won't be surprised if there is a simple way to monitor the initialization state without modifying the project code. Do you have any suggestions on how this can be handled? Thanks.
Hi,
Subscribe for new Message \ News doesn't work for me, Any idea?
Hello again, sorry to always be the bearer of bad news! I updated to the new beta of Xcode (7.2 Beta 3) and tvOS (9.1 Beta 2), both of which were released today, and found that I am unable to build the project due to the following. Hopefully this requires nothing more than a rebuild with updated libs or something similar. It could also be a bug in the beta releases.
EVCloudKitDao.swift -
'shouldSendContentAvailable' is unavailable
'shouldSendContentAvailable' has been explicitly marked unavailable here
The attempt to set the property value occurs in the "subscribe" method, line 636:
let createSubscription = { () -> () in
let subscription = CKSubscription(recordType: recordType, predicate: predicate, subscriptionID:key, options: [.FiresOnRecordCreation, .FiresOnRecordUpdate, .FiresOnRecordDeletion])
subscription.notificationInfo = CKNotificationInfo()
subscription.notificationInfo!.shouldSendContentAvailable = true
if let configure = configureNotificationInfo {
configure(notificationInfo: subscription.notificationInfo!)
}
self.database.saveSubscription(subscription, completionHandler: { savedSubscription, error in
self.handleCallback(error, errorHandler: errorHandler, completionHandler: {
EVLog("Subscription created for key \(key)")
})
})
Quick question for you... I am continuing to test and ran into an issue. I am simulating situations where items are added on one device and removed on another. I am hitting an unhanded exception if I follow these steps:
The unhandled exception is occurring in the EVCloudData deleteObject method.
/**
Delete an object from every data collection where it's part of
- parameter recordId: The recordId of the object that will be deleted
:return: No return value
*/
private func deleteObject(recordId: String) {
for (filter, _) in self.data {
let itemID: Int? = data[filter]!.EVindexOf {item in return item.recordID.recordName == recordId}
if itemID != nil {
data[filter]!.removeAtIndex(itemID!)
NSOperationQueue.mainQueue().addOperationWithBlock {
(self.deletedHandlers[filter]!)(recordId: recordId, dataIndex:itemID!)
(self.dataChangedHandlers[filter]!)()
self.postDataDeletedNotification(filter, recordId: recordId, dataIndex: itemID!)
}
dataIsUpdated(filter)
}
}
}
This line of code is the culprit:
let itemID: Int? = data[filter]!.EVindexOf {item in return item.recordID.recordName == recordId}
It blows up when the item's recordName/recordId comparison is attempted. Here's the exception output:
2015-11-30 15:52:47.748 Marquees[1818:672274] -[__NSDictionaryM recordName]: unrecognized selector sent to instance 0x147758100
2015-11-30 15:52:47.751 Marquees[1818:672274] *** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[__NSDictionaryM recordName]: unrecognized selector sent to instance 0x147758100'
*** First throw call stack:
(0x1857458e8 0x184db3f80 0x18574c5fc 0x1857495a0 0x18564d6cc 0x1001e18f4 0x1001e0d40 0x1001d5da4 0x1001d6278 0x1001e55f0 0x1001f2f20 0x1001db530 0x100083010 0x1000830f8 0x18a6200b0 0x18a60ef20 0x186ceb790 0x186cebb10 0x1856fcee4 0x1856fc978 0x1856fa678 0x1856296c0 0x186b34088 0x18a3d1d64 0x1000855b0 0x1851ca974)
libc++abi.dylib: terminating with uncaught exception of type NSException
Message from debugger: Terminated due to signal 6
Prior to executing the problem line, I inspected the item:
Printing description of item:
(EVCloudKitDao.EVCloudKitDataObject) item = 0x00000001476fb3f0 {
EVReflection.EVObject = {
ObjectiveC.NSObject = {}
}
recordID = 0x0000000147758100 {
ObjectiveC.NSObject = {}
}
recordType = "Performance"
creationDate = 0x000000014775cab0 2015-11-30 21:48:51 UTC
creatorUserRecordID = Some {
Some = 0x0000000147757ec0 {
ObjectiveC.NSObject = {}
}
}
modificationDate = 0x000000014775cb20 2015-11-30 21:48:51 UTC
lastModifiedUserRecordID = Some {
Some = 0x000000014773f5e0 {
ObjectiveC.NSObject = {}
}
}
recordChangeTag = "ihmhmw90"
encodedSystemFields = 0 bytes {
Some = 0x000000014773f060 0 bytes
}
}
Have you seen this issue before? I'm not sure if any of my recent changes would have impacted this or not.
Hey EV! Your library here is really amazing, thank you so much for your hard work on it.
I have a couple of (probably stupid) questions about when you cache data. I have looked through the docs and I'm not seeing all the details about how it is caching...
1 - Is it saving a file to NSUserDefaults? I'm assuming the cache persists on app launch/close.
2 - I know EVCloudKitDao will be able to view/edit/save this data, but I am wondering what kind of format this file is in. (I'm hoping to avoid having to set up Core Data to interface with CloudKit)
3 - Can you explain what I can expect if the user launches the app without internet, but with a local cache saved?
Thanks! I'm a beginner so I appreciate your patience :)
Code could be added to detect changes to the user's iCloud account status so the connectStatus property value is automatically updated and (possibly) severed connections can be automatically restored.
I would like your opinion on adding this to your library. It makes sense to me, since it seems your library is meant to avoid the need to directly work with CloudKit or manage error conditions related to the user's iCloud account status. Basically, some code like I have in my tvOS app would need to be added:
// NotificationManager class from http://moreindirection.blogspot.com/2014/08/nsnotificationcenter-swift-and-blocks.html. Could modify to use NSNotificationManager directly in this case.
private let notificationManager = NotificationManager()
notificationManager.registerObserver(NSUbiquityIdentityDidChangeNotification) { [unowned self] note in
// Verify iCloud account status hasn't changed
self.verifyiCloudAccountStatus()
}
func verifyiCloudAccountStatus() {
Async.main { [unowned self] in
var reset: Bool = false
let tokenKey = "NL.EVICT.CloudKit.UbiquityIdentityToken"
// Get the current iCloud token
let currentToken = NSFileManager.defaultManager().ubiquityIdentityToken
if currentToken != nil {
// Check for a previous token being written to user defaults and compare tokens if found
let existingToken = NSUserDefaults.standardUserDefaults().objectForKey(tokenKey)
if (existingToken == nil || currentToken!.isEqual(existingToken)) {
let newTokenData: NSData = NSKeyedArchiver.archivedDataWithRootObject(currentToken!)
NSUserDefaults.standardUserDefaults().setObject(newTokenData, forKey: tokenKey)
reset = true
}
} else {
NSUserDefaults.standardUserDefaults().removeObjectForKey(tokenKey)
reset = true
}
if reset {
// TODO: Update connectStatus property and possibly reestablish connections as needed
}
}
}
Also, FYI, this will probably be the last change I will be suggesting for a while. Your library works great overall. The effort you've put into it is greatly appreciated. I don't know of anything else I need from it that it doesn't already deliver at this point.
I realized that there are limits for the daily transfer of the data in icloud. As for EVCloudDataObject, it contains many field, such as modify date, recordID, and so on. I am wondering what's the size of one EVCloudDataObject? Have you encountered any problem that would go over the limit of the icloud storage?
Since in my app, it continuously retrieves certain public data whenever the view is loaded. I kind of want to know the impact of this limitation of the cloudkit.
I am taking advantage of your recently-added support for subobjects to be written and read from iCloud. I have local caching disabled to avoid the current failure to restore all properties via NSCoder.
I have the following class/property structure:
class PerformanceContent:
var options: PerformanceOptions:
var playback: PlaybackOptions:
var endlessPlayback: CloudBool
var state: CloudBoolState (an integer-based enumeration)
This nested class structure results in this field in the PerformanceContent CKRecord:
options__playback__endlessPlayback__state
When I assign a different enumeration value to the state property, it is written as expected. I see the correct value when I inspect the record in the CloudKit dashboard. However, it is always set to its initially-assigned enumeration value (Unassigned, -1) when I shut down and restart the app.
Note that the top-level PerformanceContent class (which the CKRecord is based on) doesn't contain any immediate properties, meaning strings, ints, etc. Every field in the record is a sub-object. I mention this in case it gives a clue as to what is causing this issue.
Here is the source for my CloudBool class. I expected the setValue:forUndefinedKey method to be called to restore the state property's value since it is an enumeration, but a breakpoint on the method never gets hit.
public class CloudBool: TBCloudKitDataObject {
public enum CloudBoolState: Int, EVRawInt {
case Unassigned = -1, False = 0, True = 1
public init() { self = Unassigned }
public init(value: Bool?) {
self.init()
assignOptionalBool(value)
}
public mutating func assignOptionalBool(value: Bool?) {
if let val = value {
if val {
self = .True
} else {
self = .False
}
} else {
self = .Unassigned
}
}
public mutating func assignRawValue(rawValue: Int) {
if let val = CloudBoolState(rawValue: rawValue) {
self = val
}
}
public func asBool() -> Bool {
switch self {
case .True:
return true
default:
return false
}
}
public func asOptionalBool() -> Bool? {
switch self {
case .Unassigned:
return nil
case .False:
return false
case .True:
return true
}
}
}
public var state: CloudBoolState = .Unassigned
public override func setValue(value: AnyObject!, forUndefinedKey key: String) {
switch key {
case "state":
state.assignRawValue(value as! Int)
default:
super.setValue(value, forUndefinedKey: key)
}
}
}
EVCloudKitDao does not currently appear to support silent notifications. For example, when silent notifications are received, the method didReceiveRemoteNotification:fetchCompletionHandler will be called in place of the current AppDelegate didReceiveRemoteNotification.
Certain fields should not appear in the notification info which trigger CloudKit into recognizing this is a silent notification.
fyi: I receive the following deprecation warnings in some/all view controllers when compiling in iOS 9
.../RootViewController.swift:100:75: 'firstName' was deprecated in iOS 9.0: Use -[[CKDiscoveredUserInfo displayContact] givenName]
What is the plan (if any) to migrate the project forward to ios9?
The fetchChangeNotifications method is accessed via EVCloudData.privateDB and EVCloudData.publicDB. If my app is storing data in both the public and private DBs, should calls to both DB's fetchChangeNotifications methods be made?
Also, I see messages like this in the console all the time, indicating that received notifications are not CloudKit Query notifications, even though they do appear to be just that. Is this indicating a problem, or can it be safely ignored?
01/20/2016 15:38:16:975 Marquees)[459:.] AppDelegate.swift(257) application(_:didReceiveRemoteNotification:):
Push received
01/20/2016 15:38:16:976 Marquees)[459:.] AppDelegate.swift(259) application(_:didReceiveRemoteNotification:):
Not a CloudKit Query notification.
01/20/2016 15:38:16:978 Marquees)[459:.] EVCloudKitDao.swift(1195) didReceiveRemoteNotification(_:executeIfNonQuery:inserted:updated:deleted:completed:):
WARNING: The retrieved notification is not a CloudKit query notification.
===>userInfo = [ck: {
ce = 2;
cid = "iCloud.com.tommyb.Marquees";
nid = "363ec9be-24c5-4c2f-949e-5989c0567272";
}]
notification = <CKNotification: 0x14470a510; notificationType=0, notificationID=<CKNotificationID: 0x144725e90; UUID=363ec9be-24c5-4c2f-949e-5989c0567272>, containerIdentifier=iCloud.com.tommyb.Marquees>
Thanks for the needed clarification on this.
Is there currently a way to force the public/private db's accountStatus property to be updated when a NSUbiquityIdentityDidChangeNotification is received or when the app becomes active and a change in the iCloud account status is detected by inspecting the NSFileManager.defaultManager().ubiquityIdentityToken?
Hello again, sorry if I'm becoming a pest! And apologies if this is a newb question as well.
I am displaying an activity indicator when I call EVCloudData.privateDB.connect(). I need to stop the activity indicator when my completionHandler is called but I don't see how to know that the query has finished returning results.
In looking at the source, I see that a cursor is received by the queryCompletionBlock closure and that if the completionHandler returns true and the cursor is not nil, the private queryRecords method is called with the cursor being passed to it to continue retrieving query results.
It seems like passing an "isFinished" boolean value (defined as "cursor == nil") to the completionHandler would be an easy way to handle this if there isn't an existing means of determining this. If this is what is needed, I will be happy to produce a pull request that makes the needed changes to the project.
Thanks as always for your efforts on this project.
Hi,
I am currently trying to query a record with NSPredicate(format: "field BEGINSWITH %@", searchfield), I notice that the code inside the completion block is run with a noticeable delay. I used connect method before with different NSPredicate, but the result is pretty fast. I am wondering, if I just use the query method directly, would it slow down the completion block execution, or is it because of the NSPredicate type?
Hey Edwin, fantastic work on this library! I have learned a lot from it... I did not want the reflection mapping, so I ended up borrowing bits and pieces from your lib, but now that I have done so, I wish I had just taken the whole thing! :-)
Anyway, this isn't really an issue with your code, so much as a Swift language change with v2.0. I noticed that you can no longer specify binary operators as such (see: http://stackoverflow.com/questions/30761996/swift-2-0-binary-operator-cannot-be-applied-to-two-uiusernotificationtype):
var subscription = CKSubscription(recordType: recordType, predicate: predicate, subscriptionID:key, options: .FiresOnRecordCreation | .FiresOnRecordUpdate | .FiresOnRecordDeletion)
Instead, I believe the way to do it now, is as so:
var subscription = CKSubscription(recordType: recordType, predicate: predicate, subscriptionID:key, options: [.FiresOnRecordCreation, .FiresOnRecordUpdate, .FiresOnRecordDeletion])
Hope this helps!
-Gene
I need to connect to the same container from both my tvOS app and its TVTopShelfProvider extension. Thus far during the development of my app, I've been using its default container and EVCloudKit's publicDB and privateDB accessors. According to the Apple iCloud documentation (quoted below), I should be able to connect to one app's default container from another app that is developed using the same developer team account.
I attempted to do this by changing references to publicDB and privateDB to publicDBForContainer and privateDBForContainer and passing my app's bundle identifier name as the container name. After doing this, I don't see any error messages in the console output, but my app is no longer retrieving any data. Will I need to create a custom container to be used by the app and extension in order for this to work with EVCloudKitDao?
_Share Containers Between Apps_
Optionally, configure your app to use multiple containers or share a container with other apps. For example, you might use one app internally to create record types and records programmatically to return a database to a known state. This app needs to share the same container as the end-user app you are developing and testing. _To do this, you enable the first app to use the default container of the second app_ or create a custom container that both apps share. iOS and Mac apps can also be configured to share the same containers.
Reported by @tbaggett
locally-cached data is still loaded if you change the caching strategy to None after caching was previously enabled. I wouldn't think this would matter unless there are explicit use cases where the caching strategy would be intentionally changed from/to None based on certain criteria.
Currently the code doesn't consider the caching strategy when attempting to restore data. I just deleted my app to remove the cache files in my case, since disabling caching is a temporary fix for the issue I ran into.
Error:
NSLocalizedDescription=Field '___createTime' is not marked sortable}, ErrorDescription: Field '___createTime' is not marked sortable]
CODE
Caused by this statement.
EVCloudKitDao
query.sortDescriptors = [NSSortDescriptor(key: "creationDate", ascending: false)]
REPRODUCE
running XCode 6.3 Swift, swift XCTestCase module.
Follow steps similar to the official prescribed methods or this
https://github.com/evermeer/EVCloudKitDao/blob/master/AppMessage/AppMessage/Controlers/TestsViewController.swift.
Create any new object and wait 10 seconds, query it, it will produce this error.
They suggest the problem will resolve with time. It isn't my case.
http://stackoverflow.com/questions/24907319/cloudkit-data-purged-again-and-now-i-cannot-sort-on-creationdate
Title says it all. I'd like to specify sorting criteria when calling EVCloudData.connect(...), which will need to in turn pass the criteria to EVCloudKitDao. I don't currently see a way to do that. Am I missing it, or is it something you plan to add? Thanks.
Hi Edwin,
I REALLY hope you will have some immediate ideas on an issue I ran into today.
First, a little background info: tvOS apps have what is called a "Top Shelf" extension. You implement the extension when you want to provide dynamic content at the top of the main Apple TV interface if your app is moved to the top row and selected. See https://developer.apple.com/tvos/human-interface-guidelines/#top-shelf for more info.
I need to access some of my iCloud data in the extension code, which requires me moving some of my support code and your EVCloudKitDao pod into a framework that can be accessible by both my app and the extension. I took it a step further, creating both a reusable "CommonData" framework, which contains my observable classes and EVCloudKitDao, and an app-specific data framework, which contains my app-specific EVCloudKitDataObject-derived classes and models.
Everything compiles and runs successfully, but no data is ever retrieved. IOW, all the EVCloudData.privateDB.data[""] members are empty arrays. I also don't see any errors indicated in the console output.
FYI, I made a copy of the whole project to make these changes in. If I run the original unchanged project, everything works fine, even after updating to the latest pod versions. I can also still see all the data in the iCloud Dashboard.
I realize this may not be a problem with your project at all, but I am not very familiar with how your EVReflection library that EVCloudKitDao depends on works. Is it possible that there is a reflection issue being caused by EVCloudKitDao being in one framework and the EVCloudKitDataObject-derived classes being in another framework?
Any possible directions to look to solve this is GREATLY appreciated. I'm stumped at the moment. Thanks!
I have found the root cause of some of my problems with property values not being their expected values when retrieving data saved in CloudKit using EVCloudKitDao. I'm not sure if this can be easily or at all fixed though. Please review my observations on this and provide your thoughts.
Consider a class structure like the following:
public class A : EVCloudKitDataObject {
public var b = B()
public required init() {
super.init()
b.intProp = 9
}
}
public class B: EVCloudKitDataObject {
public required init() {
super.init()
intProp = 5
}
public var intProp = 0 {
didSet {
EVLog("intProp set to \(intProp)")
}
}
// NOT storing the intProp property value in CloudKit is key to this issue
override public func propertyMapping() -> [(String?, String?)] {
return [("intProp", nil)]
}
}
If you set breakpoints in Class A's assignment of 9 to the b.intProp property and the EVLog("intProp set to...") statements, you will see the following behavior in action.
If an instance of class A is saved to CloudKit using EVCloudKitDao, then retrieved, the value of the "b" variable in the class A instance will be 5, not 9 as expected. This is due to a second instance of class B being directly instantiated by EVCloudKitDao and assigned to the class A instance's "b" property after it has already been instantiated from an instance of Class A. This also means sub-objects are instantiated multiple times, one per level of however deep they are nested in the root EVCloudKitDataObject being saved.
Is it possible to assign stored values to the already-instantiated sub-objects instead of instantiating new instances? If not, I believe this means I will have to remove any objects that I was expecting EVCloudKitDao to ignore using the propertyMapping override.
Hi, I'm trying to run the demo app on my iPhone 5S with iOS v9.1 and keep getting a "DENIED CLOUDKIT" state in the permission scope popup when running it. If I delete the app and remove it, I am always prompted to allow push notifications, but I never see a system prompt to allow the app access to my iCloud account.
It seems to run okay in the simulator using an iPhone 6s Plus virtual device, although the news items don't update unless I shut down and restart the app (no notification receipt and automatic updating if left on the news screen). I believe this is an issue with the simulator though. Doesn't it have problems receiving push notifications?
I did the following as instructed in order to build and run the app:
When I launch the app, I get this console output:
10/26/2015 03:36:26:752 AppMessage)[209:.] EVCloudKitDao.swift(172) init():
Container identifier = Optional("iCloud.com.tommyb.AppMessage")
10/26/2015 03:36:26:768 AppMessage)[209:.] EVCloudKitDao.swift(170) init():
Account status = 1 (0=CouldNotDetermine/1=Available/2=Restricted/3=NoAccount)
10/26/2015 03:36:27:565 AppMessage)[209:.] EVCloudKitDao.swift(423) getUserInfo(_:errorHandler:):
requestDiscoverabilityPermission : No permissions
2015-10-26 03:36:27.570 AppMessage[209:4657] Error: Optional(Error Domain=EVCloudKitDao Code=1 "(null)")
10/26/2015 03:36:27:572 AppMessage)[209:.] RootViewController.swift(86) getUser:
ERROR in getUserInfo: Error Domain=EVCloudKitDao Code=1 "(null)"
10/26/2015 03:36:27:575 AppMessage)[209:.] RootViewController.swift(87) getUser:
You have to log in to your iCloud account. Open the Settings app, Go to iCloud and sign in with your account. (It could also be that your project iCloud entitlements are wrong)
10/26/2015 03:36:27:588 AppMessage)[209:.] Helper.swift(34) showError:
ERROR: Could not get user: The operation couldn’t be completed. (EVCloudKitDao error 1.)
I have tried deleting the app and shutting down and powering up my phone but I still see the same behavior. Any suggestions on what I can do to fix this?
When using the recently-added sorting capability in EVCloudData, new items that are created and saved aren't added to existing filter results in the order specified by the filter's sorting criteria. The lists are sorted correctly if I restart the app with local caching disabled, in which case the results are retrieved from CloudKit again. Caching may not effect this, but I currently have it disabled and these are the results I see in that case.
Testing on current beta releases of Xcode (7.2 Beta 3) and tvOS (9.1 Beta 2) on both the simulator and an Apple TV device against my iCloud development environment.
Attempting to retrieve signed-in iCloud user's first and last name by calling the EVCloudKitDao.privateDB.discoverUserInfo() method.
CloudKit is returning a nil value for the user. Line 421, where an attempt to call the provided completion handler, causes an exception when an attempt is made to unwrap the nil value. I verified that a non-nil recordID was returned. Any idea why no user information is being returned?
I also tried using the publicDB.discoverUserInfo() method and saw the same results.
public func discoverUserInfo(completionHandler: (user: CKDiscoveredUserInfo) -> Void, errorHandler:((error:NSError) -> Void)? = nil) {
container.fetchUserRecordIDWithCompletionHandler({recordID, error in
self.handleCallback(error, errorHandler: errorHandler, completionHandler: {
self.container.discoverUserInfoWithUserRecordID(recordID!, completionHandler: { user, error in
self.handleCallback(error, errorHandler: errorHandler, completionHandler: {
self.activeUser = user
// no errors were returned by CloudKit but user is nil
completionHandler(user: user!)
})
})
})
})
}
As discussed with @tbaggett
What is processed by NSCoding is the same as what's processed using the .toDictionary. So the propertyMapping will also be applied. Now I think of it, it might be a good idea to change the performKeyCleanup for that function to support more situations like:
raw - where everything comes pure from the Reflect
standard - where the propertyMapping and propertyConverter are executed
cleaned - where keywords, illegal characters, pascal, camel and snake case conversions are executed.
Then NSCoding should use the raw version since you want identical objects back.
Hi Edwin,
Per the tvOS documentation, local storage is very limited for tvOS apps. I wanted to get your thoughts on disabling local EVCloudData caching on tvOS or forcing it to store the local files in the cache folder instead of the documents directory. I will be happy to implement what is needed once I hear from you. Thanks!
Apple documentation on tvOS storage:
Local Storage for Your App Is Limited
The maximum size of an Apple TV app is limited to 200 MB. Moreover, your app can only access 500 KB of persistent storage that is local to the device (using the NSUserDefaults class). Outside of this limited local storage, all other data must be purgeable by the operating system when space is low. You have a few options for managing these resources:
Hey,
I neither can add contacts to the messaging app, nor is the description correct. (You've stated that I should open the left side menu for contacts where the right side menu says "contacts".
Is this app currently within restructuring?
I just found an error occurred during startup, "Could not cast value of type 'CKNotification' (0x35cde2ac) to 'CKQueryNotification' (0x35cde2d4)." in the line : var queryNotification: CKQueryNotification = cloudNotification as! CKQueryNotification. This is inside the function didReceiveRemoteNotification() in the EVCloudKitDao.swift. I think its the downcast problem.
Hi, I just found a problem with the EVCloudData.connect method, I called initializeConnection() method in the viewDidAppear() function, then in deinit I called disconnect method. However, when I switch back to another view and come back, it will call viewDidAppear again and called EVCloudData.connect first, then it will call the deinit of the previous view to disconnect, this would cause the EVCloudData.publicDB.data[filterID] filed to be nil which causes error when loading data in the tableview.
I wonder if calling disconnect is necessary? otherwise I have to manually check when to call the disconnect method in order to prevent the crash.
Hi there, I kept crashing here:
EVCloudKitDao.publicDB.createRecordTypes([Message(), Asset(), News()])
With error:
*** Terminating app due to uncaught exception 'CKException', reason: 'recordType (AppMessage.Message) contains invalid characters'
Here is the log:
AppMessage.Message {
hash = -751037153
key = MessageType, value = T
key = From_ID, value =
key = encodedSystemFields, value =
key = Asset, value =
key = creatorUserRecordID, value =
key = FromLastName, value =
key = Asset_ID, value =
key = recordType, value =
key = ToFirstName, value =
key = Longitude, value = 4.8653827
key = FromFirstName, value =
key = To, value =
key = creationDate, value = 2015-05-20 05:16:35 +0000
key = To_ID, value =
key = Text, value =
key = lastModifiedUserRecordID, value =
key = modificationDate, value = 2015-05-20 05:16:35 +0000
key = ToLastName, value =
key = From, value =
key = recordID, value = <CKRecordID: 0x7b088f10; 96F70DAC-8AA2-4F46-A027-D8368155B014:(_defaultZone:defaultOwner)>
key = recordChangeTag, value =
key = Latitude, value = 52.8350711
}
When I try to run it I get these three errors:
diff: /../Podfile.lock: No such file or directory
diff: /Manifest.lock: No such file or directory
error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.
Tried several ways in this thread to little avail.
http://stackoverflow.com/questions/24030814/swift-language-nsclassfromstring
e.g.
@objc(Messsage)
class Message : EVCloudKitDataObject
Fix:
In EVReflection:
Use objc_getClass(anyobjectTypeString) instead of swiftClassFromString(anyobjectTypeString).
Hi Edwin,
If the app accesses iCloud too early, one might experience this error
<CKError 0x16e798a0: "Not Authenticated" (9/1002); "CloudKit access was denied by user settings"; Retry after 3.0 seconds>.
This function was partially unable to manage the time specific variable given the return CKError.
Perhaps adds a special case to the function below like so..
public static func handleCloudKitErrorAs(error:NSError?, retryAttempt:Double = 1) ->
...
case .NotAuthenticated .NetworkUnavailable, .NetworkFailure, .ServiceUnavailable, .RequestRateLimited, .ZoneBusy, .ResultsTruncated:
...
Not aware they mention this in the documentation, the following implementation also support NotAuthenticated in my test.
if let userInfo = error?.userInfo {
if let retry = userInfo[CKErrorRetryAfterKey] as? NSNumber {
seconds = Double(retry)
}
Thanks for the private email addressed my inquiry!
update from @evermeer: I updated the questions. Some info was not rendered.
The UIBackgroundFetchResult enum is prohibited from use in tvOS. Sample app's AppDelegate didReceiveRemoteNotification example should be documented accordingly for tvOS developers who are using the iOS sample app as a guide.
Enumeration declaration from tvOS 9.1's UIApplication.h file:
typedef NS_ENUM(NSUInteger, UIBackgroundFetchResult) {
UIBackgroundFetchResultNewData,
UIBackgroundFetchResultNoData,
UIBackgroundFetchResultFailed
} NS_ENUM_AVAILABLE_IOS(7_0) __TVOS_PROHIBITED;
Please see this function below for reference.
database.fetchRecordWithID returns an optional value from "record" in the completionHandler.
In the following segment, this function, "self.fromCKRecord(record)", doesn't accept optional value. Will crash if the record is nil value.
Hope it helps.
Thanks for your comment here
http://stackoverflow.com/questions/30726681/find-user-name-or-first-last-name-in-ios9-cloudkit-abperson-abaddressbook/30816414#30816414
K.
My fix:
..
database.fetchRecordWithID(CKRecordID(recordName: recordId), completionHandler: { (record: CKRecord?, error: NSError?) in
..
Reference:
public func getItem(recordId: String, completionHandler: (result: EVCloudKitDataObject) -> Void, errorHandler:((error: NSError) -> Void)? = nil) {
database.fetchRecordWithID(CKRecordID(recordName: recordId), completionHandler: {record, error in
self.handleCallback(error, errorHandler: errorHandler, completionHandler: {
if let parsed = self.fromCKRecord(record) {
completionHandler(result: parsed);
I know that the saveItem is used to save new record. Does it apply to modifying the existing record? or should use another API to do so? Thanks for the great library btw
discoverUserInfo spent a long long long time and nothing happen
how can i get the current user id without using discoverUserInfo
`HUDshow(self.view)
let sema = dispatch_semaphore_create(0)
EVCloudKitDao.publicDB.discoverUserInfo({
print("user \($0)")
HUDhide(self.view)
dispatch_semaphore_signal(sema);
}, errorHandler: {
print("error \($0)")
HUDhide(self.view)
dispatch_semaphore_signal(sema);
})
HUDhide(self.view)
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);`
I am seeing this warning every time a CKRecordID instance is handled:
WARNING: Should have been handled somewere else: value <CKRecordID: 0x127f971b0; 02F113F2-39F5-4E21-83A0-008CFB786440:(_defaultZone:__defaultOwner__)> type CKRecordID
Note that I am compiling things for tvOS 9.1. I am also restricting EVCloudKitDao and EVReflection to extension-compatible APIs so they can be used with my tvOS top shelf extension.
Can this be safely ignored?
FYI, I just updated my branch to incorporate your latest changes. I am seeing a build failure due to this line of code in TestsViewController::ignoreFieldTest():
let record = EVCloudKitDao().toCKRecord(myObj)
The reported error is:
'EVCloudKitDao' cannot be constructed because it has no accessible initializers
Thanks for what looks like an AWESOME library! I'm new to tvOS, Swift, Cocoapods, etc., but I think this should be relatively straightforward. It should just be a matter of adding a target for tvOS 9.0 to the pod config, right?
I was hoping to create a single private CKRecord type that contains fields that are defined by multiple child classes. For example, an ApplicationOptions record type that contains all options, but having those options divided up into child classes, like the example below. I thought a way to do this might be to have the child classes inherit from EVObject, like so:
public class ApplicationOptions: EVCloudKitDataObject {
public var audio = AudioOptions()
public var video = VideoOptions()
public var playback = PlaybackOptions()
}
public class AudioOptions: EVObject {
public audioPreferenceOne: Bool = false
}
public class VideoOptions: EVObject {
public videoPreferenceOne: Bool = false
}
public class PlaybackOptions: EVObject {
public playbackPreferenceOne: Bool = false
}
The desired result would be an ApplicationOptions CKRecord that contains fields named something like audio_audioPreferenceOne, video_videoPreferenceOne and playback_playbackPreferenceOne. In this case, there will only ever be a single record instance for application options, and it would be overkill imho to also create separate record types for each of the child classes. They are also expected to only result in a single record instance per user.
When I attempt something like the above example, I get this unhandled exception:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Objects of class __NSDictionaryM cannot be set on CKRecord'
Is there any way to accomplish this type of thing, or is this beyond the scope of what is supposed to be possible? Thanks...
Is the recent release Xcode 7-beta-5 / iOS9 / Swift 2.0 compatible?
Hi, I tested this, but there have a problem.
If App name includes a blank, there have a problem in EVReflection.swift class.
For example, my App is "Practice Reading".
Xcode convert automatically the App name "Practice_Reading", but 'swiftStringFromClass' method in EVReflection.swift recognition likes this "Practice Reading".
Work Around
Have any solution?
EVCloudKitDao.publicDB.getItem(messa.asset_id, completionHandler: { result in
let asset = result as! Asset
if let image = asset.image(){
ImageStore(image,imageName: messa.assetName)
}
}, errorHandler: {error in
print("photo \(error)")
})
Pending notifications should be checked 1) when your App launches, or 2) becomes active, or 3) it receives a push notification, it should first check with CloudKit if there are CKNotification objects pending to be processed".
Thus, there should be checks for notifications in methods:
didFinishLaunchingWithOptions
applicationDidBecomeActive
didReceiveRemoteNotification
The example covers (1) and (3), though does not check notifications for (2). This could have adverse affect if the app is backgrounded, then foregrounded, notifications might be missed/not processed.
Brian
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.