[ios] Xcode - delegate pattern & protocol

Oxong·2022년 2월 17일
0

iOS 개발 관련 모음

목록 보기
4/8

공부한 것을 정리하는 용도의 글이므로 100% 정확하지 않을 수 있습니다.
참고용으로만 봐주시고, 내용이 부족하다고 느끼신다면 다른 글도 보시는 것이 좋습니다.
+ 틀린 부분, 수정해야 할 부분은 언제든지 피드백 주세요. 😊
                                            by. Oxong






delegate pattern?


객체 프로그래밍에서 하나의 객체가 모든 일을 처리하는게 아니라
처리해야 할 일 중의 일부를 따로 객체로 만들어주고 그걸 넘기는(위임: Deletate) 방법을 말한다.

즉, 대리자(delegate)를 지정하여 이벤트 처리를 위임하고, 이벤트가 발생하면 delegate가 콜백 메소드를 호출한다.


정확한 뜻이지만... 초급자 입장에서는 사실 이렇게만 말하면 당연히 무슨 말인지 이해가 가지 않을 수 있다.

개인적으로는 Swift :: delegate패턴 알아보기에서 설명한 붕어빵 비유가 굉장히 이해가 잘 되었다.
위의 설명이 이해가 잘 가지 않는다면 ㄱㄱ ! :)


iOS에서 delegate pattern은 protocol을 통해 구현한다.

protocol은 ... 양이 너무 많기 때문에 delegate pattern과의 관계를 이해할 수 있을 정도로만 간략하게만 알아보자.



protocol ?


protocol은 class나 구조체가 특수한 목적을 달성하기 위해 구현해야 하는 메소드와 프로퍼티의 목록 이다.
인터페이스 같은 개념으로 명령만 한다고 생각하면 쉽다.
protocol은 구현한 객체의 메소드나 속성을 은닉하고 protocol에 선언된 명세의 내용만 제공한다.

protocol은 제시만 할 뿐, 스스로 기능을 구현하진 않는다.



프로토콜을 사용하기 전에 프로토콜의 채택과 준수 에 대해서는 아래 포스트에 상세하게 나와 있으므로 읽어보는 것도 좋다.
Difference between Conformance and Adoption in Swift Protocol

저는 간단하게 정리만 하고 넘어가겠습니다~~


🔹 Conformance (준수)

existing type에서 이미 protocol이 명세한 요구 조건(property or method)를 이미 구현해 놓은 상태라면,
extension 과 protocol 이름만 명시하고, 구현부를 공백으로 두면 해당 타입이 그 프로토콜을 준수하게 된다.

protocol TextRepresentable {
    var textualDescription: String { get }
}

struct Hamster {
    var name: String
    var textualDescription: String {
        return "A hamster named \(name)"
    }
}

extension Hamster: TextRepresentable {}

구현한 것 → conform 준수
colon :으로 덧붙이는 부분 → adopt 채택


🔹 Adoption (채택)

※ 코드는 준수와 동일하다.

protocol TextRepresentable {
    var textualDescription: String { get }
}

struct Hamster {
    var name: String
    var textualDescription: String {
        return "A hamster named \(name)"
    }
}

extension Hamster: TextRepresentable {}

Hamster struct 에서 TextRepresentable protocol 의 요구 사항을 준수한 상태라면,
빈 extension으로 protocol을 채택하면 된다.



아래 그림은 준수와 채택을 비교한 이미지.



Protocol 정의

protocol TestDelegate: class {
  func sendData(data: String) -> String
}

데이터를 주고 받을 때 사용할 함수를 정의하기 위해 Protocol을 만들었다.
함수의 내용은 작성하지 않고 함수 형태만 작성해주면 된다.

데이터를 받을 화면을 A, 데이터를 전달할 화면을 B라고 해보자.
각각의 화면에서 처리해야 하는 일은 아래와 같다.

A (데이터 받을 화면)
⇢ protocol 채택
⇢ 실제 구현
⇢ 대리자 위임


B (데이터를 넘길 화면)
⇢ type이 protocol인 property 작성
⇢ delegate 사용


A 화면

class ViewController: UIViewController, TestDelegate {
	
     override func viewDidLoad() {
        super.viewDidLoad()
     }
     
