Provided schema version 1 is less than last set version 0 in React native - react-native

Although the question looks like a duplicate with another existing question, I could not get it through. In order to change the type of one property for a schema, I added the versionSchema as required, (e.g., let realm = new Realm({schemaVersion:1, schema:[PersonSchema]}), but it shows:
Provided schema version 1 is less than last set version 0
I tried to both deleting the added sentence and editing the sentence, but it shows the same error as though it's already remembered it.
How can I fix this?

The problem can occur if your database is set to readOnly. I changed that to false and the migration block started getting called.

In AppDelegate.m :
#import <Realm/RLMRealmConfiguration.h>
//Inside your [AppDelegate didFinishLaunchingWithOptions:]
//Realm detect new properties and removed properties
RLMRealmConfiguration *config = [RLMRealmConfiguration defaultConfiguration];
// 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).
config.schemaVersion = 1;
// Set the block which will be called automatically when opening a Realm with a
// schema version lower than the one set above
config.migrationBlock = ^(RLMMigration *migration, uint64_t oldSchemaVersion) {
// 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
[RLMRealmConfiguration setDefaultConfiguration:config];
// Now that we've told Realm how to handle the schema change, opening the file
// will automatically perform the migration
[RLMRealm defaultRealm];

Related

Handlebars with Assemble build returning "not an own property of its parent"

I am experiencing the dreaded not an "own property" of its parent issue when attempting to build my Handlebars project.
I have been down the rabbit hole and seen the many explanations of using #handlebars/allow-prototype-access to allow the issue to be bypassed, however it seems the project does not use a standard implementation of Handlebars...
It seems I am using something called engine-handlebars
Where I would expect to implement that allow-prototype-access change, I see the following:
app.pages('./source/pages/**/*.hbs');
app.engine('hbi', require('engine-handlebars'));
I can't fathom how I am supposed to implement the prototype access with this setup...
It seems, after a bit of trial and error, commenting lines out as I go, that the line app.pages('./source/pages/**/*.hbs'); is actually causing the issue...
When I run the project with this line in, I get the error:
Handlebars: Access has been denied to resolve the property "path" because it is not an "own property" of its parent.
You can add a runtime option to disable the check or this warning:
See https://handlebarsjs.com/api-reference/runtime-options.html#options-to-control-prototype-access for details
[10:54:49] ERROR - undefined: Cannot read property 'substring' of undefined
The plugin #handlebars/allow-prototype-access works by modifying the Handlebars instance.
const _Handlebars = require('handlebars');
const { allowInsecurePrototypeAccess } = require('#handlebars/allow-prototype-access');
const Handlebars = allowInsecurePrototypeAccess(_Handlebars);
Note that allowInsecurePrototypeAccess does not modify the instance in place, but creates an isolated instance via Handlebars.create() so you must use its return value.
In your case, engine-handlebars exposes the Handlebars instance in different ways depending on what version you are using.
Based on your code you provided, my guess is you are using <1.0.0, but I'll provide methods for adjusting this for all its versions.
engine-handlebars#<0.6.0
Unfortunately these versions don't expose Handlebars in any way, so if you are using this version I recommend upgrading engine-handlebars to a later version.
engine-handlebars#>=0.6.0 <1.0.0
Version 0.6.0 exposed Handlebars as a property on the exported engine function. This is then referenced throughout the library via this.Handlebars.
You can then change this before setting the app.engine() and it should work.
const _Handlebars = require('handlebars');
const { allowInsecurePrototypeAccess } = require('#handlebars/allow-prototype-access');
const engine = require('engine-handlebars');
// elsewhere...
// const app = ...
// Do this *before* setting app.engine
const insecureHandlebars = allowInsecurePrototypeAccess(_Handlebars);
engine.Handlebars = insecureHandlebars;
app.engine('hbi', engine);
engine-handlebars#>=1.0.0
For version 1.0.0 and beyond, you must pass the Handlebars instance yourself.
const Handlebars = require('handlebars');
const engine = require('engine-handlebars')(Handlebars);
Thus you don't need to set anything on engine, you just pass in the modified instance when you need it.
const _Handlebars = require('handlebars');
const { allowInsecurePrototypeAccess } = require('#handlebars/allow-prototype-access');
// elsewhere...
// const app = ...
// Do this *before* setting app.engine
const insecureHandlebars = allowInsecurePrototypeAccess(_Handlebars);
const engine = require('engine-handlebars')(insecureHandlebars);
app.engine('hbi', engine);

UWP SyncFusion SfDataGrid Serialization Exception

I'm trying to make use of the SfDataGrid component in my UWP app and have everything working just fine in debug mode. When I switched over to release mode to regression test the app before publishing to the Windows store the app throws an exception during grid serialization.
I have an SfDataGrid defined with 4 text columns, 1 numeric column and 1 template column. The template column just includes a delete button so that the user to can remove the row.
I have a method to return the serialization options as follows:
private SerializationOptions GetGridSerializationOptions()
{
return new SerializationOptions
{
SerializeFiltering = false,
SerializeColumns = true,
SerializeGrouping = true,
SerializeSorting = true,
SerializeTableSummaries = true,
SerializeCaptionSummary = true,
SerializeGroupSummaries = true,
SerializeStackedHeaders = true
};
}
Then I have another method to serialize the grid settings as follows:
private void RetrieveDefaultGridSettings()
{
using (MemoryStream ms = new MemoryStream())
{
gridReport.Serialize(ms, GetGridSerializationOptions());
_defaultGridSettings = Convert.ToBase64String(ms.ToArray());
}
}
I've followed the SyncFusion documentation (https://help.syncfusion.com/uwp/datagrid/serialization-and-deserialization) which describes how to serialize template columns. I have everything working perfectly in debug mode, but when I switch to release mode I get an exception on this line:
gridReport.Serialize(ms, GetGridSerializationOptions());
The exception is:
System.Runtime.Serialization.InvalidDataContractException: 'KnownTypeAttribute attribute on type 'Syncfusion.UI.Xaml.Grid.SerializableGridColumn' specifies a method named 'KnownTypes' to provide known types. Static method 'KnownTypes()' was not found on this type. Ensure that the method exists and is marked as static.'
I've had a look at the SerializableGridColumn class and can see a public static method called KnownTypes so I don't really understand why this exception is happening. I'm even more confused about why it's only happening in release mode.
In attempt to fix the problem I have tried referencing the entire SDK, removing the SDK and referencing the specific assemblies (Syncfusion.SfGrid.UWP, Syncfusion.Data.UWP, Syncfusion.SfInput.UWP, Syncfusion.SfShared.UWP, Syncfusion.SfGridConverter.UWP, Syncfusion.XlsIO.UWP and Syncfusion.Pdf.UWP) but neither yields a different result and the exception still occurs, but only in release mode.
Switching off the setting "Compile with .NET Native tool chain" does resolve the problem, but is not a practical solution as this blocks me from publishing the app to the Windows store.
Thanks very much for any assistance anyone can provide.
After exhausting all possible problems with my own code, I logged with an issue with SyncFusion. They're investigating and will hopefully provide a fix soon.

Realm migration race condition

How can I guarantee that realm migrations will finish before accessing realm? There seems to be a race condition in migrating a realm db and beginning to read/write to the realm. Here's my issue:
Currently I am setting the realm configurations with a migration closure when a user launches the app. Once the realm configuration has been set with RLMRealmConfiguration.setDefaultConfiguration(config), I set the RootViewController and begin accessing realm data.
However, when a migration is needed, realm is sometimes accessed before the migrations can complete - causing a crash (RLMException: Object has been deleted or invalidated). If a migration is an async task with no callback, how can we guarantee that it finishes before accessing realm?
Here's my realm config code:
class func SetRealmConfigurationForUser(userId: String) {
let config = RLMRealmConfiguration.defaultConfiguration()
// migration setup
config.schemaVersion = 10
config.migrationBlock = { migration, oldSchemaVersion in
if oldSchemaVersion < 2 {
migration.enumerateObjects(ContactUser.className()) { oldObject, newObject in
// Change primary key from PhoneNumber to Phone_BaseId_Key
let phone = oldObject?["PhoneNumber"]
let baseId = oldObject?["BaseId"]
newObject?["Phone_BaseId_Key"] = String(phone) + "_" + String(baseId)
}
}
if oldSchemaVersion < 3 {
migration.enumerateObjects(DigitsContact.className()) { oldObject, newObject in
newObject?["ExternalId"] = ""
}
}
if oldSchemaVersion < 9 {
migration.deleteDataForClassName(DigitsContact.className())
migration.deleteDataForClassName(Address.className())
migration.deleteDataForClassName(Email.className())
migration.deleteDataForClassName(PhoneNumber.className())
}
}
// Use default directory, but replace filename with the userId
config.path = (((config.path! as NSString).stringByDeletingLastPathComponent as NSString)
.stringByAppendingPathComponent(userId) as NSString)
.stringByAppendingPathExtension("realm")
RLMRealmConfiguration.setDefaultConfiguration(config)
According to Realm's Migrations docs, migrations are only applied when opening a Realm with the configuration on which the schema version and migration blocks are set:
When creating a Realm with this configuration, the migration block will be applied to update the Realm to the given schema version if a migration is needed.
So if you're calling SetRealmConfigurationForUser asynchronously, any concurrent Realm accesses will use the previous default configuration and not see the migration block that you're in the process of defining.
Performing a migration always happens synchronously, by either implicitly by creating the Realm with a migration block in its configuration or explicitly by calling RLMRealm.migrateRealm(_:).
All other Realm accesses (both reads & writes) will be blocked on the migration completing. So as long as the migration block & configuration are set before any Realm is initialized, there should be no race condition.
PS: You're doing a lot of wasted work in your migration block when oldSchema < 3 because you're setting all your DigitsContact.ExternalId properties to "", only to then delete all the DigitsContact objects. Just removing the oldSchemaVersion < 3 conditional and its contents will lead to the same result.
PPS: Is there a reason you're using Realm Objective-C from Swift rather than Realm Swift? Realm has a dedicated API that's much nicer to use from Swift: https://realm.io/docs/swift/latest/

Realm Migration and Optional Properties

I recently upgrade my Realm library from 0.92 (I think) to 0.96.2, but am having some trouble with the new support for ‘optional’ properties. I also need to do a migration for the first time, which is also complicating matters.
The new scheme needed to add a single field to one of the data types, so I coded up a migration that establishes this new property on existing objects:
RLMRealmConfiguration* config = [RLMRealmConfiguration defaultConfiguration];
config.schemaVersion = 1;
config.migrationBlock = ^(RLMMigration* migration, uint64_t oldSchemaVersion)
{
[migration enumerateObjects:Foo.className
block:^(RLMObject* oldObject, RLMObject* newObject) {
if (oldSchemaVersion < 1)
{
newObject[#"user"] = #"";
}
}];
};
[RLMRealmConfiguration setDefaultConfiguration:config];
However, as soon as the code tries to open a Realm, I get an error message about optional property types:
'Migration is required for object type 'Person' due to the following errors:
- Property 'name' has been made optional.
- Property ‘company’ has been made optional.
- Property 'title' has been made optional.
- Property 'phone' has been made optional.
- Property 'email' has been made optional.
- Property 'homeAddress' has been made optional.'
Question #1 - Since the model is going from ‘required’ properties to ‘optional’, there’s guaranteed to be a value already present for existing objects; so I’m struggling to see why a migration is required.
Question #2 - I’d still like to migrate the objects, and nil out the properties if the string is empty, so I coded up a migration:
RLMRealmConfiguration* config = [RLMRealmConfiguration defaultConfiguration];
config.schemaVersion = 1;
config.migrationBlock = ^(RLMMigration* migration, uint64_t oldSchemaVersion)
{
NSLog(#"RUNNING REALM MIGRATION");
// ...basic migration of adding a new property (above)
[migration enumerateObjects:Person.className
block:^(RLMObject* oldObject, RLMObject* newObject) {
if (oldSchemaVersion < 1)
{
if ([oldObject[#"name"] length] == 0)
newObject[#"name"] = nil;
else
newObject[#"name"] = oldObject[#"name"];
// … repeat for other properties
}
}];
};
[RLMRealmConfiguration setDefaultConfiguration:config];
However, the migration doesn’t appear to be run; a breakpoint inside the if (oldSchemaVersion < 1) block is never hit, and the "RUNNING REALM MIGRATION" message never prints.
The outer block — setting up the RLMRealmConfiguration — is hit inside application:didFinishLaunchingWithOptions:
Question #1
By default, all properties were designated 'required' in Realm versions before 0.96. In 0.96, they are marked as 'optional' by default. As such, due to the changes in the new underlying file format to accomodate this (relatively non-trivial change), a migration is required to convert these previously required properties to optional.
If you want to keep these properties as required, you can define this by overriding the [RLMObject requiredProperties] method.
Question 2
Hmm... looking at the sample code, it recommends you encapsulate the enumeration statement inside the if (oldSchemaVersion < 1) conditional block, not the other way around. It's possible that might be causing things to happen out of order. Have you tried swapping that around?
Let me know if that helps! :)
The issue seems to be that I'm setting up the default RLMRealmConfiguration with my migration info, but [RLMRealm realmWithPath:] ignores the default configuration.
Instead of using realmWithPath:, you can copy the default configuration (including your migrationBlock and schemaVersion), set the path property, and pass it to [RLMRealm realmWithConfiguration:error:]
RLMRealmConfiguration* config = [RLMRealmConfiguration defaultConfiguration];
config.path = file;
NSError* error = nil;
RLMRealm* realm = [RLMRealm realmWithConfiguration:config
error:&error];

Creating new smartform data using Ektron ContentTypes

Ektron 8.0.1 SP1
I am using SmartForms and Content Types to read (and hopefully write) data. I can read data but now I am attempting to write a new record similar to the following.
ContentTypeManager<member> contentTypeManager = new ContentTypeManager<member>();
ContentType<member> newmem = new ContentType<member>();
newmem.SmartForm.details.field1 = "Chuck"; // This line throws 'Object reference not set to an instance of an object.' error
newmem.SmartForm.details.field2 = "Norris";
contentTypeManager.Update(newmem);
I get the error "Object reference not set to an instance of an object." for that first assignment line. What am I missing?
I am having trouble finding good documentation on ContentTypes for 8.0.1 now that the Ektron website has been redesigned.
Thx.
Thanks for clarifying, to ADD content to a folder that has a smartform assigned to it, the basic code block should get you started: (Note: the Html attribute of the content is simply the xml matched to the schema you created)
Ektron.Cms.Framework.Content.ContentManager cmanager = new Cms.Framework.Content.ContentManager();
Ektron.Cms.ContentData cdata = new ContentData();
cdata.FolderId = 0;
cdata.XmlConfiguration.Id = 0; //SMARTFORM ID HERE
cdata.Html = "<root><field1>field1 value</field1><field2>field2 value</field2></root>";
cmanager.Add(cdata);
You could update ContentTypes.cs to include an Add method. Just copy the Update method and change contentManager.Update to contentManager.Add.
public void Add(ContentType<T> contentType)
{
Initialize();
contentType.Content.Html = Ektron.Cms.EkXml.Serialize(typeof(T), contentType.SmartForm);
contentManager.Add(contentType.Content);
}
Unfortunately, contentManager.Add returns void. Ideally it should return the new content ID.