Updating object with primary key not working - swift

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

Related

How to fix error when looking for child in firebase database (Swift)?

I am trying to save items to a Firebase database under a child taken from a text field. I haven't got to that yet though, I am still trying to work out how to check if a value already exists. Here is my code...
#objc func submitNote() {
print("Attempting to save or update note")
//Check if file exists
if name != nil && text != nil {
//Access database
var ref: DatabaseReference!
ref = Database.database().reference()
ref.child("lists").observeSingleEvent(of: .value, with: { (snapshot) in
if snapshot.hasChild(self.name!){
print("true")
//Update
return
} else {
print("false")
//Create
self.uploadNew()
}
})
return
} else {
print("Error")
return
}
}
It has to be an #objc function as it is run using #selector in a menu bar. As you can see it checks to make sure the text fields aren't empty. Here are the variables...
public var name: String?
public var text: String?
Which are set inside separate functions...
name = titleText.text
text = textView.text
The reason I have set the variables this way is because I am creating the view programically and both individual text views/fields are set inside functions so I don't think I can access them from the submitNote function. Anyway the error I'm getting it this...
*** Terminating app due to uncaught exception 'InvalidPathValidation', reason: '(hasChild:) Must be a non-empty string and not contain '.' '#' '$' '[' or ']''
I have checked and triple checked but there aren't any of those values in the text fields and even though the if statement is meant to stop the app continuing unless they are not equal to nil I tried with text in the fields but no matter what if I press the button to run this function I get the same error. Does anyone know how to fix it?
You only check if name is not null but not if it is empty ("")
if let value = self.name, !value.isEmpty...
Your Firebase structure was not included in the question so I'll make one up for you
lists
Leroy: true
Billy: true
James: true
now some code to see if a name exists in the lists node. We're using snapshot.exists to see if any data was returned in the snapshot.
func doesNameExistInLists(name: String) {
let ref = self.ref.child("lists").child(name)
ref.observeSingleEvent(of: .value, with: { snapshot in
if snapshot.exists() {
print("found it")
} else {
print("name not found")
}
})
}
then when it's called, here are the results
self.doesNameExistInLists(name: "Billy")
console: found it
self.doesNameExistInLists(name: "aaaa")
console: name not found

Custom validation method for Core Data is not called on insert

