I updated my data model and wrote the migration in application: didFinishLaunchingWithOptions per Realm documentation:
func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
let config = Realm.Configuration(
schemaVersion: 1,
migrationBlock: { migration, oldSchemaVersion in
if (oldSchemaVersion < 1) {
}
})
Realm.Configuration.defaultConfiguration = config
let realm = try! Realm()
My initial VC has a tableView and is embedded in a navigation controller that calls the realm as shown below:
class AllRoundsVC: UIViewController, UITableViewDelegate, UITableViewDataSource {
let realm = try! Realm()
When I leave it like this, my app crashes upon launch stating "fatal error: 'try!' expression unexpectedly raised an error: Error Domain=io.realm Code=0 "Provided schema version 0 is less than last set version 1." UserInfo={NSLocalizedDescription=Provided schema version 0 is less than last set version 1.}
However, when I put a dummy VC infront of the navigation controller with a simple button to segue to the navigation controller everything works fine. I know this has to do with appDidFinishLaunchingWithOptions loading before or after initial VC.
My question is, how can I stop from crashing while still calling try! Realm() on my initial VC? I don't want to keep a pointless VC infront of the tableView just to load the proper schema.
Thanks
after doing some realm research on an unrelated issue I came across a solution to this problem. Here is a link to the solution! link
How can I get the migration to run before the app starts to run the code?
I had the same issue. I solved it with just two lines of code.
let config = Realm.Configuration(schemaVersion: 2)
var realm: Realm = try! Realm(configuration: config)
Its solved error just increasing the schemaVersion.
I had the same issue. I got this error: Realm schema version 1 is less than last set version 3 Swift
Here's what needed to be done:
public lazy var realmConfig: Realm.Configuration = {
var config = Realm.Configuration()
config.schemaVersion = 3 //the old code is 'config.schemaVersion = 1'
config.migrationBlock = { newSchemaVersion, oldSchemaVersion in
if oldSchemaVersion < 3 {}
if oldSchemaVersion < 2 {}
if oldSchemaVersion < 1 {}
}
return config
}()
when you run the app with config.schemaVersion = 3, the realm cannot be set to lower than 3. So you'd better not use the appbuild(kCFBundleVersionKey) to set the realmConfig.schemaVersion.
Related
I am completely at this and out of ideas for about a week now.
I have a CoreData stack with multiple coordinators and managed contexts, and I am attempting to implement a logout feature in the app.
The tableviews that interact with CoreData is inside a TabBarController, and there are on-boarding (i.e. registration & login) NavigationControllers and ViewControllers that lead up to it.
What I am attempting to implement is a logout (i.e. unwind to initial ViewController. I have tried so far - to no avail:
reset() each managedObjectContext individually
every possible combination of tableView.beginUpdates(), tableView.endUpdates(), tableView.reloadData(), frc.performFetch()
Delete each entity in each managedObject then reset() each managedObjectContext individually
set tableView, fetchedResultsController delegates and datasource to nil, then to self on viewDidLoad()
Having a NSNotification that fires just before logging out - deleting everything in CoreData and saving it, effectively updating and emptying the table.
a whole lot more
Is there a way to completely reset CoreData so when the user unwinds to initial ViewController and re-run the whole process I won't get
Serious application error. Exception was caught during Core Data
change processing. This is usually a bug within an observer of
NSManagedObjectContextObjectsDidChangeNotification. attempt to insert
row 9 into section 0, but there are only 9 rows in section 0 after the
update with userInfo (null)
or in the case I emptied the table via Notifications:
Serious application error. Exception was caught during Core Data
change processing. This is usually a bug within an observer of
NSManagedObjectContextObjectsDidChangeNotification. attempt to insert
row 9 into section 0, but there are only 0 rows in section 0 after the
update with userInfo (null)
CoreData works just fine if I logout, close the app, and restart. No errors this way. This makes me think there is a way to completely reset CoreData (and any tableViews, fetchedResultsControllers associated with it) or reset to a pristine state when unwinding to the initial ViewController.
or should I just duplicate the storyboard and have a separate set of on-boarding viewControllers just for logging out so CoreData doesn't reinitialize?
Any ideas? Thanks.
In iOS9 and above you can use destroyPersistentStore and optionally add a new one
func destroyAllData(storeType : String = NSSQLiteStoreType) throws {
guard let storeURL = persistentStoreCoordinator.persistentStores.last?.url else {
print("Missing store URL")
return
}
try persistentStoreCoordinator.destroyPersistentStore(at: storeURL, ofType: storeType)
// try persistentStoreCoordinator.addPersistentStore(ofType: storeType, configurationName: nil, at: storeURL)
}
try this!
static let moduleName = "moduleName"
//Model
lazy var managedObjectModel:NSManagedObjectModel = {
let modelURL = Bundle.main.url(forResource: moduleName, withExtension: "momd")!
return NSManagedObjectModel(contentsOf: modelURL)!
}()
func deleteAllEntities() {
let entities = managedObjectModel.entities
for entity in entities {
delete(entityName: entity.name!)
}
}
func delete(entityName: String) {
let fetchRequest = NSFetchRequest<NSFetchRequestResult>(entityName: entityName)
let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest)
do {
try persistentStoreCoordinator.execute(deleteRequest, with: DataCoordinator.shared.managedObjectContext)
debugPrint("Deleted Entitie - ", entityName)
} catch let error as NSError {
debugPrint("Delete ERROR \(entityName)")
debugPrint(error)
}
}
I don't know if I'm missing something here.
I got a crash after running my app without a migration block after updating a property (same problem as here Realm Migration not working)
But now when I run the app, it must run the migration because it no longer crashes but my object's properties are not updated.
I have updated the below object ("minReps" is the one I've added):
class ExerciseGeneratorObject: Object {
#objc dynamic var name = ""
#objc dynamic var minReps = 0
#objc dynamic var maxReps = 0
convenience init(name: String, minReps: Int, maxReps: Int) {
self.init()
self.name = name
self.minReps = minReps
self.maxReps = maxReps
}
Then I'm running an empty migration block like this in appDelegate :
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let config = Realm.Configuration(
schemaVersion: 3,
migrationBlock: { migration, oldSchemaVersion in
if (oldSchemaVersion < 3) {
}
})
Realm.Configuration.defaultConfiguration = config
let realm = try! Realm()
I thought Realm was meant to update object properties automatically if you run an empty migration block - is that wrong? Am I missing some code to make this work?
There's a very similar problem here (Realm migrations in Swift) (the isn't me!) but looks out of date now (and sure I've tried the solutions there as above!)
Current schema version should be set in the app via realm configuration, and you should increase it, in your code you set schema version to 3, and asking realm to migrate realm if oldSchemaVersion less than 3, set schema version to 4, and it will work
var config = Realm.Configuration(
// Set the new schema version. This must be greater than the previously used
// version (if you've never set a schema version before, the version is 0).
schemaVersion: 4,
// Set the block which will be called automatically when opening a Realm with
// a schema version lower than the one set above
migrationBlock: { migration, oldSchemaVersion in
// We haven’t migrated anything yet, so oldSchemaVersion == 0
if (oldSchemaVersion < 3) {
// Nothing to do!
// Realm will automatically detect new properties and removed properties
// And will update the schema on disk automatically
}
})
Realm.Configuration.defaultConfiguration = config
config = Realm.Configuration()
config.deleteRealmIfMigrationNeeded = true
I'm going mental trying to work this out. I had 2 old classes which are no longer used in my code.
I'm trying to get rid of them in my Realm. I have tried a migration and a delete and restart and they just keep coming back (they don't have any objects in but it's untidy and annoying!)
I've tried this :
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
let config = Realm.Configuration(
schemaVersion: 2,
migrationBlock: { migration, oldSchemaVersion in
if (oldSchemaVersion < 1) {
migration.deleteData(forType: "Exercise")
migration.deleteData(forType: "Workout")
}
})
Realm.Configuration.defaultConfiguration = config
let realm = try! Realm()
and I've also tried this :
let realm = try! Realm()
try! realm.write {
realm.deleteAll()
}
One of the classes is called "Workout" and I've got a Struct called "Workout" elsewhere in code but it hasn't even got the same fields etc.
Research I'm sure I've tried what others have in Delete a Realm model with Swift , https://www.realm.io/docs/objc/latest/#migrations and here How to delete a class from realm file
Any ideas? I've done a CMD+SHIFT+F and the old classes definitely aren't in my code so I'm guessing I'm not doing migration correctly?
Update for those having same issue
I deleted the file completely using the details here How to completely remove Realm database from iOS?
I'm not sure what I did wrong though!
Swift 3, Xcode 8, RealmSwift 2.0.2, Realm Object Server 1.0
In my app delegate, I have a function that sets my Realm configuration to connect to a remote sync server I have set up. I'm just using a test account to authenticate until I can get the basics of sync working. 1.1.1.1 isn't my real IP address. ;)
let username = "test"
let password = "test"
let address = "http://1.1.1.1:9080"
let syncAddress = "realm://1.1.1.1:9080/~/myapp"
SyncUser.authenticate(with: Credential.usernamePassword(username: username, password: password, actions: []), server: URL(string: address)!, onCompletion: { user, error in
guard let user = user else {
fatalError(String(describing: error))
}
// Open Realm
Realm.Configuration.defaultConfiguration = Realm.Configuration(
syncConfiguration: (user, URL(string: syncAddress)!)
)
})
This seems to work fine. I see data appear on my server, and I get no errors. My assumption is that setting the Realm configuration here means that all instances of Realm() will use this configuration.
I then set a realm object as a class property in two separate view controllers:
class TableViewControllerA: UITableViewController{
let realm = try! Realm()
override func viewDidLoad() {
// CORRECT: Prints "nil" as it should for a remotely synced Realm instance
print(realm.configuration.fileURL)
}
}
...and another in another file:
class ViewControllerB: UIViewController{
let realm = try! Realm()
override func viewDidLoad() {
// WRONG: Prints the path to the local realm file in the Simulator
print(realm.configuration.fileURL)
}
}
As noted in the code comments above, the two instances of realm are different. On some of my view controllers, I can save objects to the server and see them appear on my device. On other view controllers, I don't see any data because it's using the wrong Realm database.
Can I not reliably expect a Realm configuration to persist throughout my app? Do I need to do something else to use the same configuration?
You're setting the default configuration within the authentication completion handler. This callback is invoked asynchronously after the user has been authenticated. If an instance of one of your view controller subclasses happens to be created before the callback runs, the Realm it opens will use the default default configuration, prior to any changes you make in your authentication completion handler.
I have an object NotSureItem in which I have three properties title whose name is renamed from text and textDescription which I had added later and a dateTime property. Now when I am going to run my app it crashes when I want to add something to these properties. It shows following statements.
'Migration is required for object type 'NotSureItem' due to the following errors:
- Property 'text' is missing from latest object model.
- Property 'title' has been added to latest object model.
- Property 'textDescription' has been added to latest object model.'
Here is my code:
import Foundation
import Realm
class NotSureItem: RLMObject {
dynamic var title = "" // renamed from 'text'
dynamic var textDescription = "" // added afterwards
dynamic var dateTime = NSDate()
}
As long as you have not released your app you can simply delete your app and run it again.
Everytime you change properties on your Realm objects your existing database becomes incompatible to the new one.
As long as you are still in the developing stage you can simply delete the app from the simulator / device and start it again.
Later when your app has been released and you change properties on your objects you have to implement a migration to the new database version.
To actually perform a migration you implement a Realm migration block. Typically you would add the block to application(application:didFinishLaunchingWithOptions:):
var configuration = Realm.Configuration(
schemaVersion: 1,
migrationBlock: { migration, oldSchemaVersion in
if oldSchemaVersion < 1 {
// if just the name of your model's property changed you can do this
migration.renameProperty(onType: NotSureItem.className(), from: "text", to: "title")
// if you want to fill a new property with some values you have to enumerate
// the existing objects and set the new value
migration.enumerateObjects(ofType: NotSureItem.className()) { oldObject, newObject in
let text = oldObject!["text"] as! String
newObject!["textDescription"] = "The title is \(text)"
}
// if you added a new property or removed a property you don't
// have to do anything because Realm automatically detects that
}
}
)
Realm.Configuration.defaultConfiguration = configuration
// opening the Realm file now makes sure that the migration is performed
let realm = try! Realm()
Whenever your scheme changes your have to increase the schemaVersion in the migration block and update the needed migration within the block.
Delete the app and re-install is not a good practice. We should incorporate some migration steps during development from the first time we encounter migration need. The link given by SilentDirge is good: realm migration document, which gives good examples for handling different situations.
For a minimum migration task, the following code snippet from the above link can automatically do the migration and is to be used with AppDelegate's disFinishLaunchWithOptions method:
let config = Realm.Configuration(
// Set the new schema version. This must be greater than the previously used
// version (if you've never set a schema version before, the version is 0).
schemaVersion: 1,
// Set the block which will be called automatically when opening a Realm with
// a schema version lower than the one set above
migrationBlock: { migration, oldSchemaVersion in
// We haven’t migrated anything yet, so oldSchemaVersion == 0
if (oldSchemaVersion < 1) {
// Nothing to do!
// Realm will automatically detect new properties and removed properties
// And will update the schema on disk automatically
}
})
// Tell Realm to use this new configuration object for the default Realm
Realm.Configuration.defaultConfiguration = config
// Now that we've told Realm how to handle the schema change, opening the file
// will automatically perform the migration
let _ = try! Realm()
Below code is working for me
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
config.schemaVersion = 2;
config.migrationBlock = ^(RLMMigration *migration, uint64_t oldSchemaVersion) {
// The enumerateObjects:block: method iterates
// over every 'Person' object stored in the Realm file
[migration enumerateObjects:Person.className
block:^(RLMObject *oldObject, RLMObject *newObject) {
// Add the 'fullName' property only to Realms with a schema version of 0
if (oldSchemaVersion < 1) {
newObject[#"fullName"] = [NSString stringWithFormat:#"%# %#",
oldObject[#"firstName"],
oldObject[#"lastName"]];
}
// Add the 'email' property to Realms with a schema version of 0 or 1
if (oldSchemaVersion < 2) {
newObject[#"email"] = #"";
}
}];
};
[RLMRealmConfiguration setDefaultConfiguration:config];
// now that we have updated the schema version and provided a migration block,
// opening an outdated Realm will automatically perform the migration and
// opening the Realm will succeed
[RLMRealm defaultRealm];
return YES;
}
More info : https://realm.io/docs/objc/latest/#getting-started
Just increment the schema version
Realm will automatically detect new properties and removed properties
var config = Realm.Configuration(
// Set the new schema version. This must be greater than the previously used
// version (if you've never set a schema version before, the version is 0).
schemaVersion: 2,
// Set the block which will be called automatically when opening a Realm with
// a schema version lower than the one set above
migrationBlock: { migration, oldSchemaVersion in
// We haven’t migrated anything yet, so oldSchemaVersion == 0
if (oldSchemaVersion < 1) {
// Nothing to do!
// Realm will automatically detect new properties and removed properties
// And will update the schema on disk automatically
}
})
do{
realm = try Realm(configuration: config)
print("Database Path : \(config.fileURL!)")
}catch{
print(error.localizedDescription)
}
Your modified database is no longer compatible with the saved database which is why a migration is required. Your options are to delete the old database file and start fresh (works great if you are in the initial dev phase), or if you are live, do the migration.
You do this by defining a schema version and providing a database migration 'script' within your Realm configuration. The entire process is documented here (along with code samples): here
You can erase database on launch like this:
[[NSFileManager defaultManager] removeItemAtURL:[RLMRealmConfiguration defaultConfiguration].fileURL error:nil];
If you are getting this error even after incrementing the schemaVersion. Then do a double-check. Whether you are calling any Realm object before updating the schema version in App Delegate.
In My case, I was trying to access a Realm Object in App Delegate before that code migration statement is executed.
Always write the migration code in the first line of App Delegate (DidfinishLaunchingWithOptions) to be on a safer side.