[SwiftUI] Protocols

Junyoung Park·2022년 8월 22일
0

SwiftUI

목록 보기
43/136
post-thumbnail

How to use Protocols in Swift | Advanced Learning #15

Protocols

구현 목표

  • 프로토콜 지향 언어인 스위프트 언어의 특성 상 대부분의 디폴트 구조체, 클래스, 열거형 등이 프로토콜 및 익스텐션을 통해 효율적 코드로 구현되어 있음 → 변수, 함수 등을 통해 확장성 확보하기
  • 프로토콜을 사용하는 까닭

구현 태스크

  1. 프로토콜을 통한 특정 변수 전달의 확장성
  2. 프로토콜 다중 상속
  3. 프로토콜 익스텐션 사용

핵심 코드

protocol ButtonTextProtocol {
    var buttonText: String { get }
}

extension ButtonTextProtocol {
    func buttonPreesed2() {
        print("IT IS EXTENDED VERSION!")
    }
}

protocol ButtonPressedProtocol {
    func buttonPressed()
}

protocol ButtonDataSourceProtocol: ButtonTextProtocol, ButtonPressedProtocol {
}
  • ButtonTextProfocolButtonPressedProcotol은 각각 변수와 함수를 필요조건으로 삼고 있는 프로토콜
  • 해당 프로토콜을 상속하는 오브젝트(구조체, 클래스 등)는 필수적으로 해당 필요조건을 구체적으로 표현해야 함
  • ButtonTextProfocol, ButtonPressedProtocol을 모두 사용해야 하는 조건이라면 두 개를 모두 쓰거나 두 개를 상속한 ButtonDataSourceProtocol이라는 새로운 프로토콜을 생성 및 사용 가능
  • 프로토콜 또한 익스텐션을 사용 가능 → buttonPressed2() 함수는 프로토콜 내부에서 필요 조건으로 지정된 변수, 함수와 다르게 해당 프로토콜을 상속하는 모든 오브젝트에서 동일한 함수에 접근 및 사용 가능 → 해당 프로토콜을 상속하는 오브젝트 내에서 새로운 커스텀 함수 buttonPressed2()를 구현하는 것 역시 가능

소스 코드

import SwiftUI

protocol ColorThemeProtocol {
    // REQUIREMENTS
    var primary: Color { get }
    var secondary: Color { get }
    var tertiary: Color { get }
}

struct DefaultColorTheme: ColorThemeProtocol {
    let primary: Color = .white
    let secondary: Color = .blue
    let tertiary: Color = .gray
}

struct AlternativeColorTheme: ColorThemeProtocol {
    let primary: Color = .black
    
    let secondary: Color = .orange
    
    let tertiary: Color = .brown
    
}
  • 서로 다른 색을 가지고 있는 구조체
  • 프로토콜이 없다면 각 구조체 타입을 선언해야 함
protocol ButtonTextProtocol {
    var buttonText: String { get }
}

extension ButtonTextProtocol {
    func buttonPreesed2() {
        print("IT IS EXTENDED VERSION!")
    }
}

protocol ButtonPressedProtocol {
    func buttonPressed()
}

protocol ButtonDataSourceProtocol: ButtonTextProtocol, ButtonPressedProtocol {
}

class DefaultDataSource: ButtonDataSourceProtocol {
    var buttonText: String = "PROTOCOLS ARE AWESOME!"
    
    func buttonPressed() {
        print("BUTTON WAS PRESSED!")
    }
}

class AlternativeDataSource: ButtonDataSourceProtocol {
    var buttonText: String = "PROTOCOLS ARE LAME!"
    
    func buttonPressed() {
        print("BUTTON IS PRESSED VIA ALTERNATIVE ONE!")
    }
}
  • 두 개의 프로토콜을 상속한 하나의 프로토콜을 받고 있는 구조체
struct ProtocolsBootCamp: View {
    @State var colorTheme: ColorThemeProtocol
    @State var datasource: ButtonDataSourceProtocol
    var body: some View {
        ZStack {
            colorTheme.tertiary.ignoresSafeArea()
            
            VStack {
                Spacer()
                
                Text(datasource.buttonText)
                    .font(.headline)
                    .foregroundColor(colorTheme.secondary)
                    .padding()
                    .background(colorTheme.primary)
                    .cornerRadius(10)
                    .onTapGesture {
                        datasource.buttonPressed()
                        datasource.buttonPreesed2()
                    }
                Spacer()
                if datasource is DefaultDataSource {
                    Text("CURENT COLORTHEME IS DEFAULT")
                        .font(.headline)
                        .foregroundColor(.blue)
                        .padding()
                        .background()
                        .cornerRadius(10)
                    Text("CURENT DATASOURCE IS DEFAULT")
                        .font(.headline)
                        .foregroundColor(.blue)
                        .padding()
                        .background()
                        .cornerRadius(10)
                } else {
                    Text("CURENT COLORTHEME IS ALTERNATIVE")
                        .font(.headline)
                        .foregroundColor(.blue)
                        .padding()
                        .background()
                        .cornerRadius(10)
                    Text("CURRENT DATASOURCE IS ALTERNATIVE")
                        .font(.headline)
                        .foregroundColor(.pink)
                        .padding()
                        .background()
                        .cornerRadius(10)
                }
                Button {
                    if colorTheme is DefaultColorTheme {
                        colorTheme = AlternativeColorTheme()
                    } else {
                        colorTheme = DefaultColorTheme()
                    }
                    
                    if datasource is DefaultDataSource {
                        datasource = AlternativeDataSource()
                    } else {
                        datasource = DefaultDataSource()
                    }
                    datasource.buttonPressed()
                    datasource.buttonPreesed2()
                } label: {
                    Text("Change Button DataSource and Color theme!")
                        .font(.headline)
                        .fontWeight(.semibold)
                        .withDefaultButtonFormmating(.blue)
                        .padding(.horizontal, 10)
                }
                .withPressableStyle(0.9)
                Spacer()
            }
        }
    }
}
  • @State 변수로 외부에서 값을 받고 있는 colorTheme, datasource 변수는 구체적인 구조체 타입이 아니라 해당 구조체가 상속하고 있는 프로토콜로 정의 → 해당 프로토콜을 받고 있는 오브젝트라면 모두 변수에 대입 가능
  • 이니셜라이즈 이후에도 Default 구조체와 Alternative 구조체 간의 할당이 자유로운 까닭은 모두 동일한 프로토콜을 상속하고 있기 때문

구현 화면

  • datasource를 변경하는 버튼을 누르면 datasource의 구체적인 타입이 변경됨 → 해당 구조체 내 프로토콜 필요조건으로 선언된 buttonPressed()는 서로 다른 텍스트를 출력하는 함수라는 점을 알 수 있음 → 프로토콜의 확장성
profile
JUST DO IT

0개의 댓글