13: protocol and extensions

그루두·2024년 4월 22일
0

100 days of SwiftUI

목록 보기
20/108

100 days of swiftui: 13
https://www.hackingwithswift.com/100/swiftui/13

Protocol

protocol은 내가 이해하기론 규칙을 만족해야 하는 struct 같다.

예시로 commute() 함수가 있다.

func commute(distance: Int, using vehicle: Car) {
    if vehicle.estimateTime(for: distance) > 100 {
        print("That's too slow! I'll try a different vehicle.")
    } else {
        vehicle.travel(distance: distance)
    }
}

이 함수는 distance와 vehicle을 매개변수로 받는데, 만약 교통수단의 경우가 Car 말고도 Train, Bus 등도 사용할 수 있게 하려면 어떻게 해야 할까?

이럴 때 유용한 게 protocol이다. commute()를 실행하려면 estimateTime(for: distance) -> Int 함수를 활용할 수 있어야 한다. 이렇게 특정 속성을 만족할 수 있게 하기 위해 Vehicle protocol을 아래처럼 설정할 수 있다.

protocol Vehicle {
    var name: String { get }
    var currentPassengers: Int { get set }
    func estimateTime(for distance: Int) -> Int
    func travel(distance: Int)
}

protocol은 이름 앞에 protocol 키워드를 작성해야 하고, 안에는 최소한의 정의만 해야 한다. method의 경우 함수의 이름, 매개변수의 이름과 타입, 리턴 타입을 정의해야 하고, 멤버 변수나 상수의 경우는 이름과 타입, get 혹은 set 특징을 설정해야 한다. 그리고 get, set 특징을 둘 다 가지는 경우엔 { get set }으로 콤마 없이 작성해야 한다.
❗️ swift에서는 set only property를 만들 수 없다.

그리고 이 Vehicle을 만족하는 여러 struct를 만들 수 있다.

struct Car: Vehicle {
    var name: String
    var currentPassengers: Int
    func estimateTime(for distance: Int) -> Int {
        distance / 50
    }
    func travel(distance: Int) {
        print("I'm driving \(distance)km.")
    }
    func openSunroof() {
        print("It's a nice day!")
    }
}

struct Bicycle: Vehicle {
    var name: String
    var currentPassengers: Int
    func estimateTime(for distance: Int) -> Int {
        distance / 10
    }
    func travel(distance: Int) {
        print("I'm cycling \(distance)km.")
    }
}

Car, Bicycle은 필수로 get(상수이거나 변수일 수 있는) name, 변수인(get, set) currentPassengers, 메소드 estimateTime(for distance: Int) -> Int, travel(distance: Int)를 정의해야 한다.

코드 파일
https://github.com/soaringwave/Ios-studying/commit/6d7a3feb07eb9ce1922d26976b973b9d3b4c7d3e

opaque return types

https://www.hackingwithswift.com/quick-start/beginners/how-to-use-opaque-return-types

swift는 모든 데이터의 타입을 안다. 반대로 말하면 불분명한 데이터 타입은 용납하지 않는다. 그러니 만약 반환값의 타입이 예시로 Vehicle protocol을 만족하는 것 중 하나라고 설정할 수도 있다. 그럴 때 opaque return types를 이용한다고 이해했다.

반환값 의미
Vehicle: "any sort of Vehicle type but we don't know what"
some Vehicle: means "a specific sort of Vehicle type but we don't want to say which one.”

아주아주 흔한 예시로, swiftui를 이용해 화면의 ui를 설계한다. 이럴 땐 이 화면에 TextField, TabView, NavigationStack 등 View protocol을 만족하는 것을 반환할 것이라고 설정은 하지만 모두 적을 수는 없다. 그래서 some View를 사용한다.

extension

모든 데이터 타입에 대한 연장 설정이라고 이해했다.

예 1: String 타입에 trimmed() 메소드를 추가하기

extension String {
    func trimmed() -> String {
        self.trimmingCharacters(in: .whitespacesAndNewlines)
    }
}

var quote = "   The truth is rarely pure and never simple   "
let trimmed = quote.trimmingCharacters(in: .whitespacesAndNewlines)
let trimmedWithExtenstion = quote.trimmed()
print(trimmed)
print(trimmedWithExtenstion)

예 2: Book 타입에 init() 추가하기

struct Book {
    let title: String
    let pageCount: Int
    let readingHours: Int
}

extension Book {
    init(title: String, pageCount: Int) {
        self.title = title
        self.pageCount = pageCount
        self.readingHours = pageCount / 50
    }
}

var book1 = Book(title: "book 1", pageCount: 30, readingHours: 1)
var book2 = Book(title: "book 2", pageCount: 100)

전역 함수와 다른 extension의 장점:
1. 설정한 데이터 타입의 변수나 상수와 .을 입력하면, 해당 데이터 타입의 메소드에 포함되어 확인할 수 있다.
2. 코드 정리가 깔끔하다.
3. 해당 데이터 타입의 일원이기에 내부 데이터에 대한 접근이 가능하다.

추가 예시:

extension Bool {
	func toggled() -> Bool {
		if self == true {
			return false
		} else {
			return true
		}
	}
}

extension Int {
	func cubed() -> Int {
		return self * self * self
	}
}

extension String {
	var isLong: Bool {
		return count > 25
	}
}

코드 파일
https://github.com/soaringwave/Ios-studying/commit/574cc18a0fe94978ba89491c3acfeddac3f3cef7

protocol extension

모든 데이터 타입에 extension을 사용할 수 있듯이, protocol도 extension을 사용할 수 있다.

예 1: Collection protocol에 isNotEmpty 속성 추가

extension Collection {
    var isNotEmpty: Bool {
        isEmpty == false
    }
}

let guests = ["Mario", "Luigi", "Peach"]

if guests.isNotEmpty {
    print("Guest count: \(guests.count)")
}

예 2: Person protocol의 필수 메소드인 sayHello() 정의하기

protocol Person {
    var name: String { get }
    func sayHello()
}

extension Person {
    func sayHello() {
        print("Hi, I'm \(name)")
    }
}

struct Employee: Person {
    let name: String
}

let sam = Employee(name: "Sam Bla")
sam.sayHello()

이 경우엔 EmployeePerson protocol을 만족시켜야 하는데, protocol extension으로 sayHello()가 이미 설정되어 struct 내에서 정의하지 않아도 된다.

코드 파일
https://github.com/soaringwave/Ios-studying/commit/7cbf10d965999cbb7166469fa8a013cc96e1ff73

profile
계속 해보자

0개의 댓글

관련 채용 정보