---
# System prepended metadata

title: 'ContackMangerUI [Step3] Jason & Blue'

---

# ContackMangerUI [Step3] Jason & Blue
안녕하세요, 제이크! (@jryoun1)
💰TEAM BUJA💰의 Blue & Jason입니다!

이번에 Step3과 Bonus Step이 함께 공개되었는데요.
저희는 Bonus Step 진행하는 것보다 Step3 구현과 기존 코드를 리팩토링 및
이번주 ViVi가 방문해주셔서 API Design GuideLine의 네이밍 부분을 아직까지 신경을 못쓰는 것 같다고 말씀해주셔서 Naming 수정에도 신경 써보기로 결정하였습니다!

리뷰 잘 부탁드립니다! 늘 정성스러운 리뷰 감사합니다!!! 🙇🏻‍♀️ 🙇🏻

### **🧑🏼‍💻 작업 목록**
[ Group Work  ]

🚀 **Step3**
- [x] 연락처 목록 화면의 우상단 + 버튼을 통해 연락처 추가 화면으로 진입 (Modal)
- [x] 오토 레이아웃과 스택뷰를 활용한 화면 레이아웃
- [x] 각 필드에 맞는 키보드 종류를 지정
    - 이름 - ASCII Capable
    - 나이 - Number Pad
    - 연락처 - Phone Pad
- [x] 새 연락처 추가 화면에서 네비게이션 바의 버튼을 누르는 경우 알림창이 나타나도록 구현
    - [x] 취소 버튼을 누르면 정말 취소할 것인지 묻도록 알림창 구현
    - [x] 저장 버튼을 누르면 입력한 정보가 올바르게 입력되었는지 확인하고 올바르지 않은 경우 해당 정보를, 올바른 경우 연락처 목록 화면으로 이동한 후 입력한 연락처를 목록에 추가하도록 구현
- [x] 이름은 사용자가 중간에 띄어쓰기를 하더라도 띄어쓰기를 제거
- [x] 나이는 숫자로만 입력, 세 자리수 이하
- [x] 연락처는 중간에 -로 구분, -는 두 개 존재, -을 제외하고 숫자는 9자리 이상


### **💭 학습 키워드**

- Delegate Pattern
- P.O.P (프로토콜 초기구현)
- Dependency Injection **(추가 학습 필요)**
- Auto Layout priority
- Reusable Cell
- Generic **(추가 학습 필요)**

### **🤔고민과 해결**

- `modal`로 `present`했던 VC를 `dismiss` 했을 때 `viewWillAppear`가 호출되지 않아 TableView를 reload 할 수 없었던 문제가 있었습니다.
    ```swift
    // ContactManagerTableViewController.swift

    @IBAction func tappedAddNewContactAction(_ sender: UIBarButtonItem) {
        // ...
        addNewContactVC.modalTransitionStyle = .coverVertical
        addNewContactVC.modalPresentationStyle = .automatic

        addNewContactVC.delegate = self

        present(addNewContactVC, animated: true)
    }
    ```
    저희가 작성한 코드에서는 위와 같이 새로운 연락처를 추가하는 VC를 present하는 Style을 **automatic**으로 설정하였습니다.
    automatic이 어떤 style을 지정하는지 확인하기 위해 lldb를 통해 modalPresentationStyle의 `rawValue`를 확인해 보았습니다.
<img src="https://user-images.githubusercontent.com/71758542/217978425-6f1d6c07-9587-44a3-b161-33c532f1e06e.png" width="300">
1에 해당하는 Style은 `pageSheet`으로, 배경이 되는(호출한) view가 사라지는 것이 아닌 해당 view 위에 레이어를 추가하는 방식으로 그려지게 된다고 학습하였습니다.
이로 인해 첫번째 VC의 view가 view 계층에서 사라지지 않기 때문에 dismiss 하더라도 view가 새로 appear하지 않으므로 `viewWillAppear()`가 호출되지 않는 것으로 파악하였습니다.
view가 전환되면서 TableView의 데이터를 reload하는 코드를 delegate가 실행하는 `sendData(newData:)` 내부에 구현하는 것으로 해결하였습니다!
> ❓🤔 Jake! 혹시 해당 방법 외에 추천해주실 방법이나, 현업에서 사용하고 계신 방식을 찾을 수 있는 학습 키워드가 있을까요?
    
