struct Predicate<each Input> : Sendable, Codable, CodableWithConfiguration
💡 검색 또는 필터링을 목적으로 입력값들의 집합을 검사하는데 사용되는 논리적 조건식.
2023년
NSPredicate
타입 대체를 목적으로 추가되어 iOS 17, … 이상에서 사용 가능하다.
NSPredicate
과 비교해 다음과 같은 장점이 있다:
- 타입 검사를 통한 타입 안정성 보장
- Xcode Syntax Highlighting 및 자동완성 기능 사용 가능
- 더이상 Obj-C 구문 규칙에 구애받지 않음
- 모든 Swift 타입에 범용적으로 사용 가능
- Sendable 및 Codable 지원
공식 문서 및 proposal 문서, Foundation 오픈소스에서 긁어낸 내용을 다음 노션 페이지들에 정리해놓았다.
아래 글은 Predicate 노션 페이지의 내용을 조금 더 축약한 것이다.
Predicate은 Boolean 값(true/false)으로 평가되는 논리적 조건식이다. 표현식(expression) 트리 구조로서 전체 predicate을 구성하고, 연산자 또한 피연산자 표현식을 포함한 하나의 표현식으로서 정의된다. 컬렉션에서 필터링을 수행하거나 특정 요소를 검색할 때 사용하는데, 구체적인 주요 활용처는 다음과 같다:
NSPredicate
생성자로 Predicate
→ NSPredicate
변환 가능)NSArray
, NSSet
, NSOrderedSet
, NSMutable〃
)Sequence
프로토콜의 filter(_ predicate:)
메소드predicate을 작성할 때는 #Predicate
매크로를 사용하여 클로저 내에 단일 표현식으로 조건식을 작성한다:
// Predicate 클로저가 전달받는 매개변수 message는 해당 Predicate에서 평가될 값이다.
let messagePredicate = #Predicate<Message> { message in
message.length < 100 && message.sender == "Jeremy"
}
predicate을 작성할 때 클로저를 사용하지만, #Predicate
매크로는 컴파일시 해당 클로저 코드를 PredicateExpressions
네임스페이스에 정의된 표현식(PredicateExpression
) 타입 구조로 해석하여 Predicate
타입으로 변환시킨다. 따라서 클로저에 있는 코드는 프로그램에서 실제로 작동되는 것이 아니라, 매크로에 전달하는 일종의 요청일 뿐이다.
Swift Predicate
에서 지원하는 표현식은 StandardPredicateExpression
프로토콜을 준수하는 표현식들만으로 한정되며, 개발자들이 해당 프로토콜을 준수하는 새 타입을 추가하는 것은 금지되어 있다.
PredicateExpressions
네임스페이스로부터, Predicate
정의 내에서 사용할 수 있는 연산 및 표현식을 알수 있다:
+
, -
, *
, /
, %
-
...
, ..<
<
, <=
, >
, >=
, ==
, !=
? :
if
&&
, ||
, !
?
, ??
, !
, if-let
표현식as?
, as!
, is
contains(_:)
caseInsensitiveCompare(_:)
, localizedCompare(_:)
, localizedStandardContains(_:)
allSatisfy()
, filter()
, contains()
, contains(where:)
, starts(with:)
, max()
, min()
contains(_:)
[]
subscript(position:)
subscript(bounds:)
subscript(key:)
subscript(key:default:)
.
문법으로 멤버 접근nil
, true
, false
등 포함) 및 외부 상수 사용Predicate
사용(FoundationPreview 0.3 버전에서 추가됨)Predicate
정의 내에서 사용할 수 없는 항목은 다음과 같다:
if
, for
문 등의 흐름 제어 '구문'(위의 조건 표현식과는 다르다.)flatMap(_:)
메소드를 호출해서 언래핑 - 참조).
문법 또는 self 등으로 외부 변수 접근(참조) (Predicate
타입에서 가변적 타입 매개변수를 통해 외부 변수 참조 가능)복잡한 질의 구문을 표현하기 위해 표현식 클로저를 중첩시킬 수도 있다.
// Sequence의 contains(where:) 메소드를 활용하여 중첩된 표현식 클로저를 만들었다.
// 여전히, 해당 클로저 내부에서도 단일 표현식 형태가 요구된다.
let messagePredicate = #Predicate<Message> { message in
message.recipients.contains {
$0.firstName == message.sender.firstName
}
}
가변적 타입 매개변수 문법을 활용하면 N개의 입력값을 받아 더욱 복잡한 질의 구문을 표현할 수도 있다. 평가할 값 이외에 추가 변수를 제공하고 싶은 경우 유용할 수 있다.
// 가변적 타입 매개변수 문법을 통해 클로저에서 추가 매개변수를 사용하고,
// predicate 평가시에는 추가 인자를 전달한다.
let myPredicate = #Predicate<Message, Int> { message, limit in
message.content.count < limit && $0.sender.firstName == "Jeremy"
}
let result = try myPredicate.evaluate(message, 280)
FoundationPreview 0.3 버전부터는 미리 정의된 predicate을 다른 predicate 내에 중첩하여, 표현식으로서 사용할 수 있게 되었다.
let ageCondition = #Predicate<Person> { age == 12 }
let heightCondition = #Predicate<Person> { height > 150 }
let predicate = #Predicate<Person> {
ageCondition && heightCondition
}
Predicate
은 Codable
을 준수하므로 안전하게 encoding 및 decoding이 가능하며, 파일로부터 predicate을 불러올 수도 있다. 또한 Sendable
을 준수하므로 동시성 작업 영역간 전달될 수 있다. 아카이브된 predicate을 읽어들일 때 허용할 타입 및 keypath 목록을 직접 정의하려면 PredicateCodableConfiguration
을 사용해야 한다.
📎 gist.github.com: Swift Predicate Archiving Pitch - Encoded Format에서 Predicate 타입을 인코딩했을 때 생성되는 아카이브 포맷에 대해 확인할 수 있다.
expression
프로퍼티는 predicate 자료구조 전체에 해당하는 최상위 표현식을 가지므로, 해당 프로퍼티를 활용하면 predicate을 다른 표현으로 수정할 수도 있다. 예를 들어, 해당 predicate의 표현식 일부를 수정•추가하거나, 다른 질의(query) 언어 표현으로 변환할 수도 있다.
Swift Predicate 도메인 API들은 커스텀 Predicate 타입을 제작하는 것 또한 함께 고려되었다.
커스텀 Predicate 타입을 제작할 때 고려해야할 사항들은 다음과 같다:
expression
및 variables
프로퍼티, 생성자 및 evaluate
메소드 등과 유사한 인터페이스를 사용한다.StandardPredicateExpression
프로토콜과 유사한 역할의 프로토콜을 정의하여 사용 가능한 표현식의 종류를 제한하고, 각 표현식은 PredicateExpression
프로토콜을 준수하도록 한다.PredicateBinding
타입을 사용해 입력값을 predicate 자료구조에 전파한다.CodableWithConfiguration
프로토콜 준수를 돕는 API도 제공된다. gist.github.com: Swift Predicate Pitch - API Support for Third Party Predicates 페이지에서 해당 API 목록 및 사용 예시를 확인할 수 있다.그러나 커스텀 Predicate 타입을 정의하는 방법 및 관련 API까지는 아직 명확한 방법 문서 또는 proposal이 올라오지 않았다. github.com/apple: Swift-Foundation 에서 Predicate
및 관련 구현 코드를 열람할 수 있으며, 8/23일 github.com/apple: Swift Foundation-macros 에 Predicate 매크로의 구현 코드가 공개되었다.