티스토리 뷰

출처

https://www.raywenderlich.com/163857/whats-new-swift-4




Api Changes

String

더 이상 characters array 를 사용할 필요는 없습니다.
direct 로 iterate 를 돌 수 있습니다.


var str = "Hello🐮"

for char in str {

    print(char)

}




direct 로 돌 수 있듯이, 

Sequence, Collection 의 기능들을 모두 사용할 수 있습니다.



let count = str.count

let isEmpty = str.isEmpty

let elloString = str.dropFirst()

let lloString = str.dropFirst(2)

let reverseString = String(str.reversed())


let filterString = str.filter { (char) -> Bool in

    let isAscii = char.unicodeScalars.reduce(true, { (result, scalar) -> Bool in

        return result && scalar.isASCII

    })

    return isAscii

}




Swift 4 에서는 String 의 subsequence 로 Substring type 이 제공됩니다. 

StringProtocol 이 추가되습니다.

String, SubString 은 String Protocol 을 따르고 다양한 기능을 제공합니다.



let endIndex = str.index(str.startIndex, offsetBy: 4)

var helloSubString = str[str.startIndex...endIndex]

helloSubString += "🐮"

let helloWithEmoji = String(helloSubString)

let hasPrefix = helloWithEmoji.hasPrefix("He")

let hasSuffix = helloWithEmoji.hasSuffix("🐮")




String 이 이모티콘을 해석하는 방식이 변경되었습니다.

유니코드 때문에 다양한 code 들이 생성되었었는데요,

skin-tone 이 들어간 경우 4글자, 아니면 2글자의 unicode 로 해석되던 것이

모두 1글자로 해석되도록 변경되었습니다.



"👩‍💻".count // Now: 1, Before: 2

"👍🏽".count // Now: 1, Before: 2

"👨‍❤️‍💋‍👨".count // Now: 1, Before, 4






Dictionary and Set

Sequence Based Initialization

sequence key-value pair 로 Dictionary 를 만들 수 있습니다.



let nearestStarNames = ["Proxima Centauri", "Alpha Centauri A", "Alpha Centauri B", "Barnard's Star", "Wolf 359"]

let nearestStarDistances = [4.24, 4.37, 4.37, 5.96, 7.78]

let starDistanceDict = Dictionary(uniqueKeysWithValues: zip(nearestStarNames, nearestStarDistances))

// ["Wolf 359": 7.78, "Alpha Centauri B": 4.37, "Proxima Centauri": 4.24,

//"Alpha Centauri A": 4.37, "Barnard's Star": 5.96]




Duplicate Key Resolution

Array 에 duplicate 정보가 들어있을 때도 쉽게 key 값으로 변경하여 사용할 수 있습니다.



let intElement = repeatElement(1, count: favoriteStarVotes.count) //1, 1, 1, 1

let keysAndValues = zip(favoriteStarVotes, intElement)

let mergedKeysAndValues = Dictionary(keysAndValues, uniquingKeysWith: +)

// ["Barnard's Star": 1, "Alpha Centauri A": 2, "Wolf 359": 1]





Dictionary Mapping

Dictionary 의 mapValues 를 통해 value 들에 직접 접근에 수정할 수 있습니다.



let mappedCloseStars = closeStars.mapValues { "\($0)" }

mappedCloseStars // ["Proxima Centauri": "4.24", "Alpha Centauri A": "4.37", "Alpha Centauri B": "4.37"]




Dictionary Default Values

Dictionary 에서 key 값이 없을 때 처리를 우아하게 할 수 있습니다.
dictionary 로부터 값을 get, set 시 defaultValue 를 지정해 줄 수 있습니다.


//Get

let siriusDistance = mappedCloseStars["Wolf 359", default: "unknown"] // "unknown"


//Set

var starWordsCount: [String: Int] = [:]

for starName in nearestStarNames {

    let numWords = starName.split(separator: " ").count

    starWordsCount[starName, default: 0] += numWords

    // ["Wolf 359": 2, "Alpha Centauri B": 3, "Proxima Centauri": 2,

    //"Alpha Centauri A": 3, "Barnard's Star": 2]

}




