Swift 5.7 Release(1)

유재경·2022년 6월 29일

iOS New Release

목록 보기
10/12

(※ hackingWithSwift의 글을 번역한 것으로 아래 출처를 남겨두었습니다. 약간의 오역이 있을 수 있으니 지적해주시면 감사드리겠습니다.)

if let shortened

if let을 이전보다 더 짧은 형태로 사용할 수 있게 되었다. '=' 표시없이 간결하게 사용할 수 있어서 훨씬 모던하게 느껴진다.

if let name = name { // 기존 if-let 사용법
	Text("name: \(name)")
}

var name: String? = "Linda"

if let name { // 짧아진 if let 사용법
	Text("name: \(name)")
}

closure내 return 타입 표시X

기존 closure내에 '->' 표시로 리턴 타입을 명시했어야하는데 이제는 명시하지 않아도 실질적 return 타입을 스마트하게 인식하는 것 같다.

let scores = [100, 60, 30]

let newResutls = scores.map { score in // 리턴 타입 명시X
	if let score > 85 {
    	return "Pass: \(score)"
    } else {
   		return "Fail: \(score)"
    }
}

let prevResults = scores.map { score -> String in
	if let score > 85 {
    	return "Pass: \(score)"
    } else {
   		return "Fail: \(score)"
    }
}

Clock, Instant, Duration

time(시각)과 duration(지속시간)에 대해 Swift가 새롭고, 표준화된 방법을 선보였다고 하는데..

  • Clock: 흘러가는 시간을 측정하기 위한 방법을 제공. 시스템이 비활성화(asleep)인 상태에서 continuous clock은 계속해서 시간을 증가시킨다. 반면, suspending clock은 시스템이 비활성화되면 시간이 멈추게 된다.
  • Instants: 정확한 시각을 제공
  • Duration: 두 Instant 사이의 경과를 제공

새롭게 업그레이드된 Task API에서 이를 적용하기 좋은데, 나노 초 단위보다 더 정밀하게 표현해주기 때문이다.

try await Task.sleep(until: .now() + .seconds(1), clock: .continuous)

또한, tolerance(성능을 극대화하기 위해 약간의 sleep deadline을 제공해줌)를 정확하게 표현하기도 좋다. 아래 코드를 보면, 적어도 1초 안에 sleep되는 것이 이상적이지만 0.5초 정도의 버퍼는 허용하는 것이다. (즉, 최대 1.5초 안에 sleep되기를 기대) tolerance를 사용할 때 명심할 것은 우리가 정의한 default sleep time에서 더 추가(+)되는 것을 의미한다.

try await Task.sleep(until: .now() + .seconds(1), tolerance: .seconds(0.5), clock: .continuous)

이렇게 되면, 근시일 내에 나노 초 단위가 없어질 것으로 보인다.

Clocks는 어떤 작업을 수행할 때 얼마나 걸렸는지 정확히 측정할 때 사용할 수 있는데, 예를 들면 사용자가 파일 '내보내기'할 때 얼마나 걸렸는지 등이 있다.

let clock = ContinuousClock()

let time = clock.measure {
	// complex work here
}

print("Takes \(time.component.second) seconds")

Regular Expression(정규 표현식)

정규 표현식에 대한 간단한 설명

(정규 표현식을 쓸 일이 아주 많지는 않지만, 알아두면 유용할 수 있으므로 어렵더라도 차근차근 알아보자!)

let message = "the cat sat on the mat"

// 평소에 익숙한 메소드
print(message.ranges(of: "at"))
print(message.replacing("cat", with: "dog"))
print(message.trimmingPrefix("the "))

// 정규 표현식에 대한 설명

// 소문자 a부터 z까지 at에 해당하는 range를 뽑아냄
print(message.ranges(of: /[a-z]at/))
// result: cat, sat, mat의 위치(인덱스) 추출

// 소문자 a부터 m까지 해당되는 at이 들어가는 단어를 dog로 바꿈
print(message.replacing(/a-m/at, with: "dog"))
// result: "the dog sat on the dog"

// The 혹은 the(대소문자 무시) 해당하는 단어를 trimmingPrefix
print(message.trimmingPrefix(/The/.ignoringCase()))

