티스토리 뷰

swift/문법

21. Swift 문법 - Extensions

DevBee 2020. 10. 26. 09:49

1. Extension - Syntax

Extension 은 확장이라는 의미를 가지고 있으며 형식을 확장하는 데 사용됩니다. 형식을 확장한다는 것은 속성, 메소드, 생성자와 같은 멤버들을 형식에 추가하는 것입니다. Extension 으로 확장할 수 있는 대상은 Class, Structure, Enumeration, Protocol 입니다.

 

Extension 은 형식 선언에 새로운 멤버를 추가하는 것은 아닙니다. 새로운 멤버는 별도의 코드 조각으로 구현하고 형식과 연관 시켜서 기존 멤버와 함께 사용하는 것입니다. 형식 선언이 포함되어 있는 코드를 수정할 수 없는 경우에도 문제없이 확장할 수 있습니다.

 

Int 나 String 같은 기본 자료형은 실제로 구조체로 구현되어 있습니다. Apple 이 제공하는 라이브러리를 직접 수정할 수는 없지만, extension 을 통해 기존 자료형에 새로운 멤버를 추가하는 것은 가능합니다. extension 으로 멤버를 추가하는 것은 가능하지만 기존 멤버를 overriding 하는 것은 불가능합니다. 만약 overriding 이 필요하다면 상속을 통해 subclassing 이 필요합니다.

 

문법을 살펴보면 다음과 같습니다.

extension Type {
    computedProperty
    computedTypeProperty
    instanceMethod
    typeMethod
    initializer
    supscript
    NestedType
}

// 기존 형식에 프로토콜 구현을 추가할 때 사용하는 문법
extension Type: Protocol, ... {
    requirements
}

 

간단한 구조체를 구현한 뒤 해당 구조체를 확장하는 코드를 살펴보겠습니다.

struct Size {
    var width = 0.0
    var height = 0.0
}

// 계산 속성을 확장
extension Size {
    var area: Double {
        return width * height
    }
}

let s = Size()
s.width
s.height
s.area  // 구조체 선언에서 구현한 속성과 동일하게 접근이 가능합니다.

// Equatable protocol 을 준수하도록 확장 (추후 protocol 관련 글에서 자세히 살펴보겠습니다.)
extension Size: Equatable {
    public static func == (lhs: Size, rhs: Size) -> Bool {
        return lhs.width == rhs.width && lhs.height == rhs.height
    }
}

 

2. Adding Properties

extension 으로 추가할 수 있는 속성은 계산 속성으로 제한됩니다. 저장 속성을 추가하거나 property observer 를 추가하는 것은 불가능합니다. 그리고 형식에 존재하는 속성을 overriding 하는 것도 불가능합니다.

 

Date 형식에 year, month 계산 속성을 추가하여 Date 인스턴스의 년, 월을 반환하도록 확장해보겠습니다.

extension Date {
    var year: Int {
        let cal = Calendar.current
        return cal.component(.year, from: self)
    }
    
    var month: Int {
        let cal = Calendar.current
        return cal.component(.month, from: self)
    }
}

let today = Date()
today.year
today.month

 

Double 형식에 radian, degree 계산 속성을 추가해보겠습니다.

extension Double {
    var radianValue: Double {
        return (Double.pi * self) / 180.0
    }
    
    var degreeValue: Double {
        return self * 180.0 / Double.pi
    }
}

let dv = 45.0
dv.radianValue
dv.radianValue.degreeValue

 

3. Adding Methods

Double 형식에 섭씨 값을 화씨로 화씨 값을 섭씨로 변환하는 메소드를 추가해보겠습니다.

extension Double {
	// 인스턴스 메소드 확장
    func toFahrenheit() -> Double {
        return self * 9 / 5 + 32
    }
    
    func toCelsius() -> Double {
        return (self - 32) * 5 / 9
    }
    
    // 타입 메소드 확장
    static func converToFahrenheit(from celsius: Double) -> Double {
        return celsius.toFahrenheit()
    }
    
    static func converToCelsius(from fahrenhsit: Double) -> Double {
        return fahrenhsit.toCelsius()
    }
}

let c = 30.0
c.toFahrenheit()

Double.converToFahrenheit(from: 30.0)

 

원하는 날짜 포맷으로 Date 인스턴스의 날짜를 리턴하는 메소드를 추가해보겠습니다.

extension Date {
    func toString(format: String = "yyyyMMdd") -> String {
        let privateFormatter = DateFormatter()
        privateFormatter.dateFormat = format
        return privateFormatter.string(from: self)
    }
}

today.toString()  // 20201026
today.toString(format: "MM/dd/yyyy") // 10/26/2020

 

원하는 길이의 랜덤 문자열을 반환하는 메소드를 추가해보겠습니다.

extension String {
    static func random(length: Int, charactersIn chars: String = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789") -> String {
        var randomString = String()
        randomString.reserveCapacity(length)
        
        for _ in 0 ..< length {
            guard let char = chars.randomElement() else {
                continue
            }
            
            randomString.append(char)
        }
        
        return randomString
    }
}

String.random(length: 5)

 

4. Adding Initializer

년, 월, 일을 파라미터로 받아 Date 인스턴스를 생성하도록 생성자를 확장해 보겠습니다.

extension Date {
    init?(year: Int, month: Int, day: Int) {
        let cal = Calendar.current
        var comp = DateComponents()
        comp.year = year
        comp.month = month
        comp.day = day
        
        guard let date = cal.date(from: comp) else {
            return nil
        }
        
        self = date
    }
}

Date(year: 1995, month: 10, day: 5)

 

RGB 값을 0부터 255 사이의 숫자 파라미터로 입력 받아 UIColor 인스턴스를 생성하도록 생성자를 확장해 보겠습니다.

extension UIColor {
    convenience init(red: Int, green: Int, blue: Int) {
        self.init(red: CGFloat(red) / 255, green: CGFloat(green) / 255, blue: CGFloat(blue) / 255, alpha: 1.0)
    }
}

UIColor(red: 0, green: 0, blue: 255)

 

구조체의 경우 extension 을 통해 생성자를 추가하면 기존에 자동으로 제공되던 생성자와 함께 추가된 생성자를 같이 사용할 수 있습니다.

struct Size {
    var width = 0.0
    var height = 0.0
}

extension Size {
    init(value: Double) {
        width = value
        height = value
    }
}

Size()
Size(width: 12, height: 34)
Size(value: 12)

 

5. Adding Subscripts

문자열 기본 구현은 정수 인덱스를 subscript 로 전달할 수 없습니다. 반드시 String.Index 를 전달해야 합니다. 정수를 파라미터로 받는 subscript 를 형식에 추가하고 해당 위치에 있는 문자를 리턴하도록 구현해보겠습니다.

extension String {
    subscript(idx: Int) -> String? {
        guard (0 ..< count).contains(idx) else {
            return nil
        }
        
        let target = index(startIndex, offsetBy: idx)
        return String(self[target])  // 형식에 구현되어 있는 다른 subscript 를 호출할 때는 self 를 사용합니다.
    }
}

let str = "Swift"
str[1]    // "w"
str[100]  // nil

 

Calendar.Component 형식의 파라미터를 입력 받아 Date 인스턴스의 Calendar.Component 에 해당하는 값을 리턴하는 subscript 를 구현해보겠습니다.

extension Date {
    subscript(component: Calendar.Component) -> Int? {
        let cal = Calendar.current
        return cal.component(component, from: self)
    }
}

today[.year]   // 2020
today[.month]  // 10
today[.day]    // 26
댓글
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/11   »
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
글 보관함