---
### **🙋 질문 사항**
#### ❓Array의 Index 직접 접근하지 않고 해결하는 방법
- 데이터를 인덱스로 접근하는 것에 대한 위험성은 인지하고 있으나, 이 부분을 어떻게 대체해야 하는가에 대한 좋은 방안이 생각나지 않아 jake에게 질문드립니다!
    ```swift
    @IBAction func tappedSaveButton(_ sender: UIBarButtonItem) {
        // ...
        if errorMessage.isEmpty {
                let sendingContactData = ContactInformation(name: newContactData[0], age: newContactData[1], phoneNumber: newContactData[2])
            //...
            }
    }
    ```
    우선, 저희가 생각해 본 방법은 다음과 같습니다.
    - index를 숫자로 직접 넣는 대신 index 값을 가지고 있는 변수를 선언하여 해당 변수로 접근하는 방법
        > 저희의 경우 배열 형태가 [이름, 나이, 연락처] 순서대로 지정되어 있어서 0, 1, 2 대신 name, age, phoneNumber로 접근하면 어떨까? 했지만, 해당 방법은 index로 접근하는 방식과 크게 다르지 않다고 생각하였습니다.
    - Array의 `.first`, `.last`와 같은 메서드 활용
        > 해당 방법이 인덱스로 직접 접근하는 방식에 비해 안전하다고 생각하였으나, 중간 요소에 접근할 수 없어서 실제로 사용하기는 어렵겠다고 판단하였습니다!

    ❓🤔 저희가 생각한 방법 중 괜찮은 방법이 있거나, 없다면 jake가 추천해주실만한 방법을 찾을 수 있는 키워드가 있을까요?

#### ❓SOLID의 DIP관련하여 질문드립니다.
- 리펙토링 과정에서 저희가 생각했던 방법은 외부에서 의존성을 주입할 객체의 인스턴스를 생성 후
초기화 과정에서 넣어주는 방법으로 진행하려고 했습니다.
```Swift
// ⛔️
let detector = Detector()
let convertor = Convertor()
let checker = Checker()
let contactManager = ContackManager(detector: detector, convertor: convertor, checker: checker)
```
위와 같이 작성하였더니 UI Target에서 AddNewContactViewController에서 ContactManager를 구현할 때 다시 생성하여 사용해야하는 상황이 발생하여 어색하다고 느껴져 아래와 같이 구현하여 해결하였습니다.

```Swift
// ✅ 현재 코드
let detector: Detectable
let convertor: Convertable
let checker: Checkable

init(detector: Detectable = Detector(), convertor: Convertable = Converter(), checker: Checkable = Checker()) {
    self.detector = detector
    self.convertor = convertor
    self.checker = checker
}
```

위와 같이 하다보니 정확하게 DIP 조건을 충족하는 것이 맞는지 확신이 들지 않았습니다..
❓🤔 저희가 리펙토링한 위 코드처럼 작성하여도 맞게 작성한 것인지 궁금합니다!

---
### **💭 부연 설명**
#### 1️⃣ ContactInformation 데이터 저장 관련
저희가 구현하는 과정에서 놓친 부분이 있었습니다..🥲
앞서 진행했던 프로젝트에서 Model의 데이터를 저장하는 
```Swift
var contactInformationArray: Set<ContactInformation> = []
```
위 Collection이 있는데 ContactManagerTableViewController에서 
```Swift
private var contactInfomation = [ContactInformation]()
```
따로 구현하여 처리하게되었습니다. 좀 더 신경써서 Model에 있는 곳에서 관리했어야 했는데
시간이 좀 부족해서 위와 같이 진행하게 되었습니다

#### 2️⃣ 연락처 '-'(hyphen) 로직 관련
다른 팀들을 참고해보며 format 형식을 만들어 관리해주는 팀도 있고
```Swift
func textField(_ textField: UITextField, shouldChangeCharactersIn range: NSRange, replacementString string: String) -> Bool {
        
    }
```
위 메서드를 통해 처리하기도하며 방법은 여러 가지였습니다. 
서칭을 통해 이미 오픈 라이브러리로 처리된 부분도 많아서 하드코딩식으로 진행하게되었습니다.
개인적으로(Jason) 이 부분을 TextFieldDelegate의 메서드를 여러개 사용하여 해결할 수 있는
요구사항이라고 생각이 들어 추후 리펙토링 작업을 진행해보려고 합니다.

---
### 💻  **실행 화면**
<img src="https://user-images.githubusercontent.com/71758542/217981706-6b88c8f3-d8bc-461a-8125-4cb0363cbb56.gif">