User info keys documentation
March 15, 2018 ยท View on GitHub
Below are details about how to annotate your .xcdatamodel entities and attributes to be able to leverage each Realm features when generating your Realm models with Gyro.
Summary :
- Primary key
- Ignore attribute
- Read only
- Inverse Relationships
- Optionnals fields and wrapper types
- Support Annotations
- Handling enums
- Add comments to the generated classes
- JSON Mapping
- Custom ValueTransformers
Primary key
To tell which attribute will be used as a primary key, add the following 'user info' to the entity:
| Key | Value |
|---|---|
identityAttribute | name_of_the_attribute |
Example: On the "FidelityCard" entity:

๐ Sample of the generated code in Java (Android)
package com.gyro.tests;
import io.realm.RealmObject;
import io.realm.annotations.PrimaryKey;
/* DO NOT EDIT | Generated by gyro */
public class FidelityCard extends RealmObject {
[...]
@PrimaryKey
private short identifier;
[...]
}
๐ Sample of the generated code in Swift (iOS)
/* DO NOT EDIT | Generated by gyro */
import RealmSwift
import Foundation
final class FidelityCard: Object {
[...]
dynamic var identifier: Int16 = 0
override static func primaryKey() -> String? {
return "identifier"
}
}
Ignore attribute
You can decide to ignore some attributes of the .xcdatamodel file. They will not be persisted to Realm. To do so, add the following 'user info' to the attribute:
| Key | Value |
|---|---|
realmIgnored | value |
Example: on the attribute ignored of the entity Shop:

๐ Sample of the generated code in Java (Android)
package com.gyro.tests;
import io.realm.RealmList;
import io.realm.RealmObject;
import io.realm.annotations.Ignore;
/* DO NOT EDIT | Generated by gyro */
public class Shop extends RealmObject {
[...]
@Ignore
private String ignored;
[...]
}
๐ Sample of the generated code in Swift (iOS)
/* DO NOT EDIT | Generated by gyro */
import RealmSwift
import Foundation
final class Shop: Object {
dynamic var ignored: String = ""
// Specify properties to ignore (Realm won't persist these)
override static func ignoredProperties() -> [String] {
return ["ignored"]
}
}
Read only (DEPRECATED)
Information about read only 'user info'
On iOS/macOS, you can define attributes which are not persisted and whose value is computed dynamically. To do so, add the following 'user info' to **the attribute**| Key | Value |
|---|---|
realmReadOnly | the_code_source_to_generate |
Example: On the readOnly attribute of the Shop entity:

