swift & iOS/swift

[swift] delegate 패턴 (feat. 프로토콜) & 구현해보기

whale3 2022. 2. 1. 21:57

Delegate 패턴

delegate 패턴은 mvc, mvvm... 처럼 디자인 패턴의 한 종류라고 한다. 하나의 객체가 모든 일을 다 처리하기 보다는 처리할 것 일부를 다른 객체에 넘기는 것이다. 스위프트에서는 delegate 디자인 패턴으로 작성할 때 보통 프로토콜을 많이 활용한다고 한다.

 

'디자인 패턴'에 대한 정의는 a proven solution to a common problem 인데, 그럼 어떤 problem이 있었길래 delegate 디자인 패턴이 생긴 것일까? 예를 들어, UITextField 라는 클래스가 있다. UITextField를 참조하게 될 많은 클래스들이 텍스트 필드에 어떤 이벤트가 발생하면 UITextField로 부터 알림을 받고 싶을 것이다. 하지만 UITextField가 이 클래스들을 다 아는 것은 불가능하다. 그러면 UITextField를 누가 사용하던지 상관없이 UITextField를 참조하는 곳에 어떻게 알림을 줄 수 있을까? 가 문제였다고 한다. (유데미 강의 참고하였음,,) 그래서 여기에 대한 한 가지 솔루션이 delegate 패턴이라고 한다. delegate로 등록된 클래스가 어떤 것이든 상관없이 UITextField에 어떤 이벤트가 감지 되었을때 delegate로 등록된 클래스의 메소드를 호출하며 알려줄 수 있다. (delegate로 등록된 클래스가 특정 프로토콜을 채택하고 준수하기만 한다면)

 

UITextField를 사용할 때 아래처럼 UITextField 인스턴스의 delegate 프로퍼티에 해당 클래스를 할당하는데, UITextField 클래스 내부를 보면 실제로 delegate 라는 이름의 프로퍼티가 존재하고 데이터 타입은 UITextFieldDelegate로 되어있다. 

import UIKit

class WeatherViewController: UIViewController, UITextFieldDelegate {
    @IBOutlet weak var searchTextField: UITextField!
    
    override func viewDidLoad() {
        super.viewDidLoad()
        searchTextField.delegate = self
    }    
}

그리고 UITextField 클래스 내부에서 찾지는 못했지만 내부 메소드 중에 아마 delegate.UITextFieldDelegate에선언된메소드들중하나() 이런식으로 되어 있는 부분이 있어서 UITextField에서 어떤 이벤트가 발생할 때 delegate된 클래스의 내부에 구현된 delegate 프로토콜의 메소드들을 호출할 수 하는 것 같다. 

 

이렇게 delegate를 이용하면 UITextField는 어떤 클래스가 자기를 참조할 것인지 알 필요도 없고 그저 delegate에 등록된 클래스의 메소드를 필요에 맞게 호출하면 등록된 클래스에서 상황에 맞게 처리하고 싶은대로 처리하면 될 것이다. 

 

 

프로토콜의 이름: ~Delegate, ~DataSource

프로토콜 이름을 보면 ~Delegate인 것이 있고 ~DataSource인 것이 있는데 이렇게 프로토콜의 이름으로 각각 주로 어떤 목적을 위해 만들어진 것인지 알 수 있다. 

- ~Delegate: 주로 어떤 액션에 대한 반응을 하는 메소드의 모음

- ~DataSource: 주로 데이터를 받아 뷰를 그려주는 메소드 모음

 

 

 

delegate 패턴 구현해보기

가짜 UITextField, UITextFieldDelegate, ViewController를 만들어 delegate 패턴 흉내를 내보았다..

 

1. protocol 만들기 (이름은 ~Delegate면 될 듯)

2. 1에서 만든 protocol을 데이터 타입으로 하는 delegate 프로퍼티를 가지는 클래스 만들기

      delegate로 등록하려는 클래스/구조체가 왜 protocol을 채택해야 하냐면, protocol을 채택한 클래스/구조체 내부에 필요한 메소드가 구현되어 있을 것이고 2번 내부에 delegate.프로토콜메소드 처럼 작성되었을 때 delegate로 등록된 클래스/구조체에 구현된 메소드가 호출 될 것이기 때문

3. delegate로 등록하려는 클래스, 구조체에서 init 내부에 자기 자신을 2의 delegate에 할당하기

protocol FakeUITextFieldDelegate {
    func keyboardAppeared()
    func keyboardDismissed()
}

class FakeUITextField {
    var delegate: FakeUITextFieldDelegate?
    
    func startInput() {
        delegate?.keyboardAppeared()
    }
    
    func endInput() {
        delegate?.keyboardDismissed() // delegate로 등록된 클래스/구조체 내부에 구현된 keyboardDismissed 메소드를 호출하게 된다
    }
}

class FakeViewController: FakeUITextFieldDelegate {
    init(fakeUItf: FakeUITextField) {
        fakeUItf.delegate = self
    }
    
    func keyboardAppeared() {
        print("keyboard appeared!")
    }
    
    func keyboardDismissed() {
        print("keyboard dismissed!")
    }
}

 

let fakeUITextField = FakeUITextField()
let fakeVC = FakeViewController(fakeUItf: fakeUITextField)
fakeUITextField.startInput() // FakeViewController에서 구현한 print 부분이 출력됨. keyboard appeared!

 

 

 

 

 

 

 

 

이건 저번에 정리한 프로토콜. 저번에 이거 포스팅할 때 delegate 패턴도 정리한다고 했었기 때문에...

https://what-whale-wants-to-say-is.tistory.com/94

 

[swift] 프로토콜 protocol

프로토콜이 좀 생소해서 항상 챙겨보고 있는 아래 유튜브 영상을 많이 참고했다. 아 물론 swift 공식문서도... https://www.youtube.com/watch?v=p19mUgg1CFQ&list=PLJqaIeuL7nuFbWKMhG8-xLzF1T7gIPr8Z&index=69..

what-whale-wants-to-say-is.tistory.com

 

반응형