Programming/Swift(iOS)

Swift Enum - 1

홍열 2021. 4. 1. 20:53
728x90

지금부터는 swift에서 사용하는 enum타입에 대한 공부를 해볼까 합니다. 

 

swift에서의 enum의 쓰임새는 다양합니다. 

c와는 쓰임새가 정말 많이 달라서 처음에는 어렵게 느껴집니다. 

 

제가 보는 책에서 swift enum을 유용하게 사용할 수 있는 경우를 찾아봤습니다.

  1. 제한된 선택지를 주고 싶을때 
  2. 정해진 값 이외에는 입력을 받고 싶지 않을때 
  3. 예상된 입력값이 한정되어 있을 때 

-> enum은 하나를 선택하면 다른 하나를 선택 못하기때문에 Error 처리에서도 자주 등장합니다.

Swift에서 자주 사용하는 Optional도 Enum으로 구현되어 잇습니다. 

 

enum School {
	case primary
    case elementary
    case middle
    case high
    case university
}
// 변수를 생성할때 타입을 명시해줬기 때문에 값할당시에 School을 생략 가능합니다.
//var highestEducationLevel:School = School.middle
var highestEducationLevel:School = .middle

 

기본적인 예제를 설명하기 보다는 실전에서 사용하는 방법들에 대해서 적어볼까 합니다.

 

enum은 상호 베타적입니다. 

 

아래 예제에서 hasJoined과 hasLeft가 동시에 true로 설정될 수 있습니다. 

struct Message {
    let userId:Int
    let contents:String?
    let data:Date
    
    let hasJoined:Bool
    let hasLeft:Bool
}

let joinMessage = Message(userId: 1, contents: nil, data: Date(), hasJoined: true, hasLeft: false)
let textMessage = Message(userId: 1, contents: "Hello", data: Date(), hasJoined: false, hasLeft: false)
let leftMessage = Message(userId: 1, contents: nil, data: Date(), hasJoined: false, hasLeft: true)

//문제점: 잘못된 상태의 객체가 만들어질 수 있습니다.
//join과 left가 둘 다 true인 경우가 있다.
let wrongMessage = Message(userId: 1, contents: "Hi", data: Date(), hasJoined: true, hasLeft: true)

 

 

위 부분을 enum으로 변환해보겠습니다. 

참고로 enum의 case에는 tuple이 들어갈 수 있습니다.

enum Message {
    case text(userId:Int, content:String, date:Date)
    case join(userId:Int, date:Date)
    case leave(userId:Int, date:Date)
}

//타입 추론이 가능하므로 Message.join이 아니라 .join만 사용해도 된다. 

let joinMessage:Message = .join(userId:1, date:Date())
let textMessage:Message = .text(userId:1, content:"Hello", date:Date())
let leaveMessage:Message = .leave(userId:1, date:Date())


// 넘겨진 enum값에 따라서 출력을 해주는 부분

func logMessage(message:Message) {
    #if false
    switch message {
    case .text(userId: let userId, content: let contents, date:let date):
        print(userId, contents, date)
        break
    case .join(userId:let userId, date: let date):
        print(userId, date)
        break
    case .leave(userId:let userId, date:let date):
        print(userId, date)
        break
    }
    #endif
    #if false
    switch message {
    case .text(let userId, let content, let date):
        print(userId, content, date)
        break
    case .join(let userId, let date):
        print(userId, date)
        break
    case .leave(let userId, let date):
        print(userId, date)
        break
    }
    #endif
    #if false
    switch message {
    case let .text(userId, contents, date):
        print(userId, contents, date)
        break
    case let .join(userId, date):
        print(userId, date)
        break
    case let .leave(userId, date):
        print(userId, date)
        break
    }
    #endif 
}
logMessage(message:joinMessage)
logMessage(message:leaveMessage)
logMessage(message:textMessage)

enum의 case로 test, join, leave을 나타낼 수 있어 확실하게 구분이 가능합니다. 

 

logMessage함수를 보면 전달받은 message를 switch로 분기하면서 enum case에 대해서 처리를 합니다. 

let에 대한 처리를 어디서 하느냐에 따라서 좀 더 축약형이 될 수가 있습니다.

 

그렇다면  단일 항목에 대한 처리는 어떻게 할까요?

아래와 같이 textMessage를 가져와서 case에 대한 단일 항목 처리를 하면 됩니다.

if case .text(userId:let userId, content:let contents, date:let date) = textMessage {
    print(userId, contents, date)
}

if case .join(let userId, let date) = joinMessage {
    print(userId, date)
}

if case let .leave(userId, date) = leaveMessage {
    print(userId, date)
}