티스토리 뷰
함수형 프로그래밍에 대해 알아보기 전에 우선 Swift라는 언의의 패러다임을 알아볼 필요가 있다.
그 중 가장 강조되는 패러다임은 다음과 같다.
- 함수형 프로그래밍
- 프로토콜 지향 프로그래밍
먼저 프로토콜은 클래스, 열거형, 구조체에 프로토콜을 채택함으로 특정 기능을 구현할 수 있는 기능이다.
이를 이용해서 기능별로 모듈화를 할 수 있다.
Swift의 기본 타입(뼈대)들은 모두 구조체로 만들어져 있다.
상속이 되지 않는 구조체에 그 많은 기능들을 넣기 힘들어 보이지만, 프로토콜, 익스텐션, 제넬릭을 이용해 구현되었다.
프로토콜에 대한 내용은 다음에 따로 포스팅을 할 예정이다.
1. 개요, 함수형 프로그래밍이란?
함수형 프로그래밍의 중요한 개념은 함수를 일급 객체로 다루는 점이다.
일급 객체의 특징
- 변수에 할당할 수 있음
- 함수의 매개변수(parameter)로 사용할 수 있음
- 함수의 반환값(return)으로 사용할 수 있음
정리하자면 함수도 우리가 늘 사용하는 Int, String 등 처럼 활용할 수 있다는 내용이다.
Swift에서의 함수형 프로그래밍은 클로저로써 많이 사용되는데 처음 배울 때 개념을 익히는데 어려웠던 기억이 있다.
우리가 늘 사용하던 UIKit에도 함수형 프로그래밍은 밀접한 관계인 클로저로써 많이 구현되어 있고 자연스럽게 사용하고 있을 것이다.
앱을 만들다보면 하단에 붙어있는 버튼을 구현하는 일이 매우 자주있다. 버튼 구현이야 굉장히 간단한 일이다.
하지만 SafeArea를 고려한 하단 버튼 UI를 구현하는 것을 매번 반복 작업하는 것은 상당히 귀찮다.
그래서 나는 주로 하단에 붙는 버튼을 앱 별로 만들어 놓고 나를 포함한 팀원들이 간편하게 사용할 수 있도록 한다.
(간단하지만 효율적이다)
이때 함수형 프로그래밍을 어떻게 활용해 코드를 만들 수 있을지 간단한 예제와 함께 소개하고자 한다.
2. 일단 delegate로 먼저 구현해보자
UI는 굉장히 간단하다. 하단에 버튼만 있을 뿐이다.
보통의 경우 버튼을 만들고 button의 addTarget을 통해 Selector 함수를 연결해준다.
나는 탭 이벤트와 3번 탭을 했을 경우에 이벤트가 호출되도록 내부 구현을 해두었다.
새로 버튼 클래스를 만든 경우에는 일반적인 방법으로 delegate 패턴을 사용하는 좋은 방법을 생각해 볼 수 있다.
delegate 패턴을 사용한 방식을 먼저 소개한다.
protocol FooterActionButtonDelegate: AnyObject {
func singleTapEvent()
func tripleTapEvent()
}
class FooterActionButton: UIView {
weak var delegate: FooterActionButtonDelegate?
/// 클릭 카운트
private var tapCount = 0 {
didSet {
self.observeTapCount()
}
}
private lazy var actionButton: UIButton = {
let button = UIButton(type: .system)
button.backgroundColor = backgroundColor
button.tintColor = .white
button.addTarget(self, action: #selector(actionButtonTapped), for: .touchUpInside)
return button
}()
// 세번 클릭했을 때
func observeTapCount() {
if self.tapCount == 3 {
// delegate 방식 호출
self.delegate?.tripleTapEvent()
}
}
@objc private func actionButtonTapped() {
self.delegate?.singleTapEvent()
self.tapCount += 1
}
}
구현한 프로토콜인 FooterActionButtonDelegate를 약한 참조로써 클래스에 옵셔널로 선언해주고,
클릭 이벤트를 delegate에 선언된 함수로 보내주는 방법이다.
/// Protocol을 이용한 delegate 방식으로 액션을 전달받습니다.
extension ThirdViewController: FooterActionButtonDelegate {
func singleTapEvent() {
print("버튼 눌렸어요")
}
func tripleTapEvent() {
print("버튼 세번 눌렸어요")
}
}
뷰 컨트롤러에서는 해당 프로토콜을 채택함으로 이벤트를 받을 수 있다.
많이 사용되는 좋은 방법이지만, 함수형 프로그래밍을 사용한다면 조금 더 간편하게 구현할 수 있다.
3. 함수형 프로그래밍으로 구현해보자
UI와 관련된 코드는 제외하고 기능의 코드만 살펴보자.
init 함수에서는 handler라는 파라미터로 옵셔널 함수를 받고, tripleTapAction 파라미터로 함수를 받고 있다.
파라미터로 들어온 함수는 클래스의 변수인 tapHandler와 tripleTapHandler에 초기값을 넣고 있다.
그리고 각 이벤트를 실행하는 곳에서는 handler?() 로 함수를 실행해준다.
class FooterActionButton: UIView {
/// 버튼 탭 이벤트
var tapHandler: (() -> Void)?
/// 트리플 탭 이벤트
var tripleTapHandler: (() -> Void)?
/// 클릭 카운트
private var tapCount = 0 {
didSet {
self.observeTapCount()
}
}
init(handler: (() -> Void)? = nil, tripleTapHandler: (() -> Void)? = nil) {
self.tapHandler = handler
self.tripleTapHandler = tripleTapHandler
super.init(frame: .zero)
}
// 세번 클릭하면 tripleTapEvent 호출
private func observeTapCount() {
if self.tapCount == 3 {
self.tripleTapHandler?()
}
}
@objc private func actionButtonTapped() {
self.tapHandler?()
self.tapCount += 1
}
}
옵셔널로 선언한 이유는 사용하는 쪽에서 handler 액션이 필요 없을 경우 구현하지 않기 위해서다. 필요한 경우에만 구현하도록 초기값을 nil로 선언했다. 그렇지 않다면 다른 변수처럼 반드시 handler를 구현해야 할 것이다.
그렇다면 사용하는 뷰 컨트롤러쪽의 코드는 다음과 같다.
class FirstViewController: UIViewController {
//MARK: - Properties
/// 👉지연 저장 프로퍼티를 이용해 변수 선언과 동시에 액션을 지정해줍니다.
lazy var footerButton = FooterActionButton(title: "계속하기", fontSize: 18, backgroundColor: .systemIndigo, style: .inside(inset: 16)) { [weak self] in
// 👇 여기서 버튼 클릭 액션을 정의합니다.
self?.someAction()
} tripleTapAction: {
// 👇 여기서 트리플 버튼 클릭 액션을 정의합니다.
print("버튼이 세번 눌렸어요🔔")
}
}
우리가 아까 만든 init함수에는 함수가 파라미터로 들어가기 때문에 FooterActionButton을 생성함과 동시에 클로저로써 함수를 넣어줄 수 있다. 이렇게 하면 클로저로 넘겨준 함수가 FooterActionButton의 클래스 변수인 handler에 들어가고, 버튼 클릭을 할 때 실행이 된다.
4. 이렇게도 쓸 수 있다.
예제의 SecondViewController에서는 초기값으로 지정을 하는 것이 아닌
setupButton 함수 내에서 footerButton의 프로퍼티에 접근하여 함수를 클로저로 넘겨주는 방법으로도 사용할 수 있다.
class SecondViewController: UIViewController {
//MARK: - Properties
let footerButton = FooterActionButton(title: "⭐️눌러봐요", fontSize: 18, backgroundColor: .systemBlue, style: .fill)
/// 👉버튼 내부의 변수에 접근하여 각 이벤트에서 호출될 함수를 정의해줍니다.
private func setupButton() {
// 버튼 탭 이벤트 정의
self.footerButton.tapHandler = {
print("버튼이 눌렸어요")
}
// 세번 눌림 액션 정의
self.footerButton.tripleTapHandler = { [weak self] in
self?.tripleButtonTapEvent()
}
}
}
5. 결론
간단한 UI에서 어떻게 함수형 프로그래밍을 사용하는지 정리를 해보았다.
delegate와 클로저를 활용한 방법을 모두 구현하며 비교해보았는데, 클로저를 사용한 방식이 더욱 간결하게 구현할 수 있었다.
다만, 두가지 방법 중 어떤 방식이 좋고 나쁨이 아니고 상황에 따라 구현 방식이 다를 것이다.
예제는 간단한 UI와 기능이기에 클로저가 더 간단하게 보였지만,
객체간에 전달할 이벤트가 많다면 delegate로 정리를 하는게 더욱 좋을 수 있다.
소스코드: https://github.com/jisu15-kim/examples/tree/main/FunctionUseCase
'iOS' 카테고리의 다른 글
[iOS] FLO 앱 만들기(1) - AVFoundation 개요 / 앱 구조 설계 (1) | 2023.10.14 |
---|---|
[iOS] URL과 전화번호를 인식하는 Label View 만들기(TextView, dataDetectorTypes, LineBreakStrategy) (0) | 2023.10.13 |
[iOS] Push Notification으로 화면 전환하기 (0) | 2023.10.13 |
[iOS] 커스텀 팝업 선택 뷰 만들기 (0) | 2023.10.13 |
[iOS] 재사용 뷰 - 커스텀 테이블뷰와 컬렉션뷰 만들기(RxSwift 활용) (0) | 2023.10.13 |
- Total
- Today
- Yesterday
- watchOS
- SwiftUI
- easy cue
- flo
- AVFoundation
- retry
- 애플워치 데이터 전송
- DateFormatter
- demical
- open-api-generator
- locale
- avplayer
- KVO
- musicplayer
- TextField
- 회고
- 2024년
- Xcode15
- OAS
- 애플워치
- watch connectivity
- Swift
- openapi-generator
- swift날짜
- 소수점
- auth
- 토큰
- IOS
- keyboardtype
- Xcode
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |