├── .gitignore ├── .ruby-version ├── .slather.yml ├── .swift-version ├── .travis.yml ├── CONTRIBUTING.md ├── LICENSE ├── Package.swift ├── PrediKit.podspec ├── PrediKit.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcshareddata │ └── xcschemes │ ├── PrediKit OSX.xcscheme │ ├── PrediKit iOS.xcscheme │ ├── PrediKit tvOS.xcscheme │ └── PrediKit watchOS.xcscheme ├── README.md ├── Sources ├── Builders │ ├── FinalizedIncluder.swift │ ├── PredicateBuilder.swift │ └── PredicateSubqueryBuilder.swift ├── Extensions │ ├── NSPredicate+PrediKit.swift │ └── Number+PrediKit.swift ├── Protocols │ ├── Matchable.swift │ ├── NilComparable.swift │ ├── Queryable.swift │ └── Reflectable.swift ├── Queries │ ├── BasicQuery.swift │ ├── BooleanQuery.swift │ ├── DateQuery.swift │ ├── MemberQuery.swift │ ├── NumberQuery.swift │ ├── SequenceQuery.swift │ └── StringQuery.swift └── Value Types │ ├── PredicateOptions.swift │ └── SubqueryMatchTypes.swift ├── Supporting Files ├── OSX │ ├── Info.plist │ ├── PrediKit.h │ └── TestsInfo.plist ├── iOS │ ├── Info.plist │ ├── PrediKit.h │ └── TestsInfo.plist ├── tvOS │ ├── Info.plist │ ├── PrediKit.h │ └── TestsInfo.plist └── watchOS │ ├── Info.plist │ └── PrediKit.h └── Tests ├── Keypaths.swift └── PrediKitTests.swift /.gitignore: -------------------------------------------------------------------------------- 1 | ## OS X Finder 2 | .DS_Store 3 | 4 | ## Build generated 5 | build/ 6 | DerivedData 7 | 8 | ## Various settings 9 | *.pbxuser 10 | !default.pbxuser 11 | *.mode1v3 12 | !default.mode1v3 13 | *.mode2v3 14 | !default.mode2v3 15 | *.perspectivev3 16 | !default.perspectivev3 17 | xcuserdata 18 | 19 | ## Other 20 | *.xccheckout 21 | *.moved-aside 22 | *.xcuserstate 23 | *.xcscmblueprint 24 | 25 | ## Obj-C/Swift specific 26 | *.hmap 27 | *.ipa 28 | 29 | ## Playgrounds 30 | timeline.xctimeline 31 | playground.xcworkspace 32 | 33 | # Swift Package Manager 34 | # 35 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 36 | # Packages/ 37 | .build/ 38 | 39 | # CocoaPods 40 | # 41 | # We recommend against adding the Pods directory to your .gitignore. However 42 | # you should judge for yourself, the pros and cons are mentioned at: 43 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 44 | # 45 | # Pods/ 46 | 47 | # Carthage 48 | # 49 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 50 | # Carthage/Checkouts 51 | 52 | Carthage/Build 53 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 2.4.0 2 | -------------------------------------------------------------------------------- /.slather.yml: -------------------------------------------------------------------------------- 1 | coverage_service: coveralls 2 | xcodeproj: PrediKit.xcodeproj 3 | scheme: PrediKit iOS 4 | source_directory: Sources 5 | ignore: 6 | - Tests/* 7 | - Supporting Files/** 8 | -------------------------------------------------------------------------------- /.swift-version: -------------------------------------------------------------------------------- 1 | 4.2 2 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | osx_image: xcode10.0 3 | env: 4 | global: 5 | - LC_CTYPE=en_US.UTF-8 6 | - LANG=en_US.UTF-8 7 | - PROJECT=PrediKit.xcodeproj 8 | - iOS_FRAMEWORK_SCHEME="PrediKit iOS" 9 | - OSX_FRAMEWORK_SCHEME="PrediKit OSX" 10 | - tvOS_FRAMEWORK_SCHEME="PrediKit tvOS" 11 | - watchOS_FRAMEWORK_SCHEME="PrediKit watchOS" 12 | - iOS_SDK=iphonesimulator11.2 13 | - OSX_SDK=macosx10.13 14 | - tvOS_SDK=appletvsimulator11.2 15 | - watchOS_SDK=watchsimulator4.2 16 | matrix: 17 | - DESTINATION="OS=11.2,name=iPhone 7 Plus" SCHEME="$iOS_FRAMEWORK_SCHEME" SDK="$iOS_SDK" RUN_TESTS="YES" POD_LINT="YES" 18 | 19 | - DESTINATION="arch=x86_64" SCHEME="$OSX_FRAMEWORK_SCHEME" SDK="$OSX_SDK" RUN_TESTS="YES" POD_LINT="NO" 20 | 21 | - DESTINATION="OS=11.2,name=Apple TV 4K (at 1080p)" SCHEME="$tvOS_FRAMEWORK_SCHEME" SDK="$tvOS_SDK" RUN_TESTS="YES" POD_LINT="NO" 22 | 23 | - DESTINATION="OS=4.2,name=Apple Watch - 42mm" SCHEME="$watchOS_FRAMEWORK_SCHEME" SDK="$watchOS_SDK" RUN_TESTS="NO" POD_LINT="NO" 24 | before_install: 25 | - 'echo ''gem: --no-ri --no-rdoc'' > ~/.gemrc' 26 | - gem install xcpretty -N 27 | - gem install slather cocoapods 28 | script: 29 | - set -o pipefail 30 | - xcodebuild -version 31 | - xcodebuild -showsdks 32 | 33 | # Build Framework in Debug and Run Tests if specified 34 | - if [ $RUN_TESTS == "YES" ]; then 35 | xcodebuild -project "$PROJECT" -scheme "$SCHEME" -sdk "$SDK" -destination "$DESTINATION" -configuration Debug ONLY_ACTIVE_ARCH=NO ENABLE_TESTABILITY=YES test | xcpretty -c; 36 | else 37 | xcodebuild -project "$PROJECT" -scheme "$SCHEME" -sdk "$SDK" -destination "$DESTINATION" -configuration Debug ONLY_ACTIVE_ARCH=NO build | xcpretty -c; 38 | fi 39 | 40 | # Run `pod lib lint` if specified 41 | - if [ $POD_LINT == "YES" ]; then 42 | pod lib lint --quick; 43 | fi 44 | after_success: 45 | - slather 46 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | Contributing Guidelines 2 | -------------------------------------------------- 3 | 4 | This document provides general guidelines about how to contribute to the project. Keep in mind these important things before you start contributing. 5 | 6 | ### Asking Questions 7 | 8 | We do not use github issues for general library support. We think this questions should be posted on stack overflow using [PrediKit](http://http://stackoverflow.com/questions/tagged/PrediKit) tag. 9 | 10 | ### Reporting issues 11 | 12 | * Use [github issues](https://github.com/KrakenDev/PrediKit/issues) to report a bug. 13 | * Before creating a new issue: 14 | * Make sure you are using the [latest release](https://github.com/KrakenDev/PrediKit/releases). 15 | * Check if the issue was [already reported or fixed](https://github.com/KrakenDev/PrediKit/issues?utf8=%E2%9C%93&q=is%3Aissue). Notice that it may not be released yet. 16 | * If you found a match add a brief comment "I have the same problem" or "+1". This helps prioritize the issues addressing the most common and critical first. If possible add additional information to help us reproduce and fix the issue. Please use your best judgement. 17 | * Reporting issues: 18 | * Please include the following information to help maintainers to fix the problem faster: 19 | * Xcode version you are using. 20 | * iOS version you are targeting. 21 | * Full Xcode console output of stack trace or code compilation error. 22 | * Any other additional detail you think it would be useful to understand and solve the problem. 23 | 24 | 25 | ### Pull requests 26 | 27 | The easiest way to start contributing is searching open issues by `help wanted` tag. We also add a `difficulty` tag (difficulty: easy, difficulty: moderate, difficulty: hard) in order to give an idea of how complex it can be to implement the feature according maintainers project experience. 28 | 29 | * Add test coverage to the feature or fix. We only accept new feature pull requests that have related test coverage. This allows us to keep the library stable as we move forward. 30 | * Remember to document the new feature. We do not accept new feature pull requests without its associated documentation. 31 | * In case of a new feature please update the example project showing the feature. 32 | * Please only one fix or feature per pull request. This will increase the chances your feature will be merged. 33 | 34 | 35 | ###### Suggested git workflow to contribute 36 | 37 | 1. Fork the PrediKit repository. 38 | 2. Clone your forked project into your developer machine: `git clone git@github.com:/PrediKit.git` 39 | 3. Add the original project repo as upstream repository in your forked project: `git remote add upstream git@github.com:TheKrakenDev/PrediKit.git` 40 | 4. Before starting a new feature make sure your forked master branch is synchronized upstream master branch. Considering you do not mere your pull request into master you can run: `git checkout master` and then `git pull upstream master`. Optionally `git push origin master`. 41 | 5. Create a new branch. Note that the starting point is the upstream master branch HEAD. `git checkout -b my-feature-name` 42 | 6. Stage all your changes `git add .` and commit them `git commit -m "Your commit message"` 43 | 7. Make sure your branch is up to date with upstream master, `git pull --rebase upstream master`, resolve conflicts if necessary. This will move your commit to the top of git stack. 44 | 8. Squash your commits into one commit. `git rebase -i HEAD~6` considering you did 6 commits. 45 | 9. Push your branch into your forked remote repository. 46 | 10. Create a new pull request adding any useful comment. 47 | 48 | 49 | ### Feature proposal 50 | 51 | We would love to hear your ideas and make a discussions about it. 52 | 53 | * Use github issues to make feature proposals. 54 | * We use `type: feature request` label to mark all [feature request issues](https://github.com/TheKrakenDev/PrediKit/labels/type%3A%20feature%20request). 55 | * Before submitting your proposal make sure there is no similar feature request. If you found a match feel free to join the discussion or just add a brief "+1" if you think the feature is worth implementing. 56 | * Be as specific as possible providing a precise explanation of feature request so anyone can understand the problem and the benefits of solving it. 57 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 Hector Matos 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | import PackageDescription 2 | 3 | let package = Package( 4 | name: "PrediKit" 5 | ) 6 | -------------------------------------------------------------------------------- /PrediKit.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | s.name = 'PrediKit' 3 | s.version = '4.2.0' 4 | s.license = 'MIT' 5 | s.summary = 'A Swift NSPredicate DSL for iOS & OS X inspired by SnapKit, lovingly written in Swift, and created by the awesome peeps at KrakenDev.io!' 6 | s.homepage = 'https://github.com/KrakenDev/PrediKit' 7 | s.authors = { 'Hector Matos' => 'hectormatos2011@gmail.com' } 8 | s.social_media_url = 'http://twitter.com/allonsykraken' 9 | s.source = { :git => 'https://github.com/KrakenDev/PrediKit.git', :tag => s.version } 10 | 11 | s.ios.deployment_target = '8.0' 12 | s.osx.deployment_target = '10.9' 13 | s.tvos.deployment_target = '9.0' 14 | s.watchos.deployment_target = '2.0' 15 | 16 | s.source_files = 'Sources/**/*.swift' 17 | 18 | s.requires_arc = true 19 | end 20 | -------------------------------------------------------------------------------- /PrediKit.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 46; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 287D0A6E1C4B73BD004566D6 /* PrediKitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 287D0A6D1C4B73BD004566D6 /* PrediKitTests.swift */; }; 11 | 28F828881C494B2C00330CF4 /* PrediKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 28F8287D1C494B2C00330CF4 /* PrediKit.framework */; }; 12 | 5DAE5D451CFC6D130029355E /* PrediKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 5DAE5D3E1CFC6D130029355E /* PrediKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; 13 | 5DAE5D491CFC6EE60029355E /* PrediKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 5DAE5D421CFC6D130029355E /* PrediKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; 14 | 5DAE5D871CFC7D590029355E /* Matchable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5D7B1CFC7D590029355E /* Matchable.swift */; }; 15 | 5DAE5D881CFC7D590029355E /* Matchable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5D7B1CFC7D590029355E /* Matchable.swift */; }; 16 | 5DAE5D891CFC7D590029355E /* NilComparable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5D7C1CFC7D590029355E /* NilComparable.swift */; }; 17 | 5DAE5D8A1CFC7D590029355E /* NilComparable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5D7C1CFC7D590029355E /* NilComparable.swift */; }; 18 | 5DAE5D8B1CFC7D590029355E /* Queryable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5D7D1CFC7D590029355E /* Queryable.swift */; }; 19 | 5DAE5D8C1CFC7D590029355E /* Queryable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5D7D1CFC7D590029355E /* Queryable.swift */; }; 20 | 5DAE5D8D1CFC7D590029355E /* Reflectable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5D7E1CFC7D590029355E /* Reflectable.swift */; }; 21 | 5DAE5D8E1CFC7D590029355E /* Reflectable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5D7E1CFC7D590029355E /* Reflectable.swift */; }; 22 | 5DAE5D8F1CFC7D590029355E /* BasicQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5D801CFC7D590029355E /* BasicQuery.swift */; }; 23 | 5DAE5D901CFC7D590029355E /* BasicQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5D801CFC7D590029355E /* BasicQuery.swift */; }; 24 | 5DAE5D911CFC7D590029355E /* BooleanQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5D811CFC7D590029355E /* BooleanQuery.swift */; }; 25 | 5DAE5D921CFC7D590029355E /* BooleanQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5D811CFC7D590029355E /* BooleanQuery.swift */; }; 26 | 5DAE5D931CFC7D590029355E /* DateQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5D821CFC7D590029355E /* DateQuery.swift */; }; 27 | 5DAE5D941CFC7D590029355E /* DateQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5D821CFC7D590029355E /* DateQuery.swift */; }; 28 | 5DAE5D951CFC7D590029355E /* MemberQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5D831CFC7D590029355E /* MemberQuery.swift */; }; 29 | 5DAE5D961CFC7D590029355E /* MemberQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5D831CFC7D590029355E /* MemberQuery.swift */; }; 30 | 5DAE5D971CFC7D590029355E /* NumberQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5D841CFC7D590029355E /* NumberQuery.swift */; }; 31 | 5DAE5D981CFC7D590029355E /* NumberQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5D841CFC7D590029355E /* NumberQuery.swift */; }; 32 | 5DAE5D991CFC7D590029355E /* SequenceQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5D851CFC7D590029355E /* SequenceQuery.swift */; }; 33 | 5DAE5D9A1CFC7D590029355E /* SequenceQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5D851CFC7D590029355E /* SequenceQuery.swift */; }; 34 | 5DAE5D9B1CFC7D590029355E /* StringQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5D861CFC7D590029355E /* StringQuery.swift */; }; 35 | 5DAE5D9C1CFC7D590029355E /* StringQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5D861CFC7D590029355E /* StringQuery.swift */; }; 36 | 5DAE5DA11CFC7DAE0029355E /* FinalizedIncluder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5D9E1CFC7DAE0029355E /* FinalizedIncluder.swift */; }; 37 | 5DAE5DA21CFC7DAE0029355E /* FinalizedIncluder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5D9E1CFC7DAE0029355E /* FinalizedIncluder.swift */; }; 38 | 5DAE5DA31CFC7DAE0029355E /* PredicateBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5D9F1CFC7DAE0029355E /* PredicateBuilder.swift */; }; 39 | 5DAE5DA41CFC7DAE0029355E /* PredicateBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5D9F1CFC7DAE0029355E /* PredicateBuilder.swift */; }; 40 | 5DAE5DA51CFC7DAE0029355E /* PredicateSubqueryBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5DA01CFC7DAE0029355E /* PredicateSubqueryBuilder.swift */; }; 41 | 5DAE5DA61CFC7DAE0029355E /* PredicateSubqueryBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5DA01CFC7DAE0029355E /* PredicateSubqueryBuilder.swift */; }; 42 | 5DAE5DA91CFC7DDA0029355E /* NSPredicate+PrediKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5DA81CFC7DDA0029355E /* NSPredicate+PrediKit.swift */; }; 43 | 5DAE5DAA1CFC7DDA0029355E /* NSPredicate+PrediKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5DA81CFC7DDA0029355E /* NSPredicate+PrediKit.swift */; }; 44 | 5DAE5DAE1CFC7E1E0029355E /* PredicateOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5DAC1CFC7E1E0029355E /* PredicateOptions.swift */; }; 45 | 5DAE5DAF1CFC7E1E0029355E /* PredicateOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5DAC1CFC7E1E0029355E /* PredicateOptions.swift */; }; 46 | 5DAE5DB01CFC7E1E0029355E /* SubqueryMatchTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5DAD1CFC7E1E0029355E /* SubqueryMatchTypes.swift */; }; 47 | 5DAE5DB11CFC7E1E0029355E /* SubqueryMatchTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5DAD1CFC7E1E0029355E /* SubqueryMatchTypes.swift */; }; 48 | 5DAE5DD51CFC909D0029355E /* PrediKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5DAE5DCB1CFC909C0029355E /* PrediKit.framework */; }; 49 | 5DAE5DF31CFC917D0029355E /* PrediKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 5DAE5DE81CFC91440029355E /* PrediKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; 50 | 5DAE5DF41CFC918C0029355E /* PrediKit.h in Headers */ = {isa = PBXBuildFile; fileRef = 5DAE5DE41CFC91440029355E /* PrediKit.h */; settings = {ATTRIBUTES = (Public, ); }; }; 51 | 5DAE5DF51CFC91D50029355E /* PrediKitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 287D0A6D1C4B73BD004566D6 /* PrediKitTests.swift */; }; 52 | 5DAE5DF61CFC91D50029355E /* Keypaths.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DDAE95D1CEA6F1100C01903 /* Keypaths.swift */; }; 53 | 5DAE5DF71CFC91E40029355E /* NSPredicate+PrediKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5DA81CFC7DDA0029355E /* NSPredicate+PrediKit.swift */; }; 54 | 5DAE5DF91CFC91E40029355E /* Matchable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5D7B1CFC7D590029355E /* Matchable.swift */; }; 55 | 5DAE5DFA1CFC91E40029355E /* NilComparable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5D7C1CFC7D590029355E /* NilComparable.swift */; }; 56 | 5DAE5DFB1CFC91E40029355E /* Queryable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5D7D1CFC7D590029355E /* Queryable.swift */; }; 57 | 5DAE5DFC1CFC91E40029355E /* Reflectable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5D7E1CFC7D590029355E /* Reflectable.swift */; }; 58 | 5DAE5DFD1CFC91E40029355E /* FinalizedIncluder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5D9E1CFC7DAE0029355E /* FinalizedIncluder.swift */; }; 59 | 5DAE5DFE1CFC91E40029355E /* PredicateBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5D9F1CFC7DAE0029355E /* PredicateBuilder.swift */; }; 60 | 5DAE5DFF1CFC91E40029355E /* PredicateSubqueryBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5DA01CFC7DAE0029355E /* PredicateSubqueryBuilder.swift */; }; 61 | 5DAE5E001CFC91E40029355E /* BasicQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5D801CFC7D590029355E /* BasicQuery.swift */; }; 62 | 5DAE5E011CFC91E40029355E /* BooleanQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5D811CFC7D590029355E /* BooleanQuery.swift */; }; 63 | 5DAE5E021CFC91E40029355E /* DateQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5D821CFC7D590029355E /* DateQuery.swift */; }; 64 | 5DAE5E031CFC91E40029355E /* MemberQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5D831CFC7D590029355E /* MemberQuery.swift */; }; 65 | 5DAE5E041CFC91E40029355E /* NumberQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5D841CFC7D590029355E /* NumberQuery.swift */; }; 66 | 5DAE5E051CFC91E40029355E /* SequenceQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5D851CFC7D590029355E /* SequenceQuery.swift */; }; 67 | 5DAE5E061CFC91E40029355E /* StringQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5D861CFC7D590029355E /* StringQuery.swift */; }; 68 | 5DAE5E071CFC91E40029355E /* PredicateOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5DAC1CFC7E1E0029355E /* PredicateOptions.swift */; }; 69 | 5DAE5E081CFC91E40029355E /* SubqueryMatchTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5DAD1CFC7E1E0029355E /* SubqueryMatchTypes.swift */; }; 70 | 5DAE5E091CFC91E50029355E /* NSPredicate+PrediKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5DA81CFC7DDA0029355E /* NSPredicate+PrediKit.swift */; }; 71 | 5DAE5E0B1CFC91E50029355E /* Matchable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5D7B1CFC7D590029355E /* Matchable.swift */; }; 72 | 5DAE5E0C1CFC91E50029355E /* NilComparable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5D7C1CFC7D590029355E /* NilComparable.swift */; }; 73 | 5DAE5E0D1CFC91E50029355E /* Queryable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5D7D1CFC7D590029355E /* Queryable.swift */; }; 74 | 5DAE5E0E1CFC91E50029355E /* Reflectable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5D7E1CFC7D590029355E /* Reflectable.swift */; }; 75 | 5DAE5E0F1CFC91E50029355E /* FinalizedIncluder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5D9E1CFC7DAE0029355E /* FinalizedIncluder.swift */; }; 76 | 5DAE5E101CFC91E50029355E /* PredicateBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5D9F1CFC7DAE0029355E /* PredicateBuilder.swift */; }; 77 | 5DAE5E111CFC91E50029355E /* PredicateSubqueryBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5DA01CFC7DAE0029355E /* PredicateSubqueryBuilder.swift */; }; 78 | 5DAE5E121CFC91E50029355E /* BasicQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5D801CFC7D590029355E /* BasicQuery.swift */; }; 79 | 5DAE5E131CFC91E50029355E /* BooleanQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5D811CFC7D590029355E /* BooleanQuery.swift */; }; 80 | 5DAE5E141CFC91E50029355E /* DateQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5D821CFC7D590029355E /* DateQuery.swift */; }; 81 | 5DAE5E151CFC91E50029355E /* MemberQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5D831CFC7D590029355E /* MemberQuery.swift */; }; 82 | 5DAE5E161CFC91E50029355E /* NumberQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5D841CFC7D590029355E /* NumberQuery.swift */; }; 83 | 5DAE5E171CFC91E50029355E /* SequenceQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5D851CFC7D590029355E /* SequenceQuery.swift */; }; 84 | 5DAE5E181CFC91E50029355E /* StringQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5D861CFC7D590029355E /* StringQuery.swift */; }; 85 | 5DAE5E191CFC91E50029355E /* PredicateOptions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5DAC1CFC7E1E0029355E /* PredicateOptions.swift */; }; 86 | 5DAE5E1A1CFC91E50029355E /* SubqueryMatchTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DAE5DAD1CFC7E1E0029355E /* SubqueryMatchTypes.swift */; }; 87 | 5DAF1EB51CFA9619006BAD71 /* PrediKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5DAF1EAB1CFA9619006BAD71 /* PrediKit.framework */; }; 88 | 5DAF1EC21CFA97C9006BAD71 /* PrediKitTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 287D0A6D1C4B73BD004566D6 /* PrediKitTests.swift */; }; 89 | 5DAF1EC31CFA97C9006BAD71 /* Keypaths.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DDAE95D1CEA6F1100C01903 /* Keypaths.swift */; }; 90 | 5DDAE95E1CEA6F1100C01903 /* Keypaths.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DDAE95D1CEA6F1100C01903 /* Keypaths.swift */; }; 91 | 5DDCFE081D8E77CB000DB59C /* Number+PrediKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DDCFE071D8E77CB000DB59C /* Number+PrediKit.swift */; }; 92 | 5DDCFE091D8E77CB000DB59C /* Number+PrediKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DDCFE071D8E77CB000DB59C /* Number+PrediKit.swift */; }; 93 | 5DDCFE0A1D8E77CB000DB59C /* Number+PrediKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DDCFE071D8E77CB000DB59C /* Number+PrediKit.swift */; }; 94 | 5DDCFE0B1D8E77CB000DB59C /* Number+PrediKit.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5DDCFE071D8E77CB000DB59C /* Number+PrediKit.swift */; }; 95 | /* End PBXBuildFile section */ 96 | 97 | /* Begin PBXContainerItemProxy section */ 98 | 28F828891C494B2C00330CF4 /* PBXContainerItemProxy */ = { 99 | isa = PBXContainerItemProxy; 100 | containerPortal = 28F828741C494B2C00330CF4 /* Project object */; 101 | proxyType = 1; 102 | remoteGlobalIDString = 28F8287C1C494B2C00330CF4; 103 | remoteInfo = PrediKit; 104 | }; 105 | 5DAE5DD61CFC909D0029355E /* PBXContainerItemProxy */ = { 106 | isa = PBXContainerItemProxy; 107 | containerPortal = 28F828741C494B2C00330CF4 /* Project object */; 108 | proxyType = 1; 109 | remoteGlobalIDString = 5DAE5DCA1CFC909C0029355E; 110 | remoteInfo = "PrediKit-tvOS"; 111 | }; 112 | 5DAF1EB61CFA9619006BAD71 /* PBXContainerItemProxy */ = { 113 | isa = PBXContainerItemProxy; 114 | containerPortal = 28F828741C494B2C00330CF4 /* Project object */; 115 | proxyType = 1; 116 | remoteGlobalIDString = 5DAF1EAA1CFA9619006BAD71; 117 | remoteInfo = "PrediKit-OSX"; 118 | }; 119 | /* End PBXContainerItemProxy section */ 120 | 121 | /* Begin PBXFileReference section */ 122 | 287D0A6D1C4B73BD004566D6 /* PrediKitTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = PrediKitTests.swift; path = Tests/PrediKitTests.swift; sourceTree = SOURCE_ROOT; }; 123 | 28F8287D1C494B2C00330CF4 /* PrediKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = PrediKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 124 | 28F828871C494B2C00330CF4 /* PrediKit iOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "PrediKit iOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 125 | 5DAE5D3C1CFC6D130029355E /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 126 | 5DAE5D3D1CFC6D130029355E /* TestsInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = TestsInfo.plist; sourceTree = ""; }; 127 | 5DAE5D3E1CFC6D130029355E /* PrediKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PrediKit.h; sourceTree = ""; }; 128 | 5DAE5D401CFC6D130029355E /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 129 | 5DAE5D411CFC6D130029355E /* TestsInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = TestsInfo.plist; sourceTree = ""; }; 130 | 5DAE5D421CFC6D130029355E /* PrediKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PrediKit.h; sourceTree = ""; }; 131 | 5DAE5D7B1CFC7D590029355E /* Matchable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Matchable.swift; sourceTree = ""; }; 132 | 5DAE5D7C1CFC7D590029355E /* NilComparable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NilComparable.swift; sourceTree = ""; }; 133 | 5DAE5D7D1CFC7D590029355E /* Queryable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Queryable.swift; sourceTree = ""; }; 134 | 5DAE5D7E1CFC7D590029355E /* Reflectable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Reflectable.swift; sourceTree = ""; }; 135 | 5DAE5D801CFC7D590029355E /* BasicQuery.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BasicQuery.swift; sourceTree = ""; }; 136 | 5DAE5D811CFC7D590029355E /* BooleanQuery.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BooleanQuery.swift; sourceTree = ""; }; 137 | 5DAE5D821CFC7D590029355E /* DateQuery.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DateQuery.swift; sourceTree = ""; }; 138 | 5DAE5D831CFC7D590029355E /* MemberQuery.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MemberQuery.swift; sourceTree = ""; }; 139 | 5DAE5D841CFC7D590029355E /* NumberQuery.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NumberQuery.swift; sourceTree = ""; }; 140 | 5DAE5D851CFC7D590029355E /* SequenceQuery.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SequenceQuery.swift; sourceTree = ""; }; 141 | 5DAE5D861CFC7D590029355E /* StringQuery.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StringQuery.swift; sourceTree = ""; }; 142 | 5DAE5D9E1CFC7DAE0029355E /* FinalizedIncluder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FinalizedIncluder.swift; sourceTree = ""; }; 143 | 5DAE5D9F1CFC7DAE0029355E /* PredicateBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PredicateBuilder.swift; sourceTree = ""; }; 144 | 5DAE5DA01CFC7DAE0029355E /* PredicateSubqueryBuilder.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PredicateSubqueryBuilder.swift; sourceTree = ""; }; 145 | 5DAE5DA81CFC7DDA0029355E /* NSPredicate+PrediKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSPredicate+PrediKit.swift"; sourceTree = ""; }; 146 | 5DAE5DAC1CFC7E1E0029355E /* PredicateOptions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PredicateOptions.swift; sourceTree = ""; }; 147 | 5DAE5DAD1CFC7E1E0029355E /* SubqueryMatchTypes.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SubqueryMatchTypes.swift; sourceTree = ""; }; 148 | 5DAE5DBE1CFC90430029355E /* PrediKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = PrediKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 149 | 5DAE5DCB1CFC909C0029355E /* PrediKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = PrediKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 150 | 5DAE5DD41CFC909D0029355E /* PrediKit tvOSTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "PrediKit tvOSTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 151 | 5DAE5DE31CFC91440029355E /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 152 | 5DAE5DE41CFC91440029355E /* PrediKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PrediKit.h; sourceTree = ""; }; 153 | 5DAE5DE51CFC91440029355E /* TestsInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = TestsInfo.plist; sourceTree = ""; }; 154 | 5DAE5DE71CFC91440029355E /* Info.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 155 | 5DAE5DE81CFC91440029355E /* PrediKit.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = PrediKit.h; sourceTree = ""; }; 156 | 5DAF1EAB1CFA9619006BAD71 /* PrediKit.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = PrediKit.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 157 | 5DAF1EB41CFA9619006BAD71 /* PrediKit OSXTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "PrediKit OSXTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 158 | 5DDAE95D1CEA6F1100C01903 /* Keypaths.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; name = Keypaths.swift; path = Tests/Keypaths.swift; sourceTree = SOURCE_ROOT; }; 159 | 5DDCFE071D8E77CB000DB59C /* Number+PrediKit.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Number+PrediKit.swift"; sourceTree = ""; }; 160 | /* End PBXFileReference section */ 161 | 162 | /* Begin PBXFrameworksBuildPhase section */ 163 | 28F828791C494B2C00330CF4 /* Frameworks */ = { 164 | isa = PBXFrameworksBuildPhase; 165 | buildActionMask = 2147483647; 166 | files = ( 167 | ); 168 | runOnlyForDeploymentPostprocessing = 0; 169 | }; 170 | 28F828841C494B2C00330CF4 /* Frameworks */ = { 171 | isa = PBXFrameworksBuildPhase; 172 | buildActionMask = 2147483647; 173 | files = ( 174 | 28F828881C494B2C00330CF4 /* PrediKit.framework in Frameworks */, 175 | ); 176 | runOnlyForDeploymentPostprocessing = 0; 177 | }; 178 | 5DAE5DBA1CFC90430029355E /* Frameworks */ = { 179 | isa = PBXFrameworksBuildPhase; 180 | buildActionMask = 2147483647; 181 | files = ( 182 | ); 183 | runOnlyForDeploymentPostprocessing = 0; 184 | }; 185 | 5DAE5DC71CFC909C0029355E /* Frameworks */ = { 186 | isa = PBXFrameworksBuildPhase; 187 | buildActionMask = 2147483647; 188 | files = ( 189 | ); 190 | runOnlyForDeploymentPostprocessing = 0; 191 | }; 192 | 5DAE5DD11CFC909D0029355E /* Frameworks */ = { 193 | isa = PBXFrameworksBuildPhase; 194 | buildActionMask = 2147483647; 195 | files = ( 196 | 5DAE5DD51CFC909D0029355E /* PrediKit.framework in Frameworks */, 197 | ); 198 | runOnlyForDeploymentPostprocessing = 0; 199 | }; 200 | 5DAF1EA71CFA9619006BAD71 /* Frameworks */ = { 201 | isa = PBXFrameworksBuildPhase; 202 | buildActionMask = 2147483647; 203 | files = ( 204 | ); 205 | runOnlyForDeploymentPostprocessing = 0; 206 | }; 207 | 5DAF1EB11CFA9619006BAD71 /* Frameworks */ = { 208 | isa = PBXFrameworksBuildPhase; 209 | buildActionMask = 2147483647; 210 | files = ( 211 | 5DAF1EB51CFA9619006BAD71 /* PrediKit.framework in Frameworks */, 212 | ); 213 | runOnlyForDeploymentPostprocessing = 0; 214 | }; 215 | /* End PBXFrameworksBuildPhase section */ 216 | 217 | /* Begin PBXGroup section */ 218 | 28F828731C494B2C00330CF4 = { 219 | isa = PBXGroup; 220 | children = ( 221 | 28F828971C494B4200330CF4 /* Sources */, 222 | 28F8288B1C494B2C00330CF4 /* Tests */, 223 | 5DAE5D3A1CFC6D130029355E /* Supporting Files */, 224 | 28F8287E1C494B2C00330CF4 /* Products */, 225 | ); 226 | sourceTree = ""; 227 | }; 228 | 28F8287E1C494B2C00330CF4 /* Products */ = { 229 | isa = PBXGroup; 230 | children = ( 231 | 28F8287D1C494B2C00330CF4 /* PrediKit.framework */, 232 | 28F828871C494B2C00330CF4 /* PrediKit iOSTests.xctest */, 233 | 5DAF1EAB1CFA9619006BAD71 /* PrediKit.framework */, 234 | 5DAF1EB41CFA9619006BAD71 /* PrediKit OSXTests.xctest */, 235 | 5DAE5DBE1CFC90430029355E /* PrediKit.framework */, 236 | 5DAE5DCB1CFC909C0029355E /* PrediKit.framework */, 237 | 5DAE5DD41CFC909D0029355E /* PrediKit tvOSTests.xctest */, 238 | ); 239 | name = Products; 240 | sourceTree = ""; 241 | }; 242 | 28F8288B1C494B2C00330CF4 /* Tests */ = { 243 | isa = PBXGroup; 244 | children = ( 245 | 287D0A6D1C4B73BD004566D6 /* PrediKitTests.swift */, 246 | 5DDAE95D1CEA6F1100C01903 /* Keypaths.swift */, 247 | ); 248 | name = Tests; 249 | path = PrediKitTests; 250 | sourceTree = ""; 251 | }; 252 | 28F828971C494B4200330CF4 /* Sources */ = { 253 | isa = PBXGroup; 254 | children = ( 255 | 5DAE5DA71CFC7DDA0029355E /* Extensions */, 256 | 5DAE5D7A1CFC7D590029355E /* Protocols */, 257 | 5DAE5D9D1CFC7DAE0029355E /* Builders */, 258 | 5DAE5D7F1CFC7D590029355E /* Queries */, 259 | 5DAE5DAB1CFC7E1E0029355E /* Value Types */, 260 | ); 261 | path = Sources; 262 | sourceTree = ""; 263 | }; 264 | 5DAE5D3A1CFC6D130029355E /* Supporting Files */ = { 265 | isa = PBXGroup; 266 | children = ( 267 | 5DAE5D3B1CFC6D130029355E /* iOS */, 268 | 5DAE5D3F1CFC6D130029355E /* OSX */, 269 | 5DAE5DE21CFC91440029355E /* tvOS */, 270 | 5DAE5DE61CFC91440029355E /* watchOS */, 271 | ); 272 | path = "Supporting Files"; 273 | sourceTree = ""; 274 | }; 275 | 5DAE5D3B1CFC6D130029355E /* iOS */ = { 276 | isa = PBXGroup; 277 | children = ( 278 | 5DAE5D3E1CFC6D130029355E /* PrediKit.h */, 279 | 5DAE5D3C1CFC6D130029355E /* Info.plist */, 280 | 5DAE5D3D1CFC6D130029355E /* TestsInfo.plist */, 281 | ); 282 | path = iOS; 283 | sourceTree = ""; 284 | }; 285 | 5DAE5D3F1CFC6D130029355E /* OSX */ = { 286 | isa = PBXGroup; 287 | children = ( 288 | 5DAE5D421CFC6D130029355E /* PrediKit.h */, 289 | 5DAE5D401CFC6D130029355E /* Info.plist */, 290 | 5DAE5D411CFC6D130029355E /* TestsInfo.plist */, 291 | ); 292 | path = OSX; 293 | sourceTree = ""; 294 | }; 295 | 5DAE5D7A1CFC7D590029355E /* Protocols */ = { 296 | isa = PBXGroup; 297 | children = ( 298 | 5DAE5D7B1CFC7D590029355E /* Matchable.swift */, 299 | 5DAE5D7C1CFC7D590029355E /* NilComparable.swift */, 300 | 5DAE5D7D1CFC7D590029355E /* Queryable.swift */, 301 | 5DAE5D7E1CFC7D590029355E /* Reflectable.swift */, 302 | ); 303 | path = Protocols; 304 | sourceTree = ""; 305 | }; 306 | 5DAE5D7F1CFC7D590029355E /* Queries */ = { 307 | isa = PBXGroup; 308 | children = ( 309 | 5DAE5D801CFC7D590029355E /* BasicQuery.swift */, 310 | 5DAE5D811CFC7D590029355E /* BooleanQuery.swift */, 311 | 5DAE5D821CFC7D590029355E /* DateQuery.swift */, 312 | 5DAE5D831CFC7D590029355E /* MemberQuery.swift */, 313 | 5DAE5D841CFC7D590029355E /* NumberQuery.swift */, 314 | 5DAE5D851CFC7D590029355E /* SequenceQuery.swift */, 315 | 5DAE5D861CFC7D590029355E /* StringQuery.swift */, 316 | ); 317 | path = Queries; 318 | sourceTree = ""; 319 | }; 320 | 5DAE5D9D1CFC7DAE0029355E /* Builders */ = { 321 | isa = PBXGroup; 322 | children = ( 323 | 5DAE5D9E1CFC7DAE0029355E /* FinalizedIncluder.swift */, 324 | 5DAE5D9F1CFC7DAE0029355E /* PredicateBuilder.swift */, 325 | 5DAE5DA01CFC7DAE0029355E /* PredicateSubqueryBuilder.swift */, 326 | ); 327 | path = Builders; 328 | sourceTree = ""; 329 | }; 330 | 5DAE5DA71CFC7DDA0029355E /* Extensions */ = { 331 | isa = PBXGroup; 332 | children = ( 333 | 5DAE5DA81CFC7DDA0029355E /* NSPredicate+PrediKit.swift */, 334 | 5DDCFE071D8E77CB000DB59C /* Number+PrediKit.swift */, 335 | ); 336 | path = Extensions; 337 | sourceTree = ""; 338 | }; 339 | 5DAE5DAB1CFC7E1E0029355E /* Value Types */ = { 340 | isa = PBXGroup; 341 | children = ( 342 | 5DAE5DAC1CFC7E1E0029355E /* PredicateOptions.swift */, 343 | 5DAE5DAD1CFC7E1E0029355E /* SubqueryMatchTypes.swift */, 344 | ); 345 | path = "Value Types"; 346 | sourceTree = ""; 347 | }; 348 | 5DAE5DE21CFC91440029355E /* tvOS */ = { 349 | isa = PBXGroup; 350 | children = ( 351 | 5DAE5DE41CFC91440029355E /* PrediKit.h */, 352 | 5DAE5DE31CFC91440029355E /* Info.plist */, 353 | 5DAE5DE51CFC91440029355E /* TestsInfo.plist */, 354 | ); 355 | path = tvOS; 356 | sourceTree = ""; 357 | }; 358 | 5DAE5DE61CFC91440029355E /* watchOS */ = { 359 | isa = PBXGroup; 360 | children = ( 361 | 5DAE5DE81CFC91440029355E /* PrediKit.h */, 362 | 5DAE5DE71CFC91440029355E /* Info.plist */, 363 | ); 364 | path = watchOS; 365 | sourceTree = ""; 366 | }; 367 | /* End PBXGroup section */ 368 | 369 | /* Begin PBXHeadersBuildPhase section */ 370 | 28F8287A1C494B2C00330CF4 /* Headers */ = { 371 | isa = PBXHeadersBuildPhase; 372 | buildActionMask = 2147483647; 373 | files = ( 374 | 5DAE5D451CFC6D130029355E /* PrediKit.h in Headers */, 375 | ); 376 | runOnlyForDeploymentPostprocessing = 0; 377 | }; 378 | 5DAE5DBB1CFC90430029355E /* Headers */ = { 379 | isa = PBXHeadersBuildPhase; 380 | buildActionMask = 2147483647; 381 | files = ( 382 | 5DAE5DF31CFC917D0029355E /* PrediKit.h in Headers */, 383 | ); 384 | runOnlyForDeploymentPostprocessing = 0; 385 | }; 386 | 5DAE5DC81CFC909C0029355E /* Headers */ = { 387 | isa = PBXHeadersBuildPhase; 388 | buildActionMask = 2147483647; 389 | files = ( 390 | 5DAE5DF41CFC918C0029355E /* PrediKit.h in Headers */, 391 | ); 392 | runOnlyForDeploymentPostprocessing = 0; 393 | }; 394 | 5DAF1EA81CFA9619006BAD71 /* Headers */ = { 395 | isa = PBXHeadersBuildPhase; 396 | buildActionMask = 2147483647; 397 | files = ( 398 | 5DAE5D491CFC6EE60029355E /* PrediKit.h in Headers */, 399 | ); 400 | runOnlyForDeploymentPostprocessing = 0; 401 | }; 402 | /* End PBXHeadersBuildPhase section */ 403 | 404 | /* Begin PBXNativeTarget section */ 405 | 28F8287C1C494B2C00330CF4 /* PrediKit iOS */ = { 406 | isa = PBXNativeTarget; 407 | buildConfigurationList = 28F828911C494B2C00330CF4 /* Build configuration list for PBXNativeTarget "PrediKit iOS" */; 408 | buildPhases = ( 409 | 28F828781C494B2C00330CF4 /* Sources */, 410 | 28F828791C494B2C00330CF4 /* Frameworks */, 411 | 28F8287A1C494B2C00330CF4 /* Headers */, 412 | ); 413 | buildRules = ( 414 | ); 415 | dependencies = ( 416 | ); 417 | name = "PrediKit iOS"; 418 | productName = PrediKit; 419 | productReference = 28F8287D1C494B2C00330CF4 /* PrediKit.framework */; 420 | productType = "com.apple.product-type.framework"; 421 | }; 422 | 28F828861C494B2C00330CF4 /* PrediKit iOSTests */ = { 423 | isa = PBXNativeTarget; 424 | buildConfigurationList = 28F828941C494B2C00330CF4 /* Build configuration list for PBXNativeTarget "PrediKit iOSTests" */; 425 | buildPhases = ( 426 | 28F828831C494B2C00330CF4 /* Sources */, 427 | 28F828841C494B2C00330CF4 /* Frameworks */, 428 | 28F828851C494B2C00330CF4 /* Resources */, 429 | ); 430 | buildRules = ( 431 | ); 432 | dependencies = ( 433 | 28F8288A1C494B2C00330CF4 /* PBXTargetDependency */, 434 | ); 435 | name = "PrediKit iOSTests"; 436 | productName = PrediKitTests; 437 | productReference = 28F828871C494B2C00330CF4 /* PrediKit iOSTests.xctest */; 438 | productType = "com.apple.product-type.bundle.unit-test"; 439 | }; 440 | 5DAE5DBD1CFC90430029355E /* PrediKit watchOS */ = { 441 | isa = PBXNativeTarget; 442 | buildConfigurationList = 5DAE5DC31CFC90430029355E /* Build configuration list for PBXNativeTarget "PrediKit watchOS" */; 443 | buildPhases = ( 444 | 5DAE5DB91CFC90430029355E /* Sources */, 445 | 5DAE5DBA1CFC90430029355E /* Frameworks */, 446 | 5DAE5DBB1CFC90430029355E /* Headers */, 447 | 5DAE5DBC1CFC90430029355E /* Resources */, 448 | ); 449 | buildRules = ( 450 | ); 451 | dependencies = ( 452 | ); 453 | name = "PrediKit watchOS"; 454 | productName = "PrediKit-watchOS"; 455 | productReference = 5DAE5DBE1CFC90430029355E /* PrediKit.framework */; 456 | productType = "com.apple.product-type.framework"; 457 | }; 458 | 5DAE5DCA1CFC909C0029355E /* PrediKit tvOS */ = { 459 | isa = PBXNativeTarget; 460 | buildConfigurationList = 5DAE5DDC1CFC909D0029355E /* Build configuration list for PBXNativeTarget "PrediKit tvOS" */; 461 | buildPhases = ( 462 | 5DAE5DC61CFC909C0029355E /* Sources */, 463 | 5DAE5DC71CFC909C0029355E /* Frameworks */, 464 | 5DAE5DC81CFC909C0029355E /* Headers */, 465 | 5DAE5DC91CFC909C0029355E /* Resources */, 466 | ); 467 | buildRules = ( 468 | ); 469 | dependencies = ( 470 | ); 471 | name = "PrediKit tvOS"; 472 | productName = "PrediKit-tvOS"; 473 | productReference = 5DAE5DCB1CFC909C0029355E /* PrediKit.framework */; 474 | productType = "com.apple.product-type.framework"; 475 | }; 476 | 5DAE5DD31CFC909D0029355E /* PrediKit tvOSTests */ = { 477 | isa = PBXNativeTarget; 478 | buildConfigurationList = 5DAE5DDF1CFC909D0029355E /* Build configuration list for PBXNativeTarget "PrediKit tvOSTests" */; 479 | buildPhases = ( 480 | 5DAE5DD01CFC909D0029355E /* Sources */, 481 | 5DAE5DD11CFC909D0029355E /* Frameworks */, 482 | 5DAE5DD21CFC909D0029355E /* Resources */, 483 | ); 484 | buildRules = ( 485 | ); 486 | dependencies = ( 487 | 5DAE5DD71CFC909D0029355E /* PBXTargetDependency */, 488 | ); 489 | name = "PrediKit tvOSTests"; 490 | productName = "PrediKit-tvOSTests"; 491 | productReference = 5DAE5DD41CFC909D0029355E /* PrediKit tvOSTests.xctest */; 492 | productType = "com.apple.product-type.bundle.unit-test"; 493 | }; 494 | 5DAF1EAA1CFA9619006BAD71 /* PrediKit OSX */ = { 495 | isa = PBXNativeTarget; 496 | buildConfigurationList = 5DAF1EBC1CFA9619006BAD71 /* Build configuration list for PBXNativeTarget "PrediKit OSX" */; 497 | buildPhases = ( 498 | 5DAF1EA61CFA9619006BAD71 /* Sources */, 499 | 5DAF1EA71CFA9619006BAD71 /* Frameworks */, 500 | 5DAF1EA81CFA9619006BAD71 /* Headers */, 501 | ); 502 | buildRules = ( 503 | ); 504 | dependencies = ( 505 | ); 506 | name = "PrediKit OSX"; 507 | productName = "PrediKit-OSX"; 508 | productReference = 5DAF1EAB1CFA9619006BAD71 /* PrediKit.framework */; 509 | productType = "com.apple.product-type.framework"; 510 | }; 511 | 5DAF1EB31CFA9619006BAD71 /* PrediKit OSXTests */ = { 512 | isa = PBXNativeTarget; 513 | buildConfigurationList = 5DAF1EBF1CFA9619006BAD71 /* Build configuration list for PBXNativeTarget "PrediKit OSXTests" */; 514 | buildPhases = ( 515 | 5DAF1EB01CFA9619006BAD71 /* Sources */, 516 | 5DAF1EB11CFA9619006BAD71 /* Frameworks */, 517 | 5DAF1EB21CFA9619006BAD71 /* Resources */, 518 | ); 519 | buildRules = ( 520 | ); 521 | dependencies = ( 522 | 5DAF1EB71CFA9619006BAD71 /* PBXTargetDependency */, 523 | ); 524 | name = "PrediKit OSXTests"; 525 | productName = "PrediKit-OSXTests"; 526 | productReference = 5DAF1EB41CFA9619006BAD71 /* PrediKit OSXTests.xctest */; 527 | productType = "com.apple.product-type.bundle.unit-test"; 528 | }; 529 | /* End PBXNativeTarget section */ 530 | 531 | /* Begin PBXProject section */ 532 | 28F828741C494B2C00330CF4 /* Project object */ = { 533 | isa = PBXProject; 534 | attributes = { 535 | LastSwiftUpdateCheck = 0730; 536 | LastUpgradeCheck = 1000; 537 | TargetAttributes = { 538 | 28F8287C1C494B2C00330CF4 = { 539 | CreatedOnToolsVersion = 7.2; 540 | LastSwiftMigration = 0920; 541 | }; 542 | 28F828861C494B2C00330CF4 = { 543 | CreatedOnToolsVersion = 7.2; 544 | LastSwiftMigration = 0920; 545 | }; 546 | 5DAE5DBD1CFC90430029355E = { 547 | CreatedOnToolsVersion = 7.3.1; 548 | LastSwiftMigration = 0920; 549 | }; 550 | 5DAE5DCA1CFC909C0029355E = { 551 | CreatedOnToolsVersion = 7.3.1; 552 | LastSwiftMigration = 0920; 553 | }; 554 | 5DAE5DD31CFC909D0029355E = { 555 | CreatedOnToolsVersion = 7.3.1; 556 | LastSwiftMigration = 0920; 557 | }; 558 | 5DAF1EAA1CFA9619006BAD71 = { 559 | CreatedOnToolsVersion = 7.3.1; 560 | LastSwiftMigration = 0920; 561 | }; 562 | 5DAF1EB31CFA9619006BAD71 = { 563 | CreatedOnToolsVersion = 7.3.1; 564 | LastSwiftMigration = 0920; 565 | }; 566 | }; 567 | }; 568 | buildConfigurationList = 28F828771C494B2C00330CF4 /* Build configuration list for PBXProject "PrediKit" */; 569 | compatibilityVersion = "Xcode 3.2"; 570 | developmentRegion = English; 571 | hasScannedForEncodings = 0; 572 | knownRegions = ( 573 | en, 574 | ); 575 | mainGroup = 28F828731C494B2C00330CF4; 576 | productRefGroup = 28F8287E1C494B2C00330CF4 /* Products */; 577 | projectDirPath = ""; 578 | projectRoot = ""; 579 | targets = ( 580 | 28F8287C1C494B2C00330CF4 /* PrediKit iOS */, 581 | 28F828861C494B2C00330CF4 /* PrediKit iOSTests */, 582 | 5DAF1EAA1CFA9619006BAD71 /* PrediKit OSX */, 583 | 5DAF1EB31CFA9619006BAD71 /* PrediKit OSXTests */, 584 | 5DAE5DCA1CFC909C0029355E /* PrediKit tvOS */, 585 | 5DAE5DD31CFC909D0029355E /* PrediKit tvOSTests */, 586 | 5DAE5DBD1CFC90430029355E /* PrediKit watchOS */, 587 | ); 588 | }; 589 | /* End PBXProject section */ 590 | 591 | /* Begin PBXResourcesBuildPhase section */ 592 | 28F828851C494B2C00330CF4 /* Resources */ = { 593 | isa = PBXResourcesBuildPhase; 594 | buildActionMask = 2147483647; 595 | files = ( 596 | ); 597 | runOnlyForDeploymentPostprocessing = 0; 598 | }; 599 | 5DAE5DBC1CFC90430029355E /* Resources */ = { 600 | isa = PBXResourcesBuildPhase; 601 | buildActionMask = 2147483647; 602 | files = ( 603 | ); 604 | runOnlyForDeploymentPostprocessing = 0; 605 | }; 606 | 5DAE5DC91CFC909C0029355E /* Resources */ = { 607 | isa = PBXResourcesBuildPhase; 608 | buildActionMask = 2147483647; 609 | files = ( 610 | ); 611 | runOnlyForDeploymentPostprocessing = 0; 612 | }; 613 | 5DAE5DD21CFC909D0029355E /* Resources */ = { 614 | isa = PBXResourcesBuildPhase; 615 | buildActionMask = 2147483647; 616 | files = ( 617 | ); 618 | runOnlyForDeploymentPostprocessing = 0; 619 | }; 620 | 5DAF1EB21CFA9619006BAD71 /* Resources */ = { 621 | isa = PBXResourcesBuildPhase; 622 | buildActionMask = 2147483647; 623 | files = ( 624 | ); 625 | runOnlyForDeploymentPostprocessing = 0; 626 | }; 627 | /* End PBXResourcesBuildPhase section */ 628 | 629 | /* Begin PBXSourcesBuildPhase section */ 630 | 28F828781C494B2C00330CF4 /* Sources */ = { 631 | isa = PBXSourcesBuildPhase; 632 | buildActionMask = 2147483647; 633 | files = ( 634 | 5DAE5D9B1CFC7D590029355E /* StringQuery.swift in Sources */, 635 | 5DAE5D951CFC7D590029355E /* MemberQuery.swift in Sources */, 636 | 5DAE5D971CFC7D590029355E /* NumberQuery.swift in Sources */, 637 | 5DAE5D8F1CFC7D590029355E /* BasicQuery.swift in Sources */, 638 | 5DAE5DA51CFC7DAE0029355E /* PredicateSubqueryBuilder.swift in Sources */, 639 | 5DAE5D931CFC7D590029355E /* DateQuery.swift in Sources */, 640 | 5DAE5D8B1CFC7D590029355E /* Queryable.swift in Sources */, 641 | 5DAE5D8D1CFC7D590029355E /* Reflectable.swift in Sources */, 642 | 5DAE5DB01CFC7E1E0029355E /* SubqueryMatchTypes.swift in Sources */, 643 | 5DAE5DA91CFC7DDA0029355E /* NSPredicate+PrediKit.swift in Sources */, 644 | 5DAE5D871CFC7D590029355E /* Matchable.swift in Sources */, 645 | 5DAE5DA31CFC7DAE0029355E /* PredicateBuilder.swift in Sources */, 646 | 5DAE5D911CFC7D590029355E /* BooleanQuery.swift in Sources */, 647 | 5DAE5DA11CFC7DAE0029355E /* FinalizedIncluder.swift in Sources */, 648 | 5DAE5D991CFC7D590029355E /* SequenceQuery.swift in Sources */, 649 | 5DAE5DAE1CFC7E1E0029355E /* PredicateOptions.swift in Sources */, 650 | 5DDCFE081D8E77CB000DB59C /* Number+PrediKit.swift in Sources */, 651 | 5DAE5D891CFC7D590029355E /* NilComparable.swift in Sources */, 652 | ); 653 | runOnlyForDeploymentPostprocessing = 0; 654 | }; 655 | 28F828831C494B2C00330CF4 /* Sources */ = { 656 | isa = PBXSourcesBuildPhase; 657 | buildActionMask = 2147483647; 658 | files = ( 659 | 5DDAE95E1CEA6F1100C01903 /* Keypaths.swift in Sources */, 660 | 287D0A6E1C4B73BD004566D6 /* PrediKitTests.swift in Sources */, 661 | ); 662 | runOnlyForDeploymentPostprocessing = 0; 663 | }; 664 | 5DAE5DB91CFC90430029355E /* Sources */ = { 665 | isa = PBXSourcesBuildPhase; 666 | buildActionMask = 2147483647; 667 | files = ( 668 | 5DAE5E0D1CFC91E50029355E /* Queryable.swift in Sources */, 669 | 5DAE5E121CFC91E50029355E /* BasicQuery.swift in Sources */, 670 | 5DAE5E101CFC91E50029355E /* PredicateBuilder.swift in Sources */, 671 | 5DAE5E1A1CFC91E50029355E /* SubqueryMatchTypes.swift in Sources */, 672 | 5DAE5E161CFC91E50029355E /* NumberQuery.swift in Sources */, 673 | 5DAE5E151CFC91E50029355E /* MemberQuery.swift in Sources */, 674 | 5DAE5E091CFC91E50029355E /* NSPredicate+PrediKit.swift in Sources */, 675 | 5DAE5E0E1CFC91E50029355E /* Reflectable.swift in Sources */, 676 | 5DAE5E141CFC91E50029355E /* DateQuery.swift in Sources */, 677 | 5DAE5E181CFC91E50029355E /* StringQuery.swift in Sources */, 678 | 5DAE5E171CFC91E50029355E /* SequenceQuery.swift in Sources */, 679 | 5DAE5E0B1CFC91E50029355E /* Matchable.swift in Sources */, 680 | 5DAE5E111CFC91E50029355E /* PredicateSubqueryBuilder.swift in Sources */, 681 | 5DAE5E131CFC91E50029355E /* BooleanQuery.swift in Sources */, 682 | 5DAE5E0F1CFC91E50029355E /* FinalizedIncluder.swift in Sources */, 683 | 5DAE5E0C1CFC91E50029355E /* NilComparable.swift in Sources */, 684 | 5DDCFE0B1D8E77CB000DB59C /* Number+PrediKit.swift in Sources */, 685 | 5DAE5E191CFC91E50029355E /* PredicateOptions.swift in Sources */, 686 | ); 687 | runOnlyForDeploymentPostprocessing = 0; 688 | }; 689 | 5DAE5DC61CFC909C0029355E /* Sources */ = { 690 | isa = PBXSourcesBuildPhase; 691 | buildActionMask = 2147483647; 692 | files = ( 693 | 5DAE5DFB1CFC91E40029355E /* Queryable.swift in Sources */, 694 | 5DAE5E001CFC91E40029355E /* BasicQuery.swift in Sources */, 695 | 5DAE5DFE1CFC91E40029355E /* PredicateBuilder.swift in Sources */, 696 | 5DAE5E081CFC91E40029355E /* SubqueryMatchTypes.swift in Sources */, 697 | 5DAE5E041CFC91E40029355E /* NumberQuery.swift in Sources */, 698 | 5DAE5E031CFC91E40029355E /* MemberQuery.swift in Sources */, 699 | 5DAE5DF71CFC91E40029355E /* NSPredicate+PrediKit.swift in Sources */, 700 | 5DAE5DFC1CFC91E40029355E /* Reflectable.swift in Sources */, 701 | 5DAE5E021CFC91E40029355E /* DateQuery.swift in Sources */, 702 | 5DAE5E061CFC91E40029355E /* StringQuery.swift in Sources */, 703 | 5DAE5E051CFC91E40029355E /* SequenceQuery.swift in Sources */, 704 | 5DAE5DF91CFC91E40029355E /* Matchable.swift in Sources */, 705 | 5DAE5DFF1CFC91E40029355E /* PredicateSubqueryBuilder.swift in Sources */, 706 | 5DAE5E011CFC91E40029355E /* BooleanQuery.swift in Sources */, 707 | 5DAE5DFD1CFC91E40029355E /* FinalizedIncluder.swift in Sources */, 708 | 5DAE5DFA1CFC91E40029355E /* NilComparable.swift in Sources */, 709 | 5DDCFE0A1D8E77CB000DB59C /* Number+PrediKit.swift in Sources */, 710 | 5DAE5E071CFC91E40029355E /* PredicateOptions.swift in Sources */, 711 | ); 712 | runOnlyForDeploymentPostprocessing = 0; 713 | }; 714 | 5DAE5DD01CFC909D0029355E /* Sources */ = { 715 | isa = PBXSourcesBuildPhase; 716 | buildActionMask = 2147483647; 717 | files = ( 718 | 5DAE5DF61CFC91D50029355E /* Keypaths.swift in Sources */, 719 | 5DAE5DF51CFC91D50029355E /* PrediKitTests.swift in Sources */, 720 | ); 721 | runOnlyForDeploymentPostprocessing = 0; 722 | }; 723 | 5DAF1EA61CFA9619006BAD71 /* Sources */ = { 724 | isa = PBXSourcesBuildPhase; 725 | buildActionMask = 2147483647; 726 | files = ( 727 | 5DAE5D9C1CFC7D590029355E /* StringQuery.swift in Sources */, 728 | 5DAE5D961CFC7D590029355E /* MemberQuery.swift in Sources */, 729 | 5DAE5D981CFC7D590029355E /* NumberQuery.swift in Sources */, 730 | 5DAE5D901CFC7D590029355E /* BasicQuery.swift in Sources */, 731 | 5DAE5DA61CFC7DAE0029355E /* PredicateSubqueryBuilder.swift in Sources */, 732 | 5DAE5D941CFC7D590029355E /* DateQuery.swift in Sources */, 733 | 5DAE5D8C1CFC7D590029355E /* Queryable.swift in Sources */, 734 | 5DAE5D8E1CFC7D590029355E /* Reflectable.swift in Sources */, 735 | 5DAE5DB11CFC7E1E0029355E /* SubqueryMatchTypes.swift in Sources */, 736 | 5DAE5DAA1CFC7DDA0029355E /* NSPredicate+PrediKit.swift in Sources */, 737 | 5DAE5D881CFC7D590029355E /* Matchable.swift in Sources */, 738 | 5DAE5DA41CFC7DAE0029355E /* PredicateBuilder.swift in Sources */, 739 | 5DAE5D921CFC7D590029355E /* BooleanQuery.swift in Sources */, 740 | 5DAE5DA21CFC7DAE0029355E /* FinalizedIncluder.swift in Sources */, 741 | 5DAE5D9A1CFC7D590029355E /* SequenceQuery.swift in Sources */, 742 | 5DAE5DAF1CFC7E1E0029355E /* PredicateOptions.swift in Sources */, 743 | 5DDCFE091D8E77CB000DB59C /* Number+PrediKit.swift in Sources */, 744 | 5DAE5D8A1CFC7D590029355E /* NilComparable.swift in Sources */, 745 | ); 746 | runOnlyForDeploymentPostprocessing = 0; 747 | }; 748 | 5DAF1EB01CFA9619006BAD71 /* Sources */ = { 749 | isa = PBXSourcesBuildPhase; 750 | buildActionMask = 2147483647; 751 | files = ( 752 | 5DAF1EC31CFA97C9006BAD71 /* Keypaths.swift in Sources */, 753 | 5DAF1EC21CFA97C9006BAD71 /* PrediKitTests.swift in Sources */, 754 | ); 755 | runOnlyForDeploymentPostprocessing = 0; 756 | }; 757 | /* End PBXSourcesBuildPhase section */ 758 | 759 | /* Begin PBXTargetDependency section */ 760 | 28F8288A1C494B2C00330CF4 /* PBXTargetDependency */ = { 761 | isa = PBXTargetDependency; 762 | target = 28F8287C1C494B2C00330CF4 /* PrediKit iOS */; 763 | targetProxy = 28F828891C494B2C00330CF4 /* PBXContainerItemProxy */; 764 | }; 765 | 5DAE5DD71CFC909D0029355E /* PBXTargetDependency */ = { 766 | isa = PBXTargetDependency; 767 | target = 5DAE5DCA1CFC909C0029355E /* PrediKit tvOS */; 768 | targetProxy = 5DAE5DD61CFC909D0029355E /* PBXContainerItemProxy */; 769 | }; 770 | 5DAF1EB71CFA9619006BAD71 /* PBXTargetDependency */ = { 771 | isa = PBXTargetDependency; 772 | target = 5DAF1EAA1CFA9619006BAD71 /* PrediKit OSX */; 773 | targetProxy = 5DAF1EB61CFA9619006BAD71 /* PBXContainerItemProxy */; 774 | }; 775 | /* End PBXTargetDependency section */ 776 | 777 | /* Begin XCBuildConfiguration section */ 778 | 28F8288F1C494B2C00330CF4 /* Debug */ = { 779 | isa = XCBuildConfiguration; 780 | buildSettings = { 781 | ALWAYS_SEARCH_USER_PATHS = NO; 782 | APPLICATION_EXTENSION_API_ONLY = YES; 783 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 784 | CLANG_CXX_LIBRARY = "libc++"; 785 | CLANG_ENABLE_MODULES = YES; 786 | CLANG_ENABLE_OBJC_ARC = YES; 787 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 788 | CLANG_WARN_BOOL_CONVERSION = YES; 789 | CLANG_WARN_COMMA = YES; 790 | CLANG_WARN_CONSTANT_CONVERSION = YES; 791 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 792 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 793 | CLANG_WARN_EMPTY_BODY = YES; 794 | CLANG_WARN_ENUM_CONVERSION = YES; 795 | CLANG_WARN_INFINITE_RECURSION = YES; 796 | CLANG_WARN_INT_CONVERSION = YES; 797 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 798 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 799 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 800 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 801 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 802 | CLANG_WARN_STRICT_PROTOTYPES = YES; 803 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 804 | CLANG_WARN_UNREACHABLE_CODE = YES; 805 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 806 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 807 | COPY_PHASE_STRIP = NO; 808 | CURRENT_PROJECT_VERSION = 1; 809 | DEBUG_INFORMATION_FORMAT = dwarf; 810 | ENABLE_STRICT_OBJC_MSGSEND = YES; 811 | ENABLE_TESTABILITY = YES; 812 | GCC_C_LANGUAGE_STANDARD = gnu99; 813 | GCC_DYNAMIC_NO_PIC = NO; 814 | GCC_NO_COMMON_BLOCKS = YES; 815 | GCC_OPTIMIZATION_LEVEL = 0; 816 | GCC_PREPROCESSOR_DEFINITIONS = ( 817 | "DEBUG=1", 818 | "$(inherited)", 819 | ); 820 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 821 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 822 | GCC_WARN_UNDECLARED_SELECTOR = YES; 823 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 824 | GCC_WARN_UNUSED_FUNCTION = YES; 825 | GCC_WARN_UNUSED_VARIABLE = YES; 826 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 827 | MACOSX_DEPLOYMENT_TARGET = 10.9; 828 | MTL_ENABLE_DEBUG_INFO = YES; 829 | ONLY_ACTIVE_ARCH = YES; 830 | SDKROOT = iphoneos; 831 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 832 | TARGETED_DEVICE_FAMILY = "1,2"; 833 | VERSIONING_SYSTEM = "apple-generic"; 834 | VERSION_INFO_PREFIX = ""; 835 | }; 836 | name = Debug; 837 | }; 838 | 28F828901C494B2C00330CF4 /* Release */ = { 839 | isa = XCBuildConfiguration; 840 | buildSettings = { 841 | ALWAYS_SEARCH_USER_PATHS = NO; 842 | APPLICATION_EXTENSION_API_ONLY = YES; 843 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x"; 844 | CLANG_CXX_LIBRARY = "libc++"; 845 | CLANG_ENABLE_MODULES = YES; 846 | CLANG_ENABLE_OBJC_ARC = YES; 847 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 848 | CLANG_WARN_BOOL_CONVERSION = YES; 849 | CLANG_WARN_COMMA = YES; 850 | CLANG_WARN_CONSTANT_CONVERSION = YES; 851 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 852 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 853 | CLANG_WARN_EMPTY_BODY = YES; 854 | CLANG_WARN_ENUM_CONVERSION = YES; 855 | CLANG_WARN_INFINITE_RECURSION = YES; 856 | CLANG_WARN_INT_CONVERSION = YES; 857 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 858 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 859 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 860 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 861 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 862 | CLANG_WARN_STRICT_PROTOTYPES = YES; 863 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 864 | CLANG_WARN_UNREACHABLE_CODE = YES; 865 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 866 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; 867 | COPY_PHASE_STRIP = NO; 868 | CURRENT_PROJECT_VERSION = 1; 869 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 870 | ENABLE_NS_ASSERTIONS = NO; 871 | ENABLE_STRICT_OBJC_MSGSEND = YES; 872 | GCC_C_LANGUAGE_STANDARD = gnu99; 873 | GCC_NO_COMMON_BLOCKS = YES; 874 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 875 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 876 | GCC_WARN_UNDECLARED_SELECTOR = YES; 877 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 878 | GCC_WARN_UNUSED_FUNCTION = YES; 879 | GCC_WARN_UNUSED_VARIABLE = YES; 880 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 881 | MACOSX_DEPLOYMENT_TARGET = 10.9; 882 | MTL_ENABLE_DEBUG_INFO = NO; 883 | SDKROOT = iphoneos; 884 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; 885 | TARGETED_DEVICE_FAMILY = "1,2"; 886 | VALIDATE_PRODUCT = YES; 887 | VERSIONING_SYSTEM = "apple-generic"; 888 | VERSION_INFO_PREFIX = ""; 889 | }; 890 | name = Release; 891 | }; 892 | 28F828921C494B2C00330CF4 /* Debug */ = { 893 | isa = XCBuildConfiguration; 894 | buildSettings = { 895 | APPLICATION_EXTENSION_API_ONLY = YES; 896 | CLANG_ENABLE_MODULES = YES; 897 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 898 | DEFINES_MODULE = YES; 899 | DYLIB_COMPATIBILITY_VERSION = 1; 900 | DYLIB_CURRENT_VERSION = 1; 901 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 902 | INFOPLIST_FILE = "$(SRCROOT)/Supporting Files/iOS/Info.plist"; 903 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 904 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 905 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 906 | ONLY_ACTIVE_ARCH = YES; 907 | PRODUCT_BUNDLE_IDENTIFIER = "com.KrakenDev.PrediKit-iOS"; 908 | PRODUCT_NAME = PrediKit; 909 | SKIP_INSTALL = YES; 910 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 911 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 912 | SWIFT_VERSION = 4.2; 913 | }; 914 | name = Debug; 915 | }; 916 | 28F828931C494B2C00330CF4 /* Release */ = { 917 | isa = XCBuildConfiguration; 918 | buildSettings = { 919 | APPLICATION_EXTENSION_API_ONLY = YES; 920 | CLANG_ENABLE_MODULES = YES; 921 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; 922 | DEFINES_MODULE = YES; 923 | DYLIB_COMPATIBILITY_VERSION = 1; 924 | DYLIB_CURRENT_VERSION = 1; 925 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 926 | INFOPLIST_FILE = "$(SRCROOT)/Supporting Files/iOS/Info.plist"; 927 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 928 | IPHONEOS_DEPLOYMENT_TARGET = 8.0; 929 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 930 | ONLY_ACTIVE_ARCH = YES; 931 | PRODUCT_BUNDLE_IDENTIFIER = "com.KrakenDev.PrediKit-iOS"; 932 | PRODUCT_NAME = PrediKit; 933 | SKIP_INSTALL = YES; 934 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 935 | SWIFT_VERSION = 4.2; 936 | }; 937 | name = Release; 938 | }; 939 | 28F828951C494B2C00330CF4 /* Debug */ = { 940 | isa = XCBuildConfiguration; 941 | buildSettings = { 942 | APPLICATION_EXTENSION_API_ONLY = NO; 943 | CLANG_ENABLE_MODULES = YES; 944 | INFOPLIST_FILE = "$(SRCROOT)/Supporting Files/iOS/TestsInfo.plist"; 945 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 946 | PRODUCT_BUNDLE_IDENTIFIER = com.KrakenDev.PrediKit.PrediKitTests; 947 | PRODUCT_NAME = "$(TARGET_NAME)"; 948 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 949 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 950 | SWIFT_VERSION = 4.2; 951 | }; 952 | name = Debug; 953 | }; 954 | 28F828961C494B2C00330CF4 /* Release */ = { 955 | isa = XCBuildConfiguration; 956 | buildSettings = { 957 | APPLICATION_EXTENSION_API_ONLY = NO; 958 | CLANG_ENABLE_MODULES = YES; 959 | INFOPLIST_FILE = "$(SRCROOT)/Supporting Files/iOS/TestsInfo.plist"; 960 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 961 | PRODUCT_BUNDLE_IDENTIFIER = com.KrakenDev.PrediKit.PrediKitTests; 962 | PRODUCT_NAME = "$(TARGET_NAME)"; 963 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 964 | SWIFT_VERSION = 4.2; 965 | }; 966 | name = Release; 967 | }; 968 | 5DAE5DC41CFC90430029355E /* Debug */ = { 969 | isa = XCBuildConfiguration; 970 | buildSettings = { 971 | APPLICATION_EXTENSION_API_ONLY = YES; 972 | CLANG_ANALYZER_NONNULL = YES; 973 | "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; 974 | DEFINES_MODULE = YES; 975 | DYLIB_COMPATIBILITY_VERSION = 1; 976 | DYLIB_CURRENT_VERSION = 1; 977 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 978 | INFOPLIST_FILE = "$(SRCROOT)/Supporting Files/watchOS/Info.plist"; 979 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 980 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 981 | PRODUCT_BUNDLE_IDENTIFIER = "com.KrakenDev.PrediKit-watchOS"; 982 | PRODUCT_NAME = PrediKit; 983 | SDKROOT = watchos; 984 | SKIP_INSTALL = YES; 985 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 986 | SWIFT_VERSION = 4.2; 987 | TARGETED_DEVICE_FAMILY = 4; 988 | WATCHOS_DEPLOYMENT_TARGET = 2.2; 989 | }; 990 | name = Debug; 991 | }; 992 | 5DAE5DC51CFC90430029355E /* Release */ = { 993 | isa = XCBuildConfiguration; 994 | buildSettings = { 995 | APPLICATION_EXTENSION_API_ONLY = YES; 996 | CLANG_ANALYZER_NONNULL = YES; 997 | "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; 998 | DEFINES_MODULE = YES; 999 | DYLIB_COMPATIBILITY_VERSION = 1; 1000 | DYLIB_CURRENT_VERSION = 1; 1001 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 1002 | INFOPLIST_FILE = "$(SRCROOT)/Supporting Files/watchOS/Info.plist"; 1003 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 1004 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 1005 | PRODUCT_BUNDLE_IDENTIFIER = "com.KrakenDev.PrediKit-watchOS"; 1006 | PRODUCT_NAME = PrediKit; 1007 | SDKROOT = watchos; 1008 | SKIP_INSTALL = YES; 1009 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 1010 | SWIFT_VERSION = 4.2; 1011 | TARGETED_DEVICE_FAMILY = 4; 1012 | WATCHOS_DEPLOYMENT_TARGET = 2.2; 1013 | }; 1014 | name = Release; 1015 | }; 1016 | 5DAE5DDD1CFC909D0029355E /* Debug */ = { 1017 | isa = XCBuildConfiguration; 1018 | buildSettings = { 1019 | CLANG_ANALYZER_NONNULL = YES; 1020 | "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; 1021 | DEFINES_MODULE = YES; 1022 | DYLIB_COMPATIBILITY_VERSION = 1; 1023 | DYLIB_CURRENT_VERSION = 1; 1024 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 1025 | INFOPLIST_FILE = "$(SRCROOT)/Supporting Files/tvOS/Info.plist"; 1026 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 1027 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 1028 | PRODUCT_BUNDLE_IDENTIFIER = "com.KrakenDev.PrediKit-tvOS"; 1029 | PRODUCT_NAME = PrediKit; 1030 | SDKROOT = appletvos; 1031 | SKIP_INSTALL = YES; 1032 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 1033 | SWIFT_VERSION = 4.2; 1034 | TARGETED_DEVICE_FAMILY = 3; 1035 | TVOS_DEPLOYMENT_TARGET = 9.0; 1036 | }; 1037 | name = Debug; 1038 | }; 1039 | 5DAE5DDE1CFC909D0029355E /* Release */ = { 1040 | isa = XCBuildConfiguration; 1041 | buildSettings = { 1042 | CLANG_ANALYZER_NONNULL = YES; 1043 | "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; 1044 | DEFINES_MODULE = YES; 1045 | DYLIB_COMPATIBILITY_VERSION = 1; 1046 | DYLIB_CURRENT_VERSION = 1; 1047 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 1048 | INFOPLIST_FILE = "$(SRCROOT)/Supporting Files/tvOS/Info.plist"; 1049 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 1050 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 1051 | PRODUCT_BUNDLE_IDENTIFIER = "com.KrakenDev.PrediKit-tvOS"; 1052 | PRODUCT_NAME = PrediKit; 1053 | SDKROOT = appletvos; 1054 | SKIP_INSTALL = YES; 1055 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 1056 | SWIFT_VERSION = 4.2; 1057 | TARGETED_DEVICE_FAMILY = 3; 1058 | TVOS_DEPLOYMENT_TARGET = 9.0; 1059 | }; 1060 | name = Release; 1061 | }; 1062 | 5DAE5DE01CFC909D0029355E /* Debug */ = { 1063 | isa = XCBuildConfiguration; 1064 | buildSettings = { 1065 | APPLICATION_EXTENSION_API_ONLY = NO; 1066 | CLANG_ANALYZER_NONNULL = YES; 1067 | INFOPLIST_FILE = "$(SRCROOT)/Supporting Files/tvOS/TestsInfo.plist"; 1068 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 1069 | PRODUCT_BUNDLE_IDENTIFIER = "com.KrakenDev.PrediKit-tvOSTests"; 1070 | PRODUCT_NAME = "$(TARGET_NAME)"; 1071 | SDKROOT = appletvos; 1072 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 1073 | SWIFT_VERSION = 4.2; 1074 | TVOS_DEPLOYMENT_TARGET = 9.2; 1075 | }; 1076 | name = Debug; 1077 | }; 1078 | 5DAE5DE11CFC909D0029355E /* Release */ = { 1079 | isa = XCBuildConfiguration; 1080 | buildSettings = { 1081 | APPLICATION_EXTENSION_API_ONLY = NO; 1082 | CLANG_ANALYZER_NONNULL = YES; 1083 | INFOPLIST_FILE = "$(SRCROOT)/Supporting Files/tvOS/TestsInfo.plist"; 1084 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; 1085 | PRODUCT_BUNDLE_IDENTIFIER = "com.KrakenDev.PrediKit-tvOSTests"; 1086 | PRODUCT_NAME = "$(TARGET_NAME)"; 1087 | SDKROOT = appletvos; 1088 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 1089 | SWIFT_VERSION = 4.2; 1090 | TVOS_DEPLOYMENT_TARGET = 9.2; 1091 | }; 1092 | name = Release; 1093 | }; 1094 | 5DAF1EBD1CFA9619006BAD71 /* Debug */ = { 1095 | isa = XCBuildConfiguration; 1096 | buildSettings = { 1097 | CLANG_ANALYZER_NONNULL = YES; 1098 | CODE_SIGN_IDENTITY = ""; 1099 | COMBINE_HIDPI_IMAGES = YES; 1100 | DEFINES_MODULE = YES; 1101 | DYLIB_COMPATIBILITY_VERSION = 1; 1102 | DYLIB_CURRENT_VERSION = 1; 1103 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 1104 | FRAMEWORK_VERSION = A; 1105 | INFOPLIST_FILE = "$(SRCROOT)/Supporting Files/OSX/Info.plist"; 1106 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 1107 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; 1108 | MACOSX_DEPLOYMENT_TARGET = 10.9; 1109 | PRODUCT_BUNDLE_IDENTIFIER = "com.KrakenDev.PrediKit-OSX"; 1110 | PRODUCT_NAME = PrediKit; 1111 | SDKROOT = macosx; 1112 | SKIP_INSTALL = YES; 1113 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 1114 | SWIFT_VERSION = 4.2; 1115 | }; 1116 | name = Debug; 1117 | }; 1118 | 5DAF1EBE1CFA9619006BAD71 /* Release */ = { 1119 | isa = XCBuildConfiguration; 1120 | buildSettings = { 1121 | CLANG_ANALYZER_NONNULL = YES; 1122 | CODE_SIGN_IDENTITY = ""; 1123 | COMBINE_HIDPI_IMAGES = YES; 1124 | DEFINES_MODULE = YES; 1125 | DYLIB_COMPATIBILITY_VERSION = 1; 1126 | DYLIB_CURRENT_VERSION = 1; 1127 | DYLIB_INSTALL_NAME_BASE = "@rpath"; 1128 | FRAMEWORK_VERSION = A; 1129 | INFOPLIST_FILE = "$(SRCROOT)/Supporting Files/OSX/Info.plist"; 1130 | INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; 1131 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; 1132 | MACOSX_DEPLOYMENT_TARGET = 10.9; 1133 | PRODUCT_BUNDLE_IDENTIFIER = "com.KrakenDev.PrediKit-OSX"; 1134 | PRODUCT_NAME = PrediKit; 1135 | SDKROOT = macosx; 1136 | SKIP_INSTALL = YES; 1137 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 1138 | SWIFT_VERSION = 4.2; 1139 | }; 1140 | name = Release; 1141 | }; 1142 | 5DAF1EC01CFA9619006BAD71 /* Debug */ = { 1143 | isa = XCBuildConfiguration; 1144 | buildSettings = { 1145 | APPLICATION_EXTENSION_API_ONLY = NO; 1146 | CLANG_ANALYZER_NONNULL = YES; 1147 | CODE_SIGN_IDENTITY = "-"; 1148 | COMBINE_HIDPI_IMAGES = YES; 1149 | INFOPLIST_FILE = "$(SRCROOT)/Supporting Files/OSX/TestsInfo.plist"; 1150 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 1151 | MACOSX_DEPLOYMENT_TARGET = 10.11; 1152 | PRODUCT_BUNDLE_IDENTIFIER = "com.KrakenDev.PrediKit-OSXTests"; 1153 | PRODUCT_NAME = "$(TARGET_NAME)"; 1154 | SDKROOT = macosx; 1155 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 1156 | SWIFT_VERSION = 4.2; 1157 | }; 1158 | name = Debug; 1159 | }; 1160 | 5DAF1EC11CFA9619006BAD71 /* Release */ = { 1161 | isa = XCBuildConfiguration; 1162 | buildSettings = { 1163 | APPLICATION_EXTENSION_API_ONLY = NO; 1164 | CLANG_ANALYZER_NONNULL = YES; 1165 | CODE_SIGN_IDENTITY = "-"; 1166 | COMBINE_HIDPI_IMAGES = YES; 1167 | INFOPLIST_FILE = "$(SRCROOT)/Supporting Files/OSX/TestsInfo.plist"; 1168 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; 1169 | MACOSX_DEPLOYMENT_TARGET = 10.11; 1170 | PRODUCT_BUNDLE_IDENTIFIER = "com.KrakenDev.PrediKit-OSXTests"; 1171 | PRODUCT_NAME = "$(TARGET_NAME)"; 1172 | SDKROOT = macosx; 1173 | SWIFT_SWIFT3_OBJC_INFERENCE = Default; 1174 | SWIFT_VERSION = 4.2; 1175 | }; 1176 | name = Release; 1177 | }; 1178 | /* End XCBuildConfiguration section */ 1179 | 1180 | /* Begin XCConfigurationList section */ 1181 | 28F828771C494B2C00330CF4 /* Build configuration list for PBXProject "PrediKit" */ = { 1182 | isa = XCConfigurationList; 1183 | buildConfigurations = ( 1184 | 28F8288F1C494B2C00330CF4 /* Debug */, 1185 | 28F828901C494B2C00330CF4 /* Release */, 1186 | ); 1187 | defaultConfigurationIsVisible = 0; 1188 | defaultConfigurationName = Release; 1189 | }; 1190 | 28F828911C494B2C00330CF4 /* Build configuration list for PBXNativeTarget "PrediKit iOS" */ = { 1191 | isa = XCConfigurationList; 1192 | buildConfigurations = ( 1193 | 28F828921C494B2C00330CF4 /* Debug */, 1194 | 28F828931C494B2C00330CF4 /* Release */, 1195 | ); 1196 | defaultConfigurationIsVisible = 0; 1197 | defaultConfigurationName = Release; 1198 | }; 1199 | 28F828941C494B2C00330CF4 /* Build configuration list for PBXNativeTarget "PrediKit iOSTests" */ = { 1200 | isa = XCConfigurationList; 1201 | buildConfigurations = ( 1202 | 28F828951C494B2C00330CF4 /* Debug */, 1203 | 28F828961C494B2C00330CF4 /* Release */, 1204 | ); 1205 | defaultConfigurationIsVisible = 0; 1206 | defaultConfigurationName = Release; 1207 | }; 1208 | 5DAE5DC31CFC90430029355E /* Build configuration list for PBXNativeTarget "PrediKit watchOS" */ = { 1209 | isa = XCConfigurationList; 1210 | buildConfigurations = ( 1211 | 5DAE5DC41CFC90430029355E /* Debug */, 1212 | 5DAE5DC51CFC90430029355E /* Release */, 1213 | ); 1214 | defaultConfigurationIsVisible = 0; 1215 | defaultConfigurationName = Release; 1216 | }; 1217 | 5DAE5DDC1CFC909D0029355E /* Build configuration list for PBXNativeTarget "PrediKit tvOS" */ = { 1218 | isa = XCConfigurationList; 1219 | buildConfigurations = ( 1220 | 5DAE5DDD1CFC909D0029355E /* Debug */, 1221 | 5DAE5DDE1CFC909D0029355E /* Release */, 1222 | ); 1223 | defaultConfigurationIsVisible = 0; 1224 | defaultConfigurationName = Release; 1225 | }; 1226 | 5DAE5DDF1CFC909D0029355E /* Build configuration list for PBXNativeTarget "PrediKit tvOSTests" */ = { 1227 | isa = XCConfigurationList; 1228 | buildConfigurations = ( 1229 | 5DAE5DE01CFC909D0029355E /* Debug */, 1230 | 5DAE5DE11CFC909D0029355E /* Release */, 1231 | ); 1232 | defaultConfigurationIsVisible = 0; 1233 | defaultConfigurationName = Release; 1234 | }; 1235 | 5DAF1EBC1CFA9619006BAD71 /* Build configuration list for PBXNativeTarget "PrediKit OSX" */ = { 1236 | isa = XCConfigurationList; 1237 | buildConfigurations = ( 1238 | 5DAF1EBD1CFA9619006BAD71 /* Debug */, 1239 | 5DAF1EBE1CFA9619006BAD71 /* Release */, 1240 | ); 1241 | defaultConfigurationIsVisible = 0; 1242 | defaultConfigurationName = Release; 1243 | }; 1244 | 5DAF1EBF1CFA9619006BAD71 /* Build configuration list for PBXNativeTarget "PrediKit OSXTests" */ = { 1245 | isa = XCConfigurationList; 1246 | buildConfigurations = ( 1247 | 5DAF1EC01CFA9619006BAD71 /* Debug */, 1248 | 5DAF1EC11CFA9619006BAD71 /* Release */, 1249 | ); 1250 | defaultConfigurationIsVisible = 0; 1251 | defaultConfigurationName = Release; 1252 | }; 1253 | /* End XCConfigurationList section */ 1254 | }; 1255 | rootObject = 28F828741C494B2C00330CF4 /* Project object */; 1256 | } 1257 | -------------------------------------------------------------------------------- /PrediKit.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /PrediKit.xcodeproj/xcshareddata/xcschemes/PrediKit OSX.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 34 | 40 | 41 | 42 | 43 | 44 | 50 | 51 | 52 | 53 | 54 | 55 | 65 | 66 | 72 | 73 | 74 | 75 | 76 | 77 | 83 | 84 | 90 | 91 | 92 | 93 | 95 | 96 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /PrediKit.xcodeproj/xcshareddata/xcschemes/PrediKit iOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 34 | 40 | 41 | 42 | 43 | 44 | 50 | 51 | 52 | 53 | 54 | 55 | 65 | 66 | 72 | 73 | 74 | 75 | 76 | 77 | 83 | 84 | 90 | 91 | 92 | 93 | 95 | 96 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /PrediKit.xcodeproj/xcshareddata/xcschemes/PrediKit tvOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 34 | 40 | 41 | 42 | 43 | 44 | 50 | 51 | 52 | 53 | 54 | 55 | 65 | 66 | 72 | 73 | 74 | 75 | 76 | 77 | 83 | 84 | 90 | 91 | 92 | 93 | 95 | 96 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /PrediKit.xcodeproj/xcshareddata/xcschemes/PrediKit watchOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 33 | 34 | 35 | 36 | 46 | 47 | 53 | 54 | 55 | 56 | 57 | 58 | 64 | 65 | 71 | 72 | 73 | 74 | 76 | 77 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Pretty Banner](https://github.com/KrakenDev/PrediKit/wiki/Sources/Banner.jpg) 2 | [![Travis Build Status](http://img.shields.io/travis/KrakenDev/PrediKit.svg?style=flat-square)](https://travis-ci.org/KrakenDev/PrediKit) 3 | [![Coveralls](https://img.shields.io/coveralls/KrakenDev/PrediKit/master.svg?style=flat-square)](https://coveralls.io/github/KrakenDev/PrediKit?branch=master) 4 | [![Supported Platforms](https://img.shields.io/cocoapods/p/PrediKit.svg?style=flat-square)]() 5 | [![Swift Version Compatibility](https://img.shields.io/badge/swift3-compatible-4BC51D.svg?style=flat-square)](https://developer.apple.com/swift) 6 | [![Cocoapods Version](https://img.shields.io/badge/pod-4.0.0-blue.svg?style=flat-square)](https://cocoapods.org/pods/PrediKit) 7 | [![Carthage Compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat-square)](https://github.com/Carthage/Carthage) 8 | [![LICENSE](http://img.shields.io/badge/license-MIT-blue.svg?style=flat-square)](https://raw.githubusercontent.com/KrakenDev/PrediKit/master/LICENSE) 9 | 10 | # PrediKit 11 | A Swift `NSPredicate` DSL for iOS & OS X inspired by [SnapKit](https://github.com/SnapKit/SnapKit), lovingly written in Swift, and created by that weird dude at [KrakenDev](https://krakendev.io). 12 | 13 | If you're familiar with the intuitive feel of the [SnapKit](https://github.com/SnapKit/SnapKit) API, then you'll feel right at home with PrediKit! 💖 14 | 15 | # Documentation 16 | Documentation is generated by [Jazzy](https://github.com/realm/jazzy) and can be found [here for your convenience](http://krakendev.github.io/PrediKit)! 17 | 18 | # Why create PrediKit? 19 | Because I wanted to! Also, because `NSPredicate` creation is hard. When working with `CoreData` you use `NSPredicates` to fetch persisted objects. `CoreData` is hard enough, so why have the additional complexity of using a complex string-based predicate system? 20 | 21 | The language you need to use to create predicate formats are completely string-based which can lead to a host of issues: 22 | * We have a tendency to misspell words and with the current system, you can build and run without ever knowing you misspelled a property name...until it's too late. 23 | * For many an iOS/OSX developer (myself included), they may be very unfamiliar with the `SQL`-like language that comes with the creation of predicate formats. In fact, an entire [cheatsheet](https://realm.io/news/nspredicate-cheatsheet/) by the awesome guys at [Realm](https://realm.io/) was created because of this! 24 | * With complex predicate creation, it's easy to get string-blindness. Go ahead...try creating a complex predicate and reading it after a couple of hours. I dare you. 😎 25 | * If you misspell a property key name in a predicate format string but the string is parseable by the `NSPredicate` system, then nothing happens. It just fails silently. At least, I think it does. I'm currently writing this on 2 hours of sleep. Don't quote me on that. 26 | 27 | # What does it fix? 28 | Well, hopefully it fixes all of the above and more. Currently, it: 29 | * Gives the developer a closure based way to create NSPredicates. 30 | * It also, through the magic of Xcode, gives you a way to autocomplete your queries. No more referencing a cheatsheet. Just hit the dot button and enjoy the autocomplete. 31 | * I also carefully constructed the API to read as much like a book as possible. Matching strings even have a redundant API just to be grammatically correct when specifying if a string `matches` or `doesNot.match` another value. 32 | * Through a little runtime-magic/reflection, PrediKit will crash at runtime if you misspell a property key or supply a property key that does not exist in a specific class' property list. 33 | * All predicate `builder` closures do not need capture semantics as each closure is a `@noescape` closure. [Read here if you don't know what that means](http://krakendev.io/blog/hipster-swift#noescape) 🤓. 34 | 35 | # Installation 36 | PrediKit can be included in your project through any of these methods: 37 | 38 | ## CocoaPods 39 | [CocoaPods](http://cocoapods.org) is a dependency manager for Cocoa projects. You can install it with the following command: 40 | 41 | ```bash 42 | $ gem install cocoapods 43 | ``` 44 | 45 | > CocoaPods 0.39.0+ is required to build PrediKit 46 | 47 | To integrate PrediKit through CocoaPods, make sure the `use_frameworks!` line is included in your Podfile (PrediKit is written in Swift so it needs to be brought in as a framework). Make sure these lines are somewhere in your `Podfile`: 48 | 49 | ```ruby 50 | use_frameworks! 51 | #for the latest version that is compatible with Swift 4.2 use: 52 | pod 'PrediKit' 53 | #for the latest version that is compatible with legacy Swift 2.3 use this instead: 54 | pod 'PrediKit', :git => 'https://github.com/KrakenDev/PrediKit.git', :branch => 'swift2.3' 55 | #for the latest version that is compatible with legacy Swift 2.1 use this instead: 56 | pod 'PrediKit', :git => 'https://github.com/KrakenDev/PrediKit.git', :branch => 'swift2.1' 57 | ``` 58 | 59 | Then, run the following command: 60 | 61 | ```bash 62 | $ pod install 63 | ``` 64 | 65 | Afterwards, whenever you need PrediKit, add this line to the top of the file it's being used in: 66 | 67 | ```swift 68 | import PrediKit 69 | ``` 70 | 71 | ## Carthage 72 | [Carthage](https://github.com/Carthage/Carthage) is a decentralized dependency manager that builds your dependencies and provides you with binary frameworks. 73 | 74 | You can install Carthage with [Homebrew](http://brew.sh/) using the following command: 75 | 76 | ```bash 77 | $ brew update 78 | $ brew install carthage 79 | ``` 80 | 81 | To integrate PrediKit into your Xcode project using Carthage, specify it in your `Cartfile`: 82 | 83 | ```ogdl 84 | github "KrakenDev/PrediKit" 85 | ``` 86 | 87 | For legacy swift versions, I am keeping the swift2.1 && swift2.3 branches open for you. However, no more dev will be done for them (I will however, happily accept pull requests for any feature I happen to add!) If you are using these legacy versions, you should be able to use this instead in your Cartfile: 88 | 89 | ```ogdl 90 | github "KrakenDev/PrediKit" "swift2.1" 91 | github "KrakenDev/PrediKit" "swift2.3" 92 | ``` 93 | 94 | Run `carthage update` to build the framework and drag the built `PrediKit.framework` into your Xcode project. 95 | 96 | Afterwards, whenever you need PrediKit, add this line to the top of the file it's being used in: 97 | 98 | ```swift 99 | import PrediKit 100 | ``` 101 | 102 | ## Manually 103 | 104 | If you prefer not to use either of the aforementioned dependency managers, you can integrate PrediKit into your project manually. 105 | 106 | First, copy and paste these commands into Terminal: 107 | ```bash 108 | git clone https://github.com/KrakenDev/PrediKit.git 109 | open PrediKit/Sources/ 110 | ``` 111 | 112 | This should open a Finder window with the important files needed for PrediKit located in the Sources folder of the repo. Drag these folders into your project (preferable in a folder named "PrediKit") and code away! Since you would be copying these files into your project directly, there is no need for the `import PrediKit` line in any of the files that you need it. 113 | 114 | The downside to this is that you can not update PrediKit easily. You would need to repeat these steps each time you wanna grab the latest and greatest! 😱 115 | 116 | # Usage 117 | 118 | ***PSA: IF YOU HATE STRINGLY TYPED APIs LIKE I DO, THEN CHECK OUT THE SECTION ON SWIFT 3's #keyPath() AT THE BOTTOM OF THE README!!!*** 119 | 120 | PrediKit tries to make `NSPredicate` creation easy. **Heavily** inspired by [SnapKit's](https://github.com/SnapKit/SnapKit) API, the API for PrediKit is extremely similar for people who love it as much as I do. Check it out. This example creates a predicate used to fetch a `ManagedObject` from `CoreData`: 121 | 122 | ```swift 123 | let predicate = NSPredicate(ManagedLegend.self) { includeIf in 124 | includeIf.string("title").equals("The Almighty Kraken") 125 | } 126 | ``` 127 | 128 | To check if a property is nil: 129 | 130 | ```swift 131 | let predicate = NSPredicate(ManagedLegend.self) { includeIf in 132 | includeIf.string("title").equalsNil() 133 | } 134 | ``` 135 | 136 | PrediKit can also query member properties. Say you have a class structure like this: 137 | 138 | ```swift 139 | class Captain: NSObject { 140 | var name: String 141 | } 142 | class Ship: NSObject { 143 | var captain: Captain 144 | } 145 | ``` 146 | 147 | And you want to create these predicates: 148 | 149 | ```swift 150 | let someCaptain = Captain() 151 | NSPredicate(format: "captain == %@ && captain.name == 'Chief Supreme'", someCaptain) 152 | ``` 153 | 154 | Creating the above with PrediKit is easy and expressive: 155 | 156 | ```swift 157 | let someCaptain = Captain() 158 | let predicate = NSPredicate(Ship.self) { includeIf 159 | let includeIfShipCaptain = includeIf.member("captain", ofType: Captain.self) 160 | includeIfShipCaptain.equals(someCaptain) && 161 | includeIfShipCaptain.string("name").equals("Chief Supreme") 162 | } 163 | ``` 164 | 165 | PrediKit also overloads the `&&`, `||`, and `!` operators. This allows you compound and specify whether or `not` to include your `includers` (Crappy name, I know. Feel free to give me suggestions). 166 | 167 | ```swift 168 | let predicate = NSPredicate(ManagedLegend.self) { includeIf in 169 | //Include any ManagedLegend instance if the property named "string" is NOT nil and does NOT equal "The Almighty Kraken" 170 | !includeIf.string("title").equalsNil() && 171 | !includeIf.string("title").equals("The Almighty Kraken") && 172 | 173 | //Also include any ManagedLegend instance if the date property named "birthdate" is in the past or if the bool property "isAwesome" is true. 174 | includeIf.date("birthdate").isEarlierThan(NSDate()) || 175 | includeIf.bool("isAwesome").isTrue() 176 | } 177 | ``` 178 | 179 | You can even create `includers` conditionally! 180 | 181 | ```swift 182 | let predicate = NSPredicate(ManagedLegend.self) { includeIf in 183 | let isKrakenQuery = includeIf.string("title").equals("The Almighty Kraken") 184 | let birthdateQuery = includeIf.date("birthdate").isEarlierThan(NSDate()) 185 | let isAwesomeQuery = includeIf.bool("isAwesome").isTrue 186 | 187 | if shouldCheckBirthdate { 188 | (isKrakenQuery && birthdateQuery) || isAwesomeQuery 189 | } else { 190 | isKrakenQuery || isAwesomeQuery 191 | } 192 | } 193 | ``` 194 | I don't know about y'all, but the `SQL`-like `IN` operator was hard to wrap my head around. PrediKit makes this a little more human-readable: 195 | 196 | ```swift 197 | let awesomePeeps = ["Kraken", "Cthulhu", "Voldemort", "Ember", "Umber", "Voldemort"] 198 | let predicate = NSPredicate(ManagedLegend.self) { includeIf in 199 | includeIf.string("title").matchesAnyValueIn(awesomePeeps) 200 | } 201 | ``` 202 | 203 | PrediKit also has built-in support for subquery predicates: 204 | 205 | ```swift 206 | let predicate = NSPredicate(ManagedLegend.self) { includeIf in 207 | includeIf.string("title").equals("The Almighty Kraken") && 208 | 209 | //Only include Krakens that have more than three hungry cerberus friends 210 | includeIf.collection("cerberusFriends").subquery(ManagedCerberus.self) { 211 | $0.bool("isHungry").isTrue() 212 | return .IncludeIfMatched(.Amount(.IsGreaterThan(3))) 213 | } 214 | } 215 | ``` 216 | 217 | # #keyPath() + PrediKit = 💖 218 | PrediKit becomes MUCH more expressive and safer when using Swift 3's #keyPath syntax. I don't know about you but I HATE stringly typed APIs. The best part of #keyPath is that you get autocompletion of your properties and a way to get sub properties without using PrediKit's `.member` functions: 219 | 220 | ```swift 221 | let predicate = NSPredicate(ManagedLegend.self) { includeIf in 222 | includeIf.string(#keyPath(ManagedLegend.title)).equals("The Almighty Kraken") 223 | } 224 | let predicate = NSPredicate(ManagedLegend.self) { includeIf in 225 | includeIf.string(#keyPath(ManagedLegend.bestFriend.title)).equals("The Cool Elf") 226 | } 227 | ``` 228 | 229 | 230 | # Selector Extension Pattern Variation 231 | Personally, I love using a variation of the [Selector Extension Pattern](https://medium.com/swift-programming/swift-selector-syntax-sugar-81c8a8b10df3#.bypt7blba) when using PrediKit. It allows you to avoid misspelling your property names when using the API. It also allows you to rename your keypath properties at will. By renaming, every instance of that keyPath used by PrediKit should give you a compiler error so you don't miss a beat and can feel safe knowing you haven't missed any property names in a name change refactor. By creating a String extension like so: 232 | 233 | ```swift 234 | import Foundation 235 | 236 | extension String { 237 | static let title = #keyPath(Kraken.title) 238 | static let birthdate = #keyPath(Kraken.birthdate) 239 | static let age = #keyPath(Kraken.age) 240 | static let friends = #keyPath(Kraken.friends) 241 | static let isAwesome = #keyPath(Kraken.isAwesome) 242 | static let isHungry = #keyPath(Kraken.isHungry) 243 | } 244 | ``` 245 | 246 | PrediKit becomes a lot more expressive now: 247 | 248 | ```swift 249 | //BEFORE 250 | let predicate = NSPredicate(ManagedLegend.self) { includeIf in 251 | includeIf.string(#keyPath(ManagedLegend.title)).equals("The Almighty Kraken") 252 | } 253 | //AFTER 254 | let predicate = NSPredicate(ManagedLegend.self) { includeIf in 255 | includeIf.string(.title).equals("The Almighty Kraken") 256 | } 257 | ``` 258 | 259 | # LICENSE 260 | PrediKit is licensed under the `MIT` license. Check out the `LICENSE` file to learn more. 261 | -------------------------------------------------------------------------------- /Sources/Builders/FinalizedIncluder.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FinalizedIncluder.swift 3 | // PrediKit 4 | // 5 | // Created by Hector Matos on 5/30/16. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | /** 12 | A class that finalizes an includer created within the builder closure of PrediKit's `NSPredicate` convenience initializer. All includers (basically any line of code within that closure) must end in a call to a function that returns an instance or subclassed instance of this class to create a valid `NSPredicate`. 13 | */ 14 | public final class FinalizedIncluder { 15 | fileprivate let builder: PredicateBuilder 16 | fileprivate var finalArguments: [Any?] 17 | 18 | fileprivate(set) var finalPredicateString: String 19 | fileprivate(set) var ANDPredicatesToCombine: [String] = [] 20 | fileprivate(set) var ORPredicatesToCombine: [String] = [] 21 | 22 | init(builder: PredicateBuilder, arguments: [Any?] = []) { 23 | self.builder = builder 24 | self.finalPredicateString = builder.predicateString 25 | self.finalArguments = arguments 26 | } 27 | 28 | fileprivate static func combine(_ lhs: FinalizedIncluder, rhs: FinalizedIncluder, logicalAND: Bool) -> FinalizedIncluder { 29 | let lhsPredicate = lhs.finalPredicateString 30 | let rhsPredicate = rhs.finalPredicateString 31 | let predicateFormat: String 32 | 33 | var lhsPredicatesToCombine = logicalAND ? lhs.ANDPredicatesToCombine : lhs.ORPredicatesToCombine 34 | var rhsPredicatesToCombine = logicalAND ? rhs.ANDPredicatesToCombine : rhs.ORPredicatesToCombine 35 | 36 | if lhsPredicatesToCombine.isEmpty && rhsPredicatesToCombine.isEmpty { 37 | lhsPredicatesToCombine = [lhsPredicate, rhsPredicate] 38 | rhsPredicatesToCombine = lhsPredicatesToCombine 39 | } else if rhsPredicatesToCombine.isEmpty { 40 | //Operators associate to the left so there's no need to check if the lhs combination predicate array is empty since it will always have something if it gets here by failing the first check. 41 | lhsPredicatesToCombine.append(rhsPredicate) 42 | rhsPredicatesToCombine = lhsPredicatesToCombine 43 | } else { 44 | lhsPredicatesToCombine.append(contentsOf: rhsPredicatesToCombine) 45 | rhsPredicatesToCombine = lhsPredicatesToCombine 46 | } 47 | 48 | if logicalAND { 49 | lhs.ANDPredicatesToCombine = lhsPredicatesToCombine 50 | rhs.ANDPredicatesToCombine = lhsPredicatesToCombine 51 | 52 | predicateFormat = "(\(lhsPredicatesToCombine.joined(separator: " && ")))" 53 | } else { 54 | lhs.ORPredicatesToCombine = lhsPredicatesToCombine 55 | rhs.ORPredicatesToCombine = lhsPredicatesToCombine 56 | 57 | predicateFormat = "(\(lhsPredicatesToCombine.joined(separator: " || ")))" 58 | } 59 | 60 | lhs.finalPredicateString = predicateFormat 61 | 62 | lhs.builder.predicateString = lhs.finalPredicateString 63 | rhs.builder.predicateString = lhs.builder.predicateString 64 | 65 | lhs.finalArguments = lhs.finalArguments + rhs.finalArguments 66 | rhs.finalArguments = lhs.finalArguments 67 | lhs.builder.arguments = lhs.finalArguments 68 | rhs.builder.arguments = lhs.finalArguments 69 | 70 | return lhs 71 | } 72 | } 73 | 74 | /** 75 | Convenience infix `&&` operator that combines two `FinalizedIncluder` instances into one `FinalizedIncluder` that represents the ANDed compound of the `finalizedPredicate` properties in each instance. 76 | 77 | Essentially, you use this operator to join together two includers. 78 | */ 79 | @discardableResult public func && (lhs: FinalizedIncluder, rhs: FinalizedIncluder) -> FinalizedIncluder { 80 | return .combine(lhs, rhs: rhs, logicalAND: true) 81 | } 82 | 83 | /** 84 | Convenience infix `||` operator that combines two `FinalizedIncluder` instances into one `FinalizedIncluder` that represents the ORed compound of the `finalizedPredicate` properties in each instance. 85 | 86 | Essentially, you use this operator to join together two includers. 87 | */ 88 | @discardableResult public func || (lhs: FinalizedIncluder, rhs: FinalizedIncluder) -> FinalizedIncluder { 89 | return .combine(lhs, rhs: rhs, logicalAND: false) 90 | } 91 | 92 | /** 93 | Convenience prefix `!` operator that turns `FinalizedIncluder` into its NOT version. 94 | 95 | Essentially, you use this operator to indicate the opposite of an includer. 96 | */ 97 | @discardableResult public prefix func ! (rhs: FinalizedIncluder) -> FinalizedIncluder { 98 | rhs.finalPredicateString = "!(\(rhs.finalPredicateString))" 99 | rhs.builder.predicateString = rhs.finalPredicateString 100 | return rhs 101 | } 102 | -------------------------------------------------------------------------------- /Sources/Builders/PredicateBuilder.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PredicateBuilder.swift 3 | // PrediKit 4 | // 5 | // Created by Hector Matos on 5/30/16. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | /** 12 | The class that gets passed into the builder closure of PrediKit's `NSPredicate` convenience initializer. 13 | */ 14 | open class PredicateBuilder { 15 | let type: T.Type 16 | var predicateString: String = "" 17 | var arguments: [Any?] = [] 18 | 19 | /** 20 | Used to indicate that you want to query the actual object checked when the predicate is run. Behaves like the `SELF` in the SQL-like query: 21 | 22 | NSPredicate(format: "SELF in names") 23 | */ 24 | open var SELF: BasicQuery { 25 | return BasicQuery(builder: self, property: "SELF") 26 | } 27 | 28 | init(type: T.Type) { 29 | self.type = type 30 | } 31 | 32 | /** 33 | Describes the key of class `T`'s `String` property you want to query. For example, when creating a predicate that compares a class' string property to a given string: 34 | 35 | class Kraken: NSObject { 36 | var theyCallMe: String 37 | } 38 | NSPredicate(format: "theyCallMe == 'Chief Supreme'") 39 | 40 | The `property` parameter would be the "theyCallMe" in the example predicate format. 41 | 42 | - Parameters: 43 | - property: The name of the property in the class of type `T` 44 | - file: Name of the file the function is being called from. Defaults to `#file` 45 | - line: Number of the line the function is being called from. Defaults to `#line` 46 | */ 47 | open func string(_ property: String, file: String = #file, line: Int = #line) -> StringQuery { 48 | return StringQuery(builder: self, property: validatedProperty(property, file: file, line: line)) 49 | } 50 | 51 | /** 52 | Describes the key of class `T`'s number type property you want to query. For example, when creating a predicate that compares a number property to a given value: 53 | 54 | class Kraken: NSObject { 55 | var numberOfHumansEaten: Int 56 | } 57 | NSPredicate(format: "numberOfHumansEaten >= 6") 58 | 59 | The `property` parameter would be the "numberOfHumansEaten" in the example predicate format. 60 | 61 | - Parameters: 62 | - property: The name of the property in the class of type `T` 63 | - file: Name of the file the function is being called from. Defaults to `#file` 64 | - line: Number of the line the function is being called from. Defaults to `#line` 65 | */ 66 | open func number(_ property: String, file: String = #file, line: Int = #line) -> NumberQuery { 67 | return NumberQuery(builder: self, property: validatedProperty(property, file: file, line: line)) 68 | } 69 | 70 | /** 71 | Describes the key of class `T`'s `NSDate` property you want to query. For example, when creating a predicate compares a class' date property to another given date: 72 | 73 | class Kraken: NSObject { 74 | var birthdate: NSDate 75 | } 76 | NSPredicate(format: "birthdate == %@", NSDate()) 77 | 78 | The `property` parameter would be the "birthdate" in the example predicate format. 79 | 80 | - Parameters: 81 | - property: The name of the property in the class of type `T` 82 | - file: Name of the file the function is being called from. Defaults to `#file` 83 | - line: Number of the line the function is being called from. Defaults to `#line` 84 | */ 85 | open func date(_ property: String, file: String = #file, line: Int = #line) -> DateQuery { 86 | return DateQuery(builder: self, property: validatedProperty(property, file: file, line: line)) 87 | } 88 | 89 | /** 90 | Describes the key of class `T`'s `Bool` property you want to query. For example, when creating a predicate that checks against a given `Bool` flag in a class: 91 | 92 | class Kraken: NSObject { 93 | var isHungry: Bool 94 | } 95 | NSPredicate(format: "isHungry == true") 96 | 97 | The `property` parameter would be the "isHungry" in the example predicate format. 98 | 99 | - Parameters: 100 | - property: The name of the property in the class of type `T` 101 | - file: Name of the file the function is being called from. Defaults to `#file` 102 | - line: Number of the line the function is being called from. Defaults to `#line` 103 | */ 104 | open func bool(_ property: String, file: String = #file, line: Int = #line) -> BooleanQuery { 105 | return BooleanQuery(builder: self, property: validatedProperty(property, file: file, line: line)) 106 | } 107 | 108 | /** 109 | Describes the key of class `T`'s `CollectionType` property you want to query. This is also the starting point for subqueries on list properties. For example, when creating a predicate that checks if a class' array property has 5 objects: 110 | 111 | class Kraken: NSObject { 112 | var friends: [LegendaryCreatures] 113 | } 114 | NSPredicate(format: "friends.@count == 5") 115 | 116 | The `property` parameter would be the "friends" in the example predicate format. 117 | 118 | - Parameters: 119 | - property: The name of the property in the class of type `T` 120 | - file: Name of the file the function is being called from. Defaults to `#file` 121 | - line: Number of the line the function is being called from. Defaults to `#line` 122 | */ 123 | open func collection(_ property: String, file: String = #file, line: Int = #line) -> SequenceQuery { 124 | return SequenceQuery(builder: self, property: validatedProperty(property, file: file, line: line)) 125 | } 126 | 127 | /** 128 | Describes a member with a custom type that belongs to class `T` that you want to query. Recursive since it returns an instance of PredicateMemberQuery that is a subclass of PredicateBuilder. For example, when creating a predicate for a specific custom type member: 129 | 130 | class Kraken: NSObject { 131 | var friend: LegendaryCreature 132 | } 133 | NSPredicate(format: "friend == %@", legendaryCreature) 134 | 135 | The `property` parameter would be the "friend" in the example predicate format. 136 | 137 | - Parameters: 138 | - property: The name of the property member in the class of type `T` 139 | - memberType: The Reflectable type of the property member 140 | - file: Name of the file the function is being called from. Defaults to `__FILE__` 141 | - line: Number of the line the function is being called from. Defaults to `__LINE__` 142 | */ 143 | open func member(_ property: String, ofType memberType: U.Type, file: String = #file, line: Int = #line) -> MemberQuery { 144 | return MemberQuery(builder: self, property: validatedProperty(property), memberType: memberType) 145 | } 146 | 147 | internal func validatedProperty(_ property: String, file: String = #file, line: Int = #line) -> String { 148 | if !type.properties().contains(Selector(property)) && self.type != NSObject.self { 149 | #if DEBUG 150 | print("\(String(describing: type)) does not seem to contain property \"\(property)\". This could be due to the optionality of a value type. Possible property key values:\n\(type.properties()).\nWarning in file:\(file) at line \(line)") 151 | #endif 152 | } 153 | 154 | return String(describing: property) 155 | } 156 | } 157 | -------------------------------------------------------------------------------- /Sources/Builders/PredicateSubqueryBuilder.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PredicateSubqueryBuilder.swift 3 | // PrediKit 4 | // 5 | // Created by Hector Matos on 5/30/16. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | /** 12 | A class that facilitates the creation of subqueries against `T`'s `CollectionType` properties. Used in tandem with the `SequenceQuery` class. 13 | */ 14 | public final class PredicateSubqueryBuilder: PredicateBuilder { 15 | override init(type: T.Type) { 16 | super.init(type: type) 17 | } 18 | 19 | override func validatedProperty(_ property: String, file: String, line: Int) -> String { 20 | let subqueryProperty = super.validatedProperty(property, file: file, line: line) 21 | return "$\(String(describing: type))Item.\(subqueryProperty)" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Sources/Extensions/NSPredicate+PrediKit.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PrediKit.swift 3 | // KrakenDev 4 | // 5 | // Created by Hector Matos on 5/13/16. 6 | // Copyright © 2016 KrakenDev. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | /** 12 | This is where the magic happens. This extension allows anyone to construct an `NSPredicate` from a closure. 13 | */ 14 | public extension NSPredicate { 15 | /** 16 | A generic convenience initializer that accepts a `Reflectable` type and a builder closure that allows you to construct includers that describe the resulting `NSPredicate` instance. 17 | 18 | - Parameters: 19 | - type: The `Reflectable` class type that you'll be querying against. The type you supply here is what PrediKit will inspect to ensure the property names you specify in your includers are contained in that class' property list. 20 | - builder: A closure that you use to generate includers that construct each subpredicate in the created `NSPredicate` 21 | */ 22 | convenience init(_ type: T.Type, builder: ((_ includeIf: PredicateBuilder) -> Void)) { 23 | let predicateBuilder = PredicateBuilder(type: type) 24 | builder(predicateBuilder) 25 | 26 | if predicateBuilder.predicateString.isEmpty { 27 | self.init(value: false) 28 | } else { 29 | self.init(format: predicateBuilder.predicateString, argumentArray: predicateBuilder.arguments.compactMap({$0})) 30 | } 31 | } 32 | } 33 | 34 | /** 35 | Convenience infix `&&` operator that combines two `NSPredicate` instances into one ANDed `NSCompoundPredicate` 36 | */ 37 | @discardableResult public func && (lhs: NSPredicate, rhs: NSPredicate) -> NSPredicate { 38 | return NSCompoundPredicate(andPredicateWithSubpredicates: [lhs, rhs]) 39 | } 40 | 41 | /** 42 | Convenience infix `||` operator that combines two `NSPredicate` instances into one ORed `NSCompoundPredicate` 43 | */ 44 | @discardableResult public func || (lhs: NSPredicate, rhs: NSPredicate) -> NSPredicate { 45 | return NSCompoundPredicate(orPredicateWithSubpredicates: [lhs, rhs]) 46 | } 47 | 48 | -------------------------------------------------------------------------------- /Sources/Extensions/Number+PrediKit.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Number+PrediKit.swift 3 | // PrediKit 4 | // 5 | // Created by Hector Matos on 9/18/16. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | public protocol Number {} 12 | 13 | extension Int: Number {} 14 | extension UInt: Number {} 15 | extension Int8: Number {} 16 | extension UInt8: Number {} 17 | extension Int16: Number {} 18 | extension UInt16: Number {} 19 | extension Int32: Number {} 20 | extension UInt32: Number {} 21 | extension Int64: Number {} 22 | extension UInt64: Number {} 23 | extension Float: Number {} 24 | extension Double: Number {} 25 | -------------------------------------------------------------------------------- /Sources/Protocols/Matchable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Matchable.swift 3 | // PrediKit 4 | // 5 | // Created by Hector Matos on 5/30/16. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | /** 12 | Used to give a query object the capability to perform SQL-like IN queries. 13 | */ 14 | public protocol Matchable: Queryable { 15 | /** 16 | Equivalent to the SQL-like `IN` operator where a predicate is created to match if a class' property matches ANY value in a `CollectionType`. Equivalent to creating this predicate: 17 | 18 | class Kraken { 19 | var name: String 20 | } 21 | NSPredicate(format: "name IN %@", listOfObjects) 22 | 23 | "Fetch the `Kraken` object if the value of its `name` property matches any value in the `listOfObjects` array." 24 | 25 | - Parameters: 26 | - collection: An `Array` or `Set` of objects to match against. 27 | */ 28 | @discardableResult func matchesAnyValueIn(_ collection: U) -> FinalizedIncluder 29 | } 30 | 31 | public extension Matchable { 32 | @discardableResult func matchesAnyValueIn(_ collection: U) -> FinalizedIncluder { 33 | builder.predicateString = "\(property) IN %@" 34 | builder.arguments.append(collection) 35 | return FinalizedIncluder(builder: builder, arguments: [collection]) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Sources/Protocols/NilComparable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NilComparable.swift 3 | // PrediKit 4 | // 5 | // Created by Hector Matos on 5/30/16. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | /** 12 | Used to give a query object the capability to perform nil comparison queries. 13 | */ 14 | public protocol NilComparable: Queryable { 15 | /** 16 | Creates an includer that determines if the property being queried is nil. 17 | */ 18 | @discardableResult func equalsNil() -> FinalizedIncluder 19 | } 20 | 21 | public extension NilComparable { 22 | @discardableResult func equalsNil() -> FinalizedIncluder { 23 | builder.predicateString = "\(property) == nil" 24 | return FinalizedIncluder(builder: builder) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Sources/Protocols/Queryable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Queryable.swift 3 | // PrediKit 4 | // 5 | // Created by Hector Matos on 5/30/16. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | /** 12 | Indicates that a class can be used as a Query object. 13 | */ 14 | public protocol Queryable: class { 15 | /** 16 | A generic `Reflectable` that is used to maintain a consistent type throughout the creation of an includer. 17 | */ 18 | associatedtype BuilderType: Reflectable 19 | 20 | /** 21 | A reference to the dependency-injected builder parameter passed in to PrediKit's `NSPredicate` closure initialization. Any changes made to this builder is reflected in the final outcome of the outputted `NSPredicate`. 22 | */ 23 | var builder: PredicateBuilder { get } 24 | /** 25 | The property to query against. 26 | */ 27 | var property: String { get } 28 | } 29 | -------------------------------------------------------------------------------- /Sources/Protocols/Reflectable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Reflectable.swift 3 | // PrediKit 4 | // 5 | // Created by Hector Matos on 5/30/16. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | /** 12 | Used to query the property list of the conforming class. PrediKit uses this protocol to determine if the property you are specifying in the creation of a predicate actually exists in the conforming class. If it doesn't, PrediKit will print a warning to the console. 13 | 14 | All `NSObject`s conform to this protocol through a public extension declared in PrediKit. 15 | */ 16 | public protocol Reflectable: class { 17 | /** 18 | Must implement this protocol in order to take advantage of PrediKit's property warning console print behavior. 19 | 20 | - Returns: An `Array` of `Selector`s. These can just be the strings that equal the names of the properties in your class. 21 | */ 22 | static func properties() -> [Selector] 23 | } 24 | 25 | /** 26 | PrediKit is best used with instances of CoreData's `NSManagedObject`. Since each `NSManagedObject` is an `NSObject`, PrediKit's `NSPredicate` creation works out of the box for all of your `NSManagedObject` subclasses. 27 | */ 28 | private var reflectedClasses: [String : [Selector]] = [:] 29 | extension NSObject: Reflectable { 30 | /** 31 | Uses the Objective-C Runtime to determine the list of properties in an NSObject subclass. 32 | 33 | - Returns: An `Array` of `Selector`s whose string values are equal to the names of each property in the NSObject subclass. 34 | */ 35 | public static func properties() -> [Selector] { 36 | guard let savedPropertyList = reflectedClasses[String(describing: self)] else { 37 | var count: UInt32 = 0 38 | let properties = class_copyPropertyList(self, &count) 39 | var propertyNames: [Selector] = [] 40 | for i in 0..: NilComparable, Matchable { 15 | public let builder: PredicateBuilder 16 | public let property: String 17 | 18 | required public init(builder: PredicateBuilder, property: String) { 19 | self.builder = builder 20 | self.property = property 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Sources/Queries/BooleanQuery.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BooleanQuery.swift 3 | // PrediKit 4 | // 5 | // Created by Hector Matos on 5/30/16. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | /** 12 | A class that queries against `Bool` properties in the `T` class. 13 | */ 14 | public final class BooleanQuery: NilComparable, Matchable { 15 | public let builder: PredicateBuilder 16 | public let property: String 17 | 18 | required public init(builder: PredicateBuilder, property: String) { 19 | self.builder = builder 20 | self.property = property 21 | } 22 | 23 | /** 24 | Equivalent to creating this predicate: 25 | 26 | class Kraken { 27 | var isAwesome: Bool 28 | } 29 | NSPredicate(format: "isAwesome == true") 30 | 31 | "Fetch the `Kraken` object if the value of its `isAwesome` property is true" 32 | */ 33 | @discardableResult public func isTrue() -> FinalizedIncluder { 34 | builder.predicateString = "\(property) == true" 35 | return FinalizedIncluder(builder: builder) 36 | } 37 | 38 | /** 39 | Equivalent to creating this predicate: 40 | 41 | class Kraken { 42 | var isAwesome: Bool 43 | } 44 | NSPredicate(format: "isAwesome == false") 45 | 46 | "Fetch the `Kraken` object if the value of its `isAwesome` property is false" 47 | */ 48 | @discardableResult public func isFalse() -> FinalizedIncluder { 49 | builder.predicateString = "\(property) == false" 50 | return FinalizedIncluder(builder: builder) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Sources/Queries/DateQuery.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DateQuery.swift 3 | // PrediKit 4 | // 5 | // Created by Hector Matos on 5/30/16. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | /** 12 | A class that queries against `NSDate` properties in the `T` class. 13 | */ 14 | public final class DateQuery: NilComparable, Matchable { 15 | public let builder: PredicateBuilder 16 | public let property: String 17 | 18 | required public init(builder: PredicateBuilder, property: String) { 19 | self.builder = builder 20 | self.property = property 21 | } 22 | 23 | /** 24 | Equivalent to the infix `>` operator for two dates. Equivalent to creating this predicate: 25 | 26 | class Kraken { 27 | var birthdate: NSDate 28 | } 29 | NSPredicate(format: "birthdate > %@", halloween2008) 30 | 31 | "Fetch the `Kraken` object if it was born later than halloween2008" 32 | 33 | - Parameters: 34 | - date: The date to compare against the property's value. 35 | - file: Name of the file the function is being called from. Defaults to `#file` 36 | - line: Number of the line the function is being called from. Defaults to `#line` 37 | */ 38 | @discardableResult public func isLaterThan(_ date: Date) -> FinalizedIncluder { 39 | builder.predicateString = "\(property) > %@" 40 | builder.arguments.append(date) 41 | return FinalizedIncluder(builder: builder, arguments: [date]) 42 | } 43 | 44 | /** 45 | Equivalent to the infix `<` operator for two dates. Equivalent to creating this predicate: 46 | 47 | class Kraken { 48 | var birthdate: NSDate 49 | } 50 | NSPredicate(format: "birthdate < %@", halloween2008) 51 | 52 | "Fetch the `Kraken` object if it was born earlier than halloween2008" 53 | 54 | - Parameters: 55 | - date: The date to compare against the property's value. 56 | - file: Name of the file the function is being called from. Defaults to `#file` 57 | - line: Number of the line the function is being called from. Defaults to `#line` 58 | */ 59 | @discardableResult public func isEarlierThan(_ date: Date) -> FinalizedIncluder { 60 | builder.predicateString = "\(property) < %@" 61 | builder.arguments.append(date) 62 | return FinalizedIncluder(builder: builder, arguments: [date]) 63 | } 64 | 65 | /** 66 | Equivalent to the infix `>=` operator for two dates. Equivalent to creating this predicate: 67 | 68 | class Kraken { 69 | var birthdate: NSDate 70 | } 71 | NSPredicate(format: "birthdate >= %@", halloween2008) 72 | 73 | "Fetch the `Kraken` object if it was born on halloween2008 or later than halloween2008" 74 | 75 | - Parameters: 76 | - date: The date to compare against the property's value. 77 | - file: Name of the file the function is being called from. Defaults to `#file` 78 | - line: Number of the line the function is being called from. Defaults to `#line` 79 | */ 80 | @discardableResult public func isLaterThanOrOn(_ date: Date) -> FinalizedIncluder { 81 | builder.predicateString = "\(property) >= %@" 82 | builder.arguments.append(date) 83 | return FinalizedIncluder(builder: builder, arguments: [date]) 84 | } 85 | 86 | /** 87 | Equivalent to the infix `<=` operator for two dates. Equivalent to creating this predicate: 88 | 89 | class Kraken { 90 | var birthdate: NSDate 91 | } 92 | NSPredicate(format: "birthdate <= %@", halloween2008) 93 | 94 | "Fetch the `Kraken` object if it was born on halloween2008 or earlier than halloween2008" 95 | 96 | - Parameters: 97 | - date: The date to compare against the property's value. 98 | - file: Name of the file the function is being called from. Defaults to `#file` 99 | - line: Number of the line the function is being called from. Defaults to `#line` 100 | */ 101 | @discardableResult public func isEarlierThanOrOn(_ date: Date) -> FinalizedIncluder { 102 | builder.predicateString = "\(property) <= %@" 103 | builder.arguments.append(date) 104 | return FinalizedIncluder(builder: builder, arguments: [date]) 105 | } 106 | 107 | /** 108 | Equivalent to the infix `==` operator for two dates. Equivalent to creating this predicate: 109 | 110 | class Kraken { 111 | var birthdate: NSDate 112 | } 113 | NSPredicate(format: "birthdate == %@", halloween2008) 114 | 115 | "Fetch the `Kraken` object if it was born on halloween2008" 116 | 117 | - Parameters: 118 | - date: The date to compare against the property's value. 119 | - file: Name of the file the function is being called from. Defaults to `#file` 120 | - line: Number of the line the function is being called from. Defaults to `#line` 121 | */ 122 | @discardableResult public func equals(_ date: Date) -> FinalizedIncluder { 123 | builder.predicateString = "\(property) == %@" 124 | builder.arguments.append(date) 125 | return FinalizedIncluder(builder: builder, arguments: [date]) 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /Sources/Queries/MemberQuery.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MemberQuery.swift 3 | // PrediKit 4 | // 5 | // Created by Hector Matos on 5/30/16. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | /** 12 | A class that facilitates the creation of subqueries against `T`'s custom member properties. Since the creation of this class is initiated in the `PredicateBuilder` class, and this class inherits from it, then member creation is recursive. 13 | */ 14 | public final class MemberQuery: PredicateBuilder, Matchable, NilComparable { 15 | public let builder: PredicateBuilder 16 | public let property: String 17 | 18 | let memberType: MemberType.Type 19 | 20 | override var predicateString: String { 21 | didSet { builder.predicateString = predicateString } 22 | } 23 | 24 | override var arguments: [Any?] { 25 | didSet { builder.arguments.append(contentsOf: arguments) } 26 | } 27 | 28 | init(builder: PredicateBuilder, property: String, memberType: MemberType.Type) { 29 | self.builder = builder 30 | self.memberType = memberType 31 | self.property = property 32 | 33 | super.init(type: builder.type) 34 | } 35 | 36 | /** 37 | Creates an includer that determines if the property being queried is equivalent to an object of the same type as the property queried. 38 | */ 39 | @discardableResult public func equals(_ object: MemberType) -> FinalizedIncluder { 40 | builder.predicateString = "\(property) == %@" 41 | builder.arguments.append(object) 42 | return FinalizedIncluder(builder: builder, arguments: [object]) 43 | } 44 | 45 | override func validatedProperty(_ property: String, file: String = #file, line: Int = #line) -> String { 46 | if !memberType.properties().contains(Selector(property)) && self.memberType != NSObject.self { 47 | #if DEBUG 48 | print("\(String(describing: type)) does not seem to contain property \"\(property)\". This could be due to the optionality of a value type. Possible property key values:\n\(type.properties()).\nWarning in file:\(file) at line \(line)") 49 | #endif 50 | } 51 | 52 | return "\(self.property).\(String(describing: property))" 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Sources/Queries/NumberQuery.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NumberQuery.swift 3 | // PrediKit 4 | // 5 | // Created by Hector Matos on 5/30/16. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | /** 12 | A class that queries against number properties in the `T` class. 13 | */ 14 | public final class NumberQuery: NilComparable, Matchable { 15 | public let builder: PredicateBuilder 16 | public let property: String 17 | 18 | required public init(builder: PredicateBuilder, property: String) { 19 | self.builder = builder 20 | self.property = property 21 | } 22 | /** 23 | Equivalent to the `>` operator. Equivalent to creating this predicate: 24 | 25 | class Kraken { 26 | var age: Int 27 | } 28 | NSPredicate(format: "age > 5") 29 | 30 | "Fetch the `Kraken` object if the value of its `age` property is greater than 5" 31 | 32 | - Parameters: 33 | - number: The number to compare against the property's value. 34 | - options: Used to describe the sensitivity (diacritic or case) of the string comparator operation. Defaults to PredicateOptions.None 35 | - file: Name of the file the function is being called from. Defaults to `#file` 36 | - line: Number of the line the function is being called from. Defaults to `#line` 37 | */ 38 | @discardableResult public func isGreaterThan(_ number: Number) -> FinalizedIncluder { 39 | builder.predicateString = "\(property) > %@" 40 | builder.arguments.append(number) 41 | return FinalizedIncluder(builder: builder, arguments: [number]) 42 | } 43 | 44 | /** 45 | Equivalent to the `<` operator. Equivalent to creating this predicate: 46 | 47 | class Kraken { 48 | var age: Int 49 | } 50 | NSPredicate(format: "age < 5") 51 | 52 | "Fetch the `Kraken` object if the value of its `age` property is less than 5" 53 | 54 | - Parameters: 55 | - number: The number to compare against the property's value. 56 | - options: Used to describe the sensitivity (diacritic or case) of the string comparator operation. Defaults to PredicateOptions.None 57 | - file: Name of the file the function is being called from. Defaults to `#file` 58 | - line: Number of the line the function is being called from. Defaults to `#line` 59 | */ 60 | @discardableResult public func isLessThan(_ number: Number) -> FinalizedIncluder { 61 | builder.predicateString = "\(property) < %@" 62 | builder.arguments.append(number) 63 | return FinalizedIncluder(builder: builder, arguments: [number]) 64 | } 65 | 66 | /** 67 | Equivalent to the `>=` operator. Equivalent to creating this predicate: 68 | 69 | class Kraken { 70 | var age: Int 71 | } 72 | NSPredicate(format: "age >= 5") 73 | 74 | "Fetch the `Kraken` object if the value of its `age` property is greater than or equal to 5" 75 | 76 | - Parameters: 77 | - number: The number to compare against the property's value. 78 | - options: Used to describe the sensitivity (diacritic or case) of the string comparator operation. Defaults to PredicateOptions.None 79 | - file: Name of the file the function is being called from. Defaults to `#file` 80 | - line: Number of the line the function is being called from. Defaults to `#line` 81 | */ 82 | @discardableResult public func isGreaterThanOrEqualTo(_ number: Number) -> FinalizedIncluder { 83 | builder.predicateString = "\(property) >= %@" 84 | builder.arguments.append(number) 85 | return FinalizedIncluder(builder: builder, arguments: [number]) 86 | } 87 | 88 | /** 89 | Equivalent to the `<=` operator. Equivalent to creating this predicate: 90 | 91 | class Kraken { 92 | var age: Int 93 | } 94 | NSPredicate(format: "age <= 5") 95 | 96 | "Fetch the `Kraken` object if the value of its `age` property is less than or equal to 5" 97 | 98 | - Parameters: 99 | - number: The number to compare against the property's value. 100 | - options: Used to describe the sensitivity (diacritic or case) of the string comparator operation. Defaults to PredicateOptions.None 101 | - file: Name of the file the function is being called from. Defaults to `#file` 102 | - line: Number of the line the function is being called from. Defaults to `#line` 103 | */ 104 | @discardableResult public func isLessThanOrEqualTo(_ number: Number) -> FinalizedIncluder { 105 | builder.predicateString = "\(property) <= %@" 106 | builder.arguments.append(number) 107 | return FinalizedIncluder(builder: builder, arguments: [number]) 108 | } 109 | 110 | /** 111 | Equivalent to the `!=` operator. Equivalent to creating this predicate: 112 | 113 | class Kraken { 114 | var age: Int 115 | } 116 | NSPredicate(format: "age != 5") 117 | 118 | "Fetch the `Kraken` object if the value of its `age` property does not equal 5" 119 | 120 | - Parameters: 121 | - number: The number to compare against the property's value. 122 | - options: Used to describe the sensitivity (diacritic or case) of the string comparator operation. Defaults to PredicateOptions.None 123 | - file: Name of the file the function is being called from. Defaults to `#file` 124 | - line: Number of the line the function is being called from. Defaults to `#line` 125 | */ 126 | @discardableResult public func doesNotEqual(_ number: Number) -> FinalizedIncluder { 127 | builder.predicateString = "\(property) != %@" 128 | builder.arguments.append(number) 129 | return FinalizedIncluder(builder: builder, arguments: [number]) 130 | } 131 | 132 | /** 133 | Equivalent to the `==` operator. Equivalent to creating this predicate: 134 | 135 | class Kraken { 136 | var age: Int 137 | } 138 | NSPredicate(format: "age == 5") 139 | 140 | "Fetch the `Kraken` object if the value of its `age` property equals 5" 141 | 142 | - Parameters: 143 | - number: The number to compare against the property's value. 144 | - options: Used to describe the sensitivity (diacritic or case) of the string comparator operation. Defaults to PredicateOptions.None 145 | - file: Name of the file the function is being called from. Defaults to `#file` 146 | - line: Number of the line the function is being called from. Defaults to `#line` 147 | */ 148 | @discardableResult public func equals(_ number: Number) -> FinalizedIncluder { 149 | builder.predicateString = "\(property) == %@" 150 | builder.arguments.append(number) 151 | return FinalizedIncluder(builder: builder, arguments: [number]) 152 | } 153 | } 154 | -------------------------------------------------------------------------------- /Sources/Queries/SequenceQuery.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SequenceQuery.swift 3 | // PrediKit 4 | // 5 | // Created by Hector Matos on 5/30/16. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | /** 12 | A class that queries against `CollectionType` properties in the `T` class. 13 | */ 14 | public final class SequenceQuery: NilComparable, Matchable { 15 | public let builder: PredicateBuilder 16 | public let property: String 17 | 18 | required public init(builder: PredicateBuilder, property: String) { 19 | self.builder = builder 20 | self.property = property 21 | } 22 | 23 | /** 24 | Equivalent to creating this predicate: 25 | 26 | class Kraken { 27 | var friends: [LegendaryCreature] 28 | } 29 | NSPredicate(format: "friends.@count == 0") 30 | 31 | "Fetch the `Kraken` object if it doesn't have any friends" 32 | */ 33 | @discardableResult public func isEmpty() -> FinalizedIncluder { 34 | builder.predicateString = "\(property).@count == 0" 35 | return FinalizedIncluder(builder: builder) 36 | } 37 | 38 | /** 39 | Creates a subpredicate that iterates through the collection property to return qualifying queries. Equivalent to creating this predicate: 40 | 41 | class Kraken { 42 | var friends: [LegendaryCreature] 43 | } 44 | NSPredicate(format: "SUBQUERY(friends, $friend, friend.isHungry == true).@count > 0") 45 | 46 | "Fetch the `Kraken` object if it has any friends that are hungry" 47 | 48 | - Parameters: 49 | - type: The type of the objects found in the collection property being subqueried. 50 | - subbuilder: A closure that defines queries that describe each object found in the collection property being subqueried. The closure must return an instance of the `SubqueryMatch` enum. 51 | */ 52 | @discardableResult public func subquery(_ type: U.Type, subbuilder: (_ includeIf: PredicateSubqueryBuilder) -> MatchType) -> FinalizedIncluder { 53 | let subBuilder = PredicateSubqueryBuilder(type: type) 54 | let subqueryMatch = subbuilder(subBuilder) 55 | 56 | let item = "$\(String(describing: subBuilder.type))Item" 57 | let subqueryPredicate = "SUBQUERY(\(property), \(item), \(subBuilder.predicateString)).\(subqueryMatch.collectionQuery)" 58 | 59 | builder.predicateString = subqueryPredicate 60 | builder.arguments += subBuilder.arguments 61 | return FinalizedIncluder(builder: builder, arguments: builder.arguments) 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /Sources/Queries/StringQuery.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StringQuery.swift 3 | // PrediKit 4 | // 5 | // Created by Hector Matos on 5/30/16. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | /** 12 | A class that queries against `String` properties in the `T` class. 13 | */ 14 | public final class StringQuery: NilComparable, Matchable { 15 | public let builder: PredicateBuilder 16 | public let property: String 17 | 18 | required public init(builder: PredicateBuilder, property: String) { 19 | self.builder = builder 20 | self.property = property 21 | } 22 | 23 | /// Convenience function for StringQuery's equals(string:) function where we pass an empty string through. 24 | @discardableResult public func isEmpty() -> FinalizedIncluder { 25 | return equals("") 26 | } 27 | 28 | /** 29 | Equivalent to the `BEGINSWITH` operator. Equivalent to creating this predicate: 30 | 31 | class Kraken { 32 | var name: String 33 | } 34 | NSPredicate(format: "name BEGINSWITH \"K\"") 35 | 36 | "Fetch the `Kraken` object if the value of its `name` property begins with the letter 'K'" 37 | 38 | - Parameters: 39 | - string: The string to match the property's value against. 40 | - options: Used to describe the sensitivity (diacritic or case) of the string comparator operation. Defaults to PredicateOptions.None 41 | - file: Name of the file the function is being called from. Defaults to `#file` 42 | - line: Number of the line the function is being called from. Defaults to `#line` 43 | */ 44 | @discardableResult public func beginsWith(_ string: String, options: PredicateOptions = .None) -> FinalizedIncluder { 45 | builder.predicateString = "\(property) BEGINSWITH\(optionsString(options)) \"\(string)\"" 46 | return FinalizedIncluder(builder: builder) 47 | } 48 | 49 | /** 50 | Equivalent to the `ENDSWITH` operator. Equivalent to creating this predicate: 51 | 52 | class Kraken { 53 | var name: String 54 | } 55 | NSPredicate(format: "name ENDSWITH \"n\"") 56 | 57 | "Fetch the `Kraken` object if the value of its `name` property ends with the letter 'n'" 58 | 59 | - Parameters: 60 | - string: The string to match the property's value against. 61 | - options: Used to describe the sensitivity (diacritic or case) of the string comparator operation. Defaults to PredicateOptions.None 62 | - file: Name of the file the function is being called from. Defaults to `#file` 63 | - line: Number of the line the function is being called from. Defaults to `#line` 64 | */ 65 | @discardableResult public func endsWith(_ string: String, options: PredicateOptions = .None) -> FinalizedIncluder { 66 | builder.predicateString = "\(property) ENDSWITH\(optionsString(options)) \"\(string)\"" 67 | return FinalizedIncluder(builder: builder) 68 | } 69 | 70 | /** 71 | Equivalent to the `CONTAINS` operator. Equivalent to creating this predicate: 72 | 73 | class Kraken { 74 | var name: String 75 | } 76 | NSPredicate(format: "name CONTAINS \"rake\"") 77 | 78 | "Fetch the `Kraken` object if the value of its `name` property contains the word 'rake'" 79 | 80 | - Parameters: 81 | - string: The string to match the property's value against. 82 | - options: Used to describe the sensitivity (diacritic or case) of the string comparator operation. Defaults to PredicateOptions.None 83 | - file: Name of the file the function is being called from. Defaults to `#file` 84 | - line: Number of the line the function is being called from. Defaults to `#line` 85 | */ 86 | @discardableResult public func contains(_ string: String, options: PredicateOptions = .None) -> FinalizedIncluder { 87 | builder.predicateString = "\(property) CONTAINS\(optionsString(options)) \"\(string)\"" 88 | return FinalizedIncluder(builder: builder) 89 | } 90 | 91 | /** 92 | Equivalent to the `MATCHES` operator. Equivalent to creating this predicate: 93 | 94 | class Kraken { 95 | var name: String 96 | } 97 | NSPredicate(format: "name MATCHES %@", regex) 98 | 99 | "Fetch the `Kraken` object if the value of its `name` property matches the regular expression pattern stored in the regex variable." 100 | 101 | - Parameters: 102 | - string: The string to match the property's value against. 103 | - options: Used to describe the sensitivity (diacritic or case) of the string comparator operation. Defaults to PredicateOptions.None 104 | - file: Name of the file the function is being called from. Defaults to `#file` 105 | - line: Number of the line the function is being called from. Defaults to `#line` 106 | */ 107 | @discardableResult public func matches(_ string: String, options: PredicateOptions = .None) -> FinalizedIncluder { 108 | builder.predicateString = "\(property) MATCHES\(optionsString(options)) \"\(string)\"" 109 | return FinalizedIncluder(builder: builder) 110 | } 111 | 112 | /** 113 | Equivalent to the `==` operator. Equivalent to creating this predicate: 114 | 115 | class Kraken { 116 | var name: String 117 | } 118 | NSPredicate(format: "name == \"Kraken\"") 119 | 120 | "Fetch the `Kraken` object if the value of its `name` property equals the word 'Kraken'" 121 | 122 | - Parameters: 123 | - string: The string to match the property's value against. 124 | - options: Used to describe the sensitivity (diacritic or case) of the string comparator operation. Defaults to PredicateOptions.None 125 | - file: Name of the file the function is being called from. Defaults to `#file` 126 | - line: Number of the line the function is being called from. Defaults to `#line` 127 | */ 128 | @discardableResult public func equals(_ string: String) -> FinalizedIncluder { 129 | builder.predicateString = "\(property) == \"\(string)\"" 130 | return FinalizedIncluder(builder: builder) 131 | } 132 | 133 | fileprivate func optionsString(_ options: PredicateOptions) -> String { 134 | if !options.isEmpty && !options.contains(.None) { 135 | var string = "[" 136 | if options.contains(.CaseInsensitive) { 137 | string = "\(string)c" 138 | } 139 | if options.contains(.DiacriticInsensitive) { 140 | string = "\(string)d" 141 | } 142 | return "\(string)]" 143 | } 144 | return "" 145 | } 146 | } 147 | -------------------------------------------------------------------------------- /Sources/Value Types/PredicateOptions.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PredicateOptions.swift 3 | // KrakenDev 4 | // 5 | // Created by Hector Matos on 5/30/16. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | /** 12 | An `OptionSetType` that describes the options in which to create a string comparison. 13 | */ 14 | public struct PredicateOptions: OptionSet { 15 | /** 16 | The raw value of the given `PredicateOptions` value. 17 | */ 18 | public let rawValue: UInt32 19 | public init(rawValue: UInt32) { 20 | self.rawValue = rawValue 21 | } 22 | 23 | /** 24 | The strictest comparison option. When comparing two strings, the left and right hand side MUST equal each other and is diacritic AND case-sensitive. 25 | */ 26 | public static let None = PredicateOptions(rawValue: 1<<0) 27 | /** 28 | When comparing two strings, the predicate system will ignore case. For example, the characters 'e' and 'E' will match. 29 | */ 30 | public static let CaseInsensitive = PredicateOptions(rawValue: 1<<1) 31 | /** 32 | When comparing two strings, the predicate system will ignore diacritic characters and normalize the special character to its base character. For example, the characters `e é ê è` are all equivalent. 33 | */ 34 | public static let DiacriticInsensitive = PredicateOptions(rawValue: 1<<2) 35 | } 36 | -------------------------------------------------------------------------------- /Sources/Value Types/SubqueryMatchTypes.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SubqueryMatchTypes.swift 3 | // PrediKit 4 | // 5 | // Created by Hector Matos on 5/30/16. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | // MARK: MatchType Enum 12 | /** 13 | An instance of this enum must be returned in the subquery closure to indicate how the queries collected in that closure should be matched to pass the predicate. 14 | */ 15 | public enum MatchType { 16 | /** 17 | The starting point of the subquery match type. You MUST include an associated value in this case to create a valid subquery predicate. 18 | */ 19 | case includeIfMatched(FunctionType) 20 | var collectionQuery: String { 21 | switch self { 22 | case .includeIfMatched(let matchType): 23 | return matchType.matchTypeString 24 | } 25 | } 26 | 27 | // MARK: FunctionType Enum 28 | /** 29 | The type of the associated value of the each case in the `MatchType` enum. Used to indicate what function to use to match returned query collections with. 30 | */ 31 | public enum FunctionType { 32 | /** 33 | Returns the collection.count value of the queried collection. 34 | */ 35 | case amount(CompareType) 36 | /** 37 | Returns the minimum value of every number in a queried number collection. 38 | */ 39 | case minValue(CompareType) 40 | /** 41 | Returns the maximum value of every number in a queried number collection. 42 | */ 43 | case maxValue(CompareType) 44 | /** 45 | Returns the average value of every number in a number collection. Best used against an array of numbers to get the average of all numbers in that array. 46 | */ 47 | case averageValue(CompareType) 48 | 49 | var matchTypeString: String { 50 | switch self { 51 | case .amount(let compare): return "@count \(compare.compareString)" 52 | case .minValue(let compare): return "@min \(compare.compareString)" 53 | case .maxValue(let compare): return "@max \(compare.compareString)" 54 | case .averageValue(let compare): return "@avg \(compare.compareString)" 55 | } 56 | } 57 | 58 | // MARK: CompareType Enum 59 | /** 60 | The type of the associated value of the each case in the `FunctionType` enum. Used to indicate how to compare the value of the function that's used to match returned query collections with. 61 | */ 62 | public enum CompareType { 63 | case equals(Int64) 64 | case isGreaterThan(Int64) 65 | case isGreaterThanOrEqualTo(Int64) 66 | case isLessThan(Int64) 67 | case isLessThanOrEqualTo(Int64) 68 | 69 | var compareString: String { 70 | switch self { 71 | case .equals(let amount): return "== \(amount)" 72 | case .isGreaterThan(let amount): return "> \(amount)" 73 | case .isGreaterThanOrEqualTo(let amount): return ">= \(amount)" 74 | case .isLessThan(let amount): return "< \(amount)" 75 | case .isLessThanOrEqualTo(let amount): return "<= \(amount)" 76 | } 77 | } 78 | } 79 | } 80 | } 81 | -------------------------------------------------------------------------------- /Supporting Files/OSX/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 4.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Supporting Files/OSX/PrediKit.h: -------------------------------------------------------------------------------- 1 | // 2 | // PrediKit-OSX.h 3 | // PrediKit-OSX 4 | // 5 | // Created by Hector Matos on 5/28/16. 6 | // 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for PrediKit-OSX. 12 | FOUNDATION_EXPORT double PrediKit_OSXVersionNumber; 13 | 14 | //! Project version string for PrediKit-OSX. 15 | FOUNDATION_EXPORT const unsigned char PrediKit_OSXVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /Supporting Files/OSX/TestsInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Supporting Files/iOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 4.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Supporting Files/iOS/PrediKit.h: -------------------------------------------------------------------------------- 1 | // 2 | // PrediKit.h 3 | // PrediKit 4 | // 5 | // Copyright © 2016 KrakenDev. All rights reserved. 6 | // 7 | 8 | #import 9 | 10 | //! Project version number for PrediKit. 11 | FOUNDATION_EXPORT double PrediKitVersionNumber; 12 | 13 | //! Project version string for PrediKit. 14 | FOUNDATION_EXPORT const unsigned char PrediKitVersionString[]; 15 | 16 | // In this header, you should import all the public headers of your framework using statements like #import 17 | 18 | 19 | -------------------------------------------------------------------------------- /Supporting Files/iOS/TestsInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Supporting Files/tvOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 4.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Supporting Files/tvOS/PrediKit.h: -------------------------------------------------------------------------------- 1 | // 2 | // PrediKit-tvOS.h 3 | // PrediKit-tvOS 4 | // 5 | // Created by Hector Matos on 5/30/16. 6 | // 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for PrediKit-tvOS. 12 | FOUNDATION_EXPORT double PrediKit_tvOSVersionNumber; 13 | 14 | //! Project version string for PrediKit-tvOS. 15 | FOUNDATION_EXPORT const unsigned char PrediKit_tvOSVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /Supporting Files/tvOS/TestsInfo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /Supporting Files/watchOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 4.0.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Supporting Files/watchOS/PrediKit.h: -------------------------------------------------------------------------------- 1 | // 2 | // PrediKit-watchOS.h 3 | // PrediKit-watchOS 4 | // 5 | // Created by Hector Matos on 5/30/16. 6 | // 7 | // 8 | 9 | #import 10 | 11 | //! Project version number for PrediKit-watchOS. 12 | FOUNDATION_EXPORT double PrediKit_watchOSVersionNumber; 13 | 14 | //! Project version string for PrediKit-watchOS. 15 | FOUNDATION_EXPORT const unsigned char PrediKit_watchOSVersionString[]; 16 | 17 | // In this header, you should import all the public headers of your framework using statements like #import 18 | 19 | 20 | -------------------------------------------------------------------------------- /Tests/Keypaths.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Keypaths.swift 3 | // PrediKit 4 | // 5 | // Created by Hector Matos on 5/16/16. 6 | // 7 | // 8 | 9 | import Foundation 10 | 11 | extension String { 12 | static let krakenTitle = #keyPath(Kraken.title) 13 | static let cerberusTitle = #keyPath(Cerberus.title) 14 | static let elfTitle = #keyPath(Elf.title) 15 | 16 | static let krakenAge = #keyPath(Kraken.age) 17 | static let cerberusAge = #keyPath(Cerberus.age) 18 | 19 | static let krakenBirthdate = #keyPath(Kraken.birthdate) 20 | static let cerberusBirthdate = #keyPath(Cerberus.birthdate) 21 | 22 | static let friends = #keyPath(Kraken.friends) 23 | static let bestElfFriend = #keyPath(Kraken.bestElfFriend) 24 | static let isAwesome = #keyPath(Kraken.isAwesome) 25 | 26 | static let subordinates = #keyPath(Cerberus.subordinates) 27 | static let isHungry = #keyPath(Cerberus.isHungry) 28 | 29 | static let enemy = #keyPath(Elf.enemy) 30 | } 31 | -------------------------------------------------------------------------------- /Tests/PrediKitTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PrediKitTests.swift 3 | // PrediKitTests 4 | // 5 | // Copyright © 2016 KrakenDev. All rights reserved. 6 | // 7 | 8 | import XCTest 9 | import CoreData 10 | @testable import PrediKit 11 | 12 | class PrediKitTests: XCTestCase { 13 | override func setUp() { 14 | super.setUp() 15 | } 16 | 17 | func testEmptyBuilderBehavior() { 18 | let predicate = NSPredicate(Kraken.self) { includeIf in 19 | XCTAssertEqual(includeIf.predicateString, "") 20 | } 21 | XCTAssertEqual(predicate, NSPredicate(value: false)) 22 | } 23 | 24 | func testCommonIncluders() { 25 | let legendaryArray = ["Kraken", "Kraken", "Kraken", "Kraken", "Cthulhu", "Voldemort", "Ember", "Umber", "Voldemort"] 26 | let legendarySet: Set = ["Kraken", "Kraken", "Kraken", "Kraken", "Cthulhu", "Voldemort", "Ember", "Umber", "Voldemort"] 27 | 28 | let _ = NSPredicate(Kraken.self) { includeIf in 29 | includeIf.SELF.equalsNil() 30 | XCTAssertEqual(includeIf.predicateString, "SELF == nil") 31 | includeIf.string(.krakenTitle).equalsNil() 32 | XCTAssertEqual(includeIf.predicateString, "title == nil") 33 | !includeIf.string(.krakenTitle).equalsNil() 34 | XCTAssertEqual(includeIf.predicateString, "!(title == nil)") 35 | includeIf.SELF.matchesAnyValueIn(legendaryArray) 36 | XCTAssertEqual(includeIf.predicateString, "SELF IN %@") 37 | includeIf.SELF.matchesAnyValueIn(legendarySet) 38 | XCTAssertEqual(includeIf.predicateString, "SELF IN %@") 39 | includeIf.member(.bestElfFriend, ofType: Elf.self).matchesAnyValueIn(legendaryArray) 40 | XCTAssertEqual(includeIf.predicateString, "bestElfFriend IN %@") 41 | includeIf.string(.krakenTitle).matchesAnyValueIn(legendaryArray) 42 | XCTAssertEqual(includeIf.predicateString, "title IN %@") 43 | } 44 | } 45 | 46 | func testStringIncluders() { 47 | let theKrakensTitle = "The Almighty Kraken" 48 | let _ = NSPredicate(Kraken.self) { includeIf in 49 | includeIf.string(.krakenTitle).isEmpty() 50 | XCTAssertEqual(includeIf.predicateString, NSPredicate(format: "title == \"\"", theKrakensTitle).predicateFormat) 51 | includeIf.string(.krakenTitle).beginsWith(theKrakensTitle) 52 | XCTAssertEqual(includeIf.predicateString, NSPredicate(format: "title BEGINSWITH %@", theKrakensTitle).predicateFormat) 53 | includeIf.string(.krakenTitle).endsWith(theKrakensTitle) 54 | XCTAssertEqual(includeIf.predicateString, NSPredicate(format: "title ENDSWITH %@", theKrakensTitle).predicateFormat) 55 | includeIf.string(.krakenTitle).contains(theKrakensTitle) 56 | XCTAssertEqual(includeIf.predicateString, NSPredicate(format: "title CONTAINS %@", theKrakensTitle).predicateFormat) 57 | includeIf.string(.krakenTitle).matches(theKrakensTitle) 58 | XCTAssertEqual(includeIf.predicateString, NSPredicate(format: "title MATCHES %@", theKrakensTitle).predicateFormat) 59 | includeIf.string(.krakenTitle).equals(theKrakensTitle) 60 | XCTAssertEqual(includeIf.predicateString, NSPredicate(format: "title == %@", theKrakensTitle).predicateFormat) 61 | } 62 | } 63 | 64 | func testStringIncludersWithOptions() { 65 | let theKrakensTitle = "The Almighty Kraken" 66 | let _ = NSPredicate(Kraken.self) { includeIf in 67 | includeIf.string(.krakenTitle).beginsWith(theKrakensTitle, options: .CaseInsensitive) 68 | XCTAssertEqual(includeIf.predicateString, NSPredicate(format: "title BEGINSWITH[c] %@", theKrakensTitle).predicateFormat) 69 | includeIf.string(.krakenTitle).endsWith(theKrakensTitle, options: .CaseInsensitive) 70 | XCTAssertEqual(includeIf.predicateString, NSPredicate(format: "title ENDSWITH[c] %@", theKrakensTitle).predicateFormat) 71 | includeIf.string(.krakenTitle).contains(theKrakensTitle, options: .CaseInsensitive) 72 | XCTAssertEqual(includeIf.predicateString, NSPredicate(format: "title CONTAINS[c] %@", theKrakensTitle).predicateFormat) 73 | includeIf.string(.krakenTitle).matches(theKrakensTitle, options: .CaseInsensitive) 74 | XCTAssertEqual(includeIf.predicateString, NSPredicate(format: "title MATCHES[c] %@", theKrakensTitle).predicateFormat) 75 | 76 | includeIf.string(.krakenTitle).beginsWith(theKrakensTitle, options: .DiacriticInsensitive) 77 | XCTAssertEqual(includeIf.predicateString, NSPredicate(format: "title BEGINSWITH[d] %@", theKrakensTitle).predicateFormat) 78 | includeIf.string(.krakenTitle).endsWith(theKrakensTitle, options: .DiacriticInsensitive) 79 | XCTAssertEqual(includeIf.predicateString, NSPredicate(format: "title ENDSWITH[d] %@", theKrakensTitle).predicateFormat) 80 | includeIf.string(.krakenTitle).contains(theKrakensTitle, options: .DiacriticInsensitive) 81 | XCTAssertEqual(includeIf.predicateString, NSPredicate(format: "title CONTAINS[d] %@", theKrakensTitle).predicateFormat) 82 | includeIf.string(.krakenTitle).matches(theKrakensTitle, options: .DiacriticInsensitive) 83 | XCTAssertEqual(includeIf.predicateString, NSPredicate(format: "title MATCHES[d] %@", theKrakensTitle).predicateFormat) 84 | 85 | includeIf.string(.krakenTitle).beginsWith(theKrakensTitle, options: [.CaseInsensitive, .DiacriticInsensitive]) 86 | XCTAssertEqual(includeIf.predicateString, NSPredicate(format: "title BEGINSWITH[cd] %@", theKrakensTitle).predicateFormat) 87 | includeIf.string(.krakenTitle).endsWith(theKrakensTitle, options: [.CaseInsensitive, .DiacriticInsensitive]) 88 | XCTAssertEqual(includeIf.predicateString, NSPredicate(format: "title ENDSWITH[cd] %@", theKrakensTitle).predicateFormat) 89 | includeIf.string(.krakenTitle).contains(theKrakensTitle, options: [.CaseInsensitive, .DiacriticInsensitive]) 90 | XCTAssertEqual(includeIf.predicateString, NSPredicate(format: "title CONTAINS[cd] %@", theKrakensTitle).predicateFormat) 91 | includeIf.string(.krakenTitle).matches(theKrakensTitle, options: [.CaseInsensitive, .DiacriticInsensitive]) 92 | XCTAssertEqual(includeIf.predicateString, NSPredicate(format: "title MATCHES[cd] %@", theKrakensTitle).predicateFormat) 93 | } 94 | } 95 | 96 | func testNumberIncluders() { 97 | let testAge = 5 98 | let _ = NSPredicate(Kraken.self) { includeIf in 99 | includeIf.number(.krakenAge).doesNotEqual(testAge) 100 | XCTAssertEqual(includeIf.predicateString, "age != %@") 101 | includeIf.number(.krakenAge).equals(testAge) 102 | XCTAssertEqual(includeIf.predicateString, "age == %@") 103 | includeIf.number(.krakenAge).isGreaterThan(testAge) 104 | XCTAssertEqual(includeIf.predicateString, "age > %@") 105 | includeIf.number(.krakenAge).isGreaterThanOrEqualTo(testAge) 106 | XCTAssertEqual(includeIf.predicateString, "age >= %@") 107 | includeIf.number(.krakenAge).isLessThan(testAge) 108 | XCTAssertEqual(includeIf.predicateString, "age < %@") 109 | includeIf.number(.krakenAge).isLessThanOrEqualTo(testAge) 110 | XCTAssertEqual(includeIf.predicateString, "age <= %@") 111 | } 112 | } 113 | 114 | func testDateIncluders() { 115 | let rightNow = Date() 116 | let _ = NSPredicate(Kraken.self) { includeIf in 117 | includeIf.date(.krakenBirthdate).equals(rightNow) 118 | XCTAssertEqual(includeIf.predicateString, "birthdate == %@") 119 | includeIf.date(.krakenBirthdate).isEarlierThan(rightNow) 120 | XCTAssertEqual(includeIf.predicateString, "birthdate < %@") 121 | includeIf.date(.krakenBirthdate).isEarlierThanOrOn(rightNow) 122 | XCTAssertEqual(includeIf.predicateString, "birthdate <= %@") 123 | includeIf.date(.krakenBirthdate).isLaterThan(rightNow) 124 | XCTAssertEqual(includeIf.predicateString, "birthdate > %@") 125 | includeIf.date(.krakenBirthdate).isLaterThanOrOn(rightNow) 126 | XCTAssertEqual(includeIf.predicateString, "birthdate >= %@") 127 | } 128 | } 129 | 130 | func testBoolIncluders() { 131 | let truePredicate = NSPredicate(Kraken.self) { includeIf in 132 | includeIf.bool(.isAwesome).isTrue() 133 | } 134 | XCTAssertEqual(truePredicate.predicateFormat, NSPredicate(format: "isAwesome == true").predicateFormat) 135 | 136 | let falsePredicate = NSPredicate(Kraken.self) { includeIf in 137 | includeIf.bool(.isAwesome).isFalse() 138 | } 139 | XCTAssertEqual(falsePredicate.predicateFormat, NSPredicate(format: "isAwesome == false").predicateFormat) 140 | } 141 | 142 | func testCollectionIncluders() { 143 | let _ = NSPredicate(Kraken.self) { includeIf in 144 | includeIf.collection(.friends).isEmpty() 145 | XCTAssertEqual(includeIf.predicateString, NSPredicate(format: "friends.@count == 0").predicateFormat) 146 | } 147 | } 148 | 149 | func testSubqueryIncluders() { 150 | let predicate = NSPredicate(Kraken.self) { includeIf in 151 | includeIf.collection(.friends).subquery(Cerberus.self) { includeIf in 152 | includeIf.bool(.isHungry).isTrue() && 153 | includeIf.bool(.isAwesome).isTrue() 154 | return .includeIfMatched(.amount(.equals(0))) 155 | } 156 | } 157 | XCTAssertEqual(predicate.predicateFormat, NSPredicate(format: "SUBQUERY(friends, $CerberusItem, $CerberusItem.isHungry == true && $CerberusItem.isAwesome == true).@count == 0").predicateFormat) 158 | } 159 | 160 | func testSubqueryReturnTypeStrings() { 161 | let match = MatchType.includeIfMatched 162 | let amount = MatchType.FunctionType.amount 163 | XCTAssertEqual(match(amount(.equals(0))).collectionQuery, "@count == 0") 164 | XCTAssertEqual(match(amount(.isLessThan(0))).collectionQuery, "@count < 0") 165 | XCTAssertEqual(match(amount(.isLessThanOrEqualTo(0))).collectionQuery, "@count <= 0") 166 | XCTAssertEqual(match(amount(.isGreaterThan(0))).collectionQuery, "@count > 0") 167 | XCTAssertEqual(match(amount(.isGreaterThanOrEqualTo(0))).collectionQuery, "@count >= 0") 168 | 169 | let min = MatchType.FunctionType.minValue 170 | XCTAssertEqual(match(min(.equals(0))).collectionQuery, "@min == 0") 171 | XCTAssertEqual(match(min(.isLessThan(0))).collectionQuery, "@min < 0") 172 | XCTAssertEqual(match(min(.isLessThanOrEqualTo(0))).collectionQuery, "@min <= 0") 173 | XCTAssertEqual(match(min(.isGreaterThan(0))).collectionQuery, "@min > 0") 174 | XCTAssertEqual(match(min(.isGreaterThanOrEqualTo(0))).collectionQuery, "@min >= 0") 175 | 176 | let max = MatchType.FunctionType.maxValue 177 | XCTAssertEqual(match(max(.equals(0))).collectionQuery, "@max == 0") 178 | XCTAssertEqual(match(max(.isLessThan(0))).collectionQuery, "@max < 0") 179 | XCTAssertEqual(match(max(.isLessThanOrEqualTo(0))).collectionQuery, "@max <= 0") 180 | XCTAssertEqual(match(max(.isGreaterThan(0))).collectionQuery, "@max > 0") 181 | XCTAssertEqual(match(max(.isGreaterThanOrEqualTo(0))).collectionQuery, "@max >= 0") 182 | 183 | let avg = MatchType.FunctionType.averageValue 184 | XCTAssertEqual(match(avg(.equals(0))).collectionQuery, "@avg == 0") 185 | XCTAssertEqual(match(avg(.isLessThan(0))).collectionQuery, "@avg < 0") 186 | XCTAssertEqual(match(avg(.isLessThanOrEqualTo(0))).collectionQuery, "@avg <= 0") 187 | XCTAssertEqual(match(avg(.isGreaterThan(0))).collectionQuery, "@avg > 0") 188 | XCTAssertEqual(match(avg(.isGreaterThanOrEqualTo(0))).collectionQuery, "@avg >= 0") 189 | } 190 | 191 | func testMemberIncluders() { 192 | let elf = Elf() 193 | let elfName = "Aragorn" 194 | let enemyBirthdate = Date() 195 | let predicate = NSPredicate(Kraken.self) { includeIf in 196 | let bestElfFriend = includeIf.member(.bestElfFriend, ofType: Elf.self) 197 | let bestElfFriendsMortalEnemy = bestElfFriend.member(.enemy, ofType: Cerberus.self) 198 | 199 | !bestElfFriend.equalsNil() && 200 | bestElfFriend.equals(elf) && 201 | bestElfFriendsMortalEnemy.date(.cerberusBirthdate).equals(enemyBirthdate) && 202 | bestElfFriend.string(.elfTitle).equals(elfName) 203 | } 204 | XCTAssertEqual(predicate.predicateFormat, NSPredicate(format: "!(bestElfFriend == nil) && bestElfFriend == %@ && bestElfFriend.enemy.birthdate == %@ && bestElfFriend.title == %@", elf, enemyBirthdate as CVarArg, elfName).predicateFormat) 205 | } 206 | 207 | func testSimpleANDIncluderCombination() { 208 | let theKrakensTitle = "The Almighty Kraken" 209 | let rightNow = Date() 210 | 211 | let predicate = NSPredicate(Kraken.self) { includeIf in 212 | includeIf.string(.krakenTitle).equals(theKrakensTitle) && 213 | includeIf.date(.krakenBirthdate).isEarlierThan(rightNow) 214 | } 215 | let expectedPredicate = NSPredicate(format: "title == %@ && birthdate < %@", theKrakensTitle, rightNow as CVarArg) 216 | XCTAssertEqual(predicate.predicateFormat, expectedPredicate.predicateFormat) 217 | } 218 | 219 | func testChainedANDIncluderCombination() { 220 | let theKrakensTitle = "The Almighty Kraken" 221 | let rightNow = Date() 222 | let isAwesome = true 223 | let age = 5 224 | 225 | let predicate = NSPredicate(Kraken.self) { includeIf in 226 | includeIf.string(.krakenTitle).equals(theKrakensTitle) && 227 | includeIf.date(.krakenBirthdate).isEarlierThan(rightNow) && 228 | includeIf.bool(.isAwesome).isTrue() && 229 | includeIf.number(.krakenAge).equals(age) 230 | } 231 | let expectedPredicate = NSPredicate(format: "title == %@ && birthdate < %@ && isAwesome == %@ && age == \(age)", theKrakensTitle, rightNow as CVarArg, NSNumber(value: isAwesome)) 232 | XCTAssertEqual(predicate.predicateFormat, expectedPredicate.predicateFormat) 233 | } 234 | 235 | func testSimpleORIncluderCombination() { 236 | let theKrakensTitle = "The Almighty Kraken" 237 | let rightNow = Date() 238 | 239 | let predicate = NSPredicate(Kraken.self) { includeIf in 240 | includeIf.string(.krakenTitle).equals(theKrakensTitle) || 241 | includeIf.date(.krakenBirthdate).isEarlierThan(rightNow) 242 | } 243 | let expectedPredicate = NSPredicate(format: "title == %@ || birthdate < %@", theKrakensTitle, rightNow as CVarArg) 244 | XCTAssertEqual(predicate.predicateFormat, expectedPredicate.predicateFormat) 245 | } 246 | 247 | func testChainedORIncluderCombination() { 248 | let theKrakensTitle = "The Almighty Kraken" 249 | let rightNow = Date() 250 | let isAwesome = true 251 | let age = 5 252 | 253 | let predicate = NSPredicate(Kraken.self) { includeIf in 254 | includeIf.string(.krakenTitle).equals(theKrakensTitle) || 255 | includeIf.date(.krakenBirthdate).isEarlierThan(rightNow) || 256 | includeIf.bool(.isAwesome).isTrue() || 257 | includeIf.number(.krakenAge).equals(age) 258 | } 259 | let expectedPredicate = NSPredicate(format: "title == %@ || birthdate < %@ || isAwesome == %@ || age == \(age)", theKrakensTitle, rightNow as CVarArg, NSNumber(value: isAwesome)) 260 | XCTAssertEqual(predicate.predicateFormat, expectedPredicate.predicateFormat) 261 | } 262 | 263 | func testComplexIncluderCombinationsWithoutParentheses() { 264 | let theKrakensTitle = "The Almighty Kraken" 265 | let rightNow = Date() 266 | 267 | let predicate = NSPredicate(Kraken.self) { includeIf in 268 | includeIf.string(.krakenTitle).equals(theKrakensTitle) || 269 | includeIf.string(.krakenTitle).equals(theKrakensTitle) || 270 | includeIf.string(.krakenTitle).equals(theKrakensTitle) && 271 | includeIf.date(.krakenBirthdate).equals(rightNow) || 272 | includeIf.bool(.isAwesome).isTrue() && 273 | includeIf.date(.krakenBirthdate).equals(rightNow) || 274 | includeIf.bool(.isAwesome).isTrue() 275 | } 276 | let expectedPredicate = NSPredicate(format: "title == %@ || title == %@ || title == %@ && birthdate == %@ || isAwesome == true && birthdate == %@ || isAwesome == true", theKrakensTitle, theKrakensTitle, theKrakensTitle, rightNow as CVarArg, rightNow as CVarArg) 277 | XCTAssertEqual(predicate.predicateFormat, expectedPredicate.predicateFormat) 278 | } 279 | 280 | func testComplexIncluderCombinationsWithParentheses() { 281 | let theKrakensTitle = "The Almighty Kraken" 282 | let rightNow = Date() 283 | 284 | let predicate = NSPredicate(Kraken.self) { includeIf in 285 | (includeIf.string(.krakenTitle).equals(theKrakensTitle) || 286 | includeIf.string(.krakenTitle).equals(theKrakensTitle) || 287 | includeIf.string(.krakenTitle).equals(theKrakensTitle)) 288 | && 289 | (includeIf.date(.krakenBirthdate).equals(rightNow) || 290 | includeIf.bool(.isAwesome).isTrue()) 291 | && 292 | (includeIf.date(.krakenBirthdate).equals(rightNow) || 293 | includeIf.bool(.isAwesome).isTrue()) 294 | } 295 | let expectedPredicate = NSPredicate(format: "(title == %@ || title == %@ || title == %@) && (birthdate == %@ || isAwesome == true) && (birthdate == %@ || isAwesome == true)", theKrakensTitle, theKrakensTitle, theKrakensTitle, rightNow as CVarArg, rightNow as CVarArg) 296 | XCTAssertEqual(predicate.predicateFormat, expectedPredicate.predicateFormat) 297 | } 298 | 299 | func testComplexIncluderCombinationsWithSubquery() { 300 | let theKrakensTitle = "The Almighty Kraken" 301 | let theElfTitle = "The Lowly Elf" 302 | let rightNow = Date() 303 | let age = 5.5 304 | 305 | let predicate = NSPredicate(Kraken.self) { includeIf in 306 | includeIf.collection(.friends).subquery(Cerberus.self) { includeIf in 307 | let isTheKraken = includeIf.string(.krakenTitle).equals(theKrakensTitle) 308 | let isBirthedToday = includeIf.date(.krakenBirthdate).equals(rightNow) 309 | let isHungry = includeIf.bool(.isHungry).isTrue() 310 | let isOlderThan5AndAHalf = includeIf.number(.krakenAge).isGreaterThan(age) 311 | let hasElfSubordinates = includeIf.collection(.subordinates).subquery(Elf.self) { includeIf in 312 | includeIf.string(.elfTitle).equals(theElfTitle) 313 | return .includeIfMatched(.amount(.isGreaterThan(0))) 314 | } 315 | let hasCerberusSubordinates = includeIf.collection(.subordinates).subquery(Cerberus.self) { includeIf in 316 | let age = includeIf.number(.cerberusAge).equals(age) 317 | let birthdate = includeIf.date(.cerberusBirthdate).equals(rightNow) 318 | 319 | birthdate || age 320 | return .includeIfMatched(.amount(.isGreaterThan(0))) 321 | } 322 | 323 | isTheKraken || isOlderThan5AndAHalf || isHungry || (isBirthedToday && !hasElfSubordinates && hasCerberusSubordinates) 324 | 325 | return .includeIfMatched(.amount(.equals(0))) 326 | } 327 | } 328 | let expectedPredicate = NSPredicate(format: "SUBQUERY(friends, $CerberusItem, $CerberusItem.title == \"The Almighty Kraken\" OR $CerberusItem.age > \(age) OR $CerberusItem.isHungry == true OR ($CerberusItem.birthdate == %@ AND (NOT SUBQUERY($CerberusItem.subordinates, $ElfItem, $ElfItem.title == \"The Lowly Elf\").@count > 0) && SUBQUERY($CerberusItem.subordinates, $CerberusItem, $CerberusItem.birthdate == %@ || $CerberusItem.age == \(age)).@count > 0)).@count == 0", rightNow as CVarArg, rightNow as CVarArg) 329 | XCTAssertEqual(predicate.predicateFormat, expectedPredicate.predicateFormat) 330 | } 331 | 332 | func testCombiningNSPredicateOperators() { 333 | let theKrakensTitle = "The Almighty Kraken" 334 | let rightNow = "October 4" 335 | let firstPredicate = NSPredicate(format: "title == %@ && birthdate == %@", theKrakensTitle, rightNow) 336 | let secondPredicate = NSPredicate(format: "isAwesome == true && isHungry == true") 337 | let combinedANDPredicate = firstPredicate && secondPredicate 338 | let combinedORPredicate = firstPredicate || secondPredicate 339 | XCTAssertEqual(combinedANDPredicate.predicateFormat, "(title == \"The Almighty Kraken\" AND birthdate == \"October 4\") AND (isAwesome == 1 AND isHungry == 1)") 340 | XCTAssertEqual(combinedORPredicate.predicateFormat, "(title == \"The Almighty Kraken\" AND birthdate == \"October 4\") OR (isAwesome == 1 AND isHungry == 1)") 341 | } 342 | } 343 | 344 | //MARK: Test Classes 345 | class Kraken: NSObject { 346 | @NSManaged var title: String 347 | @NSManaged var age: Int64 348 | @NSManaged var birthdate: Date 349 | @NSManaged var isAwesome: Bool 350 | @NSManaged var bestElfFriend: Elf 351 | @NSManaged var friends: [Cerberus] 352 | } 353 | 354 | class Cerberus: NSObject { 355 | @NSManaged var title: String 356 | @NSManaged var age: Int64 357 | @NSManaged var birthdate: Date 358 | @NSManaged var isHungry: Bool 359 | @NSManaged var subordinates: [Elf] 360 | } 361 | 362 | class Elf: NSObject { 363 | @NSManaged var title: String 364 | @NSManaged var enemy: Cerberus 365 | } 366 | --------------------------------------------------------------------------------