Realm Blog

Realm Xamarin 0.75.0를 출시합니다

by /

Realm Xamarin 0.75.0 버전을 출시합니다. 이는 발표 이후의 첫번째 릴리즈입니다. 필수적인 기능과 수정을 포함하고 있습니다. NuGet은 이미 업데이트가 기존 프로젝트에서 사용가능하며 새로운 프로젝트에 새 버전을 사용할 수 있다는 것을 알려주고 있습니다.

호환성 주의사항

  • Realm 파일의 파일 포맷이 변경되었습니다. 파일은 자동으로 업그레이드되지만 열려있는 낡은 버전의 Realm 파일은 변경되지 않을 수 있습니다.
  • RealmResults<T>는 더 이상 INotifyCollectionChanged을 구현하지 않습니다. 대신에 새 ToNotifyCollectionChanged 메서드를 사용하세요.

주요 변경점

  • RealmResults<T>은 새 SubscribeForNotifications 메서드를 사용하여 세분화된 변화를 관찰할 수 있습니다.
  • 백그라운드 스레드에서 쓰기 트랜잭션을 사용할 수 있는 WriteAsync 메서드가 Realm에 추가되었습니다.
  • Realm 모델은 이제 바이너리 데이터를 저장하기 위해 byte[] 속성을 사용할 수 있습니다.
  • RealmResults<T>는 이제 ObservableCollection<T>를 생성하기 위해 ToNotifyCollectionChanged 확장 메서드를 받을 수 있습니다. - MVVM 데이터 바인딩에 적합한 래퍼와 같습니다.

사소한 수정들

  • Null이 지원되는 DateTimeOffset 속성이 이제 지원됩니다.
  • 문자열 속성에 null을 대입하면 제대로 null을 반환합니다.
  • Fody 설치에 실패하면 이제 NullReferenceException 대신에 “Realms.RealmException: Fody not properly installed. RDB2_with_full_Realm.Dog is a RealmObject but has not been woven.” 예외가 발생합니다.
  • 몇 멤버에서 PCL RealmConfiguration가 빠집니다.
  • Fody 위버가 이제 비 기본형 nuget 리포지토리 경로에서도 발견가능합니다.

읽어주셔서 감사합니다. Realm과 함께 앞으로 더 나아가 멋진 앱을 만들어 보세요! 우리는 항상 StackOverflow, GitHub, 또는 Twitter 로 연락하실 수 있습니다.

더 읽어보기

13,949번의 커밋과 6,148개의 이슈를 거쳐 Realm이 1.0이 되었습니다. 개발자분들의 성원에 감사드립니다

by /

Realm 을 “최초의 모바일 데이터베이스”로 소개한 것이 2014년 7월 입니다. 오늘, 13,949개의 커밋과 6,148 개의 이슈를 통해 커뮤니티와 함께 만들어진 Realm 1.0을 자신있게 발표하게 되었습니다. 이는 Realm 회사로서, 제품으로서 커다란 의미를 가질 뿐 아니라 Realm과 함께한 iOS, Android 개발자 커뮤니티에 커다란 의미입니다.

우리가 처음 제품을 발표할 때, 우리는 iOS와 Mac 사용자를 위한 Objective-C 버전만을 발표했습니다. Swift 지원과 Android 버전은 그 후에 추가가 되었고, 최근에는 React Native와 Xamarin 지원을 발표하였습니다. 이제 Realm은 모든 모바일 개발 플랫폼과 개발 언어를 지원하고 있습니다. 이번 릴리즈는 2년 이상 온 힘을 다해 만들고 다듬은 결과물이며, 이는 커뮤니티의 수많은 지원이 있지 않았다면 불가능했을 것입니다. 감사합니다! 👏

1.0 이전 버전에서도 Realm은 이미 모바일 개발 커뮤니티에서 널리 사용되고 있었습니다. 개발자들은 Github에서 12,000 개 이상의 Star를 주셨고, 이는 계속 늘어나고 있습니다. Realm 은 현재 10만명이상의 개발자가 실제로 개발에 사용하고 있으며, 스타벅스, 트위터, 알리바바, 이베이, 네이버, 카카오, 배달의민족, CJ몰 과 같은 대형앱을 포함한 수많은 앱에서 널리 사용하고 있습니다. 이 앱들은 Realm이 그들의 앱을 더 낫게 만들 수 있다는 판단하에 사용합니다. Realm을 통해서 개발자들은 더 나은 사용자 경험을 가져올 수 있으며 앱의 개발은 더 쉽고 빨라집니다. 오늘 1.0 릴리즈를 통해서 이제 개발자들은 Realm이 충분한 수준으로 성숙했고 안정성을 확보하고 있다는 더 강한 확신을 가질 수 있습니다.

어떤 점이 바뀌었는지는 이 개발 changelogs를 통해 확인하실 수 있습니다: Java, Objective-C와 Swift

Realm이 무엇인가요?

Realm은 SQLite를 기반으로하는 여타 ORM들과는 다릅니다. 우리는 모바일 개발자가 필요로하는 기능을 모두 갖춘 데이터베이스이며 처음부터 새로 만들었습니다. 네이티브 객체는 우리가 직접 개발한 데이터베이스와 동적으로 맵핑 됩니다. 이 데이터베이스는 key-value 스토어가 아닌, 모든 데이터베이스에서 요구되는 기능을 갖춘 데이터베이스입니다. 이런 방법을 통해 우리는 간결한 API는 물론 최고의 성능을 보여줍니다. Realm을 사용하면 복잡한 데이터 모델을 구현하고, 객체를 그래프와 연결하고, 복잡한 질의를 날릴 수 있습니다.

// Define you model class by extending RealmObject
public class Dog extends RealmObject {
    private String name;
    private int age;

    // ... Generated getters and setters ...
}

public class Person extends RealmObject {
    @PrimaryKey
    private long id;
    private String name;
    private RealmList<Dog> dogs; // Declare one-to-many relationships

    public Person(long id, String name) {
        this.id = id;
        this.name = name;
    }

    // ... Generated getters and setters ...
}

// Use them like regular java objects
Dog dog = new Dog();
dog.setName("Rex");
dog.setAge(1);

// Create a RealmConfiguration that saves the Realm file in the app's "files" directory.
RealmConfiguration realmConfig = new RealmConfiguration.Builder(context).build();
Realm.setDefaultConfiguration(realmConfig);

// Get a Realm instance for this thread
Realm realm = Realm.getDefaultInstance();

// Query Realm for all dogs younger than 2 years old
final RealmResults<Dog> puppies = realm.where(Dog.class).lessThan("age", 2).findAll();
puppies.size(); // => 0 because no dogs have been added to the Realm yet

// Persist your data in a transaction
realm.beginTransaction();
final managedDog = realm.copyToRealm(dog); // Persist unmanaged objects
Person person = realm.createObject(Person); // Create managed objects directly
person.getDogs().add(managedDog);
realm.commitTransaction();

// Listeners will be notified when data changes
puppies.addChangeListener(new RealmChangeListener<RealmResults<Dog>>() {
    @Override
    public void onChange(RealmResults<Dog> results) {
        // Query results are updated in real time
        puppies.size(); // => 1
    }
});

// Asynchronously update objects on a background thread
realm.executeTransactionAsync(new Realm.Transaction() {
    @Override
    public void execute(Realm bgRealm) {
        Dog dog = bgRealm.where(Dog.class).equals("age", 1).findFirst();
        dog.setAge(3);
    }
}, new Realm.Transaction.OnSuccess() {
    @Override
    public void onSuccess() {
      // Original queries and Realm objects are automatically updated.
      puppies.size(); // => 0 because there are no more puppies younger than 2 years old
      managedDog.getAge();   // => 3 the dogs age is updated
    }
});
// Define your models like regular Objective‑C classes
@interface Dog : RLMObject
@property NSString *name;
@property NSData   *picture;
@property NSInteger age;
@end
@implementation Dog
@end
RLM_ARRAY_TYPE(Dog)
@interface Person : RLMObject
@property NSString             *name;
@property RLMArray<Dog *><Dog> *dogs;
@end
@implementation Person
@end

// Use them like regular Objective‑C objects
Dog *mydog = [[Dog alloc] init];
mydog.name = @"Rex";
mydog.age = 1;
mydog.picture = nil; // properties are nullable
NSLog(@"Name of dog: %@", mydog.name);

// Query Realm for all dogs less than 2 years old
RLMResults<Dog *> *puppies = [Dog objectsWhere:@"age < 2"];
puppies.count; // => 0 because no dogs have been added to the Realm yet

// Persist your data easily
RLMRealm *realm = [RLMRealm defaultRealm];
[realm transactionWithBlock:^{
  [realm addObject:mydog];
}];

// Queries are updated in real-time
puppies.count; // => 1

// Query and update the result in another thread
dispatch_async(dispatch_queue_create("background", 0), ^{
  Dog *theDog = [[Dog objectsWhere:@"age == 1"] firstObject];
  RLMRealm *realm = [RLMRealm defaultRealm];
  [realm beginWriteTransaction];
  theDog.age = 3;
  [realm commitWriteTransaction];
});
// Define your models like regular Swift classes
class Dog: Object {
  dynamic var name = ""
  dynamic var age = 0
}
class Person: Object {
  dynamic var name = ""
  dynamic var picture: NSData? = nil // optionals supported
  let dogs = List<Dog>()
}

// Use them like regular Swift objects
let myDog = Dog()
myDog.name = "Rex"
myDog.age = 1
print("name of dog: \(myDog.name)")

// Get the default Realm
let realm = try! Realm()

// Query Realm for all dogs less than 2 years old
let puppies = realm.objects(Dog).filter("age < 2")
puppies.count // => 0 because no dogs have been added to the Realm yet

// Persist your data easily
try! realm.write {
  realm.add(myDog)
}

// Queries are updated in real-time
puppies.count // => 1

// Query and update from any thread
dispatch_async(dispatch_queue_create("background", nil)) {
  let realm = try! Realm()
  let theDog = realm.objects(Dog).filter("age == 1").first
  try! realm.write {
    theDog!.age = 3
  }
}
// Define your models and their properties
class Car {}
Car.schema = {
  name: 'Car',
  properties: {
    make:  'string',
    model: 'string',
    miles: 'int',
  }
};
class Person {}
Person.schema = {
  name: 'Person',
  properties: {
    name:    {type: 'string'},
    cars:    {type: 'list', objectType: 'Car'},
    picture: {type: 'data', optional: true}, // optional property
  }
};

// Get the default Realm with support for our objects
let realm = new Realm({schema: [Car, Person]});

// Create Realm objects and write to local storage
realm.write(() => {
  let myCar = realm.create('Car', {
    make: 'Honda',
    model: 'Civic',
    miles: 1000,
  });
  myCar.miles += 20; // Update a property value
});

// Query Realm for all cars with a high mileage
let cars = realm.objects('Car').filtered('miles > 1000');

// Will return a Results object with our 1 car
cars.length // => 1

