# 중첩타입 개념
타입 내부에 타입을 선언하는 것을 중첩타입이라고 한다. 중첩 타입의 선언은 언제나 가능하다.
class Aclass {
    struct Bstruct {
        var name: Cenum
    }
}
// 타입 선언과 인스턴스의 생성
let aClass: Aclass = Aclass()
// 멤버와이즈 이니셜라이저 호출
let bStruct: Aclass.Bstruct = Aclass.Bstruct(name: .bCase)
중첩타입을 사용하는 이유는 아래와 같다.
- 타입간의 연관성에 따라 특정 타입 내에서만 사용하기 위해서
- 타입간의 연관성에 따라 내부 구조를 디테일하게 설계
struct BlackjackCard {
    // Suit(세트) 열거형 - 중첩
    enum Suit: Character {     // 원시값(rawValue)사용
        case spades = "♠", hearts = "♡", diamonds = "♢", clubs = "♣"
    }
    // 순서(숫자) 열거형 - 중첩
    enum Rank: Int {     // 원시값(rawValue)사용
        case two = 2, three, four, five, six, seven, eight, nine, ten
        case jack, queen, king, ace   // (원시값 존재하지만 사용하지 않고자 함 ===> values)
        // Values 타입정의 (두개의 값을 사용) //===> 열거형 값(순서)을 이용 새로운 타입을 반환하기 위함
        // 기본 지정 생성자 제공
        struct Values {
            // 에이스 카드의 두 가지 역할로 인해 second라는 속성이 생긴것
            let first: Int, second: Int?
        }
        // (읽기) 계산 속성 (열거형 내부에 저장 속성은 선언 불가)
        var values: Values {
            switch self {
            case Rank.ace:
                return Values(first: 1, second: 11)    // 에이스 카드는 1 또는 11 로 쓰임
            case .jack, .queen, .king:
                return Values(first: 10, second: nil)  // 10으로 쓰임
            default:
                return Values(first: self.rawValue, second: nil)    // 2 ~ 10까지의 카드는 원시값으로 쓰임
            }
        }
    }
    // 블랙잭 카드 속성 / 메서드  =======================================
    // 어떤 카드도, 순서(숫자)와 세트(Suit)를 가짐
    let rank: Rank, suit: Suit
    // (읽기) 계산속성
    var description: String {
        get {
            var output = "\(suit.rawValue) 세트,"
            output += " 숫자 \(rank.values.first)"
            if let second = rank.values.second {   // 두번째 값이 있다면 (ace만 있음)
                output += " 또는 \(second)"
            }
            return output
        }
    }
}
개발 단계에서 좀 더 시맨틱한 개발을 위해 사용한다고 보면 된다. 아래 두 코드를 비교해보자
enum Style {
    case full
    case long
    case medium
    case none
}
struct DateFormatters {
    var style: Style
}
var dateStyle1 = DateFormatters(style: .full)
dateStyle1 = DateFormatters(style: Style.full)
dateStyle1.style = Style.full
dateStyle1.style = .full
위 코드는 Style 열거형을 DateFormatters 구조체로부터 분리하여 코드를 작성하였다. 이렇게 되면 변수 선언과 타입 작성에는 더 편하지만 dateStyle1.style = Style.full의 경우 어떤 스타일에 대한 이야기인지 명확하게 파악하기가 어렵다.
아래 코드를 다시 봐보자.
struct DateFormatters {
    var style: DateFormatters.Style
    // 중첩타입으로 선언
    enum Style {
        case full
        case long
        case medium
        case none
    }
}
var dateStyle2 = DateFormatters(style: .full)
dateStyle2 = DateFormatters(style: DateFormatters.Style.full)
dateStyle2.style = DateFormatters.Style.full
dateStyle2.style = .long
세 번째 코드의 경우 dateStyle2.style = DateFormatters.Style.full를 보면 타입에 대해 명확히 지정해줌으로써 dateStyle2라는 객체 내의 style 속성이 어떤 타입과 연관되어 있는 것인지 명확하게 파악이 가능하다.
중첩타입 파악하기
- DateFormatter.Style.full과 같이 중간에 대문자로 시작하는 접근 연산이 나타나면 중첩타입임을 알아채자
- 실제 앱을 제작할때 중첩 타입을 활용하여 타입 간의 연관성을 명확하게 하자.
# 예시
struct K {
    static let appName = "MySuperApp"
    static let cellIdentifier = "ReusableCell"
    static let cellNibName = "MessageCell"
    static let registerSegue = "RegisterToChat"
    static let loginSegue = "LoginToChat"
    struct BrandColors {
        static let purple = "BrandPurple"
        static let lightPurple = "BrandLightPurple"
        static let blue = "BrandBlue"
        static let lighBlue = "BrandLightBlue"
    }
    struct FStore {
        static let collectionName = "messages"
        static let senderField = "sender"
        static let bodyField = "body"
        static let dateField = "date"
    }
}
// 문자열 대신에 아래처럼 사용할 수 있음 ⭐️ (문자열 실수는 치명적인 에러를 발생시킴)
let app = K.appName      // "MySuperApp"
let color = K.BrandColors.blue    // "BrandLightBlue"
각종 데이터 상수화를 위해 중첩 타입을 사용한다.
아래 코드는 메세지 앱 제작 모델에 관련된 예시 코드이다. 지나치지 말고 복습할때 다시 읽어보기
class Message {
    // 상태를 중첩타입으로 (외부에서 접근못하게 하려면, private으로 선언가능)
    private enum Status {
        case sent
        case received
        case read
    }
    // 보낸 사람, 받는 사람
    let sender: String, recipient: String, content: String
    // 보낸 시간
    let timeStamp: Date
    // 메세지 상태 정보 (보냄/받음/읽음)
    private var status: Message.Status = Message.Status.sent
    init(sender: String, recipient: String, content: String) {
        self.sender = sender
        self.recipient = recipient
        self.content = content
        self.timeStamp = Date()   // 현재시간 생성 ===> 시간은 유저가 주는 정보 아님
    }
    func getBasicInfo() -> String {
        return "보낸사람: \(sender), 받는사람: \(recipient), 메세지 내용: \(content), 보낸 시간: \(timeStamp.description), "
    }
    // 메세지 상태에 따라서, 색깔 바뀜
    func statusColor() -> UIColor {
        switch status {
        case .sent:
            return UIColor(red: 1, green: 0, blue: 0, alpha: 1)
        case .received:
            return UIColor(red: 0, green: 0, blue: 1, alpha: 1)
        case .read:
            return UIColor(red: 0, green: 1, blue: 1, alpha: 1)
        }
    }
}
let message1 = Message(sender: "홍길동", recipient: "임꺽정", content: "뭐해?")
print(message1.getBasicInfo())
sleep(1)
let message2 = Message(sender: "임꺽정", recipient: "홍길동", content: "그냥있어")
print(message2.getBasicInfo())