Map a dynamic object with ValueInjecter - mapping

I'm retrieving data with Simple.Data - which maps the database table to a dynamic object.
I want to map the dynamic object to a simple type. I have tried this:
var dbObject = Database.Default.LocationStatus.FindByLocationStatusId(locationStatusId);
ILocationStatus domainObject = new LocationStatus();
domainObject.InjectFrom((object)dbObject);
But no properties in the domainObject are set.
The mapping should be simple as the property names are the same, ei: dbObject.Name and domainObject.Name
Where am I going wrong?
Note: I can in fact magically cast (duck typing?) (LocationStatus)dbObject but I'd like to know how to map with ValueInjecter. Thanks.

Strange as it may sound, I ran into this same problem a few days ago, and the solutions was simple.
You need to cast the output of your dynamic to the type your trying to map too.
In my case:
WeatherData myData = new WeatherData().InjectFrom((object)weatherData);
as shown in the post referenced in the comments above didn't work (I suspect with the same problem as the original poster), but when cast using as ...
WeatherData myData = new WeatherData().InjectFrom((object)weatherData) as WeatherData;
Everything works fine.
So it seems even with the newer versions, 3+ years later this can still be an issue, and casting the output type is the fix.

Related

best way to rename a class in Realm swift

I use a "common" library in my iOS project. This library creates a Realm database. So far, I've been using this library on only iOS projects. I want to now use that same library with a macOS project. It's Foundation based, and doesn't use UIKit, so why not?
Here's the problem: I have a Realm class named Collection
Collection is also the name of a standard Swift protocol.
While I've been able to get away with this name collision on my iOS project, for some reason, I can't do the same on my MacOS project -- it creates a name-collection.
I read about this notation that can be used like this:
#objc(SpecialCollection)
class Collection: Realm.Object {
let items: List<ItemObject>
let name: String
let url: String
....
}
So, this solves the name-collision problem. In ObjC, the name will be something different, but in Swift, I don't need to change anything.
This is all well and good except for my local Realm database. I have a lot of Collection objects that should be renamed to SpecialCollection (since Realm uses ObjC underneath Swift). I'd like to perform a migration to do this, but apparently there isn't a supported way to do this yet? I noticed tickets on github about this issue being "watched", but unfortunately, there still exists no published solution to fix this problem.
All of my Collection objects contain List objects (hence the name). So, I tried to run an enumeration on all of the Collection objects in a migration... I would just take the older object, and create a new object with the new name, like this:
migration.enumerateObjects(ofType: "Collection", { (oldObject, _) in
migration.create("SpecialCollection", value: oldObject)
}
But since oldObject has a list of other objects, Realm's migration will try and create all the items in any List objects... which can't be done, because it creates objects with the same primaryKey value (causing a crash).
So, I can't keep the old name (Collection), and I can't convert to the new name, and I can't just trash the user's data. So, I'm truly at an impasse.
Blockquote
I tried to modify oldObject before creating the new object, but you can't change oldObject in a migration.
The only rule is that the old data has to be preserved, I can't just destroy the user's realm here.
Thanks for any help in this. It is greatly appreciated.
I had a very similar problem last night. I had a couple Realm classes I wanted to rename, and where one of them had a List property referring to the second class. So the only difference compared to your problem is I was renaming ItemObject class as well.
So here's how I did it:
Migrate your Collection class first, creating SpecialCollection.
While migrating, walk the Collection's list and create new
SpecialItemObject for each ItemObject and append it to the new list.
Delete each ItemObject.
Now enumerate all ItemObject remaining
in the realm and create a new SpecialItemObject and map its values
over. The reason is there may be other ItemObject floating around
in your realm, not tied to the list.
Delete all remaining ItemObject.
migration.enumerateObjects(ofType: "Collection")
{ (oldObject, newObject) in
let specialCollection = migration.create(SpecialCollection.className())
specialCollection["name"] = oldObject!["name"]
specialCollection["url"] = oldObject!["url"]
if let oldItems = oldObject!["items"] as? List<MigrationObject>,
let newItems = specialCollection["items"] as? List<MigrationObject>
{
for oldItem in oldItems
{
let newItem = migration.create(SpecialItemObject.className())
newItem["name"] = oldItem["name"] // You didn't specify what was in your ItemObject so this example just assumes a name property.
newItems.append(newItem)
migration.delete(oldItem)
}
}
}
migration.deleteData(forType: "Collection")
// Now migrate any remaining ItemObject objects that were not part of a Collection.
migration.enumerateObjects(ofType: "ItemObject")
{ (oldObject, newObject) in
let newItem = migration.create(SpecialItemObject.className())
newItem["name"] = oldItem["name"]
}
// Now let's be sure we'll have no further ItemObject in our entire Realm.
migration.deleteData(forType: "ItemObject")
So this is how I solved it for myself last night, after finding next to nothing about most of this in cocoa-realm in GitHub or on SO or elsewhere. The above example only differs from what you asked in that you weren't asking to rename your ItemObject class. You could try just creating new ItemObject objects and mapping the properties across in the same way I show in my example. I don't see why it wouldn't work. I've provided my example exactly how I solved my own problem, since I tested some migrations last night to prove it was solid.
Since your question is almost 5 months old, I'm really just posting this answer for posterity. Hope this helps someone!
Tested with Realm 3.3.2 on iOS 11.3 sim / Xcode 9.3 / Swift 4.1

How to create an empty Results<T> object?

I'm trying to create a MutableProperty which holds a Results received from Realm.objects(_:).
To create the property I need to give it an initial value; hence an 'empty' Results.
I've tried creating one using:
var someThings = Results<SomeObject>()
MutableProperty(someThings)
But the compiler gives me the error: Cannot invoke initializer for type 'Results<SomeObject>' with no arguments.
While I understand the error, I'm not really sure how to create a Results object in this context.
Looking at the source of Results I couldn't find an init either.
So my question is; how can I create a Results myself to use in a MutableProperty?
Edit:
I've seen this question...but that doesn't really help (unless I'm going to create a "wrapper" for the MutableProperty or something).
With help of the comments on my OP; I created a mutable property with an empty set of results by fetching objects with an 'invalid' filter.
E.g. MutableProperty(realm.objects(SomeObject.self).filer("EMPTY SET")).

Swift - Coredata Migration - Set new attribute value according to old attribute value

One of my current core data entities - Entity1 - has a Boolean attribute called isSaved.
In the new core data model, I am planning to remove isSaved attribute and add a new Int attribute called type. And for all saved Entity1 objects, I'd like to set the value of type according to the value of isSaved in old core data model. (e.g. if isSaved is true, then type is 1, else type is 2).
I've read some articles about light weight core data migration, but none of them seems helpful.
Just wondering if there is any way that can make my planned migration work?
Lightweight migration can't do this. You'll have to create a mapping model and a subclass of NSEntityMigrationPolicy. It's not difficult but it's unfamiliar territory for most iOS developers. The steps run like this:
Create the mapping model. In Xcode, File --> New --> Mapping Model. When you click "Next", Xcode will ask for the source (old) and destination (new) model files for this mapping.
The model file will infer mappings where possible. Everything else will be blank. With your type and some other properties, it'll look something like the following. Entries like $source.timestamp mean to copy the existing value from before the migration.
Create a new subclass of NSEntityMigrationPolicy. Give the subclass an obvious name like ModelMigration1to2. This class will tell Core Data how to map the old boolean value to the new integer value.
Add a method to the subclass to convert the value. Something like the following. The method name doesn't matter but it's good if you choose something descriptive. You need to use ObjC types here-- e.g. NSNumber instead of Int and Bool.
#objc func typeFor(isSaved:NSNumber) -> NSNumber {
if isSaved.boolValue {
return NSNumber(integerLiteral: 1)
} else {
return NSNumber(integerLiteral: 2)
}
}
Go back to the mapping model and tell it to use your subclass as its custom mapping policy. That's in the inspector on the right under "custom policy". Be sure to include the module name and class name.
Tell the mapping model to use that function you created earlier to get values for the type property from the old isSaved property. The following says to call a function on the custom policy class named typeForIsSaved: (the : is important) with one argument, and that the argument should be the isSaved value on $source (the old managed object).
Migration should now work. You don't have to tell Core Data to use the mapping model-- it'll figure out that migration is needed and look for a model that matches the old and new model versions.
A couple of notes:
If you crash with an error that's something like Couldn't create mapping policy for class named... then you forgot the module name above in step 5 (or got it wrong).
If you get a crash with an unrecognized selector error then the method signature in step 4 doesn't match what you entered in step 6. This can also happen if you forget to include #objc in the function declaration.
Using Xcode 9.1 Beta with Swift 4, I find migration works but you have to be careful how you specify the transform method name, also it seems you need to mark your functions as #objc.
For example, my Value Expression:
FUNCTION($entityPolicy, "changeDataForData:" , $source.name)
My transformation policy method name:
class StudentTransformationPolicy: NSEntityMigrationPolicy {
#objc func changeData(forData: Data) -> String {
return String(data: forData, encoding: .utf8)!
}
}
Definitely tricky and took a lot of experimenting before I got it to trigger when launching my app after model changes. It might be easier to implement "createDestinationInstances" for your policy if all of this doesn't work, but we'll leave that for another day...