// Add another car
realm.write(() => {
  let myCar = realm.create('Car', {
    make: 'Ford',
    model: 'Focus',
    miles: 2000,
  });

// Query results are updated in real-time
cars.length // => 2
// Define your models like regular C# classes
public class Dog : RealmObject 
{
    public string Name { get; set; }
    public int Age { get; set; }
    public Person Owner { get; set; }
}

public class Person : RealmObject 
{
    public string Name { get; set; }
    public RealmList<Dog> Dogs { get; } 
}

var realm = Realm.GetInstance();

// Use LINQ to query
var puppies = realm.All<Dog>().Where(d => d.Age < 2);

puppies.Count(); // => 0 because no dogs have been added yet

// Update and persist objects with a thread-safe transaction
realm.Write(() => 
{
    var mydog = realm.CreateObject<Dog>();
    mydog.Name = "Rex";
    mydog.Age = 1;
});

// Queries are updated in real-time
puppies.Count(); // => 1

// LINQ query syntax works as well
var oldDogs = from d in realm.All<Dog>() where d.Age > 8 select d;

// Query and update from any thread
new Thread(() =>
{
    var realm2 = Realm.GetInstance();

    var theDog = realm2.All<Dog>().Where(d => d.Age == 1).First();
    realm2.Write(() => theDog.Age = 3);
}).Start();

왜 Realm을 써야 할까요?

쉬운 사용성

Realm은 언제나 쉬운 사용성을 매우 중요한 요소로 생각합니다. 개발자들은 커다란 앱도 Realm으로 바꾸는데 많은 시간이 걸리지 않았다고 이야기 합니다. 보통 개발, 최적화, 디버깅에 걸리는 시간을 획기적으로 단축해 줍니다.

빠른 속도

Realm의 쉬움이 성능상의 비용을 발생하지는 않습니다. 메모리 매핑, 지연된 불러오기, 커스텀 스토리지 엔진 덕분에 Realm은 풍부한 객체 기반의 API를 제공하면서도 SQLite에 비해 일반적으로 빠릅니다. 우리는 항상 자신의 사용 예에 맞추어 모든 사람이 테스트해보길 권합니다만 코드를 Realm으로 옮길 때 막대한 성능향상을 보아왔습니다.

크로스 플랫폼

Realm은 iOS와 안드로이드 모두를 대상으로 실행할 수 있습니다. Realm 파일 포맷이 완전히 크로스 플랫폼이고 iOS와 안드로이드에서 손쉽게 접근할 수 있습니다. 디버깅을 위해 Realm Browser에서 .realm 파일들을 열수 있습니다.

Realm 은 Java, Objective-C, React Native, Swift 그리고 Xamarin을 지원합니다. Realm 파일 포맷은 완전히 크로스 플랫폼이고 같은 데이터모델을 사용할 수 있어 비슷한 비지니스 로직을 모든 플랫폼에서 사용할 수 있습니다. 디버깅을 위해 Realm 브라우져에서 .realm 파일들을 열수 있습니다.

고급 기능들

Realm 객체는 항상 하부 데이터에 맞추어 최신 판으로 갱신됩니다. 반응형 패턴이나 단일 데이터 흐름을 따르기 매우 쉽습니다. Realm 객체를 그래프로 연결할 수 있고, 복잡한 쿼리를 통해 어떤 복잡한 질의를 할 수 있으며 내장된 AES256 암호화 기능을 사용할 수 있습니다. 또한 Realm 커뮤니티를 통해 개발된 수많은 플러그인에서 개발된 수많은 애드온을 사용해 UI 등 다른 요소들과 매우 쉽게 통합할 수 있습니다.

신뢰성

커다란 앱을 개발하고 계신가요? Realm은 당신을 위한 데이터베이스 입니다. Realm은 전 세계 수십억대의 스마트폰에서 사용되고 있으며 e-커머스 애플리케이션, 은행, 핼스 케어 제공자, 심지어 정부기관 등이 사용하고 있습니다. 또한 수많은 탑순위 앱들이 사용 중 입니다.

커뮤니티 주도

Realm은 GitHub에서 공개되어 개발이 진행되고 있습니다. 기능들의 우선순위는 사용자 요청에 의해서 정하며 오픈소스의 기여를 언제나 환영합니다. 우리는 12,000개 이상의 Star를 받았으며 우리 제품을 기반으로 수많은 플러그인과 컴포넌트가 개발되고 있습니다.

기술 지원

Realm은 다른 모든 것보다 지원과 버그 수정을 우선하고 있습니다. 데이타베이스에 관한 답을 그것을 개발하고 지원하는 사람들에게 직접 들을 수 있습니다. Stack Overflow, GitHub, 또는 Twitter로 질문해주세요.

누가 Realm을 쓰고 있나요?

Realm 은 수십억의 인구가 이미 사용하고 있습니다. 한국에서는 네이버, 카카오의 다양한 앱들과, 배달의민족, 요기요, CJ오쇼핑, 피키캐스트, 리디북스, 캐시슬라이드, 싸이메라, 리멤버, 비트윈 등 다양한 분야에서 사용하고 있습니다.

트위터

앱스토어 최상위의 앱들도 Realm을 신뢰합니다. 트위터의 비디오 앱인 Vine과 그앱을 사용하는 2억명의 실 사용자들은 2016년 부터 Realm을 사용하고 있습니다.

스타벅스

포춘 500 회사도 Realm을 그들의 대표앱에 사용하고 있습니다. 사이렌오더와 같은 선주문 기능이나 리워드 관리들에 사용합니다.

알리바바

뉴욕증권 거래소에 상장되어있는 중국의 거대한 회사인 알리바바는 Realm을 그들의 대표 상거래 앱에 사용하고 있습니다. 이 앱을 통해서 공급자와 구매자가 물건을 팔거나 구매하고 작년에느 30억달러 이상의 매매가 이루어졌습니다.

버드와이저

글로벌 500에 랭크되어있는 이 거대한 회사는 Realm으로 TapWiser라는 앱을 만들었습니다. 이 앱을 통해서 컴퓨터가 없는 수많은 바나 상점에서도 바로 맥주 주문을 할 수 있습니다.

SAP

Concur 를 사용하고 계시다면, 당신은 이미 Realm을 사용하고 있습니다: 2만명의 고객사의 3천만명 이상의 고객들이 비용 처리나 여행 계획등을 이 앱을 처리하고 있습니다.

Rite Aid (거대 약국체인)

포춘 500에 포함된 이 거대한 상거래 회사는 Realm으로 사용해 사용자의 데이터를 안전하게 저장하고 있습니다.

이 뿐만 아니라 아마존, 구글, 고프로, 월마트, 아디다스, 시스코, NBC, 나이키, NFL 등등 수많은 회사가 사용하고 있습니다.

다음 단계

우리는 계속 발전할 수 있도록 여러분들의 피드백을 듣고자 합니다. 스택오버플로우트위터등 다양한 곳을 통해서 저희에게 의견을 주실 수 있습니다.

Realm Java 시작하기 - GitHub에서 코드를 확인하세요
Realm Objective-C 시작하기 - GitHub에서 코드를 확인하세요
Realm Swift 시작하기 - GitHub에서 코드를 확인하세요
Realm React Native 시작하기 - GitHub에서 코드를 확인하세요
Realm Xamarin 시작하기 - GitHub에서 코드를 확인하세요

Realm으로 더 나은 앱을 개발 하실 수 있기를 기대합니다!

더 읽어보기

Realm Xamarin을 소개합니다

by /

오늘 새로운 Realm 모바일 데이터베이스를 공개합니다. 이번에는 Xamarin 용입니다. 이 데이터베이스는 기존 선택지 보다 빠른 성능 프로파일, 쉽게 사용할 수 있는 영속적인 객체, 전체적인 질의를 제공합니다.

다른 환경을 위한 Realm와 같이 Realm Xamarin은 반응형 앱 개발을 위해 바닥부터 만들었습니다. 라이브 객체, 변경 이벤트, 단방향 데이터 흐름을 지원하죠.

public class Dog : RealmObject
{
    public string Name { get; set; }
    public int Age { get; set; }
    public Person Owner { get; set; }
}

var realm = Realm.GetInstance();

// LINQ로 질의하기
var puppies = realm.All<Dog>().Where(d => d.Age < 2);
puppies.Count(); // => 아직 추가된 개가 없기 때문에 0입니다.

// 트랜잭션에서 기록
realm.Write(() =>
{
    var mydog = realm.CreateObject<Dog>();
    mydog.Name = "Rex";
    mydog.Age = 1;
});

// 질의는 실시간으로 갱신됩니다
puppies.Count(); // => 1

Realm은 폰, 타블렛, 웨어러블에서 작동하는 모바일 앱 개발에서 독특한 도전을 하기 위해 바닥부터 만들었습니다. Java, Objective‑C, Swift 버전을 2014년에 공개했고 React Native는 2016년 초기에 공개했습니다. 우리는 이미 트위터, 스타벅스, 시스코, 월마트, 구글, 아마존, 이베이을 비롯한 많은 앱 제작자 에 의해 수억대의 단말기에 사용하고 있습니다.

오늘 공개할 버전은 Xamarin용 버전입니다. C# 코드로 네이티브 iOS와 안드로이드 앱을 만들 수 있도록 하는 마이크로소프트의 모바일 플랫폼 기술입니다.

Realm Xamarin은 Realm에 기대하는 현대적인 디자인과 단순성을 제공합니다. 같은 코드베이스로 iOS와 안드로이드 타겟 모두를 지원합니다. 현재 Xamarin.iOS와 Xamarin.Android을 지원하고 앞으로 미래에는 Xamarin.Mac을 지원한 후 UWP와 유니티를 지원합니다.

왜 Realm인가요?

Realm은 ORM은 아니고 SQLite 위에 만들어진 것도 아닙니다. 그대신 모바일 앱 개발자를 위한 전체 데이타베이스를 만들었습니다. 네이티브 C# 객체에서 동적으로 전체가 연결되는 (키 밸류 스토어가 아닌) 커스텀 데이터베이스 엔진입니다. 심지어 성능을 향상하며 단순한 API를 제공합니다. Realm을 쓰면 복잡한 데이터를 모델링할 수 있고 그래프에 객체를 연결할 수 있고 향상된 질의를 작성할 수 있습니다.

public class Dog : RealmObject
{
    public string Name { get; set; }
    public int Age { get; set; }
    public Person Owner { get; set; }
}

var realm = Realm.GetInstance();

// 스레드 안전한 트랜잭션에서 쓰기가 발생
realm.Write(() =>
{
    var mydog = realm.CreateObject<Dog>();
    mydog.Name = "Rex";
    mydog.Age = 9;
});
// 친숙한 LINQ를 사용하는 기본질의 혹은 확장된 문장
var oldDogs = realm.All<Dog>().Where(d => d.Age > 8);
// 또는
var oldDogs = from d in realm.All<Dog>() where d.Age > 8 select d;

// 질의는 연결될 수 있다
var dogsNamedRex = oldDogs.Where(p => p.Name == "Rex");
dogsNamedRex.Count(); // => 1

realm.Write(() =>
{
    var mydog = realm.CreateObject<Dog>();
    mydog.Name = "Rex Maximus";
    mydog.Age = 10;
});

// 질의 결과는 실시간으로 갱신된다
dogsNamedRex.Count(); // => 2
public class Person : RealmObject
{
    public string Name { get; set; }
    public RealmList<Dog> Dogs { get; }
}

var realm = Realm.GetInstance();

realm.Write(() =>
{
    var jim = realm.CreateObject<Person>();
    var mydog = realm.CreateObject<Dog>();
    mydog.Name = "Fido";
    mydog.Owner = jim;
});

API를 사용하는 더 다양한 예제는 샘플 앱에서 볼 수 있습니다.

왜 Relam을 사용해야 하나요?

쉬움

Realm이 이미 위의 샘플에서 본 것 처럼 쉬운 사용에 항상 첫째로 촛점을 맞춥니다. Realm Xamarin도 전체적으로 같은 목표를 가집니다. 그것이 달성된 후 우리 다른 제품의 이점들을 적용합니다.

빠름

Realm의 쉬움이 성능상의 비용을 발생하지는 않습니다. 메모리 매핑, 지연된 불러오기, 커스텀 스토리지 엔진 덕분에 Realm은 풍부한 객체 기반의 API를 제공하면서도 SQLite에 비해 일반적으로 빠릅니다. 우리는 항상 자신의 사용예에 맞추어 모든 사람들이 테스트해보길 권합니다만 코드를 Realm으로 옮길때 막대한 성능향상을 보아왔습니다. 벤치마크 결과를 확인하세요.

크로스 플랫폼

이 말을 굳이 해야하나 싶지만, Realm Xamarin이 C# 코드를 한번 작성하면 iOS와 안드로이드 모두에서 사용할 수 있습니다. Realm 파일 포맷이 완전히 크로스 플랫폼이고 iOS와 안드로이드에서 손쉽게 접근할 수 있습니다. 디버깅을 위해 Realm Browser에서 .realm 파일들을 열 수 있습니다.

고급

Realm 객체는 항상 하부 데이터에 맞추어 최신 판으로 갱신합니다. 반응형 패턴이나 단일 데이터 흐름을 따르기 쉽습니다. Realm 객체를 그래프로 연결할 수 있고, LINQ를 통해 어떤 특성 조합을 질의할 수 있습니다. Realm 데이터를 Xamarin.Forms에 쉽게 통합할 수 있습니다.

신뢰

Realm Xamarin은 전세계 수억대에 사용되어 신뢰를 받는 Realm Java, Objective‑C, React Native, Swift와 같은 코어를 기반으로 합니다. e-커머스 애플리케이션, 은행, 핼스 케어 제공자, 심지어 정부에 의해서 사용하고 있습니다.

커뮤니티 주도

Realm Xamarin은 공개된 GitHub에 기반합니다. 기능들은 사용자 요청에 의해 우선순위를 정하며 오픈소스의 기여를 언제나 환영합니다.

지원

Realm은 다른 모든 것보다 지원과 버그 수정을 우선하고 있습니다. 데이타베이스에 관한 답을 그것을 만들고 유지보수하는 사람들에게 직접 들을 수 있습니다. 스택오버플로우, GitHub, 트위터로 질문해주세요.

테스트는 Realm, sqlite-net, Couchbase Lite의 마지막 버전을 사용하여 2016년 5월 9일에 실행하였습니다. 측정은 소스를 이용하여 아이폰 6S 플러스 128GB, iOS 9.3.1 버전에서 실시하였습니다. 만들어진 벤치마크는 항상 균일하지 못한 지표를 제공할 수 있기에 사용례에 맞는 벤치마크를 직접 작성해서 성능을 확인할 것을 추천합니다.

그 다음은?

향상점에 대한 피드백은 언제나 환영입니다. GitHub 리포지토리에 버그를 보고하는 것이나 기능을 요청하는 것은 특별히 관심이 있습니다. 몇주 사이에 API는 의미있게 향상될텐데요. 특히 마이그래이션과 질의의 고급 기능을 다듬고 있습니다.

만약 .NET 팬이고 UWP 지원유니티 지원을 원한다면 우리가 다음으로 추가할 거라고 자랑스럽게 알려드립니다.

여러분들이 Realm으로 무엇을 만들지 정말 기대됩니다!

더 읽어보기

Realm Cocoa 컨버터: CSV, XLSX 지원

by /

Realm Object-C와 Realm Swift의 부족한 부분을 보완할 수 있는 새로운 오픈소스 라이브러리 Realm Cocoa Converter가 출시되었습니다.

그동안 많은 개발자들로부터 기존의 데이터를 Realm 파일로 변환하고자할 때 시간이 많이 걸린다는 얘기를 들었습니다. Realm Cocoa Converter는 다른 포맷의 파일을 Realm 형식으로 가져오거나, Realm에서 다른 포맷으로 데이터를 내보낼 수 있는 기능을 제공하는 라이브러리입니다.

이 라이브러리는 Swift로 작성되어 있고, 쉽게 사용하실 수 있습니다. Realm 파일을 내보내려면 클래스 하나만 사용하면됩니다. 아래는 Realm 데이터를 CSV파일로 내보내는 예제입니다.

let path = ... // Realm 파일의 절대 경로
let destinationFolder = ... // CSV 파일을 저장 할 폴더의 절대 경로

let exporter = CSVDataImporter(realmFilePath: path)
exporter.exportToFolderAtPath(outputFolderPath: destinationFolder)

Realm에 데이터를 가져오려면 데이터를 내보내는 것보다 조금 더 많은 단계를 거치게됩니다. 그 이유는 Realm은 데이터를 임포트하기 전 파일에 있는 각 데이터 항목의 스키마를 생성해야하기 때문입니다.

이를 위해 Realm Cocoa Converter는 지능적으로 스키마를 생성하는 독립된 클래스를 제공하며, 생성된 스키마는 그대로 저장하거나 또는 데이터를 가져오기 전에 개발자가 수정할 수 있습니다.

생성된 스키마는 임포트 할 때 전달됩니다.

var filePaths = [String]() // Realm에 가져올 파일 경로의 배열

// 스키마 생성을 위한 인스턴스를 생성하고 데이터 파일 내용을 분석합니다.
let generator =  ImportSchemaGenerator(files: filePaths)
let schema = try! generator.generate()

let destinationRealmPath = ... // 데이터를 가져온 Realm 파일의 위치

// 임포터 객체를 생성하고 위에서 생성된 스키마를 사용해 파일을 가져옵니다.
let dataImporter = CSVDataImporter(files: filePaths)
try! dataImporter.importToPath(String(destinationRealmPath), schema: schema)

Realm Cocoa Converter는 Swift로 구현되어 있지만 Object-C에서 사용하는 경우에도 적절한 인터페이스가 제공되도록 설계되어 있습니다.

NSString *path = ... // Realm 파일의 절대 경로
NSString *destinationFolder = ... // CSV 파일을 저장 할 폴더의 절대 경로

RLMCSVDataExporter *exporter = [[RLMCSVDataExporter alloc] initWithRealmFileAtPath:realmFilePath];
[exporter exportToFolderAtPath:destinationFolder withError:nil];

현재는 데이터를 가져올 수 있는 형식으로 CSV와 Excel(XLSX)파일을 지원하고, 데이터를 내보낼 수 있는 형식으로 CSV를 지원하고 있습니다. 곧 JSON과 SQLite를 포함한 다양한 포맷을 지원할 예정입니다.

Realm Cocoa Converter는 이제 막 출시한 라이브러리이기에 더 나은 서비스를 위해 여러분의 도움이 필요합니다! 만약 개발 중 특정 사례나 지원이 필요한 파일 형식이 있다면 언제든 알려주시기 바랍니다!


읽어주셔서 감사합니다. 이제 Realm과 함께 앞으로 나아가 멋진 앱을 만들어 봅시다! 우리는 항상 StackOverflow, GitHub, 와 트위터에 있습니다. 페북에서 운영중인 Realm 한국 사용자 그룹 이나 Realm 한국 사용자 페이지와도 방문해 주십시오. 한국어 연락처는 [email protected] 입니다.

더 읽어보기

Realm Java 0.88 — 진보된 객체!

by /

우리는 방금 Realm Java의 새 버전을 우리의 웹사이트와 Maven에 출시하였습니다. 이번 릴리즈는 흥미로운 새 기능이 많이 포함되어 있어요.

진보된
객체!

이번 릴리즈부터 안드로이드용 Realm은 Realm 객체에 대한 전권을 개발자에게 드립니다. 이제 다음의 내용들을 할 수 있습니다.

  • 객체에 커스텀 메서드를 추가할 수 있습니다.
  • 인터페이스를 구현할 수 있습니다.
  • 커스텀 로직을 게터와 세터에 추가할 수 있습니다.
  • 원하는 방식으로 엑세서를 명명할 수 있습니다.
  • 모든 엑세서를 생략하고 퍼블릭 필드로 대신할 수 있습니다.
  • 원하는 대로 toString(), equals(), hashCode()를 정의할 수 있습니다.
  • Realm과 Lombok을 같이 사용할 수 있습니다.

이 말은 이제부터 Realm 객체를 다루는 일은 일반적인 POJO 객체를 다루는 것과 매우 비슷해질거라는 뜻입니다.

이렇게 하기 위해 우리는 단순히 라이브러리만 만든 것이 아니라 Realm 그래들 플러그인을 만들었습니다. 이전 글에서 언급한 바와 같이 이제부터 Realm과 프로젝트를 build.gradle 파일에서 연결하는 방법이 바뀝니다.

buildscript {
 repositories {
    jcenter()
 }

 dependencies {
    classpath 'io.realm:realm-gradle-plugin:0.88.0'
 }
}

apply plugin: 'com.android.application'
apply plugin: 'realm-android'

이제부터 Gradle을 제외한 다른 빌드 시스템을 지원하지 않습니다. 합당하지 않다고 생각하다면 우리가 미리 준비한 두가지 이슈를 통해 다시 Maven이나 Ant 지원에 대해 투표해주세요.

여러분의 의견은 우리가 Ant와 Maven 플러그인에 대해 언제 어떻게 구현할지 영향을 줍니다.

몇몇 제약들은 여전히 Realm 객체에 있습니다.

  • RealmObject이 아닌 다른 클래스로 부터 상속을 받는 것은 허용되지 않습니다.
  • final, volatile, transient 필드는 허용되지 않습니다.

기타 향상점들

이 릴리즈는 여러 다른 향상점을 가지고 있습니다.

호환성 주의사항 🚨

  • 모든 알림은 이제 Looper 큐를 사용합니다. 이전에는 트랜잭션을 커밋할 때 같은 스레드의 RealmChangeListener는 즉각 호출되었습니다. 이 변화에 따라 리스너는 보다 예측가능해지고 일관됩니다. 그들은 모두 다음 Looper 메시지를 기다립니다.
  • 모든 RxJava 옵저버블이 모두 그들을 생성한 Realm 인스턴스을 가집니다. 이 의미는 Realm파일은 옵저버블이 구독해제될 때 닫힌다는 의미입니다. 이는 이제 Realm 인스턴스를 doOnUnsubscribe를 이용해서 닫을 수 있다는 뜻입니다.

자세한 내용은 전체 변경사항을 참고하세요.


읽어주셔서 감사합니다. 이제 Realm과 함께 앞으로 나아가 멋진 앱을 만들어 봅시다! 우리는 항상 StackOverflow, GitHub, and Twitter에 있습니다. 페북에서 운영중인 Realm 한국 사용자 그룹 이나 Realm 한국 사용자 페이지와도 방문해 주십시오. 한국어 연락처는 [email protected] 입니다.

더 읽어보기

Realm 리액트 네이티브를 소개합니다

by /

이 글은 2016년 2월에 Realm 리액트 네이티브 출시를 알린 글입니다. 이후 많은 기능이 업데이트되었으니 최신 글을 공식 문서에서 확인하세요.

오늘 페이스북 React.js 컨퍼런스에서, React Native(리액트 네이티브)를 위한 새로운 Realm 모바일 데이터베이스를 공개합니다. 쉬운 객체 모델을 제공하며 모든 쿼리 기능을 제공하고 현재 있는 옵션보다 2배~10배 빠른 속도를 보입니다.

Realm의 다른 버전들 처럼 reactive 앱 개발을 위한 라이브 오브젝트, 변경 이벤트단방향 데이터 흐름 를 지원하기 위해 처음부터 새로 구현되었습니다.

사용하기 위한 코드는 아래와 같습니다:

const Realm = require('realm');

class Person {}
Person.schema = {
    name: 'Person',
    primaryKey: 'name',
    properties: {
        name: 'string',
        age: {type: 'int', default: 0},
    },
};

const realm = new Realm({schema: [Person]});

// Query
let people = realm.objects('Person', 'age >= 17');
people.size // => 0

// Write
realm.write(() => {
    savedPerson = realm.create('Person', {
        name: 'Hal Incandenza',
        age: 17,
    });
});

// Queries are updated in real-time
people.size // => 1

Realm 은 더 나은 모바일 앱 개발이라는 목표를 위해서 처음부터 새로 구현된 스마트폰, 태블릿, 웨어러블을 위한 데이터베이스 입니다. Realm은 Java와 Objective-C, Swift 버전을 2014년에 발표하였고 현재 수억개의 디바이스에서 사용되고 있으며 스타벅스, 시스코, 월마트, 구글, 아마존이나 이베이를 포함한 수많은 회사에서 사용하고 있습니다.

오늘, 우리는 개발자들이 자바스크립트로 네이티브 앱을 개발할 수 있도록 해주는 페이스북의 자바스크립트 프레임웍인 React Native(리액트 네이티브)를 위한 지원을 발표합니다. (참고: Realm은 현재 리액드 네이티브 만을 지원합니다. React 프레임웍을 지원하는 것은 아닙니다. 현재 우리는 모바일을 우선으로 지원하고있습니다!)

Realm 리액트 네이티브 는 Realm에서 기대했던 모던한 디자인과 간결함을 유지하는 것은 물론 하나의 코드베이스로 iOS와 안드로이드 앱을 동시에 개발할 수 있게 해줍니다. 오늘 공개하기는 하지만 Realm 리액트 네이비트는 이미 2개월 이상 태스크래빗 이라는 커다란 앱에서 사용되고 있습니다!

Realm 소개

Realm 은 ORM이 아니며 SQLite를 사용한 데이터베이스도 아닙니다. 대신 우리는 모바일 앱 개발자를 위한 전체 데이타베이스를 만들었습니다. 이는 (단순한 키 밸류 저장소가 아니라) 자바스크립트 객체에서 동적으로 매핑되는 전체적이고, 커스텀화된 데이타베이스 엔진입니다. 성능을 유지하며 단순한 API를 제공할 수 있게 만들었습니다. Realm을 쓰면 복잡한 데이터를 모델링할 수 있고, 객체를 그래프로 연결하고, 상급의 질의로 조합할 수 있습니다.

class Dog {}
Dog.schema = {
    name: 'Dog',
    properties: {
        name: 'string',
        age: 'int',
    }
};

let realm = new Realm({schema: [Dog]});

realm.write(() => {
    realm.create('Dog', { name: 'Rex', age: 3 });
});
// 기본 질의
let r = realm.objects('Dog').filtered('age < 8');

// 연쇄적인 질의가 가능합니다
let r2 = r.filtered('name contains "Rex"');
r2.size // => 1

realm.write(() => {
    realm.create('Dog', { name: 'Rex Maximus', age: 4 });
});

// 질의는 실시간으로 갱신됩니다
r2.size // => 2
class Person {}
Person.schema = {
    name: 'string',
    dogs: {type: 'list', objectType: 'Dog'},
};

let realm = new Realm({schema: [Dog, Person]});

realm.write(() => {
    let person = realm.create('Person', {
        name: 'Tim',
        dogs: [{name: 'rex', age: 3}],
    });
});

이 API를 사용하는 더 많은 예제는 리액트 예제 앱과 JS 테스트 파일에서 볼 수 있습니다.

왜 Realm을 써야 하나요?

쉬움

Realm은 쉽게 사용할 수 있는데 우선적으로 신경쓰고 있으며 위의 샘플에서 보듯 Realm 리액트 네이티브도 같습니다. 우리 제품에 대해 알려진 이점들은 여기에도 동일하게 적용됩니다.

빠름

Realm을 쉽게 하기 위해서 성능을 희생하지 않습니다. Realm은 풍부한 오브젝트 기반의 API를 제공하지만 메모리 매핑 때핑, 지연 로딩, 커스텀 저장 엔진을 사용하여 SQLite나 AsyncStorage보다 빠릅니다. 각자의 사용례에 맞추어 먼저 테스트하는 것을 추천드립니다만 많은 경우 Realm으로 포팅하는 것은 꽤 높은 성능 향상으로 이어진다는 것을 알려드립니다. 아래 벤치마크 결과를 살펴보세요.

크로스 플랫폼

Realm 리액트 네이티브의 API는 자바스크립트로 앱을 한번만 작성해서 iOS와 안드로이드 모두 지원할 수 있습니다. Realm 파일 포맷은 완전히 크로스 플랫폼이고 데이터는 iOS와 안드로이드에서 공유할 수 있습니다. 디버깅을 위해서 .realm 파일들을 Realm Browser에서 열 수 있습니다.

향상

Realm 객체는 하부 데이터에 맞춰 항상 갱신됩니다. 이런 특징은 리액티브 패턴이나 단방향 데이터 흐름을 따라가기 쉽게 합니다. Realm 객체를 쉽게 그래프로 연결할 수 있습니다. 표현력있는 질의 언어는 어떤 복합적인 프로퍼티도 질의할 수 있게 합니다. 리액트 네이티브 리스트뷰에 Realm 데이터native-listview).

신뢰

Realm 리액트 네이티브는 Realm 자바, 오브젝티브 C, 스위프트와 같은 코어를 기반으로 하고 있습니다. 전세계 수억의 사람들이 이미 사용한 신뢰성 있는 코어입니다. 이커머스, 은행, 건강 관리, 심지어 정부 앱에서도 사용됩니다. Realm 리액트 네이티브 자체도 TaskRabbit이 2015년 12월 부터 실 제품에 사용하고 있습니다.

커뮤니티 주도

Realm 리액트 네이티브는 깃헙에 열려 있습니다. 기능은 사용자의 요청에 의해 우선 순위가 결정됩니다. 기여는 언제나 환영합니다.

지원

Realm은 지원과 버그 수정을 다른 모든 것보다 더 중요하게 대합니다. 사람들이 작성하고 유지하는 데이타베이스 스택오버플로우와 깃헙](https://github.com/realm/realm-js/issues), 트위터에서 답을 얻을 수 있습니다.

테스트는 현재 최신 Realm 버전, SQLite를 위한 React Native SQLite 저장소, AsyncStorage를 위한 React Native Store으로 진행하였습니다. 측정은 iOS 9.2.1의 아이폰 6s와 안드로이드 5.0.1이 도는 넥서스 9에서 수행되었습니다. 소스를 확인하세요.

새로운 소식

오늘 릴리스된 Realm 리액트 네이티브는 Realm 자바, 오브젝티브 C, 스위프트가 현재 제공하는 것에 비해서는 부족합니다. 하지만 TaskRabbit와 같은 앱 제작사가 실 제품에 믿고 쓸만큼 신뢰도는 확보되었습니다. 향상을 위한 피드백은 언제나 환영합니다. 깃헙을 통해 버그 보고나 기능 요청을 해주셔도 좋습니다. API는 몇주 후 눈에 띄게 향상될 것입니다. 특히 마이그레이션과 질의는 더 좋아집니다.

만약 여러분이 자바스크립트 팬인데 리액트 네이티브가 여러분의 플랫폼에 돌지 않는 경우를 위해 우리는 코도바, 폰갭, 아이오닉 지원을 계획하고 있습니다. 그리고 Node.js (V8) 호환도 할 예정입니다.

우리는 여러분들이 Realm을 사용해서 무엇을 만들지 정말 궁금합니다!

TaskRabbit와 리액트 네이티브 팀의 지원에 정말 감사합니다. Realm 리액트 네이티브는 그들의 도움으로 훨씬 좋아졌습니다. React Conf 2016에서 런치할 수 있어 영광입니다!

</div> </div>

더 읽어보기

안드로이드 Realm 설치: 왜 Gradle 플러그인으로 바뀌나요?

by /

Realm의 제약사항

올해 우리는 안드로이드용 Realm을 발표했고 여러 제약에도 불구하고 많은분들이 좋아해주시고 사용하고 계십니다. 많은 피드백 중에 우리는 RealmObject 객체의 상속을 요구하는 것에 대해 많은 불평을 들었는데 이 상속은 아래 제약을 가져옵니다.

  • 퍼블릭 필드는 안됩니다
  • 표준적인 접근자 이름만 허용됩니다
  • 접근자에 로직 추가를 할수 없습니다.
  • 커스텀 메서드도 안됩니다
  • 인터페이스의 구현도 안됩니다

이런 제약들은 공통의 이유가 있습니다. Realm은 하부의 데이터 저장소에서 복제없이 운영하기 위해 프록시 클래스를 사용합니다. 이 제약들을 하나씩 살펴봅시다.

퍼블릭 필드는 안됩니다

자바는 프록시가 필드에 접근하는 것을 허용하지 않습니다. 그래서 Realm 프록시 객체는 이런 필드를 접근할 때 가로챌 수 없습니다. 또 필드는 자바 힙 영역에 정의됩니다. 이것은 Realm에서 Java 메모리로 데이터가 복제되어야 한다는 이야기입니다. 이것은 복제하지 않는 정책에 반하게 됩니다.

표준적인 접근자 이름만 허용됩니다

어노테이션 프로세서는 아주 강력한 도구이지만 몇몇의 제약이 있습니다. 한가지 제약은 프록시될 클래스의 실제 코드를 보는게 불가능하다는 점입니다. 어노테이션 프로세서는 필드와 메서드 이름만 접근할 수 있습니다. 이것은 우리가 예상대로 행동하는 Realm 프록시 객체를 관례대로 만들고 그것에 의존하게 합니다.

접근자에 로직 추가를 할수 없습니다.

이전 제약의 직접적으로 이어집니다. 접근자의 구현에 어노테이션 프로세서가 직접적으로 접근할 수 있는게 없기 때문에 프록시 클래스의 어떤 로직을 복제하는게 불가능합니다. 물론 우리가 단순히 super.getMyField();를 호출할 순 있습니다 하지만 이렇게 하면 프록시 클래스를 이용하지 않고 메모리 내 데이터를 다루게 됩니다.

커스텀 메서드도 안됩니다

다시 말하지만 어노테이션 프로세서가 코드를 볼 수 없기 때문에 커스텀 메서드가 실제로 무엇을 할지 짐작할 수 없습니다. 이런 이유로 Realm은 정확함을 보증하기 위해 이런 메서드를 허용할 수 없습니다.

인터페이스의 구현도 안됩니다

커스텀 메서드가 허용되지 않기 때문에 메서드의 구현을 요구하는 인터페이스를 사용하는 것 역시 허용되지 않습니다.

그래서 이건 고칠 것인가요?

이런 제약을 넘어설 한가지 방법은 바이트코드를 조작하는 것입니다. 이는 컴파일이 끝난 다음에 .class 파일들에 대해 어떤 변경을 한다는 것을 의미합니다. 이러한 조작에 대한 상세한 이야기는 앞으로의 블로그 글에서 다루겠습니다. 여기에서 다룰 중요한 점은 깔끔하게 바이트 코드 조작을 그래들 빌드에서 할 유일한 방법은 플러그인을 쓰는 것이라는 점입니다. 인기있는 라이브러리들 레트로람다휴고가 실제로 이렇게 하고 있습니다. 그리고 우리도 그 중 하나입니다.

우리의 build.gradle 파일

지금까지 Realm을 설치하는 방법은 다음과 같습니다.

repositories {
    jcenter()
}

dependencies {
    compile 'io.realm:realm-android:<version>'
}

그래들 플러그인을 쓰면 다음과 같아집니다.

buildscript {
    repositories {
        jcenter()
    }
    dependencies {
        classpath "io.realm:realm-gradle-plugin:<version>"
    }
}

apply plugin: 'realm-android'

첫번째 코드가 이미 build.gradle 파일에 있다면 다음으로 해야할 일은 classpathapply plugin: 'realm-android'로 시작하는 코드로 바꾸어 주세요.

왜 이렇게 하나요?

우리가 바이트코드 조작 버전을 릴리즈하기 전인 지금 플러그인을 제공하는 이유가 있을까요? 실제로 아래의 이유가 있습니다.

작아진 APK

플러그인을 이용하면서 Realm을 JAR 대신에 AAR로 배포하는게 가능해졌습니다. 우리는 어노테이션 프로세서를 라이브러리에 포함하지 않고 일반적인 패키지로 만들 수 있었습니다. 이는 최종 APK는 어노테이션 프로세서를 가지고 있지 않고 수 킬로바이트를 앱에서 절약할 수 있다는 이야기입니다.

쉬운 ABI 분할!

AAR로 배포하면서 얻은 다음 장점은 이전 블로그 글에서 설명한 것과 같은 우회방법을 쓸 필요없이 깔끔하게 ABI 분리를 할 수 있는 점입니다. 이는 모든 CPU 아키텍쳐를 위한 네이티브 라이브러리를 하나의 APK에 포함할 필요가 없다는 이야기입니다.

이클립스는 어떻게 하나요?

이미 알고 있는 바와 같이, 구글은 이클립스를 위한 ADT를 폐기하였고 올해 말에는 지원을 중단합니다. 우리가 말할 수 있는 것은 대부분의 주요 안드로이드 개발자들은 안드로이드 스튜디오로 전환하였고 우리의 노력도 거기에 초점을 맞추고 있다는 부분입니다. 우리는 여러분의 피드백에 맞추어 우선순위를 정하고 있고 만약 충분히 많은 사람들이 Ant나 Maven 플러그인을 요청한다면 작성할 수도 있을 겁니다.

행복한 코딩을 합시다!

우리는 이 변경점이 여러분에게 유용하길 기대하고 있습니다! 언제든지 저희 한국 Realm 팀에 연락주세요. [email protected]로 한국어 메일 주시거나 Realm 한국 페이스북 페이지, Realm 한국 페이스북 그룹 스택오버플로우, 깃헙, 한국어 트위터에 있습니다.

더 읽어보기

Realm의 2015년, 더 큰 도약을 위한 성장

by /

2015년 한 해 Realm본사는 물론 한국도 정말 바빴습니다. 블로그, 세미나에서 릴리즈까지 Realm이 2015년에 한 활동을 돌아 봅니다.

릴리즈

Realm이 처음으로 공개된 것은 2014년 중순입니다. Objective-C가 먼저 공개되었고, Android를 위한 Java 버전은 한국 DEVIEW에서 본사 제품담당 임원이 Tim Anglade가 한국에서 처음으로 공개했습니다.

2015년에 Realm Java는 0.76에서 시작해 0.87이 되었구요 Realm Cocoa는 0.89 버전에서 시작해 Realm Objective‑C & Swift 0.97로 발전 했습니다. 또한 데이터베이스라는 저희 제품의 특성상 제품의 안정성은 우리팀이 가장 중요한 가치 입니다. 언제나 버그 해결을 최우선으로 진행하였고, 그 결과 Realm은 더욱더 안정적인 소프트웨어가 되었습니다.

Realm Java는 정렬 알고리즘과 암호화 지원, ARM64 및 x86_64 지원, RealmModules, 설정, in-memory Realm, null 지원, 비동기 API, 새 migration API, dynamic API와 가장 최근 지원된 RxJava까지 많은 부분에서 개선을 해왔습니다. Kotlin 이나 블랙베리관련해서도 지원했고, 다양한 써드파티 라이브러리들과 잘 동작할 수 있도록 지원했죠.

Realm Cocoa는 Swift 지원을 시작한 것이 큰 변화였고, 전반적으로 Swift스럽게 빠른 개선이 일어났습니다. 이 밖에도 암호화, 프로세스간 Realm 공유, Objective‑C generic 지원, watchOS와 tvOS 지원, 설정, KVC collection operators, class subsets와 bitcode 등 여러 기능이 추가되었습니다.

이 밖에도 여러 부수적인 프로젝트도 진행했는데요,

  • SwiftLint - Swift의 lint가 2015년에 런칭되었습니다.
  • jazzy - Swift 문서 생성자(Doxygen)로 v0.0.19에서 v0.4.1로 버전업되면서 Objective-C 지원이 추가되었고, 웹상에서 널리 사용되고 있습니다. 물론 Realm API문서를 포함해서 말이죠.
  • Realm Browser - Realm 파일을 위한 OS X용 브라우저로 App Store에서 다운받아 사용하실 수 있습니다.

한국, 블로그, 세미나 발표 그리고 Realm 사용자모임

한국 Realm에서는 지금 저 박민우와 김용욱님이 Realm 개발과 지원은 물론 모바일 개발자 분들을 위한 다양한 콘텐츠를 Realm 한국 뉴스 블로그에에 꾸준히 만들어내고 있습니다. 본사 블로그를 번역하는 것은 물론 많은 글을 직접 쓰기도 했구요, DEVIEW, SOSCON, 나프다, GDG 등에서 발표를 하기도 했습니다. 또한 올해 4번의 Realm 사용자 모임을 통해 Realm을 소개하고, 개발자분들의 사례도 듣고 질문답변도 할 수 있는 시간을 가졌습니다.

내년에는 모바일 개발자 분들을 위한 더 다양한 채널과 콘텐츠를 계획하고 있으니 많은 관심 부탁드립니다.

2015년에 한국에서 정말 많은 앱들이 Realm을 쓰기 시작하셨는데요, Realm을 사용하는 앱들로 하루 생활을 상상해 봤습니다.

2016

Realm은 2015년에 기능면에서나 안정성 면에서 정말 많은 개선이 있었습니다. 또, 2016년에 할 일이 정말 많음을 느낍니다. 새해에는 여러 멋진 기능과 함께 더 멋진 제품으로 찾아뵐 것을 약속 드립니다. 어떤 기능이 나오는지 저희 페이스북 페이지와 트위터를 확인해 주세요! 2016년 새해 복 많이 받으시고 좋은 일만 가득하세요!

  • Realm 임직원 일동
더 읽어보기

Realm Objective-C & Swift 0.97

by /

해당 버전이 Swift 1.2 버전에 대한 마지막 Realm Swift 지원입니다.

Realm Objective-CRealm Swift 0.97.0을 출시했습니다.

이번 버전에서는 공식적인 tvOS 지원, 더 나은 Carthage 지원, 더 많아진 Object-C API 제네릭, 성능 & 쿼리 개선, deprecation API 삭제 등을 포함하고 있습니다.

tvOS 📺

드디어 Apple TV를 위한 Realm 앱을 빌드할 수 있게 되었습니다! 비록 몇몇 발 빠른 사용자들은 이미 Realm의 비공식적인 Apple TV development 브랜치을 이용하여 몇달 전부터 앱을 만들어 오긴 하였지만 이제는 저희가 공식적으로 지원하는 플랫폼이 되었습니다! 🎉

tvOS에서는 Documents 디렉토리에 쓰기를 하는 것을 허용하지 않기 때문에 기본 Realm 경로(default Realm path)가 NSCachesDirectory로 되어 있습니다. 하지만, tvOS가 언제든지 캐쉬 디렉토리의 파일들을 삭제할 수도 있다는 것을 유념하시길 바랍니다. 그렇기 때문에 중요한 유저 데이터를 직접 저장하는 것보다 Realm을 사용하기를 권장드립니다.

Carthage를 이용한, 소스코드로 Realm Swift 빌드하기

저희는 Realm Objective-C와 Real Swift 모두를 한 Xcode project에서 사용할 수 있게 만들기 위해 빌드 시스템을 매우 단순화 시켰습니다. 이를 통해 이제 Realm Swift에서 carthage --no-use-binaries를 이용하여 빌드할 수 있게 되었습니다.

암호화된 Realms 디버깅하기

지금까지 Realm의 네이티브 암호 기능의 동작 방식은 Mach exception handling에 크게 의존해 왔었고 그로 인해 LLDB의 사용을 힘들게 만들었습니다.

저희 Realm은 이제 더 이상 Mach exception을 사용하지 않기 위해 새롭게 암호화 기능을 구현하였습니다. 그 결과, 암호화된 Realm을 LLDB를 이용하여 디버깅 할 수 있게 되었습니다. 또한 암호화 작업 때문에 crash 리포트가 발생하는 일이 없어졌습니다. 마지막으로, Mach exceptions을 제거함으로써, tvOS & watchOS (Mach API 사용이 불가능한)와 같은 플랫폼들에서도 이제 암호화를 사용할 수 있게 되었습니다! 하지만 저희가 watchOS & tvOS의 추후 테스트를 위해 아직 활성화 시키지는 않았습니다.

Realm Objective-C에서의 제네릭 타입 Annotation

왜 Swift만 재밌게 놔두나요? Objective-C에서도 어디서든 제네릭 타입 annotation을 사용할 수 있게 저희 Realm Objective-C API를 업데이트 하였습니다.

호환성 주의사항

  • 이전 릴리즈에서 deprecated된 API들이 완전히 제거 되었습니다.
  • 실행되고 있는 loop에서 현재 동작하는 않는 쓰레드에 Realm notification block을 추가하게 되면 가만히 있는 것이 아니라 exception이 발생하게 됩니다.

작은 개선 (Minor Enhancements)

  • -[RLMRealm transactionWithBlock:]/Realm.write(_:)의 block 파라미터는 이제 __attribute__((noescape))/@noescape로 표시됩니다.
  • 비교 연산자 양쪽에서 key path를 가진 퀴리의 많은 형태가 이제 지원됩니다.
  • RLMResultsRLMArray에서 KVC collection 연산자 지원이 추가되었습니다.
  • Swift property가 계산된 값(computed value)으로 초기화 되었다면 +[RLMRealm sharedSchema]에서 데드락에 걸리지 않고 실파합니다. 이것은 스스로 Realm을 열도록 시도합니다.

버그 수정

  • 삭제된 객체들의 타입에 연결된 다른 클래스들이 있을때, 객체들을 필터한 RLMResults에서 -[RLMRealm deleteObjects:]를 호출 할때의 낮은 성능을 수정하였습니다.
  • 지원되지 않는 타입에서 Object properties를 정의했을 때 exception이 발생하게 됩니다.

읽어 주셔서 감사합니다. 지금 당장 Realm으로 놀라운 앱을 만들어보세요! 항상 우리는 주변인 Stack Overflow, GitHub, 또는 Facebook Realm 사용자 그룹에 있습니다.

더 읽어보기

Realm Java 0.87이 이제 RxJava를 지원합니다!

by /

어제 Realm Java 최신 버전을 이 사이트와 Maven에 릴리즈 했습니다. 이번 릴리즈는 RxJava를 위한 최고 수준의 API 지원을 포함합니다.

RxJava

RxJava 는 Netflix의 Reactive Extensions 라이브러리로 Observer pattern을 적용하였습니다. 시퀀스로 여러 오퍼레이터를 조합해서 Realm 데이터의 변화를 파악할 수 있습니다. 이제 RealmQueryRealmList를 제외한 모든 Realm 클래스를 Observable(https://github.com/ReactiveX/RxJava/wiki/Observable)로 보여줄 수 있습니다.

Realm의 Observable은 표준 리스너인 RealmChangeListener의 위에 구성됩니다. 따라서 모든 Observable은 리스너가 기존에 구독하고 있던 변경 사항과 같은 비중으로 객체를 발행할 수 있습니다.

Realm realm = Realm.getDefaultInstance();
RealmResults<Person> persons = realm.where(Person.class).findAll();
Person person = persons.first();

Observable<Realm> realmObservable = realm.asObservable();
Observable<RealmResults<Person>> resultsObservable = persons.asObservable();
Observable<Person> objectObservable = person.asObservable();

또한 Realm의 Async API에서도 유효합니다.

realm.where(Person.class).equalTo("name", "John").findAllAsync().asObservable()
  .filter(new Func1<RealmResults<Person>, Boolean>() {
      @Override
      public Boolean call(RealmResults<Person> persons) {
          // Ignore unloaded results
          return persons.isLoaded();
      }
  })
  .subscribe(new Action1<RealmResults<Person>>() {
      @Override
      public void call(RealmResults<Person> persons) {
          // Show persons...
      }
  });

더 많은 예제를 RxJava example project에서 찾을 수 있습니다.

설정

RxJava가 우리 API의 최우선 사항이긴 하지만, 사용자가 사용을 수락해야 하는 옵트인 의존성을 지니므로 직접 build.gradle에서 RxJava를 추가해야 합니다. 사용자가 RxJava 버전을 직접 지정할 수 있으므로, RxJava를 사용하지 않는 메서드까지 포함되어서 프로젝트의 메서드 카운드가 불필요하게 증가하는 현상을 막을 수 있다는 장점이 있습니다.

dependencies {
  compile 'io.realm:realm-android:0.87.0'
  compile 'io.reactivex:rxjava:1.1.0'
}

Observable이 생성되는 방식을 커스텀할 수 있도록 RxObservableFactory라는 인터페이스도 추가했습니다. 기존의 커스텀 팩토리는 RealmConfiguration를 사용해서 설정하지만 Realm은 기본 팩토리인 RealmObservableFactory 클래스를 사용합니다. 이 또한 1.1.* RxJava를 지원합니다.

RealmConfiguration config = new RealmConfiguration.Builder(context)
  .rxFactory(new MyFactory())
  .build()

개발 진행 중인 사항

이번 릴리즈에 RxJava의 최초 지원 기능이 포함되지만, RealmQueryRealmList를 위한 Observable 지원이 아직 가능하지 않습니다. 추후 업데이트에 이들 기능을 추가할 예정입니다.

RealmObjectRealmResults가 스레드에 제한적이라는 점을 기억하세요. 즉, 한 스레드에서 로드한 Realm 객체를 다른 스레드에서 사용할 수 없습니다.

// Default pattern for loading data on a background thread
Observable.defer(new Func0<Observable<RealmResults<Person>>>() {
    @Override
    public Observable<RealmResults<Person>> call() {
        return realm.where(Person.class).findAll().asObservable();
    }
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new Action1<RealmResults<Person>>() {
    @Override
    public void call(RealmResults<Person> persons) {
        // Accessing persons here would crash.
    }
});

// Instead, use Realms Async API for the same effect
realm.where(Person.class).findAllAsync().asObservable()
  .subscribe(new Action1<RealmResults<Person>>() {
      @Override
      public void call(RealmResults<Person> persons) {
          // Show data in the UI
      }
  });

RealmObjectRealmResults는 끊임없이 Realm의 최신 데이터로 갱신되는 라이브 객체입니다. 일관성 유지에는 좋지만, 스레드 안정성이 있는 불변의 객체를 선호하는 RxJava의 모델과는 맞지 않습니다. 따라서 Realm.copyFromRealm() 메서드를 추가하여 Realm 데이터를 일반 자바 객체로 복사하고 안전하게 Realm에서 분리할 수 있도록 했습니다. 속도와 메모리 사용 측면에서 비용이 발생하지만 buffer()와 같은 오퍼레이터를 사용하기가 쉬워집니다.

// Get different versions of a person
realm.where(Person.class).findFirst().<Person>asObservable()
        .map(new Func1<Person, Person>() {
            @Override
            public Person call(Person person) {
                // Convert live object to a static copy
                return realm.copyFromRealm(person);
            }
        })
        .buffer(2)
        .subscribe(new Action1<List<Person>>() {
            @Override
            public void call(List<Person> persons) {
                // Without `map` and `copyFromRealm`, v1 and v2 would be the same
                Person v1 = persons.get(0);
                Person v2 = persons.get(1);
            }
        });

Thread handoff 와 관련된 이슈를 해결하여 추후 업데이트에 반영할 예정입니다.

자세한 사항은 changelog에서 볼 수 있습니다.


읽어주셔서 감사합니다. Realm과 함께 멋진 앱을 만들어 보세요! Stack Overflow, GitHub, Twitter를 통해 언제든지 저희와 소통할 수 있습니다.

더 읽어보기

Realm Java 0.86 새로운 마이그레이션과 동적 API!

by /

새 버전의 Realm 자바를 웹 사이트와 Maven을 통해 배포하였습니다. 이 릴리스는 예전의 마이그레이션을 대체하는 새로운 구현과 새로운 동적 API를 포함합니다.

마이그레이션 API

마이그레이션은 여전히 RealmConfiguration의 마이그레이션 블록을 명시하지만 인터페이스가 바뀌었습니다. 마이그레이션은 DynamicRealm라고 명명된 새로운 Realm 타입과 동작합니다. DynamicRealm은 전통적인 Realm의 일반적인 객체 생성과 쿼리 능력은 여전히 모두 지원하면서 상호작용과 스키마 변경을 추가로 지원하는 특별한 변형입니다.

RealmMigration migration = new RealmMigration() {
  @Override
  public void migrate(DynamicRealm realm, long oldVersion, long newVersion) {

     // DynamicRealm는 편집가능한 스키마를 노출합니다
     RealmSchema schema = realm.getSchema();

     if (oldVersion == 0) {
        // version 1로 마이그레이션 합니다.
        oldVersion++;
     }

     if (oldVersion == 1) {
        // version 2로 마이그레이션 합니다.
        oldVersion++;
     }
  }
}

// 일반적인 마이그레이션 설정입니다
RealmConfiguration realmConfig = new RealmConfiguration.Builder(context)
  .schemaVersion(2)
  .migration(migration)
  .build();

클래스와 필드는 그들의 이름으로 유려하게 참고되고 생성되며 수정됩니다.

RealmSchema schema = realm.getSchema();

// 두개의 필드가 있는 Person을 생성합니다: name과 age
schema.create("Person")
    .addField("name", String.class)
    .addField("age", int.class);

// 특별한 특성의 필드를 생성합니다
schema.get("Person")
    .addField("id", long.class, FieldAttribute.PRIMARY_KEY);

// 다른 Realm 객체를 참조합니다
schema.get("Person")
    .addRealmObjectField("favoriteDog", schema.get("Dog"))
    .addRealmListField("dogs", schema.get("Dog"));

// 마이그레이션 동안 새 데이터를 생성합니다
DynamicRealmObject person = realm.createObject("Person");
person.setString("name", "John");

// 클래스와 그 데이터 전부를 삭제합니다
schema.remove("Person");

모든 RealmObject에서 특정 타입에 해당하는 커스텀 변환기를 실행하는 것도 가능합니다. 이것은 두 타입을 변환하거나 필드를 합치거나 나눌 때 유리합니다.

schema.get("Person")
  .transform(new RealmObjectSchema.Function() {
    @Override
    public void apply(DynamicRealmObject obj) {
      obj.setInt("age", obj.getInt("age") + 1); // 나이 전체에 1씩 더합니다
    }
  });

전체 옵션은 RealmSchemaRealmObjectSchema를 참고하시고 갱신된 마이그레이션 예제도 확인하세요.

동적 API

마이그레이션은 새로운 동적 API를 사용합니다. 이 API는 마이그레이션 밖에서도 사용가능합니다. 예를 들어, 모델이 컴파일 타임에 알려져 있지 않은 상태에서 CSV 파일로부터 데이터를 추출해 데이터를 사용하는 경우 등에서 유용합니다.

동적 Realm을 열 때의 설정은 전통적인 Realm을 사용할 때와 같습니다. 하지만 동적일때는 어떤 스키마, 마이그레이션, 스키마 버전은 무시됩니다.

RealmConfiguration realmConfig = new RealmConfiguration.Builder(context).build();
DynamicRealm realm = DynamicRealm.getInstance(realmConfig);

// DynamicRealm에서 모든 객체들은 DynamicRealmObjects입니다
DynamicRealmObject person = realm.createObject("Person");

// 모든 필드는 문자열로 접근가능합니다.
String name = person.getString("name");
int age = person.getInt("age");

// 하부의 스키마는 존재하기에 존재하지 않는 필드에 예외를 발생시킵니다
person.getString("I don't exist");

// 질의는 여전히 평범하게 동작합니다
RealmResults<DynamicRealmObject> persons = realm.where("Person").equalTo("name", "John").findAll();

DynamicRealm은 유언성을 위해 안정성과 성능을 일부 포기합니다. 그렇기 때문에 정말로 유연성이 필요할 때만 사용하세요.

#열거(Enum)가중요해

SortCase 옵션을 지정하기 위해 불린 값을 쓰는 대신 열거(Enum)형을 쓰도록 질의 API를 갱신하였습니다. 이 변화는 더 읽기 좋고 타입 안전한 API를 제공합니다.

realm.where(Person.class)
  .beginsWith("name", "John", Case.INSENSITIVE)
  .findAllSorted("name", Sort.ASCENDING)

전체 변경 상항에서 자세한 내용을 확인하세요.


읽어주어서 감사합니다. Realm과 함께 나아가고 멋진 앱을 만들어 봅시다! 우리는 항상 스택오버플로우, 깃헙, 트위터에서 여러분과 함께합니다.

더 읽어보기

Realm Java 0.85 — 새로운 암호화 구현!

by /

Realm Java 의 새 버전이 공개 되어 이 웹사이트와 Maven에서 다운 받으실 수 있습니다. 이 새 버전에서는 시그널 핸들러에 기반한 암호화 구현을 걷어내고 일반적인 방법으로 새로 구현하여 더 많은 디바이스에서 암호화가 안정적으로 동작하도록 하였습니다. 또한 새로운 API인 Realm.isEmpty()가 추가되었습니다.

새로운 암호화 구현

“Realm의 Core DB 엔진” 라는 글에서 말한 바와같이 우리는 Realm의 암호화 구현을 위해 POSIX 시그널 핸들러 를 사용하고 있엇습니다. 이 방법은 SW관점에서 매우 간단하고 훌륭한 구현 방법입니다. 하지만 현실의 안드로이드 세상에 적용했을 때에는 시그널 핸들러가 일부 이상한 크래시를 일으키는 원인이 됩니다.

  • 안드로이드 5.0 롤리팝 부터, 안드로이드는 플레이스토어를 통해 설치와 업데이트를 할 수 있는 Chromium WebView를 포함시켰습니다. 하지만 WebView v40에 버그가 있어서, 암호화된 Realm과 같이 사용되면 크래시가 발생하는 경우가 있었습니다. 더 자세한 사항은 여기에서 확인하실 수 있습니다.
  • 일부 오래된 기기들 (예를 들어, HTC One X 등)에서는 시그널 핸들러가 정상적으로 구현되어있지 않습니다. HTC One X에서 발생하는 문제는 Firefox for Android 에서도 발견이 되었습니다. 자세한 사항은 이 Bugzilla 페이지를 확인해 보십시오

암호화를 다양한 기기에서 동작할 수 있도록 다양한 시도를 했음에도 불구하고, HTC One X 등과 같이 API가 망가진 기기에서 시그널 핸들러를 사용한다는 것은 불가능한 일이었습니다. 그래서 이 버전에서는 우리의 암호화 구현을 완전히 새로 작성하였습니다. 이 버전(v0.85.0) 부터는 Realm의 암호화가 모든 안드로이드 기기에서 동작합니다!

이제 RealmEncryptionNotSupportedException 을 catch 하는 부분을 제거하고 Realm이 모든 안드로이드 기기에서 동작하게 하시면 됩니다.

새로 추가된 API - Realm.isEmpty()

Realm.isEmpty()를 사용하여 Realm이 비어있는지 아닌지 확인하실 수 있습니다.

호환성 주의사항

RealmEncryptionNotSupportedException 는 더이상 필요하지 않기 때문에 제거되었습니다. 🎉

Realm.executeTransaction()RealmException으로 감싸서 exception을 던지던 것이 이제 RuntimeException을 직접 던져 줍니다. 만약 Realm.executeTransaction()을 사용한 트랜잭션 블럭에서 무슨 문제가 있다면, 원래의 exception이 던져지게 되어 더 쉽게 디버깅 할 수 있게 됩니다. 예전과 같이 exception이 발생하면 트랜잭션은 rollback 되게 됩니다.

필드명이 관계가 있는 필드이고 마지막 element 가 링크라면, RealmQuery.isNull()RealmQuery.isNotNull()은 이제 RealmError 대신에 IllegalArgumentException을 던지게 됩니다.

더 읽어보기

Realm Objective-C & Swift 0.96

by /

이번 버전(0.96) 에서 읽거나 쓴 Realm 파일은 이전 버전에서 열 수 없습니다. 기존 앱에 업데이트를 적용하실 경우에 특히 주의하시길 바랍니다.

Realm Objective-CRealm Swift 0.96.0을 출시했습니다. 이 버전에서는 NULL 속성을 지원하고, keypath collection 쿼리 (count/min/max/sum/avg), Swift에서 RealmCollectionType 프로토콜 도입, 오류 헨들링의 개선과 버그 수정이 포함되었습니다.

Null 지원

이번 버전은 모든 속성 유형에 대해 NULL 값을 저장할 수 있게 되었습니다.

Objective-C

NSString *, NSDate *, NSData * 은 기본적으로 nil 을 할당이 가능합니다. 만약 nil 할당을 하지 않는 경우에는 모델에서 +requiredProperties 클래스 메서드를 재정의하고 nil 할당을 하지 않는 속성의 이름을 반환합니다. 이전 버전에서 작성된 파일에 접근 할 때 +requiredProperties에서 지정한 속성 이외에는 자동으로 nil 사용 가능으로 변환됩니다.

Optional 숫자들은 숫자 타입인 NSNumber * 프로퍼티를 사용하여 저장이 가능합니다. NSNumber<RLMInt> *, NSNumber<RLMBool> *, NSNumber<RLMFloat> *, 그리고 NSNumber<RLMDouble> *도 사용 가능합니다.

(예시)

@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

Swift

String, NSDate, NSData 속성들은 마찬가지로 optional 또는 non-optional 중 하나를 지정할 수 있습니다. 유감스럽게도 optional 숫자 타입을 직접적으로 지원하지는 않습니다.(참고. #2147) 따라서 수치 형을 nil처리를 하려면, RealmOptional으로 감싸야 할 것입니다.

(예시)

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

List 속성과 마찬가지로 RealmOptional 속성은 let으로 선언해야하며, dynamic으로는 안됩니다. RealmOptional 으로 감싼 실제 값을 읽거나 쓰려면 value 속성을 사용해야합니다.

파일 포맷 자동 변환

Realm 파일을 이번 버전에서 열면, 읽기 트렌젝션에 접근을 할 때 자동으로 새로운 파일 형식으로 변환이 됩니다. 이 자동 변환 작업은 다음과 같이 수행합니다.

  1. 모든 인데스가 다시 만들어집니다. (Int와 String 타입에만 적용이 됩니다.)
  2. 모든 NSString, NSDate, NSData 타입의 속성은 +requiredProperties 을 지정하지 않으면 NULL 사용이 가능으로 변환됩니다.
  3. 새로운 포맷의 버전 번호가 파일에 기록이 됩니다.

이 변환은 파일 쓰기를 필요로 하기 때문에 이전 버전의 Realm에서 작성된 파일은 새 버전에서 로드 할 수는 없습니다. 만약에 앱에서 Realm 파일을 묶는다면, 새로운 파일 포맷으로 업데이트 하여 교체해야합니다.

주의 : 이 파일 포맷 변환은 취소 할 수 없습니다.

Keypath Collection Queries

@count, @min, @max, @sum, @avg을 사용하는 Keypath Collection 쿼리는 RLMArray/List 속성들을 이제 지원합니다. 자세한 사용법은 Realm 웹 사이트에서 공개하고 있는 NSPredicate Cheatsheet를 참고하세요.

RealmCollectionType

Realm Objective-C는 오랫동안 공통 프로토콜 (RLMCollection)을 사용하는 RLMArrayRLMResults을 사용할 수 있었습니다. 이번 버전에서는 Swift에서도 같은 기능을 사용할 수 있도록 확장되었습니다.

ListResults 클래스는 모두 RealmCollectionType 프로토콜을 준수하도록 선언되어 있습니다. type-erased wrapper가 제공되고 있기 때문에 Swift에도 다른 레이아웃의 것을 같은 컬렉션에 포함 할 수 있습니다.

(예시)

class ViewController: UIViewController {
    var collection: AnyRealmCollection

    init(collection: RealmCollectionType) {
        super.init()
        self.collection = AnyRealmCollection(collection)
    }
}

오류 처리 (Error Handling) 개선

현재 Realm 버전에서는 트랜잭션을 커밋할 때 디스크 잔여 용량이 부족한 경우 크래쉬가 발생할 수 있습니다. 다행하게도 Realm 크래쉬는 기존 데이터가 손상되는 것을 막았지만, 틀림없이 견고한 UX는 아니었습니다.

이제는 commitWrite / commitWriteTransaction and write / transactionWithBlock은 디스크 공간이 부족한 경우에는 오류를 반환합니다.

작은 개선 (Minor Enhancements)

  • RLMRealm/Realm에 하나의 객체가 저장되어 있는지 확인하는 isEmpty 속성을 추가했습니다.

버그 수정

  • 8MB 이상 16MB 이하의 크기의 NSData를 저장했을 때 실패하는 것을 수정했습니다.
  • RLMArray/List 또는 관계 객체가 제거된 마이그레이션을 롤백할 때 실패하는 것을 수정했습니다.
  • 파일 열기에 실패했을 때 오류 객체에 경로 정보를 포함하도록 했습니다.
  • 클래스의 subset을 사용하는 첫 번째 Realm을 열은 후 Realm이 열리지 않는 버그를 수정했습니다.
  • Object(value: ...)Object 서브 클래스 속성의 디폴트 값을 초기화 할 때 발생하는 버그를 수정했습니다.
  • 클래스의 하위 클래스의 부분 집합의 일부가 아닌 배열이나 객체 속성들을 가지는 경우 예외를 발생시킵니다.
  • 여러 스레드에서 단시간에 여러번 같은 Realm 파일을 열 때 충돌이 발생하는 것을 수정했습니다.
  • 몇 가지 오류 메시지에서 잘못된 함수 이름을 표시하던 문제를 수정했습니다.

읽어 주셔서 감사합니다. 지금 당장 Realm으로 놀라운 앱을 만들어보세요! 항상 우리는 주변인 StackOverflow, GitHub, 또는 Facebook Realm 사용자 그룹에 있습니다.

더 읽어보기

Realm 자바 0.84 — 비동기 질의와 트랜잭션!

by /

새 버전의 Realm 자바를 선보입니다. 웹 사이트와 Maven에서 받을 수 있습니다. 이 릴리즈는 비동기 질의와 워커 스레드에 대한 비동기 쓰기 트랜잭션을 포함합니다!

안드로이드 어플리케이션은 본질적으로 비동기적입니다. 이런 이유 때문에 Realm 읽기 질의와 워커 스레드에서 비동기로 쓰기 연산를 지원해달라는 요청이 가장 많았습니다.

비동기 질의

이제 워커 스레드에서 비동기로 도는 질의를 할 수 있습니다.

비동기 질의는 기존의 동기 질의와 비슷합니다. 질의를 하던 익숙한 API를 그대로 사용할 수 있습니다. 호출 단계의 마지막 메서드가 RealmQuery가 동기로 동작할지 (findAll()) 비동기로 동작할지 (findAllAsync())를 결정하죠.

예제: “John”과 “Peter” 사용자들 찾아봅시다.

처음으로 질의를 만듭시다.

RealmResults<User> result = realm.where(User.class)
                              .equalTo("name", "John")
                              .or()
                              .equalTo("name", "Peter")
                              .findAllAsync();

질의가 블록되지 않고 즉시 RealmResults<User>를 반환하는 것을 주목하세요. 이것은 (표준 자바의 Future와 비슷한) 프로미스(promise) 입니다. 질의는 백그라운드 스레드에서 계속 실행이 되고 한번 완료되면 적합한 결과로 반환한 RealmResults 인스턴스를 갱신합니다.

질의가 끝났다고 알림을 받기 위해 콜백을 등록할 수 있습니다. 콜백은 Realm의 마지막 값을 반영하도록 질의가 갱신될 때마다 호출이 됩니다. (일반적으로 커밋 후에요.)

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
}

언제든지 RealmResults가 끝났는지 아닌지 확인할 수 있습니다.

result.isLoaded()

동기적으로 얻은 RealmResults의 isLoaded 를 호출하면 항상 참을 받게 됩니다.

루퍼를 사용하지 않는 스레드 경고: 비동기 질의는 Realm이 끊임없이 결과를 전달하기 위해 핸들러가 필요합니다. 루퍼 없이 스레드 안에서 비동기 질의를 호출하면 IllegalStateException 예외가 발생합니다.

비동기 트랜젝션

이제 워커 스레드에 비동기로 쓰기 트렌젝션을 수행할 수 있습니다.

현재 executeTransaction를 호출하는 방법과 동일합니다. Realm 스레드를 열었던 스레드 대신에 일을 할 다른 스레드에서 Realm이 열립니다.

트랜잭션이 끝나거나 실패할 때 알림을 받고 싶다면 콜백을 등록할 수 있습니다. (이는 Realm의 핸들러가 콜백을 전달하길 요구합니다. 만약 루퍼가 없는 스레드에서 Realm을 열고 비동기 쓰기를 시작하면 알림을 밪지 못합니다.)

realm.executeTransaction(new Realm.Transaction() {
            @Override
            public void execute(Realm bgRealm) {
                User user = bgRealm.createObject(User.class);
                user.setName("John");
                user.setEmail("[email protected]");
            }
        }, new Realm.Transaction.Callback() {
            @Override
            public void onSuccess() {
            }

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

콜백은 선택적입니다. 호출하는 걸로 끝내고 싶다면 그렇게 해도 됩니다.

realm.executeTransaction(new Realm.Transaction() {
            @Override
            public void execute(Realm bgRealm) {
                User user = bgRealm.createObject(User.class);
                user.setName("John");
                user.setEmail("[email protected]");
            }
        }, null);

쓰기 트랜잭션이 끝날 때까지 강한 참조를 가지고 있습니다. (예를들어 액티비티나 프래그먼트를 종료해서) 만일 트랜잭션을 취소하길 원한다면 cancel을 호출하면 됩니다. 이는 인스턴스나 둘러싼 객체의 누수를 막기 위해서 스케쥴된 트랜잭션을 취소하고 콜백에 대한 참조를 제거합니다.

RealmAsyncTask transaction = realm.executeTransaction(new Realm.Transaction() {
            @Override
            public void execute(Realm bgRealm) {
            }
        }, null);

// configuration change ...

public void onStop () {
    if (transaction != null && !transaction.isCancelled()) {
        transaction.cancel();
    }
}

새로운 질의 옵션

우리는 새로운 방식의 질의 두가지 isEmpty()distinct()를 추가하였습니다.

RealmQuery.isEmpty()를 이용하면 값이 널이 아니고 비어있는 것을 확인할 수 있습니다. 예를 들어 RealmList나 바이트 배열이 0개의 요소를 가지고 있다면 비어있는 것이고 문자열이 ""이면 비어있는 것입니다. isEmpty()는 정수 값에서는 동작하지 않습니다.

// 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();

Realm.distinct()는 주어진 값으로 정의된 요소에 대한 대조적인 세트를 얻기 위해 쓰입니다. distinct가 적용이 되는 필드는 인덱스(@Index 또는 @PrimaryKey)여야 합니다.

// 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이 닫혀있거나 데이터가 삭제되었는지 확인합니다.

더 구체적인 내용이 궁금하면 변경점을 확인해보세요.


읽어주셔서 감사합니다. 이제 Realm과 함께 앞으로 나아가 멋진 앱을 만들어 봅시다! 우리는 항상 StackOverflow, GitHub, and Twitter에 있습니다. 페북에서 운영중인 Realm 한국 사용자 그룹 이나 Realm 한국 사용자 페이지와도 방문해 주십시오. 한국어 연락처는 [email protected] 입니다.

더 읽어보기

Realm 자바 0.83 — Null 지원!

by /

이 버전으로 읽거나 쓴 Realm 파일은 더 이상 이전 Realm 버전에서 열 수 없습니다. 이전에 출시된 앱에 이 업데이트를 적용할 때는 주의하세요!

이 릴리즈는 가장 많은 요청을 받은 Realm 기능 널 값 지원을 포함합니다. 이 웹 사이트와 메이븐에서 Realm 자바를 받을 수 있습니다.

호환성 주의사항

이 릴리즈부터 String, Date, byte[]의 기본 값은 null이 가능합니다. (Realm 자바의 예전 버전에서는 어떤 필드도 null 값을 가질 수 없었죠.) 기존 앱들은 모델 클래스를 변경하거나 새 기능을 지원하기 위한 명시적인 이전 절차를 제공해야 합니다. 이후 내용을 더 자세히 읽어주세요.

널 값 지원을 위해 Realm 하부의 스토리지 엔진에서 파일 포맷을 변경하였습니다. 처음 Realm 파일을 열 때 Realm 파일들은 자동으로 새 파일 포맷으로 변환됩니다. 이 파일 변환은 이전은 되돌릴 수 없습니다! 이전 버전의 Realm에서 새 Realm 파일을 여는 것은 불가능합니다. 게다가 Realm 파일을 예전 파일 포맷으로 버전을 내리는 것도 불가합니다.

이전 앱들을 업그레이드하고 배포하기 전에 주의깊게 테스트하는 것을 추천합니다.

기본형 래퍼 클래스

Realm 자바 버전은 기본형(primitives)을 지원합니다. boolean, 정수형 (byte, short, int, long), 부동소수점 수 (float, double). 이 기본형 (혹은 기본형 래퍼 클래스 (primitive wrapper class, unboxed type)가 아닌 형)들은 Java에서 null을 대입할 수 없습니다. null 값을 대입할 필요가 없다면 기본형을 모델 클래스에 여전히 사용할 수 있습니다.

반면에 기본형 래퍼 클래스 (primitive wrapper class, boxed type)은 자바에서 일반적입니다. 래퍼 클래스 형의 변수와 필드에는 null을 넣을 수 있습니다.

Realm 자바는 불린형을 위한 래퍼 클래스 Boolean 정수형을 위한 Byte, Short, Integer, Long, 부동 소수점을 위한 FloatDouble를 준비합니다. 기본형 래퍼 클래스를 모델 객체에서 사용하면 필드에 null을 대입할 수 있습니다.

필드의 String, Date, byte[] 타입도 null이 될 수 있습니다. RealmListnull을 대입하는 것은 리스트가 비어있다는 의미입니다. 오브젝트들은 지워지지 않고 그것을 가리키는 레퍼런스만 제거됩니다. 게다가 RealmList 필드는 null이 될 수 없습니다. 게터는 언제나 RealmList 객체를 얻지만 리스트의 길이는 0이 됩니다.

작은 예제를 보여드리겠습니다. 아래의 모델 클래스는 두개의 integer 필드와 하나의 string 필드를 가집니다.

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);

John의 필드 3개 모두 대입한 것을 볼 수 있습니다. 반면에 Bill의 무게는 알수 없기에 null로 설정했습니다. (사실은 Person.weight의 기본값이 null이기 때문에 생략할 수 있는 부분입니다.)

새로 등장한 @Required 어노테이션

String, Date, byte[]와 기본형 래퍼 클래스의 필드 모두에 null을 대입할 수 있습니다. 위 예제에서 보였듯 가끔은 (null이 아닌) 적절한 값이 요구됩니다. null을 대입할 수 없는 필드를 지정하기 위해 새로 도입된 @Required 어노테이션을 쓸 수 있습니다. 이 어노테이션을 쓰면 Realm은 거기에 null을 대입하는 것을 허용하지 않습니다. 사람의 이름을 항상 알고 있다면 더 나은 모델은 다음과 같습니다.

class Person extends RealmObject {
    @Required
    String name;
    int age;
    Integer weight;
}

기본형 래퍼 타입 필드에 @Required을 쓰는 것도 가능합니다. Realm은 null이 아닌 값을 강제하고 저장합니다. 어노테이션은 기본형을 위서는 쓰일 수 없습니다. 기본형은 null을 가질 수 없기 때문입니다. 게다가 관계(RealmList와 단일 렘 객체들)도 @Required로 지정될 수 없습니다.

질의

@Required되지 않은 필드는 누구나 질의에 null 값을 반환할 수 있습니다. 집계하는 함수 (sum, min 등)은 계산에 null 값을 가져오지 않습니다. Realm은 모든 문자열의 시작, 끝, 중간에 null이 있을 수 있다고 가정하고 해석합니다. 아래의 질의는 모든 객체를 리턴하죠.

RealmResults<Person> persons = realm.where(Person.class).where().beginsWith("name", null).findAll();

만약에 @Required된 필드에서 null값을 질의하면 예외가 발생합니다.

마이그레이션

null 지원이 릴리즈되면서 Realm 하부의 스토리지 엔진은 자동으로 Realm 파일을 변환합니다. 스토리지 엔진이 변경되어 파일 변환이 필요합니다. null값을 모델 클래스에서 사용하지 않더라도 변환은 이루어집니다. 자동 파일 변환은 시간이 걸리고 처음에 Realm 파일에 접근하는 것은 꽤 시간이 걸릴 수 있습니다. 파일 변환이 끝나면 파일은 이전처럼 빨리 열리게 됩니다.

수동 마이그레이션을 하고 싶을 수 있습니다. 문자열, 날짜, 바이너리 (byte[]) 필드가 모델 클래스에 있는데 마이그레이션해야 합니다. 이전 버전의 Realm Java에서는 String이 null이 될 수 없었습니다. 0.83.0 버전의 Realm 자바에서는 String의 의미가 바뀌었습니다. 아래의 모델 클래스 예제를 봅시다.

class Dog {
    String name;
    int age;
}

Dog이 들어간 Realm 파일을 생성하면 name 필드는 null 대입이 안되었습니다. Realm 자바 버전 0.83.0 버전 부터는 값에 null을 허용하지 않으려면 모델 클래스를 바꿔야 합니다.

class Dog {
    @Required
    String name;
    int age;
}

만약에 모델 클래스를 바꾸고 싶지않다면 (또는 null을 사용하고 싶다면) Realm 파일을 마이그레이션 해야합니다. 이 용도로는 Table.convertColumnToNullable() 메서드를 마이그레이션 과정에 사용할 수 있습니다.

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();
더 읽어보기