๐ Sample of the generated code in Objective-C (iOS)
// DO NOT EDIT | Generated by gyro
////////////////////////////////////////////////////////////////////////////////
#pragma mark - Imports
#import "RLMShop.h"
////////////////////////////////////////////////////////////////////////////////
#pragma mark - Implementation
@implementation RLMShop
#pragma mark - Superclass Overrides
- (NSString *)readOnly
{
return self.name;
}
@end
Inverse Relationships
In realm, when you have both A -> B and B -> A relationships, you have to choose one of those relationships to be the primary one (e.g. A -> B) โ that will be stored in Realm โ and the other inverse relationship will then be computed by code. For more information, see the related RealmSwift documentation on Inverse Relationships.
To mark a relationship as being an inverse relationship (the B -> A relationship and not the primary A -> B one), the convention in gyro is to suffix the name of the relationship with an underscore _ .
This will then generate the following code in Swift for that inverse relationship:
LinkingObjects(fromType: A.self, property: "b")`
If your inverse relationship is defined to point to a unique object (inverse of a 1-* relationship for exemple, and not a *-* one), the generated code will contain both the plural form of the computed variable and a singular form returning its first element, for convenience:
let owners = LinkingObjects(fromType: Person.self, property: "dogs")
var owner: Person? { return owners.first }
Optionnals fields and wrapper types
On Android, the -p use_wrappers flag allows you to use wrapper types (Double, Short, โฆ) for optional fields instead of primitive types (double, short, โฆ).
๐ Sample of the generated code in Java (Android)
package com.gyro.tests;
import io.realm.RealmObject;
/* DO NOT EDIT | Generated by gyro */
public class FidelityCard extends RealmObject {
@PrimaryKey
private short identifier; // "optional" checkbox not checked in the xcdatamodel
private Integer points; // "optional" checkbox checked in the xcdatamodel
}
Support Annotations
On Android, the flag -p support_annotations allows you to annotate class attributes' getters & setters with @Nullable (if the attribute is optional) or @NonNull (if it isn't) attributes.
This option can be combined with the -p use_wrappers wrapper flag to generate a safer and more secure code in Android Studio, generating proper warnings if misused.
๐ Sample of the generated code in Java (Android)
package com.gyro.tests;
import io.realm.RealmObject;
/* DO NOT EDIT | Generated by gyro */
public class FidelityCard extends RealmObject {
private short identifier;
@android.support.annotation.IntRange(from=0,to=255)
private Integer points;
private User user;
[...]
@android.support.annotation.Nullable
@android.support.annotation.IntRange(from=0,to=255)
public Integer getPoints() {
return points;
}
public void setPoints(@android.support.annotation.Nullable @android.support.annotation.IntRange(from=0,to=255) final Integer points) {
this.points = points;
}
[...]
}
Furthermore, it's possible to add custom annotations to your fields. To do that, simply add the key/value pair to the UserInfos of the attribute to annotate:
| Key | Value |
|---|---|
supportAnnotation | AnnotationToAdd |
Example: If you wish to add the IntRange(from=0,to=255) annotation to an attribute, use the following:

๐ Sample of the generated code in Java (Android)
package com.gyro.tests;
import io.realm.RealmObject;
/* DO NOT EDIT | Generated by gyro */
public class FidelityCard extends RealmObject {
public interface Attributes {
String IDENTIFIER = "identifier";
String POINTS = "points";
}
private short identifier;
@android.support.annotation.IntRange(from=0,to=255)
private int points;
public short getIdentifier() {
return identifier;
}
public void setIdentifier(final short identifier) {
this.identifier = identifier;
}
@android.support.annotation.IntRange(from=0,to=255)
public int getPoints() {
return points;
}
public void setPoints(@android.support.annotation.IntRange(from=0,to=255) final int points) {
this.points = points;
}
}
Handling enums
Sometimes, an Int attribute in the model actually represents an enum member in your model. To deal with this case, you can add the following two key/value pairs to this attribute:
| Key | Value |
|---|---|
enumType | my_type |
enumValues | my_value_1, my_value_2, my_value_3 |
Note: If you also add the
JSONKeyPathUser Info key to your attribute in addition to enums, you'll have to add theJSONValuesto also tell the mapping between theenumValuesand the matching possible values found in the JSON. See the JSON Mapping below for more details.
Example: On the attribute type of the Shop entity.

๐ Sample of the generated code in Java (Android)
Shop.java:
package com.gyro.tests;
/* DO NOT EDIT | Generated by gyro */
import io.realm.RealmObject;
public class Shop extends RealmObject {
private String name;
private String type;
public String getType() {
return type;
}
public Type getTypeEnum() {
return Type.get(getType());
}
public void setTypeEnum(final Type type) {
this.type = type.getJsonValue();
}
[...]
}
Type.java:
package com.gyro.tests;
/* DO NOT EDIT | Generated by gyro */
public enum Type {
TYPE_ONE("TypeOne"),
TYPE_TWO("TypeTwo"),
TYPE_THREE("TypeThree");
[...]
}
๐ Sample of the generated code in Swift (iOS)
Shop.swift:
/* DO NOT EDIT | Generated by gyro */
import RealmSwift
import Foundation
final class Shop: Object {
[...]
dynamic var type: String = ""
var typeEnum: Type? {
get {
guard let enumValue = Type(rawValue: type) else { return nil }
return enumValue
}
set { type = newValue?.rawValue ?? "" }
}
}
Type.swift:
/* DO NOT EDIT | Generated by gyro */
enum Type: String {
case typeOne = "TypeOne"
case typeTwo = "TypeTwo"
case typeThree = "TypeThree"
}
Note: For Android and Swift, each enum is created in a separate file.
Add comments to the generated classes
To make the generated code more readable, it's possible to add comments on an entity/attribute/relationship โ e.g. to provide a short description of what this entity/attribute/relationship is supposed to represent.
To do so, simply add the following key/value pair to your entity/attribute/relationship in your .xcdatamodel:
| Key | Value |
|---|---|
comment | the_comment_text_here |
A code comment will then be generated (.swift (Swift) or .java (Android)) just before the declaration, e.g. to help the developer understand what this class/attribute/relationship is for.
JSON Mapping
You can also add the json mapping for each attribute or relationship with the following key/value pair:
| Key | Value |
|---|---|
JSONKeyPath | json_field_name |
This key is only used when using the ObjectMapper or Decodable template.
Currently, this will then generate:
- Code for
ObjectMapperorDecodableon iOS (in the future we plan to generateSourceryannotations instead so that people can choose whatever JSON library they prefer). GSONannotations (@SerializedName(โฆ)) for Android
Example: On the 'name' attribute of the 'Shop' entity:

๐ Sample of the generated code in Java (Android)
Sur Android, nous utilisons la librairie GSON
package com.gyro.tests;
/* DO NOT EDIT | Generated by gyro */
import com.google.gson.annotations.SerializedName;
import io.realm.RealmList;
import io.realm.RealmObject;
public class Shop extends RealmObject {
@SerializedName("json_name")
private String name;
private RealmList<Product> products;
[...]
}
๐ Sample of the generated code in Swift (iOS)
On iOS, we support Decodable and Object Mapper templates for parsing/mapping.
Shop+Decodable.swift:
/* DO NOT EDIT | Generated by gyro */
import protocol Decodable.Decodable
import Decodable
extension Shop: Decodable {
static func decode(_ json: Any) throws -> Shop {
let shop = Shop()
shop.name = try json => "name"
let productsSandbox: [Product] = try json => "products"
shop.products.append(objectsIn: productsSandbox)
return shop
}
}
ShopMapper.swift:
/* DO NOT EDIT | Generated by gyro */
import ObjectMapper
extension Shop: Mappable {
// MARK: Initializers
convenience init?(map: Map) {
self.init()
}
// MARK: Mappable
func mapping(map: Map) {
// MARK: Attributes
self.name <- map["name"]
// MARK: Relationships
self.products <- (map["products"], ListTransform<Product>())
}
}
Combine JSONKeyPath and enums
Note that you can combine that JSONKeyPath key with enums (see Handling enums above). If you declared the User Info keys to make your attribute an enum (enumType + enumValues) in addition to JSONKeyPath, you'll have to also add the JSONValues key to list the corresponding values in the JSON for those enumValues.
| Key | Value |
|---|---|
JSONValues | valeur_json_1,valeur_json_2,valeur_json_3 |
The number of items listed for that JSONValues key must be the same as the number of items listed for the enumValues keys, obviously.
Example:

๐ Sample of the generated code in Java (Android)
Type.java:
package com.gyro.tests;
/* DO NOT EDIT | Generated by gyro */
public enum Type {
TYPE_ONE("json_type_one"),
TYPE_TWO("json_type_two"),
TYPE_THREE("json_type_three");
[...]
}
๐ Sample of the generated code in Swift (iOS)
Type.swift:
/* DO NOT EDIT | Generated by gyro */
enum Type: String {
case typeOne = "json_type_one"
case typeTwo = "json_type_two"
case typeThree = "json_type_three"
}
Custom ValueTransformers
Only available on iOS (as Android uses the GSON library), custom ValueTransformers allows you to e.g. convrt a String into an Int or a Date when parsing the JSON. They are only used when using ObjectMapper or Decodable template.
To create a specific ValueTransformer for a field:
- Create your
ValueTransformercustom class inheritingNSValueTransformerand add it to your project - Select the attribute that will need this transformer, and in the UserInfo field, add a pair for the transformer key whose value should be the name of the
ValueTransformerclass to use:
| Key | Value |
|---|---|
transformer | NameOfTheTransformerClass |
Example:

๐ Sample of the generated code in Swift (iOS)
gyro will produce the following code. (In this example, attributes attrDouble and attrInteger32 don't have a transformer key set in their UserInfo).
Shop+Decodable.swift:
/* DO NOT EDIT | Generated by gyro */
import protocol Decodable.Decodable
import Decodable
extension Shop: Decodable {
static func decode(_ json: Any) throws -> Shop {
let shop = Shop()
shop.attrDate = try Date.decode(json => "attrDate")
shop.attrDateCustom = try Date.decode(json => "attrDateCustom")
shop.attrDouble = try json => "attrDouble"
shop.attrInteger16 = try Int.decode(json => "attrInteger16")
shop.attrInteger32 = try json => "attrInteger32"
shop.attrInteger64 = try Int.decode(json => "attrInteger64")
return shop
}
}
ShopMapper.swift:
/* DO NOT EDIT | Generated by gyro */
import ObjectMapper
extension Shop: Mappable {
// MARK: Initializers
convenience init?(_ map: Map) {
self.init()
}
// MARK: Mappable
func mapping(map: Map) {
// MARK: Attributes
self.attrDate <- (map["attrDate"], ISO8601DateTransform())
self.attrDateCustom <- (map["attrDateCustom"], CustomDateTransformer())
self.attrDecimal <- (map["attrDecimal"], MPDecimalTransformer())
self.attrDouble <- map["attrDouble"]
self.attrFloat <- (map["attrFloat"], MPDecimalTransformer())
self.attrInteger16 <- (map["attrInteger16"], MPIntegerTransformer())
self.attrInteger32 <- map["attrInteger32"]
self.attrInteger64 <- (map["attrInteger64"], MPIntegerTransformer())
}
}