JIGGAG

Objective C를 react-native-biometrics와 함께 읽기

2020년 5월 1일

react-native-biometics 라이브러리의 네이티브 코드를 수정하고자 파일을 열었다. 안드로이드는 자바로, iOS는 Objective C로 개발되어있었다. 응답형태를 키-값으로 통일하고 싶었으나 iOS 코드를 도대체 어떻게 수정해야하는지 감이 오지 않았다. 다른 코드를 읽어보며 그럴듯하게 작업은 마무리가 되었지만, 다음에 있을 기회에는 좀 더 발전됨을 준비하고자 프로그래밍 오브젝트 C 책과 react-native-biometrics 코드를 읽어보며 Objective C를 알아두고자 한다.

자바랑 비슷한데 너무 C다

객체지향인 것은 자바랑 같다. 그러나 스타일은 C이다. C를 하나도 모르는데 이해할 수 있을까 걱정했지만, 오브젝티브 씨를 공부하면서 얻고자 하는 방향성에 대해 생각해보면서 부담없이 알아보도록 하였다.

interface + implementation

interface에서 클래스의 데이터, 메소드를 선언하고 implementation에서 이를 오버라이딩하는 개념으로 구현해낸다.

객체 선언

my = [myClass alloc]으로 객체를 할당하였고 [my init]으로 초기화를 진행한다. 이를 한번에 나타내면 [[myClass alloc] init]으로 표현할 수 있으며 [myClass new]와 같다.

가비지 콜렉션

자바에서는 사용하고 있지 않은 메모리에 대해 정리해주는 가비지 콜렉션이 존재한다. 그러나 iOS에서는 이걸 지원하지 않고 있다. (왜 인지 모르겠지만...) 그래서 사용한 메모리에 대해 사용하지 않는 경우 항상 정리를 직접 해줘야한다. 이때, 어디선가 사용하고 있는 경우 retain으로 레퍼런스 카운트가 증가하고 사용하지 않는 경우 release로 감소시킨다. 그리고 레퍼런스 카운트가 0이 되면 해당 메모리가 완전하게 정리된다.

포인터

그냥 값을 가져오면 되는거 아닐까 생각했는데, 만약 엄청 큰 데이터가 있고 이를 들고서 왔다갔다 해야한다면 그때마다 메모리가 소비된다. 이때 이 데이터가 들어있는 주소만 알아내서 들고다닌다면 훨씬 효율적으로 움직일 수 있다.

카테고리, 프로토콜

카테코리: 라이브러리, 모듈화
프로토콜: 구현되어야 하는 메서드 모음

react-native-biometrics

직접 커스텀했던 라이브러리 코드를 다시 열어보았다.

#import <React/RCTBridgeModule.h>

@interface ReactNativeBiometrics : NSObject <RCTBridgeModule>

@end

ReactNativceBiometrics.h 파일로 interface 선언 부분이다. 안타깝게도 여기에 선언된 변수와 메소드가 없다... 아마 React Native 특성상 브릿지로 호출하게 되어서 그런건 아닐까 생각해본다.

NSObject를 상속받고 있으며 프로토콜로 RCTBridgeModule이 눈에 보인다.

#import "ReactNativeBiometrics.h"
#import <LocalAuthentication/LocalAuthentication.h>
#import <Security/Security.h>
#import <React/RCTConvert.h>

@implementation ReactNativeBiometrics

RCT_EXPORT_MODULE(ReactNativeBiometrics);

RCT_EXPORT_METHOD(isSensorAvailable:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject)
{
  LAContext *context = [[LAContext alloc] init];
  NSError *la_error = nil;
  BOOL canEvaluatePolicy = [context canEvaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics error:&la_error];

  if (canEvaluatePolicy) {
    NSString *biometryType = [self getBiometryType:context];
    NSDictionary *result = @{
      @"available": @(YES),
      @"biometryType": biometryType
    };

    resolve(result);
  } else {
    NSString *errorMessage = [NSString stringWithFormat:@"%@", la_error];
    NSDictionary *result = @{
      @"available": @(NO),
      @"error": errorMessage
    };

    resolve(result);
  }
}

그리고 이 코드는 ReactNativeBiometrics.m으로 implementation 구현 내용이다.

RCT_EXPORT_MODULE으로 명시된 이름올 네이티브 모듈을 사용할 수 있는데 여기에는 isSensorAvailable이 있고 인자로 resolvereject를 받는다.

첫번째 코드로 LAContext *context = [[LAContext alloc] init];이 있는데 이는 LAContext를 할당 alloc하고 init으로 초기화된 포인터가 context이다.

그다음에 NSError *la_error = nil;로 에러 변수를 초기화하였다.

위에서 초기화한 context의 메소드 canEvaluatePolicy을 바로 아래에서 사용하고 있다.

리턴된 값에 따라 아래 조건이 분기되며 [self getBiometryType:context]에서 ReactNativeBiometrics.m 파일에 같이 구현되어있는 getBiometryType 메서드를 호출한다.

그리고 NSDictionary에 키-값 형태로 만들어서 응답값을 돌려주면 해당 네이티브 모듈이 완성된다.

결론

황금 연휴에 쓱 읽어내려간 책 한권으로 이해하기에는 너무나 방대했고, C를 너무 몰랐다. 지금도 헷갈리는 부분은 많지만 읽기 전과 후가 다르게 라이브러리를 읽는데 눈에 더 보이는 것들이 있으니 앞으로 도움이 될거라 생각한다.