Skip to content
This repository has been archived by the owner on Dec 20, 2023. It is now read-only.

Latest commit

 

History

History
706 lines (465 loc) · 16.8 KB

UserInfoKeys.md

File metadata and controls

706 lines (465 loc) · 16.8 KB

User info keys documentation

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

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:

Primary Key

📑 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:

Ignored Attribute

📑 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:

Read Only

📑 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:

Support Annotation

📑 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 JSONKeyPath User Info key to your attribute in addition to enums, you'll have to add the JSONValues to also tell the mapping between the enumValues and 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.

enum

📑 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 ObjectMapper or Decodable on iOS (in the future we plan to generate Sourcery annotations instead so that people can choose whatever JSON library they prefer).
  • GSON annotations (@SerializedName(…)) for Android

Example: On the 'name' attribute of the 'Shop' entity:

JSONKeyPath

📑 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:

enum_json

📑 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 ValueTransformer custom class inheriting NSValueTransformer and 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 ValueTransformer class to use:
Key Value
transformer NameOfTheTransformerClass

Example:

transformer

📑 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())
  }
}