본문 바로가기

Swift/Deep dive!!

[Swift] CustomStringConvertible Protocol 파해치기

 특정 type을 textual한 표현으로 커스텀해주는 protocol입니다. type이란? 내가 만든 커스텀의 구조체, 클래스, 클래스 등의 타입을 의미합니다. 특정 object를 print(_:)로 출력할 때, CustomStringConvertible protocol을 채택 후 내부 instance인 description을 커스텀 했다면 String(describing:)을 통해 어떤 type이든 string으로 변환시켜줍니다. 주의할 점은 이 프로토콜을 채택한 후에 프로토콜의 instance를 바로 쓰거나 일반 제약조건로 쓰는 것은 비 추천 한다고 명시되어 있습니다.

 

Why use CustomStringConvertible? Let's Deep dive.

struct World {
    let population: String
    let countries: [Country]
}

 

 전 세계는 많은 사람들이 살고 있고 여러 나라가 있습니다. 

struct Country {
    let name: String
    let population: String
    let region: WorldRegion
}

 나라별로 국가명, 인구수, region 정보를 갖고 있습니다.

enum WorldRegion {
    case Asia
    case Africa
    case America
    case Europe
    case Oceania
    
    var description: String {
        switch self {
        case .Asia: return "Asia"
        case .Africa: return "Africa"
        case .America: return "America"
        case .Europe: return "Europe"
        case .Oceania: return "Oceania"
        }
    }
}

 world region은 5개 region으로 구성되어 있습니다. 이때 각각의 enum case에한 description을 String타입으로 반환할 수 있습니다.

let countries = [
    Country(name: "Korea", population: "51,450,829", WorldRegion: .Asia),
    Country(name: "USA", population: "338,289,857", WorldRegion: .America),
    Country(name: "France", population: "61,234,000", WorldRegion: .Europe),
]

let world = World(population: "8,008,042,200", countries: countries)
print(world)

 

 구조체, 클래스에서 사용자가 커스텀해서 함수 등을 통해 내부 인스턴스를 출력하지 않을 경우 시스템에서 지정된 형식에 의해 출력됩니다.  내부 클래스를 갖지 않고 상속을 받지 않는 단일 구조체, 클래스의 경우 구조체 이름(변수명: ...)의 형식으로 출력이 됩니다. 하지만 위와 같은 경우 World 구조체를 인스턴스로 생성한 후 출력을 한다면 아래와 같은 결과가 출력됩니다.

 

 

 World 객체는 디폴트 형식으로 출력이 되었습니다. World객체는 내부 객체인 Countries를 인스턴스로 갖고 있습니다. 이때 World의 internal name __lldb_expr_3.Country에서 __lldb_expr_3은 Swift modul's 이름입니다(Not error). 모든 클래스는 Swift module에 속해 있습니다. 특정 객체의 내부 인스턴스 이름은 ModuleName.객체명 으로 지정됩니다. ( 플레이그라운드일 경우. 앱의 경우는 AppName.Object명)

Adopt CustomStringConvertible protocol in each struct!

 World의 내부 인스턴스를 어떤 형식에 맞게 print() or dump()로 출력 하도록 구현하지 않았기 때문입니다. 지정된 "Swift module명.객체 이름"이 출력됩니다. 보기 편하게 바꾸는 방법은 CustomStringConvertible 프로토콜을 채택하는 것입니다. description을 추가하고 원하는 출력 형식에 맞게 String 반환하도록 구현하는 방법이 있습니다. ( 저는 간단하게 프로퍼티들만 출력을 했습니다.)

extension World: CustomStringConvertible {
    var description: String {
        get {
            return "\(population) countries:\(countries)"
        }
    }
}
extension Country: CustomStringConvertible {
    var description: String {
        get {
            return "\n\(name), \(population), \(region.description)"
        }
    }
}

 각각 클래스에 CustomStringConvertible프로토콜을 채택하고 내가 원하는 출력 형식에 맞는 코드를 var description을 통해 선언하면 모듈 이름 출력이 아니라 보기 편하고 쉽게 출력할 수 있습니다. 

 

짜잔!!

How to implement more concise code?

 위에서 더 간결하게 코드를 구성하는 방법은 WorldRegion enum 또한 CustomStringConvertible 프로토콜을 채택하는 것입니다.

 

 물론 WorldRegion의 타입을 String으로 지정하는 방법도 있겠지만 CustomStringConvertible 프로토콜을 채택하면  간결하게! 사용할 수 있다는 장점이 있습니다.

 

긴 글 읽어주셔서 감사합니다 :)