Cannot assign to property: 'self' is immutable

→ 스유에서는 뷰를 구조체로 구성하기 때문에, 값을 변경하기 위해서는 @State 키워드를 사용한다.

struct User {
    var name = "고래밥"
		// 값을 수정하기 위해서는 mutating 키워드 필요.
    mutating func changeNickName() {
        name = "칙촉"
    }
    
    var introduce: String {
        mutating get {
            name = "스파이더맨"
            return "안녕하세요 \\(name)입니다. "
        }
    }
}
joined(separator:)

//Returns a new string by concatenating the elements of the sequence, 
//adding the given separator between each element.
//어레이의 각 요소를 separator로 구분해 하나의 스트링으로 리턴
func joined(separator: String = "" ) -> String
import UIKit

protocol Shape {
    func draw() -> String
}

struct Triangle: Shape {
    var size: Int
    func draw() -> String {
        
        var result: [String] = []
        for length in 1...size {
            let star = String(repeating: "*", count: length)
            result.append(star)
        }
        return result.joined(separator: "\\n")
    }
}

var triangle = Triangle(size: 10)
print(triangle.draw())

struct FlippedShape<T: Shape>: Shape {
    
    var shape: T
    func draw() -> String {
        let lines = shape.draw().split(separator: "\\n")
        return lines.reversed().joined(separator: "\\n")
    }
} 

var flippedTriangle =  FlippedShape(shape: triangle)
print(flippedTriangle.draw())

//struct JoinedTriangle<T: Shape>: Shape {
//    var top: T
//    var bottom: T
//    
//    func draw() -> String {
//        let result = top.draw() + "\\n" + bottom.draw()
//        return result
//    }
//}

//let joinedTriangle = JoinedTriangle(top: triangle, bottom: flipTriangle)

//제네릭을 사용해 타입을 결정하면, 호출 부에서 타입이 결정되는데 이때, top과 bottom은 다른 타입이기 때문에
//T: shape으로 동시 구성해놓은 JoinedTriangle은 오류가 발생한다 

struct JoinedShape<T: Shape, U: Shape>: Shape {
    var top: T
    var bottom: U
    
    func draw() -> String {
        let result = top.draw() + "\\n" + bottom.draw()
        return result
    }
}

let joinedTriangle = JoinedShape(top: triangle, bottom: flippedTriangle)
print(joinedTriangle.draw())
//함수 호출 부에 타입이 결정된다. -> 타입 유출로 이어짐

struct Square: Shape {
    var size: Int
    func draw() -> String {
        let line = String(repeating: "*", count: size)
        let result = Array<String>(repeating: line, count: size)
        return result.joined(separator: "\\n")
    }
}

//불필요한 타입 유출을 제거
//유연성 및 확장성 증가, 종속성 감소, 리팩토링 용이성
//내부 구현 변경이 외부 인터페이스에 영향을 주지 않는다.
func makeTrapezoid() ->  some Shape {
    let top = Triangle(size: 2)
    let middle = Square(size: 2)
    let bottom = FlippedShape(shape: top)
    let trapezoid = JoinedShape(
        top: top,
        bottom: JoinedShape(top: middle, bottom: bottom)
    )
    return trapezoid
}

let trapezoid = makeTrapezoid()
print(trapezoid.draw())

struct VerticalShapes: Shape {
    var shapes: [any Shape]
    func draw() -> String {
        return shapes.map { $0.draw() }.joined(separator: "\\n\\n")
    }
}

let largeTriangle = Triangle(size: 5)
let largeSquare = Square(size: 5)
let vertical = VerticalShapes(shapes: [largeTriangle, largeSquare])
print(vertical.draw())

값 타입에 대한 정보 은닉화


Opaque Typesu

타입 유출을 방지하면서 얻는 장점

GenericOpaque Types 모두 공통적으로 타입에 대한 유연성을 확보할 수 있다. 두 키워드 모두 컴파일 시점에 타입이 결정되지만, Generic의 경우 타입이 선언자 외부로 유출되는 반면 Opaque Types의 경우 타입이 선언자 외부로 유출되지 않는다.

즉, 이를 통해 좀 더 높은 추상화 수준을 확보할 수 있게 된다. 따라서 내부 구현의 변경이 발생해도 특정 프로토콜만 준수할 경우 그 어떠한 인터페이스에 대해서도 대응이 가능해진다.