Exclude PFObject Sublclass' Attribute to Get Persisted

I'm working on an iOS app using the native Objective-C Parse API, and I have a subclass of PFObject called MRPlace which has a number of attributes that will be stored in the parse back-end. However, there is an attribute (Say the attribute is called isFavorite) that I would only like to be kept locally (client-side) and thus excluded from being persisted in the database.
I've looked through the documentation and the web with no luck. How can this be accomplished?
As mentioned in knshn's comment, using #synthesize isFavorite = _isFavorite for example will work.
Since they won't be stored on Parse, and really won't even be persisted locally (with no Local Data Store,) you should store these values elsewhere. There's no way to specify local-only keys on a PFObject.
I found a way to accomplish this in swift so I thought I'd share. Say there's an attribute called streetName. This is how you'd declare it normally:
#NSManaged var streetName: String
However, if you omit the #NSManaged it works as desired!
var streetName: String

GWT JSONObject adding an additional incorrect key when converting overlay type to json string

I'm encountering the following problem - I have simple GWT overlay types, and I'm trying to convert one to a JSON string on the client; I'm simply doing:
new JSONObject(this).toString();
The conversion works, but it adds an additional, incorrect key to the json string, such as:
{"key1":"value1", "key2":value2, "$H":1}
where "$H":1 doesn't correspond to anything in my overlay type.
Any idea why this is?
Any help is appreciated on this, thanks.
This issue is define in this link
The $H property comes from the
implementation of
JavaScriptObject#hashCode() (in
com.google.gwt.cire.client.impl.Impl#getHashCode(Object)).
In your case, this is due to
AbstractEditableCell maintaining a map
of value keys to their "view data",
and your use (I guess) of the default
ProvidesKey implementation
(SimpleProvidesKey) which directly
returns the item.
So, when rendering, the EditTextCell
calls getViewData, which looks up the
key in the map (and thus needs the
hashcode of the key, hence the call to
hashCode), and the key is your JSO
(hence the new $H property).
I believe that giving a ProvidesKey
implementation (in you case, returning
the name property for instance) to the
Celltable would solve your issue.

Resources