     @IBAction func ButtonTapped(_ sender: Any) {
        // 🔹 B 화면에서도 data 값을 전달해주기 위해
		let nextVC = InstancePropertyVC()
		nextVC.data = "hi~"
		self.present(nextVC, animated: false, completion: nil)
	}

	// 🔹 TestDelegate 라는 프로토콜을 채택했으니, 프로토콜을 준수하기 위해 꼭 지켜야 할 함수들을 작성해야 한다.
    func sendData(data: String) -> String{
        // 🔹 B 화면에서 입력한 data를 A 화면에서 사용 가능
            print("ViewController sendData : \(data)")
            return "\(data), nice to meet you."
    }
    
}

B 화면

class DelegateVC: UIViewController {

  // 🔹 TestDelegate의 구현체를 만들어 준다.
    weak var delegate: TestDelegate?

    override func viewDidLoad() {
        super.viewDidLoad()
        
    // viewDidLoad에서 해당 함수를 호출 한다.
    // 🔹 A화면에서도 B화면으로 데이터 전달 가능
        let data = self.delegate?.sendData(data: "hi~")
        print("data: \(data)")
    }
}

B의 viewDidLoad에서 self.delegate?.sendData 함수가 실행된 것이 보이죠?

hi~라는 데이터를 보넀고, A화면에서는 "(data) nice to meet you." 이렇게 리턴되기 때문에
결과적으로는 hi~ nice to meet you. 라는 값이 출력될 것이다.


하지만 위의 코드는 TestDelegate에 해야 할 일을 지정만 했을 뿐, 내가 대신 하겠다고 선언하는 부분이 필요하다.
B의 일을 A가 대신 해줘야 하기 때문에 선언 부분은 A화면에 필요하다. (A의 button Action 부분 확인)


보통은 test.delegate = self를 많이 사용한다.
self(viewController)에서 test의 기능들을 쓰겠다는 뜻이다.


화면끼리 양방향적으로 데이터를 주고 받는다면 delegate Pattern이 더 효율적이고, 유지보수도 좋다.




delegate pattern in tabieView



delegate 사용법을 검색하면 대부분 예시로 tableView를 많이 사용하는 것 같다.

그래서 하나 더 정리하려고 하는데, 나는 여러 tableView 예시 프로젝트 중에 아래 깃허브 프로젝트가 가장 이해가 쉬웠다.

delegate pattern in tableView 예시 프로젝트 깃허브
깃허브


간단하게 설명해보겠다.
  1. 메모를 create하는 vc에서 데이터를 전달하는 함수를 포함하는 protocol을 생성해준다. ( SendDateDelegate )

  2. 이제 protocol을 채택(extension)하고 위임(.delegate = self)한 곳에서 "이 함수를 이렇게 쓸거다" 하고 구현해주면 된다.

  3. class 안에 protocol 변수를 생성한다. ( var delegate: SendDataDelegate? )



메모를 작성한 후, 저장하기 위해 save 버튼을 클릭할 때, 데이터를 전달하는 이벤트가 발생한다.

노란색 박스를 보면 전달하고자 하는 data를 delgate.sendDate의 인자로 넣어주고, 이벤트가 실행된 이후에는 dismiss까지 되는 것을 알 수 있다.



dimiss 되어 나타난 vc에서 protocol을 채택해준다.

func senndData()를 통해 채택한 protocol의 함수를 구현해준다.
여기서는 createView에서 memo 라는 변수에 전달되어온 data를 저장하는 함수로 구현했다.



노란색 밑줄 친 vc.delegate= self가 보이는가?

prepare할 때, vc.delegate= self( self 는 현재 vc : DetailViewController )로 위임해줬다.

이를 통해, 여기서 SendDateDelegate의 sendData 함수를 처리하겠다고 한 것이다. ( memo = data 라고 구현해줌 )





위의 프로젝트에서는 CoreDataManager를 사용하여 CRUD를 구현하여 메모의 저장, 수정, 삭제 등을 구현해놓았다.

그러므로 tableView에서의 delegate pattern을 공부해보고 싶다면 위에 적어 놓은 깃허브에 들어가 프로젝트를 확인해보는 것을 추천한다.






Reference

Swift :: delegate패턴 알아보기

iOS Delegate 패턴에 대해서 알아보기

[swift] Delegate Pattern ( + delegate로 데이터 전달 )

Delegate Pattern 활용하기 (ViewController끼리 Data 전달하기 - 2)

0개의 댓글