Dictionary Grouping

특정 조건에 맞는 Dictionary 로 변경할 수 있습니다.


let starsByFirstLetter = Dictionary(grouping: nearestStarNames) { $0.first! }

// ["B": ["Barnard's Star"], "A": ["Alpha Centauri A", "Alpha Centauri B"],

//"W": ["Wolf 359"], "P": ["Proxima Centauri"]]




Reserving Capacity    

Collection 에서 reallocation 은 큰 비용을 차지하는데요, 

reserveCapacity(_:) 로 성능향상을 도모할 수 있습니다.


starWordsCount.capacity  // 6

starWordsCount.reserveCapacity(20) // reserves at _least_ 20 elements of capacity

starWordsCount.capacity // 24





Private Access Modifier

extension 을 지원하여 code 를 논리 그룹으로 묶기를 권장하는 swift 에서
fileprivate 만으로는 제한사항이 있었습니다.
이에, Swift 4 에서는 struct, class 등과 같은 타입과 해당 타입의 extension 간에
동일한 액세스 제어 범위를 공유합니다.


struct SpaceCraft {

    private let warpCode: String

    

    init(warpCode: String) {

        self.warpCode = warpCode

    }

}


extension SpaceCraft {

    func goToWarpSpeed(warpCode: String) {

        if warpCode == self.warpCode {

            // Error in Swift 3 unless warpCode is fileprivate

            print("Do it Scotty!")

        }

    }

}


let enterprise = SpaceCraft(warpCode: "KirkIsCool")

enterprise.goToWarpSpeed(warpCode: "KirkIsCool") // "Do it Scotty!"



위 변경으로 기존의 의도된 목적으로 fileprivate 를 사용할 수 있도록 변경되었습니다.



Api Additions

Archival and Serialization

기존 Swift 에서는 class 를 직렬화하려면 NSObject 를 상속받고, 
NSCoding 을 준수해야했습니다.
struct, enum 의 경우에도 다른 여러가지 방법들이 필요했는데요,
Swift 4 에서는 Codable protocol 로 이 문제를 해결합니다.


struct CuriosityLog: Codable {

    enum Discovery: String, Codable {

        case rock, water, martian

    }

    

    var sol: Int

    var discoveries: [Discovery]

}


// Create a log entry for Mars sol 42

let logSol42 = CuriosityLog(sol: 42, discoveries: [.rock, .rock, .rock, .rock])



모든 속성이 Codable 하면 프로토콜 구현은 컴파일러가 알아서 해줍니다.


Key-Value Coding

함수는 클로저이기 때문에 함수를 호출하지 않고 참조할 수 있었습니다.
지금까지 불가능했던 것은 객체의 프로퍼티에 실제로 액세스하지 않고 참조를 가지는 것이었는데요,
Swift 4 에서는 key path 를 이용하여 프로퍼티 참조를 가지고
인스턴스의 기본 값을 가져오거나 설정할 수 있습니다.


struct Lightsaber {

    enum Color {

        case blue, green, red

    }

    let color: Color

}


class ForceUser {

    var name: String

    var lightsaber: Lightsaber

    var master: ForceUser?

    

    init(name: String, lightsaber: Lightsaber, master: ForceUser? = nil) {

        self.name = name

        self.lightsaber = lightsaber

        self.master = master

    }

}


let sidious = ForceUser(name: "Darth Sidious", lightsaber: Lightsaber(color: .red))

let obiwan = ForceUser(name: "Obi-Wan Kenobi", lightsaber: Lightsaber(color: .blue))

let anakin = ForceUser(name: "Anakin Skywalker", lightsaber: Lightsaber(color: .blue), master: obiwan)


let nameKeyPath = \ForceUser.name

let obiwanName = obiwan[keyPath: nameKeyPath]  // "Obi-Wan Kenobi"

let anakinSaberColor = anakin[keyPath: \ForceUser.lightsaber.color]  // blue


