How would I save an object of type DateComponents to a CloudKit field? - field

I am using Swift for an iOS app.
I need to store a DateComponents object as a field in CloudKit.
How would I do that?
So far I think I need to set the CloudKit field to type Bytes, and convert the DateComponents object to a Data object. Looking at the documentation for the Data class, I can't figure out how to initialize the Data object. I have no idea how to use the UnsafeBufferPointer used in one of the initializers.
The following code gives a runtime error:
newRecord.setObject((dateComponents as! __CKRecordObjCValue), forKey: DatabaseNameStrings.fieldTime)
Here's the runtime error message in the debug window:
Could not cast value of type 'NSDateComponents' (0x1d04208c8) to '__C.CKRecordValue' (0x1d0420ab0).
I also need to convert Data to DateComponents. Both Data and DateComponents classes conform to the Codable protocol, which inherits the Decoder protocol. One of DateComponents' initializers is:
init(from: Decoder)
which leads me to suspect there is a way to use that init to convert type Data to DateComponents after I get the Data object from the CloudKit field of type Bytes.
The following code:
if let data = privateRecord.object(forKey: DatabaseNameStrings.fieldTime) as? Data {
let dateComponents = DateComponents(from: data) // error here
}
generates an error in the Xcode editor which says:
Argument type 'Data' does not conform to expected type 'Decoder'
I thought since both Data and DateComponents conform to Decoder protocol that code would work.

It seems you might be over-engineering this a bit. Here are a couple alternatives you could consider:
Store the date in a date field on CloudKit and re-parse to date components in your app.
Store each date component (year, month, day, etc.) as separate integer fields on CloudKit (or as strings if you want to pre-format them).
Just a thought. Good luck. :)

Related

How to read arrays with NSPersistentCloudKitContainer (CloudKit with Core Data)?

I have [String] and [Int] attributes in my entity on Core Data/type on CloudKit, where they are String list and Int64 list respectively. I'm trying to synchronize them via NSPersistentCloudKitContainer (public server). All other attributes synchronize properly, but these two do not at all. The other ones are simple - String, Date, Int64/Int. I want to make them sync, too.
Both of them are Transformable, with their respective Custom class set to [String] and [Int]. The codegen is Manual/None, so I can create unwrapped attributes for a later use.
When I run the app at first launch I get the CKRecord with their data, too - I mean I see in the debug their values in the downloaded records information. However, when I check Core Data, it tells me nil.
I've tried setting both NSSecureUnarchiveFromData and NSSecureUnarchiveFromDataTransformerName, it didn't work out.
It's clear to me that the arrays' data is downloaded, I don't understand why Core Data doesn't like the arrays.
Okay, managed to solve it:
It must not be a List of any sort on CloudKit, but Bytes.
The rest is simply converting the arrays into NSData when uploading. I did it with Data
let archivedString = try? NSKeyedArchiver.archivedData(withRootObject: [String], requiringSecureCoding: true)
Same goes for [Int].

CoreData does not generate NSManagedObject for Swift 3 [duplicate]

This question already has an answer here:
How to auto generate NSManagedObject subclasses with date attribute as Date instead of NSDate?
(1 answer)
Closed 5 years ago.
CoreData code generation doesn't generate NSManagedObject subsclass for swift3 for example: It creates NSDate properties instead of Date. Any idea how can I generate models for Swift3?
Note:- I have found nothing in CoreData Code Generation settings to switch language explicitly to Swift 3 yet we can generate Swift models. But there are some classes which have been updated in Swift 3.
A lot of property types still use subclasses of NSObject. You will most likely need to convert them manually.
For NSDate specifically you may use:
return (date as NSDate?) ?? nil
and
return (date as Date?) ?? nil
For easier usage you can create extensions on Date and NSDate to return the typecast values the way you want it. An alternative is to change the date fields in database to be something like rawDate and then create a property date that overrides both setter and getter and does the conversion to/from original property rawDate.
In any case these objects are not excluded in Swift 3, they are just replaced with newer types in most of the API.

NSCoding to CKRecord

I have a custom class with which i made an (swift-y) struct NSCodable. Now I want to convert that into a CKRecord in order to use CloudKit. Even though I set 'key value'-pairs when encoding my struct, it is in my understanding that the struct is converted into NSData and that you can't convert it to a Dictionary (or another key-value object). So I get the feeling that this is not the way to go.
Is there a way to make this conversion directly? Or with a step in-between (for instance converting the Data into a [String: String]- dictionary)?
NSCoding is only for going from/to the same class. Instead, write a toServerDictionary method on your custom class, including only the properties you want to send to CloudKit, and then use the result to call setValuesForKeys on a CKRecord.
You'll likely find there are properties that need to be specific types, in which case its better to make your method toServerRecord and create a CKRecord and return it. You can also have a updateWithServerRecord to set anything you receive back.

How to save date type to an attribute of an entity in Core Data in Swift?

I want to set current date to my entity at the time when it is initialized by an user, so I tried to use NSDate and set its instance to the attribute in Swift, like:
task.date = NSDate() // task is an instance of Entity class
However, this code returns an error: NSDate is not a subtype of NSNumber. Actually this code worked in Objective-C, which is used in this post.
Note that I properly set the attribute's type to date in my xcdatamodeld file. Also, the other attribute (like title, which is just a string type) is successfully assigned the data to. So how can I use date to my Core Data object in Swift app?
You have to make sure that you have defined the NSManagedObject subclass correctly:
#objc(Task)
class Task: NSManagedObject {
#NSManaged var date: NSDate
}
Tested. First line is optional for this scenario.

problems setting NSDate using setValuesForKeysWithDictionary and JSON

so I am happily slamming data into my system using JSON and setValuesForKeysWithDictionary until I run into an object that has an NSDate property.
The JSON contains dates with the following format:
BackOrderDate: "2011-03-15T00:00:00"
This gives me an error:
'NSInvalidArgumentException', reason: 'Unacceptable type of value for attribute: property = "BackOrderDate"; desired type = NSDate; given type = __NSCFString; value = 2011-03-15T00:00:00
I am using JSON.NET with MVC and the following settings with it:
JsonSerializerSettings serializerSettings = new JsonSerializerSettings();
serializerSettings.Converters.Add(new Newtonsoft.Json.Converters.IsoDateTimeConverter());
string json = JsonConvert.SerializeObject(objects, Formatting.None, serializerSettings);
return Content(json);
Any ideas on how to deal with this issue? I do not want to lose the use of setValuesForKeysWithDictionary since it so fricking fast.
I assume you're using Core Data (since Core Data produces the error you posted) and that your entity has a property named “BackOrderDate” of type “Date”.
The problem is that JSON doesn't have a “Date” data type. In your .NET code you've handled this by telling the JSON serialize to convert dates to IS-formatted strings.
You need to perform the opposite conversion when you parse the JSON data in your iOS app, before you pass the data to setValuesForKeysWithDictionary:.
You can parse a string into an NSDate using an NSDateFormatter, and you can easily find lots of examples of that on the web.
How you perform the parsing before calling setValuesForKeysWithDictionary: depends on what you're using the parse the JSON.
If you're using Apple's NSJSONSerialization, you should probably use the NSJSONReadingMutableContainers option. Then go into the dictionary and replace the date string with an NSDate object before calling setValuesForKeysWithDictionary:.
If you're using some other JSON parser (like SBJSON), I don't know the best way to do the replacement.

Resources