일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- ios
- Firebase
- cloud functions
- Apple Developer Academy
- task.yield()
- Flutter
- spring
- tabman
- WWDC22
- app intents
- Coding Test
- 코테
- UITableView
- Project
- Tuist
- backend
- fcm
- Sendbird
- widgetkit
- UIStackView
- Complication
- swift concurrency
- github
- SwiftUI
- watchkit
- createml
- UIDatePicker
- coreml
- Delegate Pattern
- Swift
- Today
- Total
azhy의 iOS 이야기
[iOS/Swift] delegate pattern, 데이터 주고 받기 본문
2022년 6월 29일에 작성됨
delegate pattern 은 정말 자주 쓰이기 때문에 정말 중요합니다.
delegate 가 무엇일까? 사전적 정의는 위임하다. 즉 어떤 작업을 다른 사람에게 위임해서 요청한다 라는 느낌으로 이해하면 됩니다.
delegate pattern을 쓰기 위해서는 송신자와 수신자가 필요한데 쉽게 생각하면 데이터를 주고받는 ViewController 2개가 필요하다고 생각합시다.
delegate 패턴은 보통 되돌아오는 과정 (B -> A) 경우에 사용하고 반대인 A -> B의 경우에는 프로퍼티 접근으로 쉽게 데이터를 전달할 수 있습니다. 프로퍼티 접근에 관해서는 맨 밑에서 간단히 설명해 볼게요.
프로토콜 선언
protocol TapDelegate: AnyObject {
func tapAction(value: String)
}
delegate는 보통 protocol로 만들기 때문에 본인이 필요한 func을 담은 protocol을 하나 만들어 주자. 나는 간단한 테스트를 위해 string value를 담은 func을 하나 만들었습니다.
첫 번째 뷰 (수신자) 작성
protocol을 만들었으면 첫 번째 뷰, 데이터를 받아야 하는 뷰에 protocol을 채택해 주자
보통 extension으로 많이 구성해서 나도 extension으로 작성했고, protocol을 채택하면 위에서 만든 func에 대해서 무조건 작성해주어야 합니다.
// TapDelegate protocol 채택, tapAction func 작성
extension FirstViewController: TapDelegate {
func tapAction(value: String) {
print("receive data \(value)")
}
}
Controller 본문에는 가운데에 클릭하면 두 번째 뷰로 이동하는 버튼을 하나 만들었습니다.
여기서 중요한 점은 secondViewController.delegate = self 이 부분인데 간단히 설명하면 secondViewController에 선언되어 있는 delegate에 자신을 전달해서 secondViewController.delegate 가 자기 대신 func을 실행할 수 있게 해 줍니다.
// 첫 번째 뷰, 수신자
class FirstViewController: UIViewController {
@IBOutlet weak var btn: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func btnAction(_ sender: Any) {
let storyboard: UIStoryboard = UIStoryboard(name: "SecondView", bundle: nil)
guard let secondViewController = storyboard.instantiateViewController(withIdentifier: "SecondViewController") as? SecondViewController else { return }
// secondViewController (대리자) 에게 자신을 전달
secondViewController.delegate = self
self.present(secondViewController, animated: true, completion: nil)
}
}
두 번째 뷰 (송신자) 작성
두 번째 뷰도 동일하게 가운데에 버튼이 하나 있는데, 그 버튼을 누르면 delegate를 통해 첫 번째 뷰에 정의된 func 함수가 실행됩니다.
여기서 알아두면 좋은 점은 weak var인데, weak 없이 그냥 var delegate... 해도 오류 없이 돌아갑니다. 하지만 weak 없이 진행하면 뷰 끼리 왔다 갔다 하면서 생기는 메모리가 해제되지 않아서 메모리가 누수되고 심하면 앱이 죽을 수도 있습니다. 그래서 메모리 누수를 방지하기 위해 weak를 써주는게 좋습니다.
weak 를 쓰기 위해서는 protocol에 AnyObject나 class를 상속받아야 사용이 가능합니다.
// 두 번째 뷰, 송신자
class SecondViewController: UIViewController {
@IBOutlet weak var btn: UIButton!
weak var delegate: TapDelegate?
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func btnAction(_ sender: Any) {
print("SecondViewController btn Action")
// 첫 번째 뷰에서 선언한 함수를 통해 데이터 전달
delegate?.tapAction(value: "delegate practice")
}
}
결과화면
두 번째 뷰에서 버튼을 클릭하면 정상적으로 첫 번째 뷰에서 정의된 tapAction 함수가 실행이 됩니다.
전체 코드
protocol TapDelegate: AnyObject {
func tapAction(value: String)
}
extension FirstViewController: TapDelegate {
func tapAction(value: String) {
print("receive data \(value)")
}
}
// 첫 번째 뷰, 수신자
class FirstViewController: UIViewController {
@IBOutlet weak var btn: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func btnAction(_ sender: Any) {
let storyboard: UIStoryboard = UIStoryboard(name: "SecondView", bundle: nil)
guard let secondViewController = storyboard.instantiateViewController(withIdentifier: "SecondViewController") as? SecondViewController else { return }
// secondViewController (대리자) 에게 자신을 전달
secondViewController.delegate = self
self.present(secondViewController, animated: true, completion: nil)
}
}
// 두 번째 뷰, 송신자
class SecondViewController: UIViewController {
@IBOutlet weak var btn: UIButton!
weak var delegate: TapDelegate?
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func btnAction(_ sender: Any) {
print("SecondViewController btn Action")
// 첫 번째 뷰에서 선언한 함수를 통해 데이터 전달
delegate?.tapAction(value: "delegate practice")
}
}
프로퍼티 접근
위에서 잠깐 설명했듯이 delegate pattern은 되돌아오는 과정(B -> A)에서 쓰이고 반대로 데이터 전달 과정이 A -> B 인 경우에 쓰이는 프로퍼티 접근에 대해 간단한 코드로 설명하겠습니다.
// 첫 번째 뷰
class FirstViewController: UIViewController {
@IBOutlet weak var btn: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
}
@IBAction func btnAction(_ sender: Any) {
let storyboard: UIStoryboard = UIStoryboard(name: "SecondView", bundle: nil)
guard let secondViewController = storyboard.instantiateViewController(withIdentifier: "SecondViewController") as? SecondViewController else { return }
// 버튼 클릭 -> secondViewController의 receiverData 에 접근해서 데이터 세팅
secondViewController.receiverData = "데이터 넘김"
self.present(secondViewController, animated: true, completion: nil)
}
}
// 두 번째 뷰
class SecondViewController: UIViewController {
var receiverData: String = ""
override func viewDidLoad() {
super.viewDidLoad()
print("receiverData = \(receiverData)"
}
}
정말 간단합니다. 두 번째 뷰에서 받을 데이터를 미리 객체를 만들어두고 첫 번째 뷰에서 두 번째 뷰로 이동할 때 객체에 직접 접근해서 데이터를 세팅해 주면 끝입니다.
'Swift' 카테고리의 다른 글
[iOS/Swift] UITableView, 셀 재사용 시 발생하는 문제 (0) | 2024.11.12 |
---|---|
[iOS/Swift] View와 Button 에 click action 등록하기 (0) | 2024.11.12 |
[iOS/Swift] ViewController 생명주기 (0) | 2024.11.11 |
[iOS/Swift] UIStackView 기본 (0) | 2024.11.11 |
[iOS/Swift] UITextField 글자 수 제한, 백스페이스 처리 (0) | 2024.11.11 |