let masterKeyPath = \ForceUser.master

let anakinMasterName = anakin[keyPath: masterKeyPath]?.name  // "Obi-Wan Kenobi"

anakin[keyPath: masterKeyPath] = sidious

anakin.master?.name // Darth Sidious





Multi-line String Literals

대부분의 언어가 지원했었던 기능인 multi-line string literal 기능을 지원합니다.


let star = "⭐️"

let introString = """

A long time ago in a galaxy far,

far away....


You can even dynamically add values

from properties: \(star)

"""




One-Sided Ranges

장황함을 줄이고 가독성을 높이기 위해 one-sided range 를 지원합니다.


var planets = ["Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune"]

let outsideAsteroidBelt = planets[4...] // Before: planets[4..<planets.endIndex]

let firstThree = planets[..<4]          // Before: planets[planets.startIndex..<4]




Infinite Sequence

start index 가 countable type 일 때 infinite sequence 를 사용할 수 있습니다.


var numberedPlanets = Array(zip(1..., planets))

print(numberedPlanets) // [(1, "Mercury"), (2, "Venus"), ..., (8, "Neptune")]


planets.append("Pluto")

numberedPlanets = Array(zip(1..., planets))

print(numberedPlanets) // [(1, "Mercury"), (2, "Venus"), ..., (9, "Pluto")]




Pattern matching

one-sided ranges 를 잘 사용할 수 있는 방법입니다.


func temperature(planetNumber: Int) {

    switch planetNumber {

    case ...2: // anything less than or equal to 2

        print("Too hot")

    case 4...: // anything greater than or equal to 4

        print("Too cold")

    default:

        print("Justtttt right")

    }

}


temperature(planetNumber: 3) // Earth





Generic Subscripts

subscript 를 더 잘 사용하기 위해 generic 을 지원합니다.


struct GenericDictionary<Key: Hashable, Value> {

    private var data: [Key: Value]

    

    init(data: [Key: Value]) {

        self.data = data

    }

    

    subscript<T>(key: Key) -> T? {

        return data[key] as? T

    }

}


var earthData = GenericDictionary(data: ["name": "Earth",

                                         "population": 7500000000,

                                         "moons": 1])


let name: String? = earthData["name"]

let population: Int? = earthData["population"]


extension GenericDictionary {

    subscript<Keys: Sequence>(keys: Keys) -> [Value] where Keys.Iterator.Element == Key {

        var values: [Value] = []

        for key in keys {

            if let value = data[key] {

                values.append(value)

            }

        }

        return values

    }

}


let nameAndMoons = earthData[["moons", "name"]]        // [1, "Earth"]

let nameAndMoons2 = earthData[Set(["moons", "name"])]  // [1, "Earth"]





Miscellaneous

MutableCollection.swapAt(_:_:)


func bubbleSort<T: Comparable>(_ array: [T]) -> [T] {

    var sortedArray = array

    for i in 0..<sortedArray.count - 1 {

        for j in 1..<sortedArray.count {

            if sortedArray[j-1] > sortedArray[j] {

                sortedArray.swapAt(j-1, j) // New MutableCollection method

            }

        }

    }

    return sortedArray

}


bubbleSort([4, 3, 2, 1, 0]) // [0, 1, 2, 3, 4]



Associated Type Constraints

associated types 를 where 절을 이용하여 제약할 수 있습니다.


protocol MyProtocol {

    associatedtype Element

    associatedtype SubSequence : Sequence where SubSequence.Iterator.Element == Iterator.Element

}



Class and Protocol Existential


protocol MyProtocol { }

class View { }

class ViewSubclass: View, MyProtocol { }


class MyClass {

    var delegate: (View & MyProtocol)?

}


let myClass = MyClass()

myClass.delegate = ViewSubclass()




NSNumber Bridging


let n = NSNumber(value: 999)

let v = n as? UInt8 // Swift 4: nil, Swift 3: 231














공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2024/04   »
1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30
글 보관함