티스토리 뷰

반응형

안녕하세요 오늘은 최근 앱 개발을 하며 React Native를 패치(patch) 하여 사용했던 경험을 공유하고자 합니다. 

해당 과정은 지난 이슈 포스팅에서 진행했던 방법 중 하나인데요.

 

 

 


Background

IOS는 Input type이 password인 경우에는 비밀번호를 다시 쳤을 때 입력했던 값들이 자동으로 삭제됩니다. 

이는 IOS에서 기본으로 설정되어있는 것으로 해당 부분을 수정하려면 native언어를 수정해야 합니다. 

관련 이슈를  patch-package라는 라이브러리를 사용하여서 React Native를 패치(patch) 해 보았습니다.

 

Patch-package Library

 

Patch-package 라이브러리는 npm 패키지 의존성은 그대로 유지하면서, 변경한 npm 패키지의 내용을 버전 관리 대상으로 간편하게 만들어 줍니다.

 

이는 node-modules안의 파일들을 커스텀한 상태가 배포 상태에서도 지속되도록 수정사항을 기억해뒀다가 배포 시 node-modules 위에 덮어 씌워주게 되는 방식입니다.

 

즉, node_modules에 수정한 사항이 git으로 관리되고 어떠한 실행 환경에서도 적용되도록 해줍니다.

 

라이브러리를 사용하는 방식은 아래와 같습니다. 

  1. node_modules 안의 특정 package의 수정사항을 patch 파일 형태로 patches 아래에 저장한다.
  2. patches 안의 patch를 node_modules 안에 적용한다.

 


1.  Set-up

설치는 아래와 같이 해주세요 

 

In package.json

 "scripts": {
+  "postinstall": "patch-package"
 }

patch-package  postinstall-postinstall 설치

yarn add patch-package postinstall-postinstall

 

 


 

2. Usage

2-1. NPM 패키지 코드 수정

저는 Stack Of Flow의 솔루션 코드를 참고하여 해당 코드가 있는 NPM Package 경로를 찾아 직접 수정해 주었습니다.

 

- Path

node_modules/react-native/ // Libraries/Text/TextInput/RCTBackedTextInputDelegateAdapter.m

// node_modules/react-native/   
// Libraries/Text/TextInput/RCTBackedTextInputDelegateAdapter.m


- (BOOL)textField:(__unused UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
{
    //Setting the new text.
    NSString *updatedString = [textField.text stringByReplacingCharactersInRange:range withString:string];
    textField.text = updatedString;

    //Setting the cursor at the right place
    NSRange selectedRange = NSMakeRange(range.location + string.length, 0);
    UITextPosition* from = [textField positionFromPosition:textField.beginningOfDocument offset:selectedRange.location];
    UITextPosition* to = [textField positionFromPosition:from offset:selectedRange.length];
    textField.selectedTextRange = [textField textRangeFromPosition:from toPosition:to];

    //Sending an action
    [textField sendActionsForControlEvents:UIControlEventEditingChanged];

    return NO;
}

 

2-2. 수정 코드 패치 파일 생성

 

package-name을 명시하여 패치파일을 생성해줍니다. 

yarn patch-package package-name
yarn patch-package react-native

 

 

프로젝트 root 경로에 patches 폴더가 생성된 것을 보실 수 있습니다. 해당 파일 안에서 수정사항이 저장된 것을 확인할 수 있습니다.

이 수정사항들은 Git으로 관리되고, 어떤 환경에서도 적용되도록 보장합니다. 때문에 팀 프로젝트에서도 충돌 없이 유연하게 관리 할 수 있습니다. 

diff --git a/node_modules/react-native/Libraries/Text/TextInput/RCTBackedTextInputDelegateAdapter.m b/node_modules/react-native/Libraries/Text/TextInput/RCTBackedTextInputDelegateAdapter.m
index c6c254c..bd842d0 100644
--- a/node_modules/react-native/Libraries/Text/TextInput/RCTBackedTextInputDelegateAdapter.m
+++ b/node_modules/react-native/Libraries/Text/TextInput/RCTBackedTextInputDelegateAdapter.m
@@ -70,32 +70,20 @@ - (void)textFieldDidEndEditing:(__unused UITextField *)textField
 
 - (BOOL)textField:(__unused UITextField *)textField shouldChangeCharactersInRange:(NSRange)range replacementString:(NSString *)string
 {
-  NSString *newText =
-    [_backedTextInputView.textInputDelegate textInputShouldChangeText:string inRange:range];
+    //Setting the new text.
+    NSString *updatedString = [textField.text stringByReplacingCharactersInRange:range withString:string];
+    textField.text = updatedString;
 
-  if (newText == nil) {
-    return NO;
-  }
+    //Setting the cursor at the right place
+    NSRange selectedRange = NSMakeRange(range.location + string.length, 0);
+    UITextPosition* from = [textField positionFromPosition:textField.beginningOfDocument offset:selectedRange.location];
+    UITextPosition* to = [textField positionFromPosition:from offset:selectedRange.length];
+    textField.selectedTextRange = [textField textRangeFromPosition:from toPosition:to];
 
-  if ([newText isEqualToString:string]) {
-    _textDidChangeIsComing = YES;
-    return YES;
-  }
-
-  NSMutableAttributedString *attributedString = [_backedTextInputView.attributedText mutableCopy];
-  [attributedString replaceCharactersInRange:range withString:newText];
-  [_backedTextInputView setAttributedText:[attributedString copy]];
+    //Sending an action
+    [textField sendActionsForControlEvents:UIControlEventEditingChanged];
 
-  // Setting selection to the end of the replaced text.
-  UITextPosition *position =
-    [_backedTextInputView positionFromPosition:_backedTextInputView.beginningOfDocument
-                                        offset:(range.location + newText.length)];
-  [_backedTextInputView setSelectedTextRange:[_backedTextInputView textRangeFromPosition:position toPosition:position]
-                              notifyDelegate:YES];
-
-  [self textFieldDidChange];
-
-  return NO;
+    return NO;
 }
 
 - (BOOL)textFieldShouldReturn:(__unused UITextField *)textField

 

 


 

3. Caution

해당 라이브러리는 패키지 버전이 달라지면 재설정 해주어야 합니다. patch 된 버전과 package.json or yarn.lcok 에 저장된 버전을 확인하고 수동으로 맞춰주시면 됩니다. 


 

4. Test

잘 반영이 되었는지 확인하기 위해 node_modules를 삭제하고 재설치 후 빌드까지 해봅니다. 

 

node_modules 삭제

rm -rf node_modules

node_modules 설치

yarn

 

Pod 삭제 후 재설치

cd ios
rm -rf Podfile.lock Pods
pod install
yarn ios

 

Build 

yarn ios

 


 

Reference

반응형