Primary key property has duplicate values after migration - Realm migration - swift

I have an existing Realm project with two models
Model1 -> id, updateDate, details
Model2 -> id, updateDate, title, model1
(yes I used a class object instead of the id - aargh. Both id are primary keys)
With an updated version of my app, I am adding a new property to Model1 (title) and changing Model2.model1 from type Model1 to type string (=Model1.id)
I wrote a migration block for this as per the samples provided
let migrationBlock: (RLMMigration, UInt64) -> Void = { (migration, oldSchemeVersion) in
if oldSchemeVersion < 1 {
migration.enumerateObjects(Model1.className()) { oldObject, newObject in
//Nothing needed, the title can be a blank
}
migration.enumerateObjects(Model2.className()) { oldObject, newObject in
if let oldModel1 = oldObject!["model1"] as? RLMDynamicObject {
newObject!["model1"] = oldModel1["id"]
}
}
}
}
let config = RLMRealmConfiguration.defaultConfiguration()
config.schemaVersion = newSchemaVersion
config.migrationBlock = migrationBlock
RLMRealmConfiguration.setDefaultConfiguration(config)
But at the end of the migration, when I try to access the default realm (Realm.defaultRealm), it fails with this error
Terminating app due to uncaught exception 'RLMException', reason: 'Primary key property 'id' has duplicate values after migration.'
I cannot figure out why this is going wrong and what I am supposed to do to make this work. Any help would be appreciated.
NOTE - my code uses the Realm objective-c code but in a Swift app

Related

Cannot save strings into CoreData

