Meetup1511 realm updates

Realm versions: 렘의 새 기능 소개

Realm Korea의 개발자 김용욱 lk@realm.io님이 Realm Seoul 11월 정기 모임에서 Realm의 최신 버전을 소개합니다. 최근 적용된 Java 0.82~0.85 버전과 Objective-C 0.96, Swift 0.96버전까지 Realm version 별 변경 사항을 확인할 수 있습니다.


Realm의 최신 버전과 새로운 기능

김용욱 님의 11월 20일 Realm Seoul 사용자 모임 발표입니다.

빠르게 발전하고 있는 Realm의 최신 버전별 변경 사항과 사용 예, 마이그레이션 방법까지 버전업에 따른 관련 설명을 담고 있습니다. Realm의 최신 버전에서 어떤 기능들을 제공하고 있고 어떻게 적용할 수 있는지 알아 보세요.

Cocoa v.s. Java

  • Cocoa 버전의 기능 업데이트가 더 빠름
  • 이번 발표에서 다룰 버전
    • Cocoa 0.95.1, 0.96
    • Java 0.82, 0.83, 0.84, 0.85

Java 0.82

  • Primary key에 Index를 적용
  • 이전 버전까지는 Primary Key를 적용해도 Index를 설정해야 했음
  • 이전 버전에서 마이그레이션할 경우 addSearchIndex 메서드로 index field를 추가해줘야 함

Java 0.83

  • 기본형 래퍼 클래스 (Null 가능)
class Person extends RealmObject {
    String name;
    int age;
    Integer Weight; // weight is not required!
}

Person john = realm.createObject(Person.class);
john.setName("John");
john.setAge(25);
john.setWeight(73);

Person bill = realm.createObject(Person.class);
bill.setName("Bill");
bill.setAge(41);
bill.setWeight(null);
  • Null이 필요없는 필드에 @Required로 정의 (String, Date, Byte[]가 기본으로 null)
  • Null을 쓸 수 있도록 마이그레이션 단계에서 기존 필드를 수정

이런 개발 뉴스를 더 만나보세요

RealmMigration migration = new RealmMigration() {
    @Override
    public long execute(Realm realm, long version) {
        Table table = realm.getTable(Dog.class);
        table.convertColumnToNullable(table.getColumnIndex("name"));
        return 1;
    }  
};

RealmConfiguration realmConfig = new RealmConfiguration.Builder(getContext())
    .schemaVersion(1)
    .schema(Dog.class)
    .migration(migration)
    .build();
  • 문서를 확인해서 버전별 변화에 따라 마이그레이션하는 것을 추천함

Java 0.84

  • 비동기 질의
  • 여태까지는 여러 스레드에서 Realm을 자동으로 사용하도록 지원하지 않았음
  • 이제 백그라운드에서 질의를 하고 포그라운드에서 결과를 받는 등 다른 스레드에서 사용할 수 있음
RealmResults<User> result = realm.where(User.class)
    .equalTo("name", "John")
    .or()
    .equalTo("name", "Peter")
    .findAllAsync();

private RealmChangeListener callback = new RealmChangeListener() {
    @Override
    public void onChange() { // called once the query complete and on every update
    // use the result
    }
};

public void onStart() {
    RealmResults<User> result = realm.where(User.class).findAllAsync();
    result.addChangeListener(callback);
}
  • 해제와 상태 확인 가능
public void onStop () {
    result.removeChangeListener(callback); // remove a particular listener
    // or
    result.removeChangeListeners(); // remove all registered listeners
}

result.isLoaded()
  • 비동기 트랜잭션 가능: 작업은 백그라운드에서, 결과만 포그라운드에서 받음
realm.executeTransaction(new Realm.Transaction() {
            @Override
            public void execute(Realm bgRealm) {
                User user = bgRealm.createObject(User.class);
                user.setName("John");
                user.setEmail("john@corporation.com");
            }
        }, new Realm.Transaction.Callback() {
            @Override
            public void onSuccess() {
            }

            @Override
            public void onError(Exception e) {
                // transaction is automatically rolled-back, do any cleanup here
            }
        });

  • 비동기 트랜잭션 취소 기능 지원
public void onStop () {
    if (transaction != null && !transaction.isCancelled()) {
        transaction.cancel();
    }
}
  • 질의한 결과가 null인지 비었는지 확인하는 유틸리티 함수 추가
// Find all users with no dogs
RealmResults<User> users = realm.where(User.class).isEmpty("dogs").findAll();

// Find all users with at least 1 dog
RealmResults<User> users = realm.where(User.class).not().isEmpty("dogs").findAll();
  • 유니크한 결과만 확인하고 싶을 때는 distinct 메서드 사용 (@PrimaryKey와 @index로 지정된 데이터만 사용 가능)
// Returns the set of users that all have a different name
RealmResults<User> users = realm.distinct(User.class, "name");
  • 유틸리티 함수 추가
    • Realm.isClosed():
      하부의 Realm 파일이 열려있는지 확인함
    • Realm.isInTransaction():
      Realm 파일이 여전히 쓰기 트랜잭션 상태인지 확인함
    • RealmQuery.isValid(), RealmList.isValid(), RealmResults.isValid():
      하부의 Realm이 닫혀 있거나 데이터가 삭제되었는지 확인함