I am developing a iOS app in swift that uses Core Data. I am trying to implement some Core Data validation. I don't have any custom logic yet, so I am using the "Class definition" setting in the model to make Xcode create the NSManagedObject subclasses automatically. In this case, according to the documentation, I can put my validation logic in an extension. My code looks like this:
extension Person {
func validateEmail(_ value: AutoreleasingUnsafeMutablePointer<AnyObject?>) throws {
var error: NSError? = nil
if let email = value.pointee as? String {
let regex = "^.+#([A-Za-z0-9-]+\\.)+[A-Za-z]{2}[A-Za-z]*$"
let predicate = NSPredicate(format: "SELF MATCHES %#", regex)
if !predicate.evaluate(with: email) {
let errorType = UserErrorType.invalidEmail
error = NSError(domain: errorDomain, code: errorType.rawValue, userInfo: [ NSLocalizedDescriptionKey : "The email address is invalid." ] )
}
} else {
let errorType = UserErrorType.invalidEmail
error = NSError(domain: errorDomain, code: errorType.rawValue, userInfo: [ NSLocalizedDescriptionKey : "The email address is invalid." ] )
}
if let error = error {
throw error
}
}
Unfortunately, my validation code does never get called. I know it gets called only when actually saving the context, but it is never called. I am also trying:
let entity = NSEntityDescription.entity(forEntityName: "Person", in: self.persistentContainer.viewContext)
let person = NSManagedObject(entity: entity!, insertInto: self.persistentContainer.viewContext) as! Person
person.age = 16
person.hasDrivingLicense = true
person.email = "novalidemail"
do {
try person.validateForInsert()
} catch {
let validationError = error as NSError
print(validationError)
}
But my validation method does never get called. Another test I made was to override validateValue like this in the extension:
public override func validateValue(_ value: AutoreleasingUnsafeMutablePointer<AnyObject?>, forKey key: String) throws {
print("\(key)")
}
This last method instead gets called for all keys, including the "email" key I am after.
I finally found the issue. Prefixing #objc to the validation function made it work, so it gets called. So, instead of:
func validateEmail(_ value: AutoreleasingUnsafeMutablePointer<AnyObject?>) throws {}
Just:
#objc func validateEmail(_ value: AutoreleasingUnsafeMutablePointer<AnyObject?>) throws {}
I had not tried this yet because, in this project coming from a tutorial, the validation functions do not have an #objc prefix and still they are called. I would like to understand the difference and discover why I need that prefix in my project. Thanks

realm commit write error - Can't commit a non-existing write transaction

I am trying to add a record to a realm DB table.
I have a class Connection which represents a table I need in my DB and have created dynamic vars which are to represent the columns:
import Foundation
import RealmSwift
import Realm
open class ConnectionState: Object {
open dynamic var _id : String = NSUUID().uuidString
open dynamic var a : String = ""
open dynamic var b : String = ""
open dynamic var c : Int = 0
open override class func primaryKey() -> String? {
return "_id"
}
required public init() {
super.init()
}
required public init(realm: RLMRealm, schema: RLMObjectSchema) {
super.init(realm: realm, schema: schema)
}
required public init(value: Any, schema: RLMSchema) {
fatalError("init(value:schema:) has not been implemented")
}
}
Then in my code I am trying to write and commit the write transaction like so:
let ConnectionState = ConnectionState()
ConnectionState.a = "a"
ConnectionState.b = "b"
ConnectionState.c = 1
try! self.realm.write {
self.realm.add(ConnectionState)
}
try! self.realm.commitWrite()
When running this code, I am receiving the error:
Can't commit a non-existing write transaction
What am I missing? Do I need to have inits in my ConnectionState class?
Before adding in the commitWrite, I was trying to view the db with realm browser. I found my device in xCode and chose to download the container but it was empty. Then I thought I needed to add in commitWrite
In your example you called commitWrite without having called beginWrite. You cannot commit a write transaction because you did not start one. Either start a write transaction or delete the commitWrite line.
Start transaction and commit it
self.realm.beginWrite()
self.realm.add(ConnectionState)
try! self.realm.commitWrite()
Delete commitWrite
try! self.realm.write {
self.realm.add(ConnectionState)
}
The Realm docs have two examples of adding data to the database.
Use the realm.write method
// Use them like regular Swift objects
let myDog = Dog()
myDog.name = "Rex"
myDog.age = 1
print("name of dog: \(myDog.name)")
// Get the default Realm
let realm = try! Realm()
// Query Realm for all dogs less than 2 years old
let puppies = realm.objects(Dog.self).filter("age < 2")
puppies.count // => 0 because no dogs have been added to the Realm yet
// Persist your data easily
try! realm.write {
realm.add(myDog)
}
Use realm.beginWrite() and realm.commitWrite() to start the write transaction and commit data to the database
let realm = try! Realm()
// Break up the writing blocks into smaller portions
// by starting a new transaction
for idx1 in 0..<1000 {
realm.beginWrite()
// Add row via dictionary. Property order is ignored.
for idx2 in 0..<1000 {
realm.create(Person.self, value: [
"name": "\(idx1)",
"birthdate": Date(timeIntervalSince1970: TimeInterval(idx2))
])
}
// Commit the write transaction
// to make this data available to other threads
try! realm.commitWrite()
}
try! self.realm.write {
self.realm.add(ConnectionState)
}
This code is somewhat equivalent to (possibly with some additional error handling):
realm.beginWrite()
...
try! realm.commitWrite()
Which means you're trying to commit your writes twice.
Just change your code like this:
try! self.realm.write {
self.realm.add(ConnectionState)
}
// try! self.realm.commitWrite()

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.

Primary key property has duplicate values after migration - Realm migration

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