Create Entity programmatically (Core Data) - ios

Is there a way to create a Entity programmatically on Core Data with swift2?
I searched for it, but I doesn't found something.

There are only a few tutorials on the Web (possibly only one).
I am not a fan of Xcode's GUI tools (Nibs, Storyboards, XCDataModeld, etc), so creating everything (from DB to UI) in code is usual thing for me.
The article referenced by #Lubos (2 minutes after I added a link to it in comments, hmm...) is written in ObjC.
So, here is a Swift code:
internal var _model: NSManagedObjectModel {
let model = NSManagedObjectModel()
// Create the entity
let entity = NSEntityDescription()
entity.name = "DTCachedFile"
// Assume that there is a correct
// `CachedFile` managed object class.
entity.managedObjectClassName = String(CachedFile)
// Create the attributes
var properties = Array<NSAttributeDescription>()
let remoteURLAttribute = NSAttributeDescription()
remoteURLAttribute.name = "remoteURL"
remoteURLAttribute.attributeType = .StringAttributeType
remoteURLAttribute.optional = false
remoteURLAttribute.indexed = true
properties.append(remoteURLAttribute)
let fileDataAttribute = NSAttributeDescription()
fileDataAttribute.name = "fileData"
fileDataAttribute.attributeType = .BinaryDataAttributeType
fileDataAttribute.optional = false
fileDataAttribute.allowsExternalBinaryDataStorage = true
properties.append(fileDataAttribute)
let lastAccessDateAttribute = NSAttributeDescription()
lastAccessDateAttribute.name = "lastAccessDate"
lastAccessDateAttribute.attributeType = .DateAttributeType
lastAccessDateAttribute.optional = false
properties.append(lastAccessDateAttribute)
let expirationDateAttribute = NSAttributeDescription()
expirationDateAttribute.name = "expirationDate"
expirationDateAttribute.attributeType = .DateAttributeType
expirationDateAttribute.optional = false
properties.append(expirationDateAttribute)
let contentTypeAttribute = NSAttributeDescription()
contentTypeAttribute.name = "contentType"
contentTypeAttribute.attributeType = .StringAttributeType
contentTypeAttribute.optional = true
properties.append(contentTypeAttribute)
let fileSizeAttribute = NSAttributeDescription()
fileSizeAttribute.name = "fileSize"
fileSizeAttribute.attributeType = .Integer32AttributeType
fileSizeAttribute.optional = false
properties.append(fileSizeAttribute)
let entityTagIdentifierAttribute = NSAttributeDescription()
entityTagIdentifierAttribute.name = "entityTagIdentifier"
entityTagIdentifierAttribute.attributeType = .StringAttributeType
entityTagIdentifierAttribute.optional = true
properties.append(entityTagIdentifierAttribute)
// Add attributes to entity
entity.properties = properties
// Add entity to model
model.entities = [entity]
// Done :]
return model
}
This code is equal to this CD model (created in Xcode's GUI):
Creating models in code is much more complicated than using GUI.
But, IMO, it is faster and safer than loading CoreData model file to get your model (what if no file exists? or the file is damaged?).
By 'safer' I mean that you don't have to handle disk IO errors related to reading CoreData model from disk (your model is in code, there is no need in model file). Average CoreData user just don't want to handle these errors because its easier to terminate an application

It is possible to define core data model programmatically. I found a good example, though it is written in Objective C. I am sure it is working also for Swift 2. You just need to rewrite it. Should take a few minutes.
https://www.cocoanetics.com/2012/04/creating-a-coredata-model-in-code/

Related

Is it allowed to pass NSManagedObject through performBlock of the same NSManagedContext?

Can I set the relationship between objects in DIFFERENT perform blocks of the SAME NSManagedContext?
I created a private context
and used (just example):
let pContext = persistentContainer.newBackgroundContext()
pContext.perform {
let user = pContext.fetch(<fetch Request>).first
pContext.performAndWait {
let book = pContext.fetch(<another fetch request>).first
book.name = "skjkjd"
pContext.save()
book.author = user
pContext.save()
}
}
May this code produce following error and in which cases?
Fatal Exception: NSInvalidArgumentException
Illegal attempt to establish a relationship 'author' between objects in different contexts
If I'm not mistaken but the AppDelegate persistentContainer.viewContext is a singleton. You shouldn't pass the MOC across threads the way you are trying to do.
Take a look at the apple documentation: https://developer.apple.com/documentation/coredata/using_core_data_in_the_background
TRY (not tested):
let pContext = persistentContainer.newBackgroundContext()
pContext.perform {
let user = pContext.fetch(<fetch Request>).first
let userObjcID = user.objectID
pContext.performAndWait {
let book = pContext.fetch(<another fetch request>).first
book.name = "skjkjd"
// pContext.save()
book.author = pContext.object(with: userObjcID)
pContext.save()
}
}

How get result of CoreData expression query without error

Overview:
I am trying to learn how to fetch certain statistical properties from a Core Data store (e.g. sum, avg, min, max). I have been successful retrieving an average to a dictionary with the following code, but I have to get the results as a dictionary.
Goal:
Ideally, I'd like to get the result along with the full results of the query by extending the custom managed objects class "Contact" that was autogenerated by Xcode Core Data managed object model. So far I have been unsuccessful. I get similar errors no matter what I try.
Error:
NSManagedObjectResultType not compatible with contents of propertiesToFetch'or similar
Code:
func avgdemoFloat()->Float{
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
let result:Float = 0
do{
let request = NSFetchRequest<NSFetchRequestResult>(entityName: "Contact")
let keypathExp = NSExpression(forKeyPath: "demoFloat")
let expression = NSExpression(forFunction: "average:", arguments: [keypathExp])
let avg = NSExpressionDescription()
avg.expression = expression
avg.name = "avg"
avg.expressionResultType = .floatAttributeType
request.returnsObjectsAsFaults = false
request.propertiesToFetch = [avg]
let results = try context.fetch(request)
print(results)
}
I'm not experienced enough to sort this out. Thanks for any help.
When you specify the propertiesToFetch the resultType needs to be set as shown below:
request.resultType = . dictionaryResultType

Error when attempting to assign CoreData property an NSOrderedSet

I am currently practicing with CoreData and working with a project I found on the web. I am getting the error on one project but not the other. However it is not clear to me what the issue could be.
" Cannot assign value of type 'NSOrderedSet' to type 'Location?' "
func saveRun()
{
let savedRun = NSEntityDescription.insertNewObjectForEntityForName( "Run", inManagedObjectContext: managedObjectContext!) as! Run
savedRun.distance = distance
savedRun.duration = seconds
savedRun.timestamp = NSDate()
var savedLocations = [Location]()
for location in locations
{
let savedLocation = NSEntityDescription.insertNewObjectForEntityForName( "Location", inManagedObjectContext: managedObjectContext! ) as! Location
savedLocation.timestamp = location.timestamp
savedLocation.latitude = location.coordinate.latitude
savedLocation.longitude = location.coordinate.longitude
savedLocations.append( savedLocation )
}
savedRun.locations = NSOrderedSet(array: savedLocations)
}
The question I have is simply why is it that I can not assign the property locations the value of the NSOrderedSet in one project and can in the other & how can I go about resolving this issue?
I'm quite sure that CoreData doesn't support NSOrderedSet at all. It may support NSSet for one-to-many relations. NSSorderedSet is an interesting class that lets you treat items as a set, while being able to index them at the same time, and it is rarely used.

what is effective approach to set default values using NSUserDefaults.standardUserDefaults().registerDefaults(Dictionary<String, AnyObject>)?

I have 4 user settings in the app which are independent of each other. I want to check value for each setting using appropriate method of NSUserDefaults.standardUserDefaults() for each key and if any of the key is not found in user defaults then register the default predefined value.
I do not want to call NSUserDefaults.standardUserDefaults.registerDefaults(Dictionary<String, AnyObject>) every time with app launch , so i am not registering defaults using this method in AppDelegate.Swift- didFinishLaunchingWithOptions with every app launch but i call a method in root view controller's viewDidLoad() which runs a custom logic to check each user settings. if all 4 settings already saved by user no call toregisterDefaults() at all and if any one of settings not saved, call registerDefaults().
Can anyone suggest is there any effective way of doing this or comment on the way i am trying to achieve it ? Will Apple review team approve my logic?
I want to get rid of too many IF's. Below is the logic i have written :-
let userDefaults = NSUserDefaults.standardUserDefaults() as NSUserDefaults
rate = AVSpeechUtteranceDefaultSpeechRate
speech = true
language = AVSpeechSynthesisVoice.currentLanguageCode()
defaultLanguageName = NSLocale.currentLocale().displayNameForKey(NSLocaleIdentifier, value: language)
bttnEffect = "Pop"
var defaultSpeechSettings: Dictionary<String, AnyObject> = ["speechRate": rate, "speech": speech, "languageCode": language, "defLanguageLabel": defaultLanguageName, "gender": bttnEffect ]
var isSpeechRateSettingLoaded = false
var isSpeechSettingLoaded = false
var isLangSettingLoaded = false
var isBttnEffectSettingLoaded = false
if let theRate: Float = userDefaults.valueForKey("speechRate") as? Float {
rate = theRate
defaultSpeechSettings.removeValueForKey("speechRate")
isSpeechRateSettingLoaded = true
}
if let toggleVal: Bool = userDefaults.valueForKey("speech") as? Bool {
speech = toggleVal
defaultSpeechSettings.removeValueForKey("speech")
isSpeechSettingLoaded = true
}
if let langCode = userDefaults.stringForKey("languageCode") {
language = langCode
defaultSpeechSettings.removeValueForKey("languageCode")
defaultSpeechSettings.removeValueForKey("defLanguageLabel")
isLangSettingLoaded = true
}
if let effectStyle = userDefaults.stringForKey("bttnEffect") {
bttnEffect = effectStyle
defaultSpeechSettings.removeValueForKey("bttnEffect")
isBttnEffectSettingLoaded = true
}
if !(isSpeechRateSettingLoaded && isSpeechSettingLoaded && isLangSettingLoaded && isBttnEffectSettingLoaded) {
NSUserDefaults.standardUserDefaults().registerDefaults(defaultSpeechSettings)
}
Apple's review team doesn't care if you write sloppy code.
Note Ken's comment about how registerDefaults() only affects return values if no value is set for the given key.
Aside from that, however, extensions are a nice way to tame NSUserDefaults.
extension NSUserDefaults {
private static let speechRateKey = "speechRate"
var speechRate: Float {
get {
return valueForKey(NSUserDefaults.speechRateKey) as? Float ?? AVSpeechUtteranceDefaultSpeechRate
}
set {
setObject(newValue, forKey: NSUserDefaults.speechRateKey)
synchronize()
}
}
}
// somewhere else:
let speechRate = NSUserDefaults.standardUserDefaults().speechRate
doSomethingWith(speechRate)
NSUserDefaults.standardUserDefaults().speechRate = getNewSpeechRate()

How do I save dictionary (map object) to DynamoDB using Swift

I’m trying to save a dictionary to my DynamoDB table field using low-level API. I couldn’t figure out how to do it with object mapper. There is no example for this in AWS iOS documentation and I’ve tried to research and implement Java / .NET examples of the same subject unsuccessfully.
I want to update only the dictionary field in the row using updateExpression.
I stumbled to this question while searching for answer, but it didn't help: Best way to make Amazon AWS DynamoDB queries using Swift?
Here’s the function to update dynamoDB-table:
func saveMapToDatabase(hashKey:Int, rangeKey:Double, myDict:[Int:Double]){
let nsDict:NSDictionary = NSDictionary(dictionary: myDict)
var dictAsAwsValue = AWSDynamoDBAttributeValue();dictAsAwsValue.M = nsDict as [NSObject : AnyObject]
let updateInput:AWSDynamoDBUpdateItemInput = AWSDynamoDBUpdateItemInput()
let hashKeyValue:AWSDynamoDBAttributeValue = AWSDynamoDBAttributeValue();hashKeyValue.N = String(hashKey)
let rangeKeyValue:AWSDynamoDBAttributeValue = AWSDynamoDBAttributeValue(); rangeKeyValue.N = String(stringInterpolationSegment: rangeKey)
updateInput.tableName = "my_table_name"
updateInput.key = ["db_hash_key" :hashKeyValue, "db_range_key":rangeKeyValue]
//How I usually do low-level update:
//let valueUpdate:AWSDynamoDBAttributeValueUpdate = AWSDynamoDBAttributeValueUpdate()
//valueUpdate.value = dictAsAwsValue
//valueUpdate.action = AWSDynamoDBAttributeAction.Put
//updateInput.attributeUpdates = ["db_dictionary_field":valueUpdate]
//Using the recommended way: updateExpression
updateInput.expressionAttributeValues = ["dictionary_value":dictAsAwsValue]
updateInput.updateExpression = "SET db_dictionary_field = :dictionary_value"
self.dynamoDB.updateItem(updateInput).continueWithBlock{(task:BFTask!)->AnyObject! in
//do some debug stuff
println(updateInput.aws_properties())
println(task.description)
return nil
}
}
I solved it, the problem was that AWS requires dictionary keys to always be in the form of String, any other type is not allowed.
The working solution snippet:
...
updateInput.tableName = "my_table_name"
updateInput.key = ["db_hash_key" :hashKeyValue, "db_range_key":rangeKeyValue]
let dictionaryInRightFormat:NSDictionary = ["stringKey":dictAsAwsValue]
updateInput.expressionAttributeValues = updateInput.updateExpression = "SET db_dictionary_field = :stringKey"

Resources