Java 0.85:

New encryption implementation

  • 암호화 기능을 구현
  • 유틸리티 함수 추가
    • Realm.isEmpty():
      어떤 데이터를 가졌는지 확인
    • RealmEncryptionNotSupportedException 폐기:
      모든 기기를 지원함
    • Realm.executeTransaction():
      RealmException 대신에 RunTimeException을 직접 던지며 예외가 발생하면 자동으로 롤백이 이뤄짐
    • RealmQuery.isNull(), RealmQuery.isNotNull():
      RealmError 대신 IllegalArgumentException을 던짐
  • 전반적으로 안정성이 높아짐
  • 다음 버전인 0.86에서는 마이그레이션과 필드를 추가하면서 개발할 수 있는 Dynamic API를 추가할 예정임

Objective-C 0.96

  • null을 쓰지 않는 필드에 requiredProperties 추가
@interface OptionalTypes : RLMObject
@property NSString *optionalString;
@property NSString *requiredString;
@property NSData *optionalData;
@property NSDate *optionalDate;
@property NSNumber<RLMInt> *optionalInt;
@property NSNumber<RLMBool> *optionalBool;
@property NSNumber<RLMFloat> *optionalFloat;
@property NSNumber<RLMDouble> *optionalDouble;
@end
@implementation OptionalTypes
+ (NSArray *)requiredProperties {
    return @[@"requiredString"];
}
@end

Objective-C 0.96

  • Objective-C나 Java 모델에 비해 ?로 간단하게 null을 구현할 수 있음
class OptionalTypes: Object {
    dynamic var string: String? = "B"
    dynamic var data: NSData? = "C".dataUsingEncoding(NSUTF8StringEncoding)
    dynamic var date: NSDate? = NSDate(timeIntervalSince1970: 10)
    let int = RealmOptional<Int>(1)
    let float = RealmOptional<Float>(2.2)
    let double = RealmOptional<Double>(3.3)
    let bool = RealmOptional<Bool>(true)
    let boolWithNilDefault = RealmOptional<Bool>(nil)
}

Objective-C와 Swift 0.96: 마이그레이션

  • Java와 비슷하게 설정에 마이그레이션 객체를 넘긴 후 단계별로 내용을 설정함
  • null을 쓸지 쓰지 않을지에 따라 단계가 나뉠 수 있음
// Inside your [AppDelegate didFinishLaunchingWithOptions:]
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,
// so assuming that'd be your first migration, we set it to 1.
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) {
        [migration enumerateObjects:Person.className
                          block:^(RLMObject *oldObject, RLMObject *newObject) {
            // convert null-placeholders to actual nils
            newObject[@"birthdate"] = [oldObject[@"birthdate"] timeIntervalSince1970] > 0 ? oldObject[@"birthdate"] : nil;
        }];

        // Note: Even if you don't have to convert placeholder values,
        // you still have to provide at least an empty migration block
        // when your schema has changes to nullability of properties.
    }
};
  • Swift와 Objective-C 버전은 다른 프로세스에서도 사용할 수 있는 멀티 프로세스 기능이 지원되고 있으며 Java는 이후 지원할 예정

Q&A

  • Q: Realm이 안드로이드의 SQLite보다 빠르다 했는데 iOS의 Core Data와 비교하면?
  • A: Core Data도 SQLite DB를 사용하므로 Realm이 빠릅니다.
  • Q: DB용도로 사용하긴 좋은데 Json을 파싱한다던가 매핑 기능을 지원하는 부분은?
  • A: 다른 라이브러리와 함께 사용해야 하는 데 불편한 부분이 존재하므로 해결책을 고민하고 있습니다.
  • Q: 비동기 질의와 관련해서 findAllAsync()를 UI 스레드에서 호출하는지?
  • A: UI 스레드에서 호출하면 작업을 멈추지 않고 비동기적으로 호출됩니다.
  • Q: onChange()는 작업 스레드에서 불리는지?
  • A: 네, 그렇습니다. 간단한 질의는 UI 스레드에서 해도 충분하지만, 복잡한 스레드는 비동기 질의나 비동기 트랜잭션을 쓰시면 좋을 것 같습니다.

컨텐츠에 대하여

이 컨텐츠는 저자의 허가 하에 이곳에서 공유합니다.

Realm Team

Realm의 미션은 더 나은 앱을 빠르게 개발할 수 있도록 돕는 것입니다. 이를 위해 저희는 개발자들이 실시간 협업, 가상 현실, 라이브 데이터 동기화, 오프라인 경험, 메시징 등 정교하고 강력한 기능을 쉽게 개발할 수 있도록 하는 개발 도구와 플랫폼을 제공하고 있습니다.

저희는 모바일 인터넷이 수많은 사용자와 보다 많은 디바이스가 속한 개방형 네트워크와 이들 간의 실시간 상호 작용으로 진화할 것이라고 믿으며, 개발자가 이같은 방향으로 발전할 수 있도록 돕기 위해 저희 제품들을 개발하고 있습니다.

4 design patterns for a RESTless mobile integration »

close