I'm trying to save strings I have in my string array into the Core Data. My .xcdatamodel looks like this:
My saving function (a method of a class called "Memory"):
func save(from: [String])
{
for i in 0..<from.count
{
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context = appDelegate.persistentContainer.viewContext
let saved = NSEntityDescription.insertNewObject(forEntityName: "Person", into: context)
saved.setValue(from[i], forKey: "password")
do
{
try context.save()
print("SAVED")
}
catch
{
print("ERROR - COULDN'T SAVE ", to)
}
}
print("NEW ", to, ": ")
print(save)
}
Lastly, inside my ViewController:
Memory().save(from: codes)
However, what I get is this:
Thread 1: Fatal error: Unresolved error Error
Domain=NSCocoaErrorDomain Code=134140 "Persistent store migration
failed, missing mapping model."
UserInfo={sourceModel=() isEditable
1, entities {
Person = "() name Person,
managedObjectClassName NSManagedObject, renamingIdentifier Person,
isAbstract 0, superentity name (null), properties {\n password =
\"(), name password, isOptional
1, isTransient 0, entity
You have made changes to your data model but you failed/forgot to migrate the model. If you don't have anything valuable in your current persistence store (SQLite database?) then I suggest you throw it away and let Core Data create a new one using the new model.
Otherwise you might want to fetch the previous version of your model from your source repository if you have one and do a proper migration. Here is a SO question of interest and Apple's documentation on migration

Swift DynamoDB Mapper Sending Empty Values

I'm trying to use DynamoDB using the iOS Swift SDK. I'm using Cognito with Facebook as an external identity provider. Cognito is working fine - I've tested user sync and it works OK, so I believe I have the authentication set up. Here's how I'm setting up the SDK (I have the actual values of my identity pool in my code):
let credentialsProvider = AWSCognitoCredentialsProvider(regionType:.USEast1,
identityPoolId:"<my-identity-pool-id>", identityProviderManager: FacebookProvider())
let configuration = AWSServiceConfiguration(region:.USEast1, credentialsProvider:credentialsProvider)
AWSServiceManager.default().defaultServiceConfiguration = configuration
And here's my DynamoDB mapped class:
import Foundation
import AWSDynamoDB
class SavedItem : AWSDynamoDBObjectModel, AWSDynamoDBModeling {
var userId : Int?
var timestamp : Int?
class func dynamoDBTableName() -> String {
return "my-table"
}
class func hashKeyAttribute() -> String {
return "userId"
}
class func rangeKeyAttribute() -> String {
return "timestamp"
}
}
I've verified that my code has the correct table and attribute names and that the hash key and range key values on the table are identical, including case sensitivity, with the fields in my SavedItem class.
Here's how I'm instantiating the mapper:
let dynamoDBObjectMapper = AWSDynamoDBObjectMapper.default()
let savedItem = SavedItem()
savedItem?.userId = 1
savedItem?.timestamp = 2
dynamoDBObjectMapper.save(savedItem!).continueWith(block: { (task:AWSTask<AnyObject>!) -> Any? in
if let error = task.error as? NSError {
print("The request failed. Error: \(error)")
} else {
print("Save callback executing")
}
return nil
})
That code's more or less straight out from the AWS Documentation example. But, here's what I get back in the console when that code executes:
Error Domain=com.amazonaws.AWSCognitoIdentityErrorDomain Code=0 "(null)" UserInfo={__type=com.amazon.coral.validate#ValidationException, message=Supplied AttributeValue is empty, must contain exactly one of the supported datatypes}
I bumped console logging up to debug, and it looks like the mapper is not sending any attributes from the SavedItem object. Here's what's in the console for the save request body:
Request body:
{"Key":{"userId":{},"timestamp":{}},"TableName":"my-table","AttributeUpdates":{}}
Any idea why the values are not getting included in the save request body?
Using aws-sdk-ios v2.6.1 on in Swift 4 on iOS 11.
The problem seems to be the type of userId and timestamp. Changing them from Int to NSNumber fixed the problem.
Yes i faced this same issue. and after struggling for 3 days.i found that AWSDynamoDBObjectModel not support in swift 4. please try in swift 3 version. You will get success.

Core Data using auto generated Primary Key PK

I have a simple Person entity in Core Data, I noticed that when adding data, each row is auto generated a unique PK called objectID
I wish to retrieve the records using the objectID but I am getting the following error:
Terminating app due to uncaught exception
'NSInvalidArgumentException', reason: 'keypath objectID not found in
entity < NSSQLEntity Person id=2 >'
func fetchPerson(withID personID: Int,
context: NSManagedObjectContext,
completion: #escaping ([Person]?) -> Void) {
let fetchRequest: NSFetchRequest<Person> = Person.fetchRequest()
let predicate = NSPredicate(format: "\(#keyPath(Person.objectID)) == \(personID)")
fetchRequest.predicate = predicate
//perform aynchronous operation:
context.perform {
do {
let persons = try fetchRequest.execute()
//success - return array of persons
completion(persons)
} catch {
//error - return nil
completion(nil)
}
}
}
Calling the above :
//fetch person with specific id:
let personID: Int = 2
fetchPerson(withID: personID, context: persistentContainer.viewContext) { (persons: [Person]?) in
if persons != nil {
print("Fetching person with personID: \(personID)")
for person in persons! {
print("Person: \(person.objectID) - \(person.firstName!) \(person.lastName!)")
}
}
}
You can fetch with objectID by using the predicate "self == %#", objectID. An objectID is property of a managedObject and its type is NSManaged​Object​ID not an Int. In general it is better to create and manage your own objectId. I generally use UUID strings.
The objectID is a property of NSManagedObject but is not the name of a field in the persistent store or part of the entity description. You can't use it in predicates because it's not there to use as a filter. There is a field in the primary store that corresponds to the object ID, but it's also not available to fetch requests because that's not how Core Data is intended to be used.
If you want a primary key with Core Data, you need to create it yourself. Or if you really want SQL-style coding, use SQLite directly and don't get Core Data involved.

Updating object with primary key not working

This is the object I created with primary key username and the error when I try to run the add object code.
class Login_item: Object {
dynamic var username = String()
dynamic var password = String()
override static func primaryKey() -> String? {
return "username"
}
}
This is the code that add new object and update object.
func save_login_info() {
let new_login_entry = Login_item()
new_login_entry.username = Username.text!
new_login_entry.password = Password.text!
let realm = try! Realm()
try! realm.write {
realm.add(new_login_entry)
print("Login info save as: \(Realm.Configuration.defaultConfiguration.fileURL!)")
}
try! realm.write {
realm.add(new_login_entry, update: true)
}
}
Error I got when execute the code.
fatal error: 'try!' expression unexpectedly raised an error: Error Domain=io.realm Code=10 "Migration is required due to the following errors:
- Property 'username' has been made a primary key." UserInfo={NSLocalizedDescription=Migration is required due to the following errors:
- Property 'username' has been made a primary key., Error Code=10}: file /Library/Caches/com.apple.xbs/Sources/swiftlang/swiftlang-703.0.18.8/src/swift/stdlib/public/core/ErrorType.swift, line 54
Yeah you need to add migration instruction when you change the model, thats what the error message is describing.
let config = Realm.Configuration(schemaVersion: 1, migrationBlock: { migration, oldSchemaVersion in
if oldSchemaVersion < 1 {
}
})
The 1 in the code represents the current version, and every time you make any changes to the code you need to increase the version number and do any changes to the data that you want to.
You can read more about it here

Exception throw for null data with optional Int type in Realm

I am taking my first foray into using Realm (0.98.1 via Cocoapods, Xcode 7.2) and am running into a small problem that I am not sure how to solve.
I have a model class called Airport that declares a property
let elevationFt = RealmOptional<Int>()
I am creating a set of Airport objects and persisting them in the following way
public func cacheDataToPersistanceStore(data:NSArray) -> Bool {
var success = true
autoreleasepool {
do {
let realm = try Realm()
realm.beginWrite()
for object in data {
guard let dictionaryValues = object as? Dictionary<String, AnyObject> else {
debugPrint("Unable to convert data to correct type")
success = false
return
}
if(dictionaryValues["airportID"] as! Int == 6605) {
realm.create(Airport.self, value: dictionaryValues, update: true)
}
}
try realm.commitWrite()
}
catch(let e) {
debugPrint(e)
success = false
}
}
return success
}
For the airport entry in question, the dictionary that stores the relevant data looks to have a null value for the key "elevationFt", so I assume things will be OK for the optional Int property
Here is a string version of the dictionary:
["gps_code": 01MD, "ident": 01MD, "iata_code": , "local_code": 01MD, "keywords": , "elevationFt": , "type": seaplane_base, "municipality": Annapolis, "iso_country": US, "airportID": 6605, "longitudeDeg": -76.45600128173828, "latitudeDeg": 38.99919891357422, "iso_region": US-MD, "wikipedia_link": , "name": Annapolis Seaplane Base, "scheduled_service": no, "continent": NA, "home_link": ]
However once the create function starts for this set of data, an exception is thrown:
Terminating app due to uncaught exception 'RLMException', reason: 'Invalid value '' for property 'elevationFt''
I am guessing I have something set up incorrectly, but I am not quite sure how to fix this other than to clean my source data for that particular field.
The screenshot from the debugger shows that elevationFt is an empty string, which is not a number or null, so it is not a valid value for an optional int property.