Regex 타입과 regex literal

위에서 /[a-z]at/(regex literal) 로 표시한 방식과 비슷하게, Regex 타입을 사용하여 표현할 수 있다.

do {
	let atSearch = try Regex("[a-z]at")
    print(message.ranges(of: atSearch))
} catch {
	print("fail to create Regex")
}

그러나, regex literal과 Regex의 큰 차이점은 정규 표현식을 사용할 때runtime에서 실행되는지, compiletime에서 실행되는지이다.

  • Regex: 실제 표현식을 알아내기 위해 runtime시간 내에 String을 parsing해야 함
  • regex literal: compile time에서 확인하는 것이 가능함. 즉, 정규식에 에러가 없다는 것과 정규식이 정확하게 포함하고 있는 것을 확인한다.

regex literal(/로 표기하기) 방식은 상당히 눈에 띄기 때문에, 반복을 견딘다. compiletime내에 유효성 검사를 하고, 정규 표현식을 parsing한다.

코드로 살펴보면,

let search1 = /My name is (.+?) and I'm (\d+) years old./
let greeting1 = "My name is Taylor and I'm 26 years old."

if let result = try search1.wholeMatch(in: greeting1) {
    print("Name: \(result.1)") // Name: Taylor
    print("Age: \(result.2)") // Age: 26
}

Swift는 Tuple방식으로 .1과 .2에 해당하는 것을 정확히 알고 리턴하게 된다. (궁금해할 경우를 대비하여, .0은 완전히 일치하는 String을 리턴한다.)

또한 정규 표현식을 사용할 때 명명할 수도 있는데, 바로 .1과 .2 라는 Tuple방식을 사용하는 것이 아닌 구체적인 이름으로 대신한다. (개인적으로 .1과 .2로 표현되는 Tuple방식이 깔끔해보일지 몰라도 웬만해선 naming을 사용해 구체적으로 명시하는 것이 좋다고 생각한다.)

let search2 = /My name is (?<name>.+?)/ and I'm (?<age>\d+) years old./
let greeting2 = "My name is Taylor and I'm 26 years old."

if let result = try search2.wholeMatch(in: greeting2) {
	print("Name: \(result.name)")
    print("Age: \(result.age)")
}

DSL(Domain-Specific-Language)

앞서 정규 표현식을 string으로 표현하는 법, regex literal로 표현하는 법을 알아봤는데 Swift는 한 발 더 나아가서, SwiftUI 코드와 유사하게 DSL(도메인 특화 언어,특정 도메인에만 국한되어 있는 언어)로도 정규 표현식을 만들 수 있다.

예를 들어, “My name is Taylor and I’m 26 years old” 와 동일시하기 위해 다음과 같이 코드를 쓸 수 있다.

let search3 = Regex {
	"My name is"
    
    Capture {
    	OneOrMore(.word)
    }
    
    " and I'm"
    
    TryCapture {
    	OneOrMore(.digit)
    } transform: { match in
    	Int(match)
    }
    
    Capture(.digit)
	
    " years old."
}

이러한 접근 방식은 일종의 변형을 적용할 수 있는데, 예를 들어 String타입의 age를 Int형태로 타입을 변형하여 쓸 수 있다.
Capture말고도 TryCapture를 사용하면 실패 혹은 에러를 잡아내는 데 유용하다.

또한, 특정 타입과 함께 명명된 match를 찾고 싶은 경우 다음과 같이 표현될 수 있다.

let nameRef = Reference(Substring.self)
let ageRef = Reference(Int.self)

let search5 = Regex {
	"My name is "
    
    Capture(as: nameRef) {
    	OneOrMore(.word)
    }
    
    " and I'm"
    
    TryCapture(as: ageRef) {
    	OneOrMore(.digit)
    } transform: { match in
    	Int(match)
    }
    
    Capture(.digit)
    
    " years old."
}

if let result = greeting.firstMatch(of: search5) {
	print("Name: \(result[nameRef])")
    print("Age: \(result[ageRef])")
}

(출처: https://www.hackingwithswift.com/articles/249/whats-new-in-swift-5-7)

profile
iOS 개발

0개의 댓글