Swift 제네릭으로 재사용 가능한 코드 만들기
제네릭 함수와 타입, 타입 제약을 이용해 타입 안전성을 유지하면서 중복을 줄이는 방법을 정리합니다.

타입만 다르고 하는 일은 같은 함수를 여러 번 만들기 시작하면 구현보다 중복이 먼저 눈에 들어온다. 제네릭은 구체적인 타입을 잠시 미뤄 두고 공통 동작을 한 번만 작성하게 해준다.
Generic Fuction
함수를 선언할 때 제네릭을 사용하여 타입에 의존하지 않는 범용 코드를 작성할 수 있다. <T>와 같은 형태의 타입 매개변수(type parameter)를 사용하여 임의의 타입 T를 정의할 수 있다.
var a = 2
var b = 3
var apple = "apple"
var banana = "banana"
func swapElements<T>(_ first: inout T, _ second: inout T) {
let temp = first
first = second
second = temp
}
swapElements(&a, &b) // Int 타입
swapElements(&apple, &banana) // String 타입
Comma(,)를 통해 여러 개의 타입을 선언할 수도 있다.
func someFunction<One, Two>(_ first: One, _ second: Two) { ... }
Generic Type
함수 이외에도 구조체, 클래스, 열거형 타입에 대한 제네릭 타입을 선언할 수 있다.
struct Stack<Element> {
var items = [Element]()
mutating func push(_ item: Element) {
items.append(item)
}
mutating func pop() -> Element {
return items.removeLast()
}
}
제너릭 타입에 대한 인스턴스를 생성할 때 어떤 타입으로 사용할 것인지 명시해야 한다.
let stack1: Stack<Int> = .init() // Int 타입에 대한 인스턴스
let stack2 = Stack<String>.init() // String 타입에 대한 인스턴스
Extending a Generic Type
익스텐션을 이용해 제네릭 타입을 확장할 수 있는데, 이때 기존에 선언한 타입 파라미터를 사용해야 한다.
extension Array {
mutating func pop() -> Element {
return self.removeLast()
}
}
Type Constraints
제네릭에서 특정 클래스를 상속하거나 특정 프로토콜을 따르도록 제한을 둘 수 있다.
func someFunction<T: SomeClass, U: SomeProtocol>(someT: T, someU: U) { ... }