diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md deleted file mode 100644 index 68ce080a2..000000000 --- a/.github/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,31 +0,0 @@ -> Please fill out this template when filing an issue. -> -> All comment lines beginning with an **>** instruct you with what info we expect. You can delete those lines once you've filled in the info. -> - -[ ] I've read, understood, and done my best to follow the [Contributing guidelines](https://github.com/SwifterSwift/SwifterSwift/tree/master/CONTRIBUTING.md) before opening this issue. - - -### What did you do? -> Please replace this with what you did. - - -### What did you expect to happen? -> Please replace this with what you expected to happen. - - -### What happened instead? -> Please replace this with of what happened instead. - - -### SwifterSwift Environment - -- SwifterSwift version: -- Xcode version: -- macOS version running Xcode: -- Swift version: -- Platform(s) running SwifterSwift: - - -### Demo Project ->Please link to or upload a project we can download that reproduces the issue. diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 000000000..0597d53a9 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,40 @@ +--- +name: Bug report +about: Create a report to help us improve +title: '' +labels: possible bug +assignees: '' + +--- + +**Describe the bug** +A clear and concise description of what the bug is. + +**To Reproduce** +Steps to reproduce the behavior: +1. Go to '...' +2. Click on '....' +3. Scroll down to '....' +4. See error + +**Expected behavior** +A clear and concise description of what you expected to happen. + +**Screenshots** +If applicable, add screenshots to help explain your problem. + +**Desktop (please complete the following information):** + - OS: [e.g. iOS] + - Browser [e.g. chrome, safari] + - Version [e.g. 22] + +**Smartphone (please complete the following information):** + - Device: [e.g. iPhone6] + - OS: [e.g. iOS8.1] + - Browser [e.g. stock browser, safari] + - Version [e.g. 22] + +**Additional context** +Add any other context about the problem here. + +- [ ] I've read, understood, and done my best to follow the [Contributing guidelines](https://github.com/SwifterSwift/SwifterSwift/tree/master/CONTRIBUTING.md) before opening this issue. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 000000000..bbfa01c4c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,22 @@ +--- +name: Feature request +about: Suggest an idea for this project +title: '' +labels: '' +assignees: '' + +--- + +**Is your feature request related to a problem? Please describe.** +A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] + +**Describe the solution you'd like** +A clear and concise description of what you want to happen. + +**Describe alternatives you've considered** +A clear and concise description of any alternative solutions or features you've considered. + +**Additional context** +Add any other context or screenshots about the feature request here. + +- [ ] I've read, understood, and done my best to follow the [Contributing guidelines](https://github.com/SwifterSwift/SwifterSwift/tree/master/CONTRIBUTING.md) before opening this issue. diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index ffb964e2d..30e6844ef 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -4,8 +4,8 @@ - [ ] I checked the [**Contributing Guidelines**](https://github.com/SwifterSwift/SwifterSwift/blob/master/CONTRIBUTING.md) before creating this request. -- [ ] New extensions are written in Swift 5.0. -- [ ] New extensions support iOS 10.0+ / tvOS 9.0+ / macOS 10.10+ / watchOS 2.0+, or use `@available` if not. +- [ ] New extensions are written in Swift 5.6. +- [ ] New extensions support iOS 12.0+ / tvOS 12.0+ / macOS 10.13+ / watchOS 4.0+, or use `@available` if not. - [ ] I have added tests for new extensions, and they passed. - [ ] All extensions have a **clear** comments explaining their functionality, all parameters and return type in English. - [ ] All extensions are declared as **public**. diff --git a/.github/workflows/CI.yml b/.github/workflows/CI.yml index 5b5f217ce..3e68f2e20 100644 --- a/.github/workflows/CI.yml +++ b/.github/workflows/CI.yml @@ -11,10 +11,10 @@ on: jobs: Darwin: name: Darwin - runs-on: macos-latest + runs-on: macos-13 env: PROJECT: SwifterSwift.xcodeproj - DEVELOPER_DIR: /Applications/Xcode_11.7.app/Contents/Developer + DEVELOPER_DIR: /Applications/Xcode.app/Contents/Developer steps: - uses: actions/checkout@v1 - name: Bundle Install @@ -29,7 +29,7 @@ jobs: bash <(curl -s https://codecov.io/bash) -cF ios -J 'SwifterSwift' env: SCHEME: SwifterSwift-iOS - DESTINATION: platform=iOS Simulator,name=iPhone 11 + DESTINATION: platform=iOS Simulator,name=iPhone 15 - name: Test MacOS run: | xcodebuild clean build test -project $PROJECT -scheme $SCHEME -destination "$DESTINATION" | XCPRETTY_JSON_FILE_OUTPUT="xcodebuild-macos.json" xcpretty -f `xcpretty-json-formatter` @@ -43,12 +43,12 @@ jobs: bash <(curl -s https://codecov.io/bash) -cF tvos -J 'SwifterSwift' env: SCHEME: SwifterSwift-tvOS - DESTINATION: platform=tvOS Simulator,name=Apple TV 4K (at 1080p) + DESTINATION: platform=tvOS Simulator,name=Apple TV - name: Build WatchOS run: xcodebuild clean build -project $PROJECT -scheme $SCHEME -destination "$DESTINATION" env: SCHEME: SwifterSwift-watchOS - DESTINATION: name=Apple Watch Series 5 - 40mm + DESTINATION: platform=watchOS Simulator,name=Apple Watch Series 5 (40mm) - name: Danger Swift # Hack to by pass the key invalidation mechanism @@ -58,7 +58,7 @@ jobs: bundle exec danger --verbose Swiftlint: - runs-on: macos-latest + runs-on: macos-13 name: SwiftLint steps: - uses: actions/checkout@v1 @@ -69,12 +69,12 @@ jobs: swiftlint CocoaPods: name: CocoaPods - runs-on: macos-latest + runs-on: macos-13 strategy: matrix: platform: ['ios', 'macos', 'tvos', 'watchos'] env: - DEVELOPER_DIR: /Applications/Xcode_11.7.app/Contents/Developer + DEVELOPER_DIR: /Applications/Xcode.app/Contents/Developer steps: - uses: actions/checkout@v1 - name: Bundle Install @@ -83,8 +83,8 @@ jobs: run: bundle exec pod lib lint --skip-tests --allow-warnings --verbose --platforms=${{ matrix.platform }} Linux: - runs-on: [ubuntu-16.04] - container: swift:5.2.3 + runs-on: [ubuntu-latest] + container: swift:5.8 steps: - uses: actions/checkout@v1 - name: Linux diff --git a/.swiftformat b/.swiftformat index 89038d5f2..304ae1d72 100644 --- a/.swiftformat +++ b/.swiftformat @@ -8,4 +8,4 @@ --importgrouping testable-top --nospaceoperators ..<,... --maxwidth 120 ---swiftversion 5.2 +--swiftversion 5.3 diff --git a/Brewfile b/Brewfile index d42f401f9..b6648c992 100644 --- a/Brewfile +++ b/Brewfile @@ -1,3 +1,2 @@ brew "swiftformat" -brew "swiftlint" -brew "xctool" +brew "ruby" \ No newline at end of file diff --git a/Brewfile.lock.json b/Brewfile.lock.json index 6c72ad5c7..0eac37b89 100644 --- a/Brewfile.lock.json +++ b/Brewfile.lock.json @@ -1,61 +1,62 @@ { "entries": { "brew": { - "xctool": { - "version": "0.3.7", + "swiftformat": { + "version": "0.49.14", "bottle": { - "cellar": ":any", - "prefix": "/usr/local", + "rebuild": 0, + "root_url": "https://ghcr.io/v2/homebrew/core", "files": { - "catalina": { - "url": "https://homebrew.bintray.com/bottles/xctool-0.3.7.catalina.bottle.tar.gz", - "sha256": "0cf8c734d095ab97b2d5537b67d3f13e6ff8f38c46503ea02b9eba98ff35942c" + "arm64_monterey": { + "cellar": ":any_skip_relocation", + "url": "https://ghcr.io/v2/homebrew/core/swiftformat/blobs/sha256:7f8d9c244ced7e092cb18af0a36e1b64414e28dfbbc18beabe25fe0080aa3bca", + "sha256": "7f8d9c244ced7e092cb18af0a36e1b64414e28dfbbc18beabe25fe0080aa3bca" }, - "mojave": { - "url": "https://homebrew.bintray.com/bottles/xctool-0.3.7.mojave.bottle.tar.gz", - "sha256": "8b116346555e2616619e577d3ce3c69a24d66cb505ee048ba316ab2880736043" + "arm64_big_sur": { + "cellar": ":any_skip_relocation", + "url": "https://ghcr.io/v2/homebrew/core/swiftformat/blobs/sha256:f1c062ceb1c7d933edb1e94f0cb1cb1b79b33821d565d26ebfbdf805def3b8f9", + "sha256": "f1c062ceb1c7d933edb1e94f0cb1cb1b79b33821d565d26ebfbdf805def3b8f9" + }, + "monterey": { + "cellar": ":any_skip_relocation", + "url": "https://ghcr.io/v2/homebrew/core/swiftformat/blobs/sha256:a3242f98aee8736737fac5091c35ed34532c6e9a7ec66532e9418731b12ce158", + "sha256": "a3242f98aee8736737fac5091c35ed34532c6e9a7ec66532e9418731b12ce158" + }, + "big_sur": { + "cellar": ":any_skip_relocation", + "url": "https://ghcr.io/v2/homebrew/core/swiftformat/blobs/sha256:76d9cd1efe09137f656c94c6498b80c2d9d7ca1f9cbd5a98d0699aa15629bd52", + "sha256": "76d9cd1efe09137f656c94c6498b80c2d9d7ca1f9cbd5a98d0699aa15629bd52" }, - "high_sierra": { - "url": "https://homebrew.bintray.com/bottles/xctool-0.3.7.high_sierra.bottle.tar.gz", - "sha256": "055172ba606bf94416513e418007f849a08ff24a3b3484fb67c1b4f854123bb9" - } - } - } - }, - "swiftlint": { - "version": "0.37.0", - "bottle": { - "cellar": ":any_skip_relocation", - "prefix": "/usr/local", - "files": { "catalina": { - "url": "https://homebrew.bintray.com/bottles/swiftlint-0.40.3.catalina.bottle.tar.gz", - "sha256": "84368120d02e9a22515d4fdfe852dcdddcfa47f8f688e5d0aa6eba837d52d890" + "cellar": ":any_skip_relocation", + "url": "https://ghcr.io/v2/homebrew/core/swiftformat/blobs/sha256:ac73b68d0225d794f415d5aedf0e2ffdbd1251e68e874a95f43c43431eeac518", + "sha256": "ac73b68d0225d794f415d5aedf0e2ffdbd1251e68e874a95f43c43431eeac518" }, - "mojave": { - "url": "https://homebrew.bintray.com/bottles/swiftlint-0.40.3.mojave.bottle.tar.gz", - "sha256": "42de59d77fdd150558306b024d1cceb757da3d83aa3c03fd985636e24ef8ab9b" + "x86_64_linux": { + "cellar": "/home/linuxbrew/.linuxbrew/Cellar", + "url": "https://ghcr.io/v2/homebrew/core/swiftformat/blobs/sha256:89ba5a1898f9e5ce08582cd56399d18713c4091cb2b56a91ef839420dae49096", + "sha256": "89ba5a1898f9e5ce08582cd56399d18713c4091cb2b56a91ef839420dae49096" } } } }, - "swiftformat": { - "version": "0.44.17", + "xctool": { + "version": "0.3.7", "bottle": { - "cellar": ":any_skip_relocation", + "cellar": ":any", "prefix": "/usr/local", "files": { "catalina": { - "url": "https://homebrew.bintray.com/bottles/swiftformat-0.47.0.catalina.bottle.tar.gz", - "sha256": "81431257faa84ed092b02dbcaa5703e2759d915cb18c3a7c8f663311cbd2d79e" + "url": "https://homebrew.bintray.com/bottles/xctool-0.3.7.catalina.bottle.tar.gz", + "sha256": "0cf8c734d095ab97b2d5537b67d3f13e6ff8f38c46503ea02b9eba98ff35942c" }, "mojave": { - "url": "https://homebrew.bintray.com/bottles/swiftformat-0.47.0.mojave.bottle.tar.gz", - "sha256": "af859141fb9f4ecf149a4f905cca94381fb64aaddcb3a113b0f1ff1fe5d9e5b3" + "url": "https://homebrew.bintray.com/bottles/xctool-0.3.7.mojave.bottle.tar.gz", + "sha256": "8b116346555e2616619e577d3ce3c69a24d66cb505ee048ba316ab2880736043" }, "high_sierra": { - "url": "https://homebrew.bintray.com/bottles/swiftformat-0.47.0.high_sierra.bottle.tar.gz", - "sha256": "6842c5c972883b6c176cf02fa8dd12b09663889369b1c9ef18201d594120b642" + "url": "https://homebrew.bintray.com/bottles/xctool-0.3.7.high_sierra.bottle.tar.gz", + "sha256": "055172ba606bf94416513e418007f849a08ff24a3b3484fb67c1b4f854123bb9" } } } @@ -64,13 +65,21 @@ }, "system": { "macos": { - "catalina": { - "HOMEBREW_VERSION": "2.5.6", + "big_sur": { + "HOMEBREW_VERSION": "2.5.6-118-gede7a83", "HOMEBREW_PREFIX": "/usr/local", - "Homebrew/homebrew-core": "2ead2d7574778b5d236b241f8e760e6eeb8cf22d", + "Homebrew/homebrew-core": "c878e5e69e91114d8f27203aedc873fecbfda3e0", "CLT": "", - "Xcode": "11.7", - "macOS": "10.15.6" + "Xcode": "12.3", + "macOS": "11.0.1" + }, + "monterey": { + "HOMEBREW_VERSION": "3.5.8", + "HOMEBREW_PREFIX": "/opt/homebrew", + "Homebrew/homebrew-core": "802096aba4fdaa0d3cd080a2354cbce4dbe4044f", + "CLT": "13.4.0.0.1.1651278267", + "Xcode": "13.4.1", + "macOS": "12.5" } } } diff --git a/CHANGELOG.md b/CHANGELOG.md index 4e514131b..310ffd9b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,26 +3,118 @@ The changelog for **SwifterSwift**. Also see the [releases](https://github.com/SwifterSwift/SwifterSwift/releases) on GitHub. ## Upcoming Release + +### Fixed +- `UIView.GradientDirection` initializer and constants had access level `internal` instead of `public`. [#1152](https://github.com/SwifterSwift/SwifterSwift/pull/1152) by [guykogus](https://github.com/guykogus) + +## [v6.0.0](https://github.com/SwifterSwift/SwifterSwift/releases/tag/6.0.0) +### Breaking Change +- **Minimum deployment target** + - Bumped up the minimum deployment targets to match the minimums of Xcode 15. [#1142](https://github.com/SwifterSwift/SwifterSwift/issues/1142) by [guykogus](https://github.com/guykogus) + - iOS 12.0 + - macOS 10.13 + - tvOS 12.0 + - watchOS 4.0 +- **Color** + - Renamed typealias `Color` typealias to `SFColor` to fix namespace conflicts with swiftUI's `Color` Type. [#1055](https://github.com/SwifterSwift/SwifterSwift/pull/1055) by [MussaCharles](https://github.com/MussaCharles) +- **EdgeInsets** + - Renamed `EdgeInsets` typealias to `SFEdgeInsets` to fix namespace conflicts with swiftUI's `EdgeInsets` Type. [#1055](https://github.com/SwifterSwift/SwifterSwift/pull/1055) by [MussaCharles](https://github.com/MussaCharles) +- **Font** + - Renamed `Font` typealias to `SFFont` to fix namespace conflicts with swiftUI's `Font` Type. [#1055](https://github.com/SwifterSwift/SwifterSwift/pull/1055) by [MussaCharles](https://github.com/MussaCharles) +- **String** + - Reversed the parameters for the `~=` operator in order to not break the default implementation, which causes issues in `switch` statements, for example. [#1113](https://github.com/SwifterSwift/SwifterSwift/issues/1113) by [guykogus](https://github.com/guykogus) + +### Removed +[#1145](https://github.com/SwifterSwift/SwifterSwift/issues/1145) by [guykogus](https://github.com/guykogus) +- **Array**: + - `sorted(by:ascending:)`, `sort(by:ascending:)` +- **Sequence**: + - `map(by:)`, `compactMap(by:)`, `filter(by:)`, `last(where:)` +- **String** + - `init(randomOfLength:)` +- **UIDatePicker** + - `textColor` + +### Deprecated +- **String** + - `init(randomOfLength:)` deprecated in favor of `String.random(ofLength:)`. [#1115](https://github.com/SwifterSwift/SwifterSwift/issues/1115) by [guykogus](https://github.com/guykogus) +- **UIDatePicker** + - `textColor` deprecated as the implementation hacked a keypath that is no longer available. [#1136](https://github.com/SwifterSwift/SwifterSwift/issues/1136) by [guykogus](https://github.com/guykogus) + +### Added +- **Future** + - Added `init(asyncFunc:)` to create a `Future` from an `async` value. [#1139](https://github.com/SwifterSwift/SwifterSwift/pull/1139) by [RomanPodymov](https://github.com/RomanPodymov) +- **UINavigationController** + - Added `pushViewController(_:hidesBottomBar:animated:)` for hiding the bottom bar when pushing any `UIViewController`. [#1117](https://github.com/SwifterSwift/SwifterSwift/issues/1117) by [imdkhairul](https://github.com/imdkhairul) +- **UIButton** + - Added `setBackgroundColor(_:for:)` method for setting background color for the specified UI state. [#1041](https://github.com/SwifterSwift/SwifterSwift/pull/1041) by [TwizzyIndy](https://github.com/TwizzyIndy) + - Added `imageForFocused`, `titleColorForFocused`, `titleForFocused`, `attributedTitleForFocused` to handle focused state. [#1063](https://github.com/SwifterSwift/SwifterSwift/pull/1063) by [Roman Podymov](https://github.com/RomanPodymov) +- **Array** + - Added `init(count:element:)` initializer for creating an array of a given size with a closure. [#1051](https://github.com/SwifterSwift/SwifterSwift/pull/1051) by [viktart](https://github.com/viktart) +- **DefaultStringInterpolation** + - Added `appendInterpolation(_:placeholder:predicate:)` method for providing placeholder string if interpolated value is `nil` or the optional predicate closure returns `false`. [#1074](https://github.com/SwifterSwift/SwifterSwift/pull/1074) by [Shiva Huang](https://github.com/ShivaHuang) +- **SFEdgeInsets** + - Excluded type alias `SFEdgeInsets` when targeting visionOS platform. [#1126](https://github.com/SwifterSwift/SwifterSwift/pull/1126) by [MarcoEidinger](https://github.com/MarcoEidinger) +- **String** + - Added `formatLocalized(comment:arguments:)` method to format localized strings easily. [#1100](https://github.com/SwifterSwift/SwifterSwift/pull/1100) by [makcakir](https://github.com/makcakir) +- **UIAlertController** + - Excluded `show(animated:vibrate:completion:)` when targeting visionOS platform. [#1126](https://github.com/SwifterSwift/SwifterSwift/pull/1126) by [MarcoEidinger](https://github.com/MarcoEidinger) +- **UITabBar** + - Fixed compilation error for `setColors(background:selectedBackground:item:selectedItem:)` when targeting visionOS platform. [#1126](https://github.com/SwifterSwift/SwifterSwift/pull/1126) by [MarcoEidinger](https://github.com/MarcoEidinger) +- **URL** + - Added `allQueryParameters`, `appendingQueryParameters(_:)` and `appendQueryParameters(_:)` for using `URLQueryItem`, as an addition to the `[String: String]` variants, to handle `nil`-value query parameters. [#1116](https://github.com/SwifterSwift/SwifterSwift/issues/1116) by [guykogus](https://github.com/guykogus) +- **URLRequest** + - Added `method()` to duplicate the request and modify the HTTP method (verb) for the request (i.e.: GET, POST, PUT) [#1133](https://github.com/SwifterSwift/SwifterSwift/pull/1133) by [Ricardo Rauber](https://github.com/ricardorauber) + - Added `header(name:, value:)` to duplicate the request and set a header with key and value. [#1134](https://github.com/SwifterSwift/SwifterSwift/pull/1134) by [Ricardo Rauber](https://github.com/ricardorauber) +- **URLSession** + - Added `dataSync(for:)` to make requests synchronously. [#1076](https://github.com/SwifterSwift/SwifterSwift/pull/1076) by [Roman Podymov](https://github.com/RomanPodymov) +- **UIStackView** + - Added `backgroundViewColor` to add background color. [#1127](https://github.com/SwifterSwift/SwifterSwift/pull/1127) by [WZBbiao](https://github.com/WZBbiao) + +### Changed +- **UIButton**: + - Add `.focused` to `UIButton.states`. [#1062](https://github.com/SwifterSwift/SwifterSwift/pull/1062) by [Roman Podymov](https://github.com/RomanPodymov). + +### Fixed +- **Date** + - Fixed incorrect calculation in `nearestTenMinutes` to align with other `nearest*` date calculations. [#1034](https://github.com/SwifterSwift/SwifterSwift/pull/1034) by [mmdock](https://github.com/mmdock) +- **Digest** + - `DigestExtensions.swift` would not compile on Xcode 14 due to an `ambiguous use of 'makeIterator()'` error. [#1042](https://github.com/SwifterSwift/SwifterSwift/issues/1042) by [theedov](https://github.com/theedov) +- **HKActivitySummary** + - Add `@available(macOS 13.0)` to fix compilation on macOS. [#1059](https://github.com/SwifterSwift/SwifterSwift/issues/1059) by [guykogus](https://github.com/guykogus) +- **NotificationCenter** + - Fixed warning in `observeOnce` that about capture in sendable closure. +- **UINavigationBar** + - Change `setTitleFont(_:color:)` and `setColors(background:text:)` to support iOS 15+. [#1105](https://github.com/SwifterSwift/SwifterSwift/pull/1105) by [TTOzzi](https://github.com/TTOzzi) +- **String** + - Fixed `subscript(safe:)` to correctly check for valid ranges of different types. [#1114](https://github.com/SwifterSwift/SwifterSwift/issues/1114) by [guykogus](https://github.com/guykogus) + +## [v5.3.0](https://github.com/SwifterSwift/SwifterSwift/releases/tag/5.3.0) ### Breaking Change - **Sequence** - Remove `last(where:)` and move `last(where:equals:)` to `BidirectionalCollection`, since it only makes semantic sense for ordered sequences. [#912](https://github.com/SwifterSwift/SwifterSwift/pull/912) by [guykogus](https://github.com/guykogus) - **UIView** - - Rename `shadowColor`, `shadowOffset`, `shadowOpacity` and `shadowRadius` to `layerShadowColor`, `layerShadowOffset`, `layerShadowOpacity` and `layerShadowRadius` to avoid naming colisions with subclasses properties defined in other modules e.g. UIKit. [#897](https://github.com/SwifterSwift/SwifterSwift/pull/897) by [LucianoPAlmeida](https://github.com/LucianoPAlmeida) + - Rename `shadowColor`, `shadowOffset`, `shadowOpacity` and `shadowRadius` to `layerShadowColor`, `layerShadowOffset`, `layerShadowOpacity` and `layerShadowRadius` to avoid naming collisions with subclasses properties defined in other modules e.g. UIKit. [#897](https://github.com/SwifterSwift/SwifterSwift/pull/897) by [LucianoPAlmeida](https://github.com/LucianoPAlmeida) + - Rename `borderColor`, `borderWidth` and `cornerRadius` to `layerBorderColor`, `layerBorderWidth`, and `layerCornerRadius` to avoid naming collisions with subclasses properties defined in other modules e.g. UIKit. [#972](https://github.com/SwifterSwift/SwifterSwift/pull/972) by [Jayxiang](https://github.com/Jayxiang) ### Added - **SCN3Vector** - Added `normalized` method, and basic division functions (`SCNVector3 / scalar`, and `SCNVector3 /= scalar`. [#908](https://github.com/SwifterSwift/SwifterSwift/pull/908) by [thisIsTheFoxe](https://github.com/thisisthefoxe) - **Dictionary** - - Added `pick(keys:)` to pick part of a dictioanry with specified keys. [#911](https://github.com/SwifterSwift/SwifterSwift/pull/911) by [MaratIbragimov](https://github.com/MaratIbragimov) + - Added `pick(keys:)` to pick part of a dictionary with specified keys. [#911](https://github.com/SwifterSwift/SwifterSwift/pull/911) by [MaratIbragimov](https://github.com/MaratIbragimov) - **UIScrollView** - Added `visibleRect`, `scrollToTop(animated:)`, `scrollToLeft(animated:)`, `scrollToBottom(animated:)`, `scrollToRight(animated:)`, `scrollUp(animated:)`, `scrollLeft(animated:)`, `scrollDown(animated:)`, `scrollRight(animated:)`. [#888](https://github.com/SwifterSwift/SwifterSwift/pull/888) by [guykogus](https://github.com/guykogus) - **XCTest** - Added `XCTAssertEqual(_:_:accuracy:_:file:line:)` for checking if `Color` objects are equal within a given level of accuracy. [#889](https://github.com/SwifterSwift/SwifterSwift/pull/889) by [guykogus](https://github.com/guykogus) +- **BinaryInteger** + - Added `bytes` and `init?(bytes:)` to make it easier to work with bytes (as `[UInt8]`). [#987](https://github.com/SwifterSwift/SwifterSwift/pull/987) by [thisIsTheFoxe](https://github.com/thisisthefoxe) - **FloatingPoint** - Moved the square root operator `√` from `Double` and `Float` to make it generic. [#880](https://github.com/SwifterSwift/SwifterSwift/pull/880) by [guykogus](https://github.com/guykogus) - **Collection** - Added `fullRange` to get the entire range of indices in a collection. [#902](https://github.com/SwifterSwift/SwifterSwift/pull/902) by [guykogus](https://github.com/guykogus) - Moved `indices(of:)` from `RandomAccessCollection` to find the indices of an element. [#863](https://github.com/SwifterSwift/SwifterSwift/pull/863) by [guykogus](https://github.com/guykogus) + - Added `adjacentPairs` to generate unique pair of elements in a collection + [#1094](https://github.com/SwifterSwift/SwifterSwift/pull/1094) by [boudhayan](https://github.com/boudhayan) - **UIViewController**: - Added `instantiate(from:bundle:identifier:)` function to `UIViewController` to make it easier to instantiate it from storyboard. [#860](https://github.com/SwifterSwift/SwifterSwift/pull/860) by [VatoKo](https://github.com/VatoKo) - **String**: @@ -49,13 +141,31 @@ The changelog for **SwifterSwift**. Also see the [releases](https://github.com/S - Added `isStandGoalMet`, `isExerciseTimeGoalMet`, and `isEnergyBurnedGoalMet`. [#875](https://github.com/SwifterSwift/SwifterSwift/pull/875) by [lhygilbert](https://github.com/lhygilbert) - **UIView**: - Added `masksToBounds` (IBInspectable) extension. [#877](https://github.com/SwifterSwift/SwifterSwift/pull/877) by [hamtiko](https://github.com/hamtiko) + - Added `loadFromNib(withClass:)`, which loads a UIView of a particular type from a nib file. [#885](https://github.com/SwifterSwift/SwifterSwift/pull/885) by [gurgeous](https://github.com/gurgeous) + - Added `findConstraint` for finding an existing constraint. [#886](https://github.com/SwifterSwift/SwifterSwift/pull/886) by [gurgeous](https://github.com/gurgeous) + - Added `widthConstraint`, `heightConstraint`, `leadingConstraint`, `trailingConstraint`, `topConstraint`, and `bottomConstraint` for finding specific constraints. [#886](https://github.com/SwifterSwift/SwifterSwift/pull/886) by [gurgeous](https://github.com/gurgeous) + - Added `UIView.subviews(ofType:)` extension which returns all the subviews of a given type recursively in the view hierarchy rooted on the view it its called. [#993](https://github.com/SwifterSwift/SwifterSwift/pull/993) by [ashercoelho](https://github.com/ashercoelho) + - Added `UIStackView.swap(_ view1:, _ view2:)` extension which exchanges two views that are arranged in the stack. [#989](https://github.com/SwifterSwift/SwifterSwift/pull/989) by [salahamassi](https://github.com/salahamassi) + - Added `addGradient(colors:locations:direction:)` extension to apply gradient color. [#1039](https://github.com/SwifterSwift/SwifterSwift/pull/1039) by [Andy0570](https://github.com/Andy0570) - **UIImage** - Added `averageColor`, which calculates the average UIColor for an entire image. [#884](https://github.com/SwifterSwift/SwifterSwift/pull/884) by [gurgeous](https://github.com/gurgeous) - - Added `loadFromNib(withClass:)`, which loads a UIView of a particular type from a nib file. [#885](https://github.com/SwifterSwift/SwifterSwift/pull/885) by [gurgeous](https://github.com/gurgeous) - - Added `findConstraint` for finding an existing constraint. [#886](https://github.com/SwifterSwift/SwifterSwift/pull/886) by [gurgeous] - - Added `widthConstraint`, `heightConstraint`, `leadingConstraint`, `trailingConstraint`, `topConstraint`, and `bottomConstraint` for finding specific constraints. [#886](https://github.com/SwifterSwift/SwifterSwift/pull/886) by [gurgeous] + - Added `withAlwaysOriginalTintColor(_:)` returns a new version of the image with a tint color that uses the .alwaysOriginal rendering mode. [#886](https://github.com/SwifterSwift/SwifterSwift/pull/886) by [jayxiang][https://github.com/jayxiang] - **StringProtocol** - Added `replacingOccurrences(ofPattern:withTemplate:options:searchRange:)` as a more convenient way to replace patterns. [#901](https://github.com/SwifterSwift/SwifterSwift/pull/901) by [gurgeous](https://github.com/gurgeous) +- **Measurement** + - Added `.degrees(_:)`, `arcMinutes(_:)`, `arcSeconds(_:)`, `radians(_:)`, `gradians(_:)` and `revolutions(_:)` to conveniently initialize measurement with corresponding unit. [#936](https://github.com/SwifterSwift/SwifterSwift/pull/936) by [Shiva Huang](https://github.com/ShivaHuang) +- **UITextField** + - Added `addToolbar(items:height:)` to add a toolbar to a `UITextField`. [#954](https://github.com/SwifterSwift/SwifterSwift/pull/954) by [Randhir Kumar](https://github.com/randhirkumar65) +- **URL** + - Added the `(unsafeString: String)` initializer for `URL` as a more conveniently to construct unsafe `URL`s from `String` by [jevonmao](https://github.com/jevonmao) +- **MKMultiPoint** + - Added `.coordinates` property, to return an array of coordinates for the provided `MKMultiPoint`. [#990](https://github.com/SwifterSwift/SwifterSwift/pull/990) by [@rizwankce](https://github.com/rizwankce). +- **NSAttributedString** + - Added `Array.joined(separator:)` to create a new `NSAttributedString` by concatenating the elements of the sequence, adding the given separator between each element. [#985](https://github.com/SwifterSwift/SwifterSwift/pull/985) by [Roman Podymov](https://github.com/RomanPodymov). +- **UIButton** + - Added `setAttributedTitleForAllStates`, `attributedTitleForDisabled`, `attributedTitleForHighlighted`, `attributedTitleForNormal` and `attributedTitleForSelected` for convenient work with attributed strings. [#1001](https://github.com/SwifterSwift/SwifterSwift/pull/1001) by [Roman Podymov](https://github.com/RomanPodymov). +- **Digest** + - Added `hexString` to get a hexadecimal representation for all digest typed in `CryptoKit` (e.g. `SHA216Digest`, `SHA512Digest`,`MD5Digest`, ...). [#1026](https://github.com/SwifterSwift/SwifterSwift/pull/1026) by [Marco Eidinger](https://github.com/MarcoEidinger). ### Changed - **NSAttributedString**: @@ -63,22 +173,28 @@ The changelog for **SwifterSwift**. Also see the [releases](https://github.com/S - `applying(attributes:)` changed access modifier from `fileprivate` to `public`. [#832](https://github.com/SwifterSwift/SwifterSwift/pull/832) by [cHaLkdusT](https://github.com/cHaLkdusT) - **Color**: - Refactored `init(light:dark:)` to remove deployment target version restrictions. [#844](https://github.com/SwifterSwift/SwifterSwift/pull/844) by [VincentSit](https://github.com/vincentsit). + - Use `enum` to declare namespace instead of using `struct`. Thus private initializer is no longer needed. [#927](https://github.com/SwifterSwift/SwifterSwift/pull/927) by [Shiva Huang](https://github.com/ShivaHuang) + - Add `init?(argbHexString:)` to support the common ARGB format used in Android. [#971](https://github.com/SwifterSwift/SwifterSwift/pull/971) by [yonat](https://github.com/yonat) - **CAGradientLayer**: - In `init(colors:locations:startPoint:endPoint:type:)` added default values to `startPoint` and `endPoint`. [#864](https://github.com/SwifterSwift/SwifterSwift/pull/864) by [guykogus](https://github.com/guykogus) - **UITextField**: - Added `addPaddingRight`,`addPaddingRightIcon`extension,[#878](https://github.com/SwifterSwift/SwifterSwift/pull/878) by [Jayxiang](https://github.com/Jayxiang) - **UIAlertController**: - Mark `show` method as unavailable for `iOSAppExtension` targets. [#918](https://github.com/SwifterSwift/SwifterSwift/pull/918) by [LucianoPAlmeida](https://github.com/LucianoPAlmeida) -- **ColorExtension**: - - Use `enum` to declare namespace instead of using `struct`. Thus private initailizer is no longer needed. [#927] by [Shiva Huang](https://github.com/ShivaHuang) +- **UIRefreshControl**: + - Add `beginRefreshing(animated:sendAction:)` that works inside any `UIScrollView` and not only `UITableView`. [#949](https://github.com/SwifterSwift/SwifterSwift/pull/949) by [yonat](https://github.com/yonat) +- **SKSpriteNode**: + - Added `aspectFill(to:)` to size SKSpriteNode with respect to aspect ratio. [#490](https://github.com/SwifterSwift/SwifterSwift/pull/490) by [erikdrobne](https://github.com/erikdrobne). ### Deprecated - **Sequence**: - - Marked `map(by:)`, `compactMap(by:)`, `filter(by:)` as deprecated in favor use of Key Path expressions as functions feature in Swift 5.2. [#862](https://github.com/SwifterSwift/SwifterSwift/pull/862) by [Roman Podymov](https://github.com/RomanPodymov). + - Marked `map(by:)`, `compactMap(by:)`, `filter(by:)` as deprecated in favor use of Key Path expressions as functions feature in Swift 5.3. [#862](https://github.com/SwifterSwift/SwifterSwift/pull/862) by [Roman Podymov](https://github.com/RomanPodymov). ### Removed - **UIDatePicker** - Disabled `textColor` when compiling for target `macCatalyst` as it will crash. [#864](https://github.com/SwifterSwift/SwifterSwift/pull/864) by [guykogus](https://github.com/guykogus) +- **MKPolyline** + - Removed `.coordinates` property, in favour of `.coordinates` property from `MKMultiPoint`. Since `MKPolyline` extends from `MKMultiPoint` it should work fine. [#990](https://github.com/SwifterSwift/SwifterSwift/pull/990) by [@rizwankce](https://github.com/rizwankce). ### Fixed - **Collection** @@ -87,12 +203,18 @@ The changelog for **SwifterSwift**. Also see the [releases](https://github.com/S - CAGradientLayer extensions inaccessible through internal level protection. [#856](https://github.com/SwifterSwift/SwifterSwift/pull/856) by [Den Andreychuk](https://github.com/denandreychuk). - **StringExtensions.swift**: - Fixed a bug: When the length of a string is 0, calling truncated method will crash. [#866](https://github.com/SwifterSwift/SwifterSwift/pull/866) by [phil zhang](https://github.com/philCc) + - Fixed `String.base64Decoded` making it a safe decode by ignore non-base64 characters. [#961](https://github.com/SwifterSwift/SwifterSwift/pull/961) by [Jayxiang](https://github.com/Jayxiang) - **UITextField** - Fixed a bug:UITextField `addPaddingLeftIcon` doesn't work on iOS 13[#876](https://github.com/SwifterSwift/SwifterSwift/issues/876) by [Jayxiang](https://github.com/Jayxiang) - **UIImage** - Fixed a bug:UIImage `rotated(by:)` lose origin scale, result in image blurred[#904](https://github.com/SwifterSwift/SwifterSwift/pull/904) by [yanpanpan](https://github.com/yanpanpan) +- **UIView** + - Fixed `rotate(toAngle)` to rotate _to_ an angle instead of _by_ an angle, as raised in [#935](https://github.com/SwifterSwift/SwifterSwift/pull/935). [#1019](https://github.com/SwifterSwift/SwifterSwift/pull/1019) by [guykogus](https://github.com/guykogus) - **ColorExtension**: - Fixed a bug: `Color.FlatUI` can be initialized. by [Shiva Huang](https://github.com/ShivaHuang) + - Fixed `Color.init?(hexString: String, transparency: CGFloat = 1)` was not handling uppercase `0X` in hex prefix [#947](https://github.com/SwifterSwift/SwifterSwift/pull/947) by [Zero.D.Saber](https://github.com/faimin) +- **URLExtension** + - Fixed `deletingAllPathComponents()` and `deleteAllPathComponents` to handle empty paths, as raised in [#1012](https://github.com/SwifterSwift/SwifterSwift/pull/1012). [#1018](https://github.com/SwifterSwift/SwifterSwift/pull/1018) by [guykogus](https://github.com/guykogus) ### Security @@ -112,6 +234,7 @@ The changelog for **SwifterSwift**. Also see the [releases](https://github.com/S - Conform to `Equatable` when `Wrapped` is `RawRepresentable` and its `RawValue` is `Equatable`. [#804](https://github.com/SwifterSwift/SwifterSwift/pull/804) by [guykogus](https://github.com/guykogus) - **CoreLocation**: - Added `Array where Element: CLLocation extension` and added `distance(unitLength:)` function. [#799](https://github.com/SwifterSwift/SwifterSwift/pull/799) by [trevorphillips](https://github.com/trevorphillips) + - Added `isInRange(of:radius:unit:)` to check if a location is within the radius of another location. [#1095](https://github.com/SwifterSwift/SwifterSwift/pull/1095) by [never-better](https://github.com/never-better) - **Decodable**: - Added `init?(data:decoder:)` to decode `Decodable` (Codable) type models. [#797](https://github.com/SwifterSwift/SwifterSwift/pull/797) by [Mustafa GUNES](https://github.com/mustafagunes). - **CLVisit**: @@ -126,6 +249,7 @@ The changelog for **SwifterSwift**. Also see the [releases](https://github.com/S - Added `init(grouping:by:)` to initialize a dictionary by grouping sequence from a hashable `KeyPath`. [#751](https://github.com/SwifterSwift/SwifterSwift/pull/751) by [mmdock](https://github.com/mmdock) - **RangeReplaceableCollection**: - Added `removeDuplicates(keyPath:)` for removing duplicate elements based on key path. [#737](https://github.com/SwifterSwift/SwifterSwift/pull/737) by [Ilya Glushchuk](https://github.com/iglushchuk). + - Added `appendIfNonNil(_:)` and `appendIfNonNil(contentsOf:)` methods that can append optional elements and sequences. [#966](https://github.com/SwifterSwift/SwifterSwift/pull/966) by [jevonmao](https://github.com/jevonmao) - **Color**: - Added `init(light:dark:)` to create an NSColor/UIColor with different variations for light and dark mode. Only available in iOS/tvOS 13.0, macOS 10.15. [#722](https://github.com/SwifterSwift/SwifterSwift/pull/722) by [MaxHaertwig](https://github.com/maxhaertwig). - **String**: @@ -174,7 +298,7 @@ The changelog for **SwifterSwift**. Also see the [releases](https://github.com/S - **UIImage**: - Implemented `filled(withColor:)` using `UIGraphicsImageRenderer` when available. [#733](https://github.com/SwifterSwift/SwifterSwift/pull/733) - Updated `kilobytesSize` to be computed independently from `bytesSize` [#753](https://github.com/SwifterSwift/SwifterSwift/pull/753) by [mmdock](https://github.com/mmdock) - - Updated `init?(base64String:)` to take in a `scale` factor paramater. [#753](https://github.com/SwifterSwift/SwifterSwift/pull/753) by [mmdock](https://github.com/mmdock) + - Updated `init?(base64String:)` to take in a `scale` factor parameter. [#753](https://github.com/SwifterSwift/SwifterSwift/pull/753) by [mmdock](https://github.com/mmdock) - **UIImage**: - Refactored `tint(_:blendMode:)` using UIGraphicsImageRenderer if available. [#731](https://github.com/SwifterSwift/SwifterSwift/pull/731) by [FraDeliro](https://github.com/FraDeliro). - **Sequence**: @@ -250,7 +374,7 @@ The changelog for **SwifterSwift**. Also see the [releases](https://github.com/S - Added `safeScrollToItem(at:at:animated:)` method to safely scroll UICollectionView to the given IndexPath. [#698](https://github.com/SwifterSwift/SwifterSwift/pull/698) by [emilrb](https://github.com/emilrb) - **Sequence**: - - Moved `divided(by:)` ArrayExtensions to SequenceExtensions. This function separates all items into 2 lists based on a given predicate. [#706](https://github.com/SwifterSwift/SwifterSwift/pull/706) by [cHaLkdusT](https://github.com/cHaLkdusT) + - Moved `divided(by:)` ArrayExtensions to SequenceExtensions. This function separates all items into 2 lists based on a given predicate. [#706](https://github.com/SwifterSwift/SwifterSwift/pull/706) by [cHaLkdusT](https://github.com/cHaLkdusT) - **UIBezierPath**: - Added `init(from:to:)`, `init(points:)`, `init(polygonWithPoints:)`, `init(ovalOf:centered:)` and `init(rectOf:centered:)` convenience initializers. [#659](https://github.com/SwifterSwift/SwifterSwift/pull/659) by [MaxHaertwig](https://github.com/maxhaertwig). @@ -266,7 +390,7 @@ The changelog for **SwifterSwift**. Also see the [releases](https://github.com/S ### Deprecated - **Array**: - - Deprecated `sorted(by:, ascending)` and `sort(by:ascending)` in favor of `sorted(by:with:)` and `sort(by:with:)` [#712](https://github.com/SwifterSwift/SwifterSwift/pull/712) by [LucianoPAlmeida](https://github.com/LucianoPAlmeida) + - Deprecated `sorted(by:ascending:)` and `sort(by:ascending:)` in favor of `sorted(by:with:)` and `sort(by:with:)` [#712](https://github.com/SwifterSwift/SwifterSwift/pull/712) by [LucianoPAlmeida](https://github.com/LucianoPAlmeida) ### Removed @@ -403,8 +527,8 @@ The changelog for **SwifterSwift**. Also see the [releases](https://github.com/S - Added scalar multiplication of CGFloat and CGVector via standard multiplication operator (\*). [#527](https://github.com/SwifterSwift/SwifterSwift/pull/527) by [moyerr](https://github.com/moyerr) - Added negation of vectors via prefix (-) operator. [#527](https://github.com/SwifterSwift/SwifterSwift/pull/527) by [moyerr](https://github.com/moyerr) - Added `init(angle:magnitude:)` to create vectors based on their angle and magnitude. [#527](https://github.com/SwifterSwift/SwifterSwift/pull/527) by [moyerr](https://github.com/moyerr) --**UIRefreshControl**: - - `beginRefresh(in tableView:, animated:, sendAction:)` UIRefreshControl extension to begin refresh programatically. [#525](https://github.com/SwifterSwift/SwifterSwift/pull/525) by [ratulSharker](https://github.com/ratulSharker) +- **UIRefreshControl**: + - `beginRefresh(in tableView:, animated:, sendAction:)` UIRefreshControl extension to begin refresh programmatically. [#525](https://github.com/SwifterSwift/SwifterSwift/pull/525) by [ratulSharker](https://github.com/ratulSharker) - **Dictionary**: - Added `removeValueForRandomKey()` to remove a value for a random key from a dictionary. [#497](https://github.com/SwifterSwift/SwifterSwift/pull/497) by [MaxHaertwig](https://github.com/maxhaertwig). - Added `mapKeysAndValues(_:)` to map a `Dictionary` into a `Dictionary` with different (or same) `Key` and `Value` types. [#546](https://github.com/SwifterSwift/SwifterSwift/pull/546) by [guykogus](https://github.com/guykogus) @@ -416,7 +540,7 @@ The changelog for **SwifterSwift**. Also see the [releases](https://github.com/S - Added `addGestureRecognizers(_:)` which accepts an array of `UIGestureRecognizer` to add multiple gesture recognizers to a view with one call. [#523](https://github.com/SwifterSwift/SwifterSwift/pull/523) by [moyerr](https://github.com/moyerr) - Added `removeGestureRecognizers(_:)` which accepts an array of `UIGestureRecognizer` to remove multiple gesture recognizers from a view with one call. [#523](https://github.com/SwifterSwift/SwifterSwift/pull/523) by [moyerr](https://github.com/moyerr) - **UIViewController** - - Added `addChildViewController(_:toContainerView)` to easily add child view controllers. Accepts a `UIViewController` and a `UIView` to add the child's view to. + - Added `addChildViewController(_:toContainerView)` to easily add child view controllers. Accepts a `UIViewController` and a `UIView` to add the child's view to. - Added `removeViewAndControllerFromParentViewController()` to remove a `UIViewController` from its parent. - **NSView** - Added `backgroundColor` which allows to change backgroundColor of NSView [#702](https://github.com/SwifterSwift/SwifterSwift/pull/702) by [RomanPodymov](https://github.com/RomanPodymov) @@ -488,14 +612,14 @@ The changelog for **SwifterSwift**. Also see the [releases](https://github.com/S - **String**: - Added `firstCharacterUppercased()` method to return a string with only the first character uppercased. [#505](https://github.com/SwifterSwift/SwifterSwift/pull/505) by [happiehappie](https://github.com/happiehappie) - **UITextView**: - - Added `wrapToContent()` method which will remove insets, offsets, paddings which lies within UITextView's `bounds` and `contenSize`. [#458](https://github.com/SwifterSwift/SwifterSwift/pull/458) by [ratulSharker](https://github.com/ratulSharker) + - Added `wrapToContent()` method which will remove insets, offsets, paddings which lies within UITextView's `bounds` and `contentSize`. [#458](https://github.com/SwifterSwift/SwifterSwift/pull/458) by [ratulSharker](https://github.com/ratulSharker) - **URL** - Added `deletingAllPathComponents()` and `deleteAllPathComponents()` to delete all path components from a URL. [#441](https://github.com/SwifterSwift/SwifterSwift/pull/441) by [setoelkahfi](https://github.com/setoelkahfi). - Added `queryValue(for:)` to get the value of a query key from a URL. [#467](https://github.com/SwifterSwift/SwifterSwift/pull/467) by [jdisho](https://github.com/jdisho). - **UITableView**: - Added `isValidIndexPath(_:)` method to check whether given IndexPath is valid within UITableView. [#441](https://github.com/SwifterSwift/SwifterSwift/pull/441) by [setoelkahfi](https://github.com/setoelkahfi). - Added `safeScrollToRow(at:at:animated:)` method to safely scroll UITableView to the given IndexPath. [#445](https://github.com/SwifterSwift/SwifterSwift/pull/445) by [setoelkahfi](https://github.com/setoelkahfi). - - Fixed `lastSection`, and `indexPathForLastRow` and `indexPathForLastRow(inSection: 0)` methods to get last section, get the lastIndexPath for section 0 if exists and get the lastIndexPath for a given section respectively . + - Fixed `lastSection`, and `indexPathForLastRow` and `indexPathForLastRow(inSection: 0)` methods to get last section, get the lastIndexPath for section 0 if exists and get the lastIndexPath for a given section respectively . [#694](https://github.com/SwifterSwift/SwifterSwift/pull/694) by [mohshin-shah](https://github.com/mohshin-shah). - **Optional**: - Added `isNilOrEmpty` property to check whether an optional is nil or empty collection. @@ -525,7 +649,7 @@ The changelog for **SwifterSwift**. Also see the [releases](https://github.com/S - The conformance of `sum()`, `last(where:)`, `reject(where:)`, `count(where:)`, `forEachReversed()`, `forEach(where:, body:)`, `accumulate(initial:, next:)`, `filtered(_:, map:)` and `contains(_:)` has been changed from Array to Sequence [#470](https://github.com/SwifterSwift/SwifterSwift/pull/470) by [MaxHaertwig](https://github.com/maxhaertwig). - The conformance of `average()`, `firstIndex(where:)`, `lastIndex(where:)`, `indices(where:)`, `forEach(slice:, body:)`, `group(by:)`, `firstIndex(of:)` and `lastIndex(of:)` has been changed from Array to Collection [#470](https://github.com/SwifterSwift/SwifterSwift/pull/470) by [MaxHaertwig](https://github.com/maxhaertwig). - **Dictionary** - - The `removeAll(keys:)` changed its paramenter keys to a generic `Sequence` instead of an `Array`. [#482](https://github.com/SwifterSwift/SwifterSwift/pull/482) by [LucianoPAlmeida](https://github.com/LucianoPAlmeida). + - The `removeAll(keys:)` changed its parameter keys to a generic `Sequence` instead of an `Array`. [#482](https://github.com/SwifterSwift/SwifterSwift/pull/482) by [LucianoPAlmeida](https://github.com/LucianoPAlmeida). ### Deprecated - **Array** @@ -621,7 +745,7 @@ The changelog for **SwifterSwift**. Also see the [releases](https://github.com/S - **UITableView** - added `register(nibWithCellClass:, at bundleClass:)` method to be able to register a cell with custom nib just by its class name. [#386](https://github.com/SwifterSwift/SwifterSwift/pull/386) by [jason-ingenuity](https://github.com/jason-ingenuity). - **URL** - - added `queryParmeters` property to get the query parameters from a URL as a dictionary. [#370](https://github.com/SwifterSwift/SwifterSwift/pull/370) by [nathanbacon](https://github.com/nathanbacon). + - added `queryParameters` property to get the query parameters from a URL as a dictionary. [#370](https://github.com/SwifterSwift/SwifterSwift/pull/370) by [nathanbacon](https://github.com/nathanbacon). - added `thumbnail(fromTime:)` to generate a thumbnail image from a given url. [410](https://github.com/SwifterSwift/SwifterSwift/pull/410) by [BennX](https://github.com/BennX). - **UserDefaults** - added `object(type: with key: usingDecoder decoder:)` method to be able to retrieve Codable objects from UserDefaults. [#388](https://github.com/SwifterSwift/SwifterSwift/pull/388) by [jason-ingenuity](https://github.com/jason-ingenuity). @@ -770,7 +894,7 @@ N/A ### Bugfixes - **Color** - Fixed a bug in `rgbComponents`, `shortHexString`, and `shortHexOrHexString` where an exception was raised when color is white or black. - - Corrected a typo in `rgbComponenets` -> `rgbComponents` + - Corrected a typo in `rgbComponents` -> `rgbComponents` @@ -869,7 +993,7 @@ N/A - added `subscript(key: String)` method to get values from UserDefaults using the [] operator. - added `float(forKey key: String)` method to get a Float value from UserDefaults. - added `date(forKey key: String)` method to get a Date value from UserDefaults. -- Improved file structre, as in [#236](https://github.com/SwifterSwift/SwifterSwift/issues/236) +- Improved file structure, as in [#236](https://github.com/SwifterSwift/SwifterSwift/issues/236) - Improved README - Removed unnecessary description in Installation section - Updated **List of All Extensions** section to match the new file structure. @@ -945,9 +1069,9 @@ N/A - **UIColor** - `init(hex: Int, transparency: CGFloat = 1)` is now a failable initializer `init?`. [#208](https://github.com/SwifterSwift/SwifterSwift/pull/208) by [@omaralbeik](https://github.com/omaralbeik) - `init(red: Int, green: Int, blue: Int, transparency: CGFloat = 1)` is now a failable initializer `init?`. [#208](https://github.com/SwifterSwift/SwifterSwift/pull/208) by [@omaralbeik](https://github.com/omaralbeik) - - `redComponent` is deprecated, use the new `rgbComponenets.red` instead. [#208](https://github.com/SwifterSwift/SwifterSwift/pull/208) by [@omaralbeik](https://github.com/omaralbeik) - - `greenComponent` is deprecated, use the new `rgbComponenets.green` instead. [#208](https://github.com/SwifterSwift/SwifterSwift/pull/208) by [@omaralbeik](https://github.com/omaralbeik) - - `blueComponent` is deprecated, use the new `rgbComponenets.blue` instead. [#208](https://github.com/SwifterSwift/SwifterSwift/pull/208) by [@omaralbeik](https://github.com/omaralbeik) + - `redComponent` is deprecated, use the new `rgbComponents.red` instead. [#208](https://github.com/SwifterSwift/SwifterSwift/pull/208) by [@omaralbeik](https://github.com/omaralbeik) + - `greenComponent` is deprecated, use the new `rgbComponents.green` instead. [#208](https://github.com/SwifterSwift/SwifterSwift/pull/208) by [@omaralbeik](https://github.com/omaralbeik) + - `blueComponent` is deprecated, use the new `rgbComponents.blue` instead. [#208](https://github.com/SwifterSwift/SwifterSwift/pull/208) by [@omaralbeik](https://github.com/omaralbeik) ### Enhancements - New **String** extensions @@ -966,7 +1090,7 @@ N/A - added `hasValidEmail` to check if textFields text is a valid email format. [#208](https://github.com/SwifterSwift/SwifterSwift/pull/208) by [@omaralbeik](https://github.com/omaralbeik) - added `textType` to set textField for common text types like email addresses & passwords. [#208](https://github.com/SwifterSwift/SwifterSwift/pull/208) by [@omaralbeik](https://github.com/omaralbeik) - New **UIColor** extensions - - added `rgbComponenets` to get RGB components for a UIColor. [#208](https://github.com/SwifterSwift/SwifterSwift/pull/208) by [@omaralbeik](https://github.com/omaralbeik) + - added `rgbComponents` to get RGB components for a UIColor. [#208](https://github.com/SwifterSwift/SwifterSwift/pull/208) by [@omaralbeik](https://github.com/omaralbeik) - Added usage examples in documentation for Foundation extensions. [#208](https://github.com/SwifterSwift/SwifterSwift/pull/208) by [@omaralbeik](https://github.com/omaralbeik) - Moved many duplicated extensions from `DoubleExtensions` and `FloatExtensions` into the new `FloatingPointExtensions`, this makes the code easier to maintain and brings support for other FloatingPoint types like CGFloat, Double32, ... [#208](https://github.com/SwifterSwift/SwifterSwift/pull/208) by [@omaralbeik](https://github.com/omaralbeik) @@ -1590,7 +1714,7 @@ UIImageExtensions: - **init(color, size)**: Create image from color and size UITextViewExtensions: -- **scrollToBotom()**: Scroll to the bottom of text view +- **scrollToBottom()**: Scroll to the bottom of text view - **scrollToTop()**: Scroll to the top of text view UISegmentedControlExtensions: diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 837c59472..12cf48352 100755 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -7,9 +7,6 @@ This document contains information and guidelines about contributing to this pro - [Asking Questions](#asking-questions) - [Ways to Contribute](#ways-to-contribute) - [Adding new Extensions](#adding-new-extensions) -- [Adding Tests](#adding-tests) -- [Adding documentation](#adding-documentation) -- [Adding changelog entries](#adding-changelog-entries) - [Reporting Issues](#reporting-issues) --- @@ -17,7 +14,7 @@ This document contains information and guidelines about contributing to this pro ## Asking Questions We don't use GitHub as a support forum. -For any usage questions that are not specific to the project itself, please ask on [Stack Overflow](https://stackoverflow.com) instead with the tag SwifterSwift. +For any usage questions that are not specific to the project itself, please ask on [Stack Overflow](https://stackoverflow.com) instead, with the tag SwifterSwift. By doing so, you'll be more likely to quickly solve your problem, and you'll allow anyone else with the same question to find the answer. This also allows us to focus on improving the project for others. @@ -30,7 +27,7 @@ You can contribute to the project in a variety of ways: - Improve documentation 🙏 - Add more extensions 👍 - Add missing unit tests 😅 -- Fixing or reporting bugs 😱 +- Fix or report bugs 😱 If you're new to Open Source or Swift the SwifterSwift community is a great place to get involved. @@ -42,157 +39,245 @@ If you're new to Open Source or Swift the SwifterSwift community is a great plac Please refer to the following rules before submitting a pull request with your new extensions: -- Make sure no similar extension already exist in SwifterSwift. -- Add your contributions to [**master branch**](https://github.com/SwifterSwift/SwifterSwift/tree/master): - - by doing this we can merge new pull-requests into **master** branch as soon as they are accepted, and add them to the next releases once they are fully tested. -- Mention the original source of extension source (if possible) as a comment inside extension: +- Make sure no similar extension already exists in SwifterSwift. +- Add your contributions to [**master branch**](https://github.com/SwifterSwift/SwifterSwift/tree/master), by doing so we can merge new pull requests as soon as they are accepted, and add them to the next releases once they are fully tested. +- A pull request should preferably only add **one extension** at a time. - ```swift -public extension SomeType { +For an extension to be suitable for SwifterSwift, it has to fulfill the following conditions: - public name: SomeType { - // https://stackoverflow.com/somepage - // .. code +### 1. Clarity +- Extension names should be in English and as clear as possible. +- Code should be readable and easy to reason about. Refer to [Swift’s API Design Guidelines]([https://www.swift.org/documentation/api-design-guidelines/](https://www.swift.org/documentation/api-design-guidelines/)) for more details. + +### 2. No dependencies +- We do not allow 3rd party dependencies in SwifterSwift. +- Extensions can not rely on each other. We prefer code duplication over-abstraction in this project. That makes it easier to reason about a single extension individually. +- Ideally, copy-pasting any single extension into another codebase should be possible. + +Allowed: + +```swift +public extension Foo { + var bar: Bar { ... } +} +``` + +Not allowed: + +```swift +public extension Foo { + func bar() -> Bar { /* ... */ } + func baz() { + let bar = bar() + // ... + } +} +``` + +### 3. Public extensions +- Extension should be **public**: + +Example: +```swift +public extension Foo { + var bar: Bar { ... } +} +``` + +- Avoid adding unnecessary `public` keywords for unites in an extension: + +```swift +public extension Foo { + public var bar: Bar { ... } +} +``` + +### 4. Examples of allowed code +- Basically, anything that can be expressed in a single extension and does not expose a new type is allowed. + +Examples where `Foo` is the extended type: + +Get-only property: +```swift +public extension Foo { + var bar: Bar { + // ... } +} +``` +Static get-only property: + +```swift +public extension Foo { + static var bar: Bar { + // ... + } } - ``` - -- A pull request should only add one extension at a time. -- Do not use an existing SwifterSwift extension inside another SwifterSwift extension. All extensions should be able to be copied and pasted and work immediately without having to copy another extension. -- All extensions should follow [Swift API Design Guidelines](https://developer.apple.com/videos/play/wwdc2016/403/) -- Always declare extensions as **public**. -- All extensions names should be as clear as possible. -- All extensions should be well documented. see [Adding documentation](#adding-documentation) -- Avoid using custom classes and objects the goal for this library is to extend the standards types available natively in Swift, iOS, macOS, watchOS, tvOS and Linux. -- Extensions could be: - - Enums - - Instance properties & type properties - - Instance methods & type methods - - Initializers - - Structs -- All extensions should be tested. See [Adding Tests](#adding-tests) to know more. -- Files are named based on the type that the contained extensions extend. - - (example: all String extensions are found in "**StringExtensions.swift**" file) -- Add [subspec](https://github.com/SwifterSwift/SwifterSwift/blob/master/SwifterSwift.podspec) if you submit extensions for a module that is not presented in podspec file yet. -- Extensions and tests are ordered inside files in the following order: - -```swift -// MARK: - enums -public enum { - // ... +``` + +Get/set property: + +```swift +public extension Foo { + var bar: Bar { + get { /* ... */ } + set { /* ... */ } + + } } +``` -// MARK: - Properties -public extension SomeType {} +Static get/set property: -// MARK: - Methods -public extension SomeType {} +```swift +public extension Foo { + static var bar: Bar { + get { /* ... */ } + set { /* ... */ } -// MARK: - Initializers -public extension SomeType {} + } +} ``` ---- +Method: -## Adding Tests +```swift +public extension Foo { + func bar() { + // ... + } +} +``` -Please follow these guidelines before submitting a pull request with new tests: +Static method: -- Every extended SwifterSwift type should have one specific subclass of XCTestCase. -- There should be a one to one relationship between methods/properties and their backing tests. -- Tests should be named using the same API of the extension it backs. - - (example: `DateExtensions` method `isBetween` is named `testIsBetween`) -- All test files are named based on the extensions which it tests. - - (example: all String extensions tests are found in "**StringExtensionsTests.swift**" file) -- The subclass should be marked as final. -- All extensions files and test files have a one to one relationship. - - (example: all tests for "**StringExtensions.swift**" are found in the "**StringExtensionsTests.swift**" file) -- SwifterSwift source files should not be added to the test target directly, but you should rather import SwifterSwift into the test target by using: @testable import SwifterSwift -- Tests are ordered inside files in the same order as extensions. See [Adding new Extensions](#adding-new-extensions) to know more. +```swift +public extension Foo { + static func bar() { + // ... + } +} +``` ---- +Initializer: -## Adding documentation +```swift +public extension Foo { + init(bar: Bar) { + // ... + } +} +``` -Use the following template to add documentation for extensions -> Replace placeholders inside <> +Failable initializer: -> Remove any extra lines, eg. if method does not return any value, delete the `- Returns:` line +```swift +public extension Foo { + init?(bar: Bar) { + // ... + } +} +``` -### Documentation template for units with single parameter +Operators and precedence groups: ```swift -/// SwifterSwift: . -/// -/// -/// -/// - Parameter : . -/// - Throws: -/// - Returns: +precedencegroup PowerPrecedence { higherThan: MultiplicationPrecedence } +infix operator **: PowerPrecedence +public func ** (x: Decimal, y: Int) -> Decimal { + return pow(x, y) +} +``` + +### 5. Examples of disallowed code +- Introducing new types, including protocols, classes, actors, structs, or enums. +- Protocol conformance. Example: + +```swift +extension Foo: Codable { + // ... +} +``` + +- Global functions (except operators): + +```swift +public func bar() { + // ... +} ``` -### Documentation template for units with multiple parameters +- Introducing new types in an extension: + +```swift +public extension Foo { + struct Bar { + // ... + } +} +``` + +### 6. Documentation +- Extensions should be well documented with an example —if possible—. +- Documentation should always start with the prefix **SwifterSwift:** +- Extensions should be fully documented in English and as clear as possible. +- In Xcode select the extension and use the shortcut `command` + `alt` + `/` to create a documentation template. or use the following template: ```swift /// SwifterSwift: . /// /// /// -/// - Parameters: -/// - : . -/// - : . +/// - Parameter : . /// - Throws: /// - Returns: ``` -### Documentation template for enums +- Mention the source —if possible— as a comment inside the extension: ```swift -/// SwifterSwift: . -/// -/// - : -/// - : -/// - : -/// - : +public extension Foo { + var bar: Bar { + // https://stackoverflow.com/... + // ... + } +} ``` -### Documentation Examples +### 7. Tested +- Every extended type should have a matching test case. Example: -```swift +For the following extension of type `Bar` defined in framework `Foo`: -/// SwifterSwift: Sum of all elements in array. -/// -/// [1, 2, 3, 4, 5].sum() -> 15 -/// -/// - Returns: Sum of the array's elements. -func sum() -> Element { - // ... -} +> Sources/Foo/BarExtensions.swift -/// SwifterSwift: Date by changing value of calendar component. -/// -/// - Parameters: -/// - component: component type. -/// - value: new value of component to change. -/// - Returns: original date after changing given component to given value. -func changing(_ component: Calendar.Component, value: Int) -> Date? { - // ... -} +```swift +import Foo +public extension Bar { + var baz: Baz { /* ... */ } +} ``` -### Power Tip +The matching test case is: -In Xcode select a method and press `command` + `alt` + `/` to create a documentation template! +> Tests/Foo/BarExtensionsTests.swift ---- - -## Adding changelog entries +```swift +import Foo -The [Changelog](https://github.com/SwifterSwift/SwifterSwift/blob/master/CHANGELOG.md) is a file which contains a curated, chronologically ordered list of notable changes for each version of a project. Please make sure to add a changelog entry describing your contribution to it every time there is a notable change. +final class BarExtensionsTests: XCTestCase { + func testBaz() { + // ... + } +} +``` -The [Changelog Guidelines](https://github.com/SwifterSwift/SwifterSwift/blob/master/CHANGELOG_GUIDELINES.md) contains instructions for maintaining (or adding new entries) to the Changelog. +- There should be a one to one relationship between methods/properties and their backing tests. +- Tests should be named using the same API of the extension it backs, example: `DateExtensions`'s method `isBetween` is named `testIsBetween`. +- Test case classes should be marked as final. +- SwifterSwift source files should not be added to the test target directly, but you should rather import SwifterSwift into the test target by using: `@testable import SwifterSwift` --- @@ -202,8 +287,7 @@ A great way to contribute to the project is to send a detailed issue when you en We always appreciate a well-written, thorough bug report. Check that the project [issues page](https://github.com/SwifterSwift/SwifterSwift/issues) doesn't already include that problem or suggestion before submitting an issue. -If you find a match, add a quick "**+1**" or "**I have this problem too**". -Doing this helps prioritize the most common problems and requests. +If you find a match, add a quick "**+1**" or "**I have this problem too**". Doing this helps prioritize the most common problems and requests. **When reporting issues, please include the following:** diff --git a/Dangerfile b/Dangerfile index 8550022bf..bd43479d6 100644 --- a/Dangerfile +++ b/Dangerfile @@ -27,7 +27,8 @@ warn('This pull request is marked as Work in Progress. DO NOT MERGE!') if github # Xcode summary def summary(platform:) - xcode_summary.report "xcodebuild-#{platform}.json" + # TODO: This is failing in CI step, commenting(since is only summary info) to unblock builds but we need to investigate. + # xcode_summary.report "xcodebuild-#{platform}.json" end def label_tests_summary(label:, platform:) diff --git a/Examples/Examples.playground/Pages/00-ToC.xcplaygroundpage/Contents.swift b/Examples/Examples.playground/Pages/00-ToC.xcplaygroundpage/Contents.swift index 03447a4cc..616e1308c 100644 --- a/Examples/Examples.playground/Pages/00-ToC.xcplaygroundpage/Contents.swift +++ b/Examples/Examples.playground/Pages/00-ToC.xcplaygroundpage/Contents.swift @@ -3,7 +3,7 @@ SwifterSwift is a library of **over 500 properties and methods**, designed to extend Swift's functionality and productivity, staying faithful to the original API design guidelines. - You can find examples of some extenstions and try them out in this playground: + You can find examples of some extensions and try them out in this playground: * [SwiftStdlib extensions](01-SwiftStdlibExtensions) * [Foundation extensions](02-FoundationExtensions) diff --git a/Examples/Examples.playground/Pages/01-SwiftStdlibExtensions.xcplaygroundpage/Contents.swift b/Examples/Examples.playground/Pages/01-SwiftStdlibExtensions.xcplaygroundpage/Contents.swift index 86208d115..a5b8b17a3 100644 --- a/Examples/Examples.playground/Pages/01-SwiftStdlibExtensions.xcplaygroundpage/Contents.swift +++ b/Examples/Examples.playground/Pages/01-SwiftStdlibExtensions.xcplaygroundpage/Contents.swift @@ -58,9 +58,6 @@ let json = dict.jsonString(prettify: true) // Returns CamelCase of string "Some variable nAme".camelCased -// Check if string is in valid email format -"someone@somewebsite.com".isEmail - // Check if string contains at least one letter and one number "123abc".isAlphaNumeric diff --git a/Examples/Examples.playground/Pages/03-UIKitExtensions.xcplaygroundpage/Contents.swift b/Examples/Examples.playground/Pages/03-UIKitExtensions.xcplaygroundpage/Contents.swift index 558d3dd83..e6283f869 100644 --- a/Examples/Examples.playground/Pages/03-UIKitExtensions.xcplaygroundpage/Contents.swift +++ b/Examples/Examples.playground/Pages/03-UIKitExtensions.xcplaygroundpage/Contents.swift @@ -97,9 +97,12 @@ view.backgroundColor = UIColor.red // Set some or all corners radiuses of view. view.roundCorners([.bottomLeft, .topRight], radius: 30) -view.cornerRadius = 30 +view.layerCornerRadius = 30 // Add shadow to view view.addShadow(ofColor: .black, radius: 3, opacity: 0.5) +// Add gradient +view.addGradient(colors: [.red], direction: .rightToLeft) + //: [Next](@next) diff --git a/Examples/Examples.playground/contents.xcplayground b/Examples/Examples.playground/contents.xcplayground index 436ad560c..7bf633c34 100644 --- a/Examples/Examples.playground/contents.xcplayground +++ b/Examples/Examples.playground/contents.xcplayground @@ -1,5 +1,5 @@ - + diff --git a/Gemfile.lock b/Gemfile.lock index 4851b25c4..2c3ea749c 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,289 +1,328 @@ GEM remote: https://rubygems.org/ specs: - CFPropertyList (3.0.2) - activesupport (4.2.11.1) - i18n (~> 0.7) - minitest (~> 5.1) - thread_safe (~> 0.3, >= 0.3.4) - tzinfo (~> 1.1) - addressable (2.7.0) - public_suffix (>= 2.0.2, < 5.0) - algoliasearch (1.27.1) + CFPropertyList (3.0.6) + rexml + activesupport (7.0.8) + concurrent-ruby (~> 1.0, >= 1.0.2) + i18n (>= 1.6, < 2) + minitest (>= 5.1) + tzinfo (~> 2.0) + addressable (2.8.5) + public_suffix (>= 2.0.2, < 6.0) + algoliasearch (1.27.5) httpclient (~> 2.8, >= 2.8.3) json (>= 1.5.1) + artifactory (3.0.15) atomos (0.1.3) - aws-eventstream (1.0.3) - aws-partitions (1.293.0) - aws-sdk-core (3.92.0) - aws-eventstream (~> 1.0, >= 1.0.2) - aws-partitions (~> 1, >= 1.239.0) - aws-sigv4 (~> 1.1) - jmespath (~> 1.0) - aws-sdk-kms (1.30.0) - aws-sdk-core (~> 3, >= 3.71.0) + aws-eventstream (1.2.0) + aws-partitions (1.824.0) + aws-sdk-core (3.181.1) + aws-eventstream (~> 1, >= 1.0.2) + aws-partitions (~> 1, >= 1.651.0) + aws-sigv4 (~> 1.5) + jmespath (~> 1, >= 1.6.1) + aws-sdk-kms (1.71.0) + aws-sdk-core (~> 3, >= 3.177.0) aws-sigv4 (~> 1.1) - aws-sdk-s3 (1.61.1) - aws-sdk-core (~> 3, >= 3.83.0) + aws-sdk-s3 (1.134.0) + aws-sdk-core (~> 3, >= 3.181.0) aws-sdk-kms (~> 1) - aws-sigv4 (~> 1.1) - aws-sigv4 (1.1.1) - aws-eventstream (~> 1.0, >= 1.0.2) - babosa (1.0.3) - claide (1.0.3) + aws-sigv4 (~> 1.6) + aws-sigv4 (1.6.0) + aws-eventstream (~> 1, >= 1.0.2) + babosa (1.0.4) + claide (1.1.0) claide-plugins (0.9.2) cork nap open4 (~> 1.3) - cocoapods (1.9.1) - activesupport (>= 4.0.2, < 5) + cocoapods (1.12.1) + addressable (~> 2.8) claide (>= 1.0.2, < 2.0) - cocoapods-core (= 1.9.1) + cocoapods-core (= 1.12.1) cocoapods-deintegrate (>= 1.0.3, < 2.0) - cocoapods-downloader (>= 1.2.2, < 2.0) + cocoapods-downloader (>= 1.6.0, < 2.0) cocoapods-plugins (>= 1.0.0, < 2.0) cocoapods-search (>= 1.0.0, < 2.0) - cocoapods-stats (>= 1.0.0, < 2.0) - cocoapods-trunk (>= 1.4.0, < 2.0) + cocoapods-trunk (>= 1.6.0, < 2.0) cocoapods-try (>= 1.1.0, < 2.0) colored2 (~> 3.1) escape (~> 0.0.4) fourflusher (>= 2.3.0, < 3.0) gh_inspector (~> 1.0) - molinillo (~> 0.6.6) + molinillo (~> 0.8.0) nap (~> 1.0) - ruby-macho (~> 1.4) - xcodeproj (>= 1.14.0, < 2.0) - cocoapods-core (1.9.1) - activesupport (>= 4.0.2, < 6) + ruby-macho (>= 2.3.0, < 3.0) + xcodeproj (>= 1.21.0, < 2.0) + cocoapods-core (1.12.1) + activesupport (>= 5.0, < 8) + addressable (~> 2.8) algoliasearch (~> 1.0) concurrent-ruby (~> 1.1) fuzzy_match (~> 2.0.4) nap (~> 1.0) netrc (~> 0.11) + public_suffix (~> 4.0) typhoeus (~> 1.0) - cocoapods-deintegrate (1.0.4) - cocoapods-downloader (1.3.0) + cocoapods-deintegrate (1.0.5) + cocoapods-downloader (1.6.3) cocoapods-plugins (1.0.0) nap - cocoapods-search (1.0.0) - cocoapods-stats (1.1.0) - cocoapods-trunk (1.4.1) + cocoapods-search (1.0.1) + cocoapods-trunk (1.6.0) nap (>= 0.8, < 2.0) netrc (~> 0.11) - cocoapods-try (1.1.0) + cocoapods-try (1.2.0) colored (1.2) colored2 (3.1.2) - commander-fastlane (4.4.6) - highline (~> 1.7.2) - concurrent-ruby (1.1.6) + commander (4.6.0) + highline (~> 2.0.0) + concurrent-ruby (1.2.2) cork (0.3.0) colored2 (~> 3.1) - danger (6.3.2) + danger (9.3.1) claide (~> 1.0) claide-plugins (>= 0.9.2) colored2 (~> 3.1) cork (~> 0.1) - faraday (~> 0.9) + faraday (>= 0.9.0, < 3.0) faraday-http-cache (~> 2.0) - git (~> 1.6) - kramdown (~> 2.0) + git (~> 1.13) + kramdown (~> 2.3) kramdown-parser-gfm (~> 1.0) no_proxy_fix - octokit (~> 4.7) - terminal-table (~> 1) + octokit (~> 6.0) + terminal-table (>= 1, < 4) danger-plugin-api (1.0.0) danger (> 2.0) - danger-swiftlint (0.24.2) + danger-swiftlint (0.33.0) danger rake (> 10) thor (~> 0.19) - danger-xcode_summary (0.5.1) + danger-xcode_summary (1.2.0) danger-plugin-api (~> 1.0) - declarative (0.0.10) - declarative-option (0.1.0) - digest-crc (0.5.1) + xcresult (~> 0.2) + declarative (0.0.20) + digest-crc (0.6.5) + rake (>= 12.0.0, < 14.0.0) domain_name (0.5.20190701) unf (>= 0.0.5, < 1.0.0) - dotenv (2.7.5) - emoji_regex (1.0.1) + dotenv (2.8.1) + emoji_regex (3.2.3) escape (0.0.4) - ethon (0.12.0) - ffi (>= 1.3.0) - excon (0.73.0) - faraday (0.17.3) - multipart-post (>= 1.2, < 3) - faraday-cookie_jar (0.0.6) - faraday (>= 0.7.4) + ethon (0.16.0) + ffi (>= 1.15.0) + excon (0.103.0) + faraday (1.10.3) + faraday-em_http (~> 1.0) + faraday-em_synchrony (~> 1.0) + faraday-excon (~> 1.1) + faraday-httpclient (~> 1.0) + faraday-multipart (~> 1.0) + faraday-net_http (~> 1.0) + faraday-net_http_persistent (~> 1.0) + faraday-patron (~> 1.0) + faraday-rack (~> 1.0) + faraday-retry (~> 1.0) + ruby2_keywords (>= 0.0.4) + faraday-cookie_jar (0.0.7) + faraday (>= 0.8.0) http-cookie (~> 1.0.0) - faraday-http-cache (2.0.0) - faraday (~> 0.8) - faraday_middleware (0.13.1) - faraday (>= 0.7.4, < 1.0) - fastimage (2.1.7) - fastlane (2.144.0) + faraday-em_http (1.0.0) + faraday-em_synchrony (1.0.0) + faraday-excon (1.1.0) + faraday-http-cache (2.5.0) + faraday (>= 0.8) + faraday-httpclient (1.0.1) + faraday-multipart (1.0.4) + multipart-post (~> 2) + faraday-net_http (1.0.1) + faraday-net_http_persistent (1.2.0) + faraday-patron (1.0.0) + faraday-rack (1.0.0) + faraday-retry (1.0.3) + faraday_middleware (1.2.0) + faraday (~> 1.0) + fastimage (2.2.7) + fastlane (2.215.1) CFPropertyList (>= 2.3, < 4.0.0) - addressable (>= 2.3, < 3.0.0) + addressable (>= 2.8, < 3.0.0) + artifactory (~> 3.0) aws-sdk-s3 (~> 1.0) - babosa (>= 1.0.2, < 2.0.0) + babosa (>= 1.0.3, < 2.0.0) bundler (>= 1.12.0, < 3.0.0) colored - commander-fastlane (>= 4.4.6, < 5.0.0) + commander (~> 4.6) dotenv (>= 2.1.1, < 3.0.0) - emoji_regex (>= 0.1, < 2.0) + emoji_regex (>= 0.1, < 4.0) excon (>= 0.71.0, < 1.0.0) - faraday (~> 0.17) + faraday (~> 1.0) faraday-cookie_jar (~> 0.0.6) - faraday_middleware (~> 0.13.1) + faraday_middleware (~> 1.0) fastimage (>= 2.1.0, < 3.0.0) gh_inspector (>= 1.1.2, < 2.0.0) - google-api-client (>= 0.29.2, < 0.37.0) - google-cloud-storage (>= 1.15.0, < 2.0.0) - highline (>= 1.7.2, < 2.0.0) + google-apis-androidpublisher_v3 (~> 0.3) + google-apis-playcustomapp_v1 (~> 0.1) + google-cloud-storage (~> 1.31) + highline (~> 2.0) + http-cookie (~> 1.0.5) json (< 3.0.0) - jwt (~> 2.1.0) + jwt (>= 2.1.0, < 3) mini_magick (>= 4.9.4, < 5.0.0) - multi_xml (~> 0.5) - multipart-post (~> 2.0.0) + multipart-post (>= 2.0.0, < 3.0.0) + naturally (~> 2.2) + optparse (~> 0.1.1) plist (>= 3.1.0, < 4.0.0) - public_suffix (~> 2.0.0) - rubyzip (>= 1.3.0, < 2.0.0) + rubyzip (>= 2.0.0, < 3.0.0) security (= 0.1.3) simctl (~> 1.6.3) - slack-notifier (>= 2.0.0, < 3.0.0) terminal-notifier (>= 2.0.0, < 3.0.0) - terminal-table (>= 1.4.5, < 2.0.0) + terminal-table (~> 3) tty-screen (>= 0.6.3, < 1.0.0) tty-spinner (>= 0.8.0, < 1.0.0) word_wrap (~> 1.0.0) xcodeproj (>= 1.13.0, < 2.0.0) xcpretty (~> 0.3.0) xcpretty-travis-formatter (>= 0.0.3) - ffi (1.12.2) + ffi (1.15.5) fourflusher (2.3.1) fuzzy_match (2.0.4) gh_inspector (1.1.3) - git (1.6.0) + git (1.18.0) + addressable (~> 2.8) rchardet (~> 1.8) - google-api-client (0.36.4) + google-apis-androidpublisher_v3 (0.49.0) + google-apis-core (>= 0.11.0, < 2.a) + google-apis-core (0.11.1) addressable (~> 2.5, >= 2.5.1) - googleauth (~> 0.9) - httpclient (>= 2.8.1, < 3.0) + googleauth (>= 0.16.2, < 2.a) + httpclient (>= 2.8.1, < 3.a) mini_mime (~> 1.0) representable (~> 3.0) - retriable (>= 2.0, < 4.0) - signet (~> 0.12) - google-cloud-core (1.5.0) + retriable (>= 2.0, < 4.a) + rexml + webrick + google-apis-iamcredentials_v1 (0.17.0) + google-apis-core (>= 0.11.0, < 2.a) + google-apis-playcustomapp_v1 (0.13.0) + google-apis-core (>= 0.11.0, < 2.a) + google-apis-storage_v1 (0.19.0) + google-apis-core (>= 0.9.0, < 2.a) + google-cloud-core (1.6.0) google-cloud-env (~> 1.0) google-cloud-errors (~> 1.0) - google-cloud-env (1.3.1) - faraday (>= 0.17.3, < 2.0) - google-cloud-errors (1.0.0) - google-cloud-storage (1.25.1) - addressable (~> 2.5) + google-cloud-env (1.6.0) + faraday (>= 0.17.3, < 3.0) + google-cloud-errors (1.3.1) + google-cloud-storage (1.44.0) + addressable (~> 2.8) digest-crc (~> 0.4) - google-api-client (~> 0.33) - google-cloud-core (~> 1.2) - googleauth (~> 0.9) + google-apis-iamcredentials_v1 (~> 0.1) + google-apis-storage_v1 (~> 0.19.0) + google-cloud-core (~> 1.6) + googleauth (>= 0.16.2, < 2.a) mini_mime (~> 1.0) - googleauth (0.11.0) - faraday (>= 0.17.3, < 2.0) + googleauth (1.8.0) + faraday (>= 0.17.3, < 3.a) jwt (>= 1.4, < 3.0) - memoist (~> 0.16) multi_json (~> 1.11) os (>= 0.9, < 2.0) - signet (~> 0.12) - highline (1.7.10) - http-cookie (1.0.3) + signet (>= 0.16, < 2.a) + highline (2.0.3) + http-cookie (1.0.5) domain_name (~> 0.5) httpclient (2.8.3) - i18n (0.9.5) + i18n (1.14.1) concurrent-ruby (~> 1.0) - jmespath (1.4.0) - json (2.3.0) - jwt (2.1.0) - kramdown (2.3.0) + jmespath (1.6.2) + json (2.6.3) + jwt (2.7.1) + kramdown (2.4.0) rexml kramdown-parser-gfm (1.1.0) kramdown (~> 2.0) - memoist (0.16.2) - mini_magick (4.10.1) - mini_mime (1.0.2) - minitest (5.14.0) - molinillo (0.6.6) - multi_json (1.14.1) - multi_xml (0.6.0) - multipart-post (2.0.0) - nanaimo (0.2.6) + mini_magick (4.12.0) + mini_mime (1.1.5) + minitest (5.20.0) + molinillo (0.8.0) + multi_json (1.15.0) + multipart-post (2.3.0) + nanaimo (0.3.0) nap (1.1.0) - naturally (2.2.0) + naturally (2.2.1) netrc (0.11.0) no_proxy_fix (0.1.2) - octokit (4.18.0) - faraday (>= 0.9) - sawyer (~> 0.8.0, >= 0.5.3) + octokit (6.1.1) + faraday (>= 1, < 3) + sawyer (~> 0.9) open4 (1.3.4) - os (1.1.0) - plist (3.5.0) - public_suffix (2.0.5) - rake (13.0.1) + optparse (0.1.1) + os (1.1.4) + plist (3.7.0) + public_suffix (4.0.7) + rake (13.0.6) rchardet (1.8.0) - representable (3.0.4) + representable (3.2.0) declarative (< 0.1.0) - declarative-option (< 0.2.0) + trailblazer-option (>= 0.1.1, < 0.2.0) uber (< 0.2.0) retriable (3.1.2) - rexml (3.2.4) + rexml (3.2.6) rouge (2.0.7) - ruby-macho (1.4.0) - rubyzip (1.3.0) - sawyer (0.8.2) + ruby-macho (2.5.1) + ruby2_keywords (0.0.5) + rubyzip (2.3.2) + sawyer (0.9.2) addressable (>= 2.3.5) - faraday (> 0.8, < 2.0) + faraday (>= 0.17.3, < 3) security (0.1.3) - signet (0.14.0) - addressable (~> 2.3) - faraday (>= 0.17.3, < 2.0) + signet (0.18.0) + addressable (~> 2.8) + faraday (>= 0.17.5, < 3.a) jwt (>= 1.5, < 3.0) multi_json (~> 1.10) - simctl (1.6.8) + simctl (1.6.10) CFPropertyList naturally - slack-notifier (2.3.2) terminal-notifier (2.0.0) - terminal-table (1.8.0) - unicode-display_width (~> 1.1, >= 1.1.1) + terminal-table (3.0.2) + unicode-display_width (>= 1.1.1, < 3) thor (0.20.3) - thread_safe (0.3.6) + trailblazer-option (0.1.2) tty-cursor (0.7.1) - tty-screen (0.7.1) + tty-screen (0.8.1) tty-spinner (0.9.3) tty-cursor (~> 0.7) - typhoeus (1.3.1) + typhoeus (1.4.0) ethon (>= 0.9.0) - tzinfo (1.2.7) - thread_safe (~> 0.1) + tzinfo (2.0.6) + concurrent-ruby (~> 1.0) uber (0.1.0) unf (0.1.4) unf_ext - unf_ext (0.0.7.7) - unicode-display_width (1.7.0) + unf_ext (0.0.8.2) + unicode-display_width (2.4.2) + webrick (1.8.1) word_wrap (1.0.0) - xcodeproj (1.15.0) + xcodeproj (1.22.0) CFPropertyList (>= 2.3.3, < 4.0) atomos (~> 0.1.3) claide (>= 1.0.2, < 2.0) colored2 (~> 3.1) - nanaimo (~> 0.2.6) + nanaimo (~> 0.3.0) + rexml (~> 3.2.4) xcpretty (0.3.0) rouge (~> 2.0.7) xcpretty-json-formatter (0.1.1) xcpretty (~> 0.2, >= 0.0.7) - xcpretty-travis-formatter (1.0.0) + xcpretty-travis-formatter (1.0.1) xcpretty (~> 0.2, >= 0.0.7) + xcresult (0.2.1) PLATFORMS - ruby + arm64-darwin-22 + x86_64-linux DEPENDENCIES cocoapods @@ -295,4 +334,4 @@ DEPENDENCIES xcpretty-json-formatter BUNDLED WITH - 2.1.4 + 2.4.19 diff --git a/Package.swift b/Package.swift index 305e77d33..048d86562 100644 --- a/Package.swift +++ b/Package.swift @@ -1,4 +1,4 @@ -// swift-tools-version:5.2 +// swift-tools-version:5.8 // The swift-tools-version declares the minimum version of Swift required to build this package. import PackageDescription @@ -6,16 +6,30 @@ import PackageDescription let package = Package( name: "SwifterSwift", platforms: [ - .iOS(.v10), - .tvOS(.v9), - .watchOS(.v2), - .macOS(.v10_10) + .iOS(.v12), + .tvOS(.v12), + .watchOS(.v4), + .macOS(.v10_13) ], products: [ .library(name: "SwifterSwift", targets: ["SwifterSwift"]) ], - dependencies: [], targets: [ - .target(name: "SwifterSwift", dependencies: []), - .testTarget(name: "SwifterSwiftTests", dependencies: ["SwifterSwift"], path: "Tests") + .target(name: "SwifterSwift", + swiftSettings: [ + .enableUpcomingFeature("ConciseMagicFile"), + .enableUpcomingFeature("ExistentialAny"), + .enableUpcomingFeature("ForwardTrailingClosures") + ]), + .testTarget( + name: "SwifterSwiftTests", + dependencies: ["SwifterSwift"], + path: "Tests", + exclude: ["Info.plist"], + resources: [.process("ResourcesTests/Resources")], + swiftSettings: [ + .enableUpcomingFeature("ConciseMagicFile"), + .enableUpcomingFeature("ExistentialAny"), + .enableUpcomingFeature("ForwardTrailingClosures") + ]) ]) diff --git a/README.md b/README.md index f3bfbd43d..73427c49c 100755 --- a/README.md +++ b/README.md @@ -10,19 +10,21 @@ [![Accio supported](https://img.shields.io/badge/Accio-supported-0A7CF5.svg?style=flat)](https://github.com/JamitLabs/Accio) [![codecov](https://codecov.io/gh/SwifterSwift/SwifterSwift/branch/master/graph/badge.svg)](https://codecov.io/gh/SwifterSwift/SwifterSwift) [![docs](http://swifterswift.com/docs/badge.svg)](http://swifterswift.com/docs) -[![Swift](https://img.shields.io/badge/Swift-5.0-orange.svg)](https://swift.org) -[![Xcode](https://img.shields.io/badge/Xcode-11.4-blue.svg)](https://developer.apple.com/xcode) +[![Swift](https://img.shields.io/badge/Swift-5.6-orange.svg)](https://swift.org) +[![Xcode](https://img.shields.io/badge/Xcode-12.4-blue.svg)](https://developer.apple.com/xcode) [![MIT](https://img.shields.io/badge/License-MIT-red.svg)](https://opensource.org/licenses/MIT) -[![Slack Channel](https://slackin-ppvrggbpgn.now.sh/badge.svg)](https://slackin-ppvrggbpgn.now.sh/) +[![Slack Channel](https://img.shields.io/badge/Slack-SwifterSwift-green.svg)](https://join.slack.com/t/swifterswift/shared_invite/zt-s6dl4g2e-R5V5baIawGpdUz2heShjiQ) SwifterSwift is a collection of **over 500 native Swift extensions**, with handy methods, syntactic sugar, and performance improvements for wide range of primitive data types, UIKit and Cocoa classes –over 500 in 1– for iOS, macOS, tvOS, watchOS and Linux. -### [Whats New in v5.2?](https://github.com/SwifterSwift/SwifterSwift/blob/master/CHANGELOG.md#v520) +[简体中文](README_CN.md) + +### [Whats New in v6.0?](https://github.com/SwifterSwift/SwifterSwift/blob/master/CHANGELOG.md#v600) ## Requirements -- **iOS** 10.0+ / **tvOS** 9.0+ / **watchOS** 2.0+ / **macOS** 10.10+ / **Ubuntu** 14.04+ -- Swift 5.0+ +- **iOS** 12.0+ / **tvOS** 12.0+ / **watchOS** 4.0+ / **macOS** 10.13+ / **Ubuntu** 14.04+ +- Swift 5.6+ ## Looking to use SwifterSwift for older versions of Swift @@ -62,6 +64,9 @@ SwifterSwift is Swift v5.0+ compatible starting from v5

- Integrate CoreLocation extensions only:

pod 'SwifterSwift/CoreLocation'
+

- Integrate CryptoKit extensions only:

+
pod 'SwifterSwift/CryptoKit'
+

- Integrate SpriteKit extensions only:

pod 'SwifterSwift/SpriteKit'
@@ -86,7 +91,7 @@ SwifterSwift is Swift v5.0+ compatible starting from v5

To integrate SwifterSwift into your Xcode project using Carthage, specify it in your Cartfile:

-
github "SwifterSwift/SwifterSwift" ~> 5.2
+
github "SwifterSwift/SwifterSwift" ~> 6.0
 
@@ -101,7 +106,7 @@ let package = Package( name: "YOUR_PROJECT_NAME", targets: [], dependencies: [ - .package(url: "https://github.com/SwifterSwift/SwifterSwift.git", from: "5.2.0") + .package(url: "https://github.com/SwifterSwift/SwifterSwift.git", from: "6.0.0") ] )
@@ -145,6 +150,8 @@ let package = Package(
  • Character extensions
  • Collection extensions
  • Comparable extensions
  • +
  • DecodableExtensions extensions
  • +
  • DefaultStringInterpolationExtensions extensions
  • Dictionary extensions
  • Double extensions
  • Float extensions
  • @@ -174,6 +181,7 @@ let package = Package(
  • NSPredicate extensions
  • URL extensions
  • URLRequest extensions
  • +
  • URLSession extensions
  • UserDefaults extensions
  • @@ -260,6 +268,14 @@ let package = Package( +
    +CryptoKit Extensions +
    + +
    +
    MapKit Extensions
    @@ -334,6 +350,14 @@ let package = Package(
    +
    +Combine Extensions +
    + +
    + ## How cool is this? SwifterSwift is a library of **over 500 properties and methods**, designed to extend Swift's functionality and productivity, staying faithful to the original Swift API design guidelines. @@ -349,9 +373,9 @@ Documentation for all extensions, with examples, is available at [swifterswift.c We want your feedback. Please refer to [contributing guidelines](https://github.com/SwifterSwift/SwifterSwift/tree/master/CONTRIBUTING.md) before participating. -## Slack Channel: [![Slack](https://slackin-ppvrggbpgn.now.sh/badge.svg)](https://slackin-ppvrggbpgn.now.sh/) +## Slack Channel: [![Slack](https://img.shields.io/badge/Slack-SwifterSwift-green.svg)](https://swifterswift.slack.com) -It is always nice to talk with other people using SwifterSwift and exchange experiences, so come [join our Slack channel](https://slackin-ppvrggbpgn.now.sh/). +It is always nice to talk with other people using SwifterSwift and exchange experiences, so come [join our Slack channel](https://swifterswift.slack.com). ## Thanks diff --git a/README_CN.md b/README_CN.md new file mode 100755 index 000000000..585c8c911 --- /dev/null +++ b/README_CN.md @@ -0,0 +1,353 @@ +

    + +

    + +[![Build Status](https://github.com/SwifterSwift/SwifterSwift/workflows/SwifterSwift/badge.svg?branch=master)](https://github.com/SwifterSwift/SwifterSwift/actions) +[![Platforms](https://img.shields.io/badge/platforms-iOS%20%7C%20tvOS%20%7C%20macOS%20%7C%20watchOS%20%7C%20Linux-lightgrey.svg)](https://github.com/SwifterSwift/swifterSwift) +[![Cocoapods](https://img.shields.io/cocoapods/v/SwifterSwift.svg)](https://cocoapods.org/pods/SwifterSwift) +[![Carthage compatible](https://img.shields.io/badge/Carthage-Compatible-brightgreen.svg?style=flat)](https://github.com/Carthage/Carthage) +[![SPM compatible](https://img.shields.io/badge/SPM-Compatible-brightgreen.svg?style=flat)](https://swift.org/package-manager/) +[![Accio supported](https://img.shields.io/badge/Accio-supported-0A7CF5.svg?style=flat)](https://github.com/JamitLabs/Accio) +[![codecov](https://codecov.io/gh/SwifterSwift/SwifterSwift/branch/master/graph/badge.svg)](https://codecov.io/gh/SwifterSwift/SwifterSwift) +[![docs](http://swifterswift.com/docs/badge.svg)](http://swifterswift.com/docs) +[![Swift](https://img.shields.io/badge/Swift-5.6-orange.svg)](https://swift.org) +[![Xcode](https://img.shields.io/badge/Xcode-12.4-blue.svg)](https://developer.apple.com/xcode) +[![MIT](https://img.shields.io/badge/License-MIT-red.svg)](https://opensource.org/licenses/MIT) +[![Slack Channel](https://img.shields.io/badge/Slack-SwifterSwift-green.svg)](https://join.slack.com/t/swifterswift/shared_invite/zt-s6dl4g2e-R5V5baIawGpdUz2heShjiQ) + +SwifterSwift 是 **500 多个原生 Swift 扩展的集合**,为 iOS、macOS、tvOS、watchOS 和 Linux 提供了(超过 500 个)适用于各种原生数据类型、UIKit 和 Cocoa 类的便捷方法、语法糖和性能改进。 + +### [v6.0 有什么新功能?](https://github.com/SwifterSwift/SwifterSwift/blob/master/CHANGELOG.md#v600) + +## 要求 + +- **iOS** 12.0+ / **tvOS** 12.0+ / **watchOS** 4.0+ / **macOS** 10.13+ / **Ubuntu** 14.04+ +- Swift 5.6+ + +## 希望将 SwifterSwift 用于旧版本的 Swift + +SwifterSwift 从 v5 开始兼容 Swift v5.0+ + +- 要与 **Swift 3 / Xcode 8.x** 一起使用,请确保您使用的是 [**`v3.1.1`**](https://github.com/SwifterSwift/SwifterSwift/releases/tag/3.1.1)。 +- 要与 Swift 3.2 / Xcode 9.x 一起使用,请确保您使用的是 [**`v3.2.0`**](https://github.com/SwifterSwift/SwifterSwift/releases/tag/3.2.0)。 + +## 安装 + +
    +CocoaPods +
    +

    要使用 CocoaPods 将 SwifterSwift 集成到您的 Xcode 项目,请在您的 Podfile 中设置:

    +

    - 集成所有扩展(推荐):

    +
    pod 'SwifterSwift'
    +

    - 仅集成 SwiftStdlib 扩展:

    +
    pod 'SwifterSwift/SwiftStdlib'
    +

    - 仅集成 Foundation 扩展:

    +
    pod 'SwifterSwift/Foundation'
    +

    - 仅集成 UIKit 扩展:

    +
    pod 'SwifterSwift/UIKit'
    +

    - 仅集成 AppKit 扩展:

    +
    pod 'SwifterSwift/AppKit'
    +

    - 仅集成 MapKit 扩展:

    +
    pod 'SwifterSwift/MapKit'
    +

    - 仅集成 CoreGraphics 扩展:

    +
    pod 'SwifterSwift/CoreGraphics'
    +

    - 仅集成 CoreLocation 扩展:

    +
    pod 'SwifterSwift/CoreLocation'
    +

    - 仅集成 SpriteKit 扩展:

    +
    pod 'SwifterSwift/SpriteKit'
    +

    - 仅集成 SceneKit 扩展:

    +
    pod 'SwifterSwift/SceneKit'
    +

    - 仅集成 StoreKit 扩展:

    +
    pod 'SwifterSwift/StoreKit'
    +

    - 仅集成 Dispatch 扩展:

    +
    pod 'SwifterSwift/Dispatch'
    +

    - 仅集成 WebKit 扩展:

    +
    pod 'SwifterSwift/WebKit'
    +

    - 仅集成 HealthKit 扩展:

    +
    pod 'SwifterSwift/HealthKit'
    +
    + +
    +Carthage +
    +

    要使用 Carthage 将 SwifterSwift 集成到您的 Xcode 项目中,请在您的 Cartfile 中设置:

    +
    github "SwifterSwift/SwifterSwift" ~> 6.0
    +
    +
    + +
    +Swift Package Manager +
    +

    你可以使用 The Swift Package Manager 来安装 SwifterSwift,请在你的 Package.swift 文件中添加正确的描述:

    +
    import PackageDescription
    +let package = Package(
    +    name: "YOUR_PROJECT_NAME",
    +    targets: [],
    +    dependencies: [
    +        .package(url: "https://github.com/SwifterSwift/SwifterSwift.git", from: "6.0.0")
    +    ]
    +)
    +
    +

    接下来,将 SwifterSwift 添加到您的 targets 依赖项中,如下所示:

    +
    .target(
    +    name: "YOUR_TARGET_NAME",
    +    dependencies: [
    +        "SwifterSwift",
    +    ]
    +),
    +

    然后运行 swift package update

    +

    请注意,Swift Package Manager 不支持为 iOS/tvOS/macOS/watchOS 应用程序编译 - 请参阅下一节中的 Accio。 +

    + +
    +Accio +

    Accio 是一个基于 SwiftPM 的依赖管理器,可以为 iOS/macOS/tvOS/watchOS 构建框架。因此,集成步骤与上述完全相同。一旦你的 Package.swift 文件被配置,你需要运行 accio update 而不是 swift package update

    +
    + +
    +手动 +
    +

    SwifterSwift 文件夹添加到您的 Xcode 项目以使用所有扩展或特定扩展。

    +

    对于您的 test targets,您还可以添加 XCTest 文件夹。

    +
    + +## 所有扩展列表 + +
    +SwiftStdlib 扩展 +
    + +
    + +
    +Foundation 扩展 +
    + +
    + +
    +UIKit 扩展 +
    + +
    + +
    +AppKit 扩展 +
    + +
    + +
    +CoreGraphics 扩展 +
    + +
    + +
    +CoreLocation 扩展 +
    + +
    + +
    +CoreAnimation 扩展 +
    + +
    + +
    +MapKit 扩展 +
    + +
    + +
    +SpriteKit 扩展 +
    + +
    + +
    +SceneKit 扩展 +
    + +
    + +
    +StoreKit 扩展 +
    + +
    + +
    +Dispatch 扩展 +
    + +
    + +
    +WebKit 扩展 +
    + +
    + +
    +HealthKit 扩展 +
    + +
    + +
    +XCTest 扩展 +
    + +
    + +
    +Combine 扩展 +
    + +
    + +## 这有多酷? + +SwifterSwift 是一个包含 **500 多个属性和方法**的库,旨在扩展 Swift 的功能和生产力,并忠实于原生的 Swift API 设计指南。 + +查看项目中的 Examples.playground 以获取一些很酷的示例! + +## 文档 + +所有扩展的文档和示例都在 [swifterswift.com/docs](http://swifterswift.com/docs) + +## 参与其中 + +我们希望得到您的反馈。参与前请参阅 [contributing guidelines](https://github.com/SwifterSwift/SwifterSwift/tree/master/CONTRIBUTING.md)。 + +## Slack 频道: [![Slack](https://img.shields.io/badge/Slack-SwifterSwift-green.svg)](https://swifterswift.slack.com) + +使用 SwifterSwift 与其他人交谈并交流经验总是很愉快,所以[加入我们的 Slack 频道](https://swifterswift.slack.com)吧。 + +## 鸣谢 + +特别感谢: + +- [Steven Deutsch](https://github.com/SD10), [Luciano Almeida](https://github.com/LucianoPAlmeida) 和 [Guy Kogus](https://github.com/guykogus) 对扩展、文档和测试的最新贡献。 +- [Paweł Urbanek](https://github.com/pawurb) 添加了 tvOS、watchOS 和 macOS 的初始化支持和帮助扩展。 +- [Mert Akengin](https://github.com/pvtmert) 和 [Bashar Ghadanfar](https://www.behance.net/lionbytes) 设计了 [project website](http://swifterswift.com) 和 logo. +- 非常感谢这个项目的所有其他[贡献者](https://github.com/SwifterSwift/SwifterSwift/graphs/contributors)。 + +## License 协议 + +SwifterSwift 在 MIT 许可协议下发布的。有关更多信息,请参阅 [LICENSE](https://github.com/SwifterSwift/SwifterSwift/blob/master/LICENSE)。 diff --git a/Sources/SwifterSwift.h b/Sources/SwifterSwift.h index 9af45b4fe..1b37843b1 100644 --- a/Sources/SwifterSwift.h +++ b/Sources/SwifterSwift.h @@ -7,7 +7,6 @@ // #import -#import "TargetConditionals.h" #if TARGET_OS_IPHONE #import diff --git a/Sources/SwifterSwift/AppKit/NSColorExtensions.swift b/Sources/SwifterSwift/AppKit/NSColorExtensions.swift index 0b711d509..78d24e7b6 100644 --- a/Sources/SwifterSwift/AppKit/NSColorExtensions.swift +++ b/Sources/SwifterSwift/AppKit/NSColorExtensions.swift @@ -1,4 +1,4 @@ -// NSColorExtensions.swift - Copyright 2020 SwifterSwift +// NSColorExtensions.swift - Copyright 2023 SwifterSwift #if canImport(AppKit) && !targetEnvironment(macCatalyst) import AppKit diff --git a/Sources/SwifterSwift/AppKit/NSImageExtensions.swift b/Sources/SwifterSwift/AppKit/NSImageExtensions.swift index 46dc65e3a..2a2668318 100644 --- a/Sources/SwifterSwift/AppKit/NSImageExtensions.swift +++ b/Sources/SwifterSwift/AppKit/NSImageExtensions.swift @@ -1,4 +1,4 @@ -// NSImageExtensions.swift - Copyright 2020 SwifterSwift +// NSImageExtensions.swift - Copyright 2023 SwifterSwift #if canImport(AppKit) && !targetEnvironment(macCatalyst) import AppKit @@ -6,7 +6,7 @@ import AppKit // MARK: - Methods public extension NSImage { - /// SwifterSwift: NSImage scaled to maximum size with respect to aspect ratio + /// SwifterSwift: NSImage scaled to maximum size with respect to aspect ratio. /// /// - Parameter maxSize: maximum size /// - Returns: scaled NSImage @@ -45,7 +45,8 @@ public extension NSImage { /// - Parameters: /// - url: Desired file URL. /// - type: Type of image (default is .jpeg). - /// - compressionFactor: used only for JPEG files. The value is a float between 0.0 and 1.0, with 1.0 resulting in no compression and 0.0 resulting in the maximum compression possible. + /// - compressionFactor: used only for JPEG files. The value is a float between 0.0 and 1.0, with 1.0 resulting in + /// no compression and 0.0 resulting in the maximum compression possible. func write(to url: URL, fileType type: NSBitmapImageRep.FileType = .jpeg, compressionFactor: NSNumber = 1.0) { // https://stackoverflow.com/a/45042611/3882644 diff --git a/Sources/SwifterSwift/AppKit/NSViewExtensions.swift b/Sources/SwifterSwift/AppKit/NSViewExtensions.swift index 1591b68fe..325ea4ae0 100644 --- a/Sources/SwifterSwift/AppKit/NSViewExtensions.swift +++ b/Sources/SwifterSwift/AppKit/NSViewExtensions.swift @@ -1,4 +1,4 @@ -// NSViewExtensions.swift - Copyright 2020 SwifterSwift +// NSViewExtensions.swift - Copyright 2023 SwifterSwift #if canImport(AppKit) && !targetEnvironment(macCatalyst) import AppKit diff --git a/Sources/SwifterSwift/Combine/FutureExtensions.swift b/Sources/SwifterSwift/Combine/FutureExtensions.swift new file mode 100644 index 000000000..0b4a62c79 --- /dev/null +++ b/Sources/SwifterSwift/Combine/FutureExtensions.swift @@ -0,0 +1,40 @@ +// FutureExtensions.swift - Copyright 2023 SwifterSwift + +#if canImport(Combine) +import Combine + +// MARK: - Methods + +@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *) +public extension Future where Failure == any Error { + /// Creates a `Future` from an `async` throwing function + /// - Parameter asyncFunc: The asynchronous throwing function to execute. + convenience init(asyncFunc: @escaping () async throws -> Output) { + self.init { promise in + Task { + do { + let result = try await asyncFunc() + promise(.success(result)) + } catch { + promise(.failure(error)) + } + } + } + } +} + +@available(iOS 13.0, macOS 10.15, tvOS 13.0, watchOS 6.0, *) +public extension Future where Failure == Never { + /// Creates a `Future` from an `async` function + /// - Parameter asyncFunc: The asynchronous function to execute. + convenience init(asyncFunc: @escaping () async -> Output) { + self.init { promise in + Task { + let result = await asyncFunc() + promise(.success(result)) + } + } + } +} + +#endif diff --git a/Sources/SwifterSwift/CoreAnimation/CAGradientLayerExtensions.swift b/Sources/SwifterSwift/CoreAnimation/CAGradientLayerExtensions.swift index 3cb0e9a19..9c86640df 100644 --- a/Sources/SwifterSwift/CoreAnimation/CAGradientLayerExtensions.swift +++ b/Sources/SwifterSwift/CoreAnimation/CAGradientLayerExtensions.swift @@ -1,21 +1,25 @@ -// CAGradientLayerExtensions.swift - Copyright 2020 SwifterSwift +// CAGradientLayerExtensions.swift - Copyright 2023 SwifterSwift #if !os(watchOS) && !os(Linux) && canImport(QuartzCore) import QuartzCore public extension CAGradientLayer { /// SwifterSwift: Creates a CAGradientLayer with the specified colors, location, startPoint, endPoint, and type. - /// - Parameter colors: An array of colors defining the color of each gradient stop - /// - Parameter locations: An array of NSNumber defining the location of each + /// + /// - Parameters: + /// - colors: An array of colors defining the color of each gradient stop. + /// - locations: An array of NSNumber defining the location of each /// gradient stop as a value in the range [0,1]. The values must be /// monotonically increasing. If a nil array is given, the stops are /// assumed to spread uniformly across the [0,1] range. When rendered, /// the colors are mapped to the output colorspace before being - /// interpolated. (default is nil) - /// - Parameter startPoint: start point corresponds to the first gradient stop (I.e. [0,0] is the bottom-corner of the layer, [1,1] is the top-right corner.) - /// - Parameter endPoint: end point corresponds to the last gradient stop - /// - Parameter type: The kind of gradient that will be drawn. Currently, the only allowed values are `axial' (the default value), `radial', and `conic'. - convenience init(colors: [Color], + /// interpolated (default is nil). + /// - startPoint: start point corresponds to the first gradient stop (I.e. [0,0] is the bottom-corner of the + /// layer, [1,1] is the top-right corner). + /// - endPoint: end point corresponds to the last gradient stop + /// - type: The kind of gradient that will be drawn. Currently, the only allowed values are `axial' (the default + /// value), `radial', and `conic'. + convenience init(colors: [SFColor], locations: [CGFloat]? = nil, startPoint: CGPoint = CGPoint(x: 0.5, y: 0), endPoint: CGPoint = CGPoint(x: 0.5, y: 1), diff --git a/Sources/SwifterSwift/CoreAnimation/CATransform3DExtensions.swift b/Sources/SwifterSwift/CoreAnimation/CATransform3DExtensions.swift index 7946ae924..a1519425e 100644 --- a/Sources/SwifterSwift/CoreAnimation/CATransform3DExtensions.swift +++ b/Sources/SwifterSwift/CoreAnimation/CATransform3DExtensions.swift @@ -1,4 +1,4 @@ -// CATransform3DExtensions.swift - Copyright 2020 SwifterSwift +// CATransform3DExtensions.swift - Copyright 2023 SwifterSwift // swiftlint:disable identifier_name @@ -24,7 +24,7 @@ extension CATransform3D: Equatable { CATransform3DEqualToTransform(lhs, rhs) } - // swiftlint:disable missing_swifterswift_prefix + // swiftlint:enable missing_swifterswift_prefix } // MARK: - Static Properties @@ -42,27 +42,28 @@ extension CATransform3D: Codable { /// Creates a new instance by decoding from the given decoder. /// - /// This initializer throws an error if reading from the decoder fails, or if the data read is corrupted or otherwise invalid. + /// This initializer throws an error if reading from the decoder fails, or if the data read is corrupted or + /// otherwise invalid. /// - Parameter decoder: The decoder to read data from. @inlinable - public init(from decoder: Decoder) throws { + public init(from decoder: any Decoder) throws { var container = try decoder.unkeyedContainer() - self.init(m11: try container.decode(CGFloat.self), - m12: try container.decode(CGFloat.self), - m13: try container.decode(CGFloat.self), - m14: try container.decode(CGFloat.self), - m21: try container.decode(CGFloat.self), - m22: try container.decode(CGFloat.self), - m23: try container.decode(CGFloat.self), - m24: try container.decode(CGFloat.self), - m31: try container.decode(CGFloat.self), - m32: try container.decode(CGFloat.self), - m33: try container.decode(CGFloat.self), - m34: try container.decode(CGFloat.self), - m41: try container.decode(CGFloat.self), - m42: try container.decode(CGFloat.self), - m43: try container.decode(CGFloat.self), - m44: try container.decode(CGFloat.self)) + try self.init(m11: container.decode(CGFloat.self), + m12: container.decode(CGFloat.self), + m13: container.decode(CGFloat.self), + m14: container.decode(CGFloat.self), + m21: container.decode(CGFloat.self), + m22: container.decode(CGFloat.self), + m23: container.decode(CGFloat.self), + m24: container.decode(CGFloat.self), + m31: container.decode(CGFloat.self), + m32: container.decode(CGFloat.self), + m33: container.decode(CGFloat.self), + m34: container.decode(CGFloat.self), + m41: container.decode(CGFloat.self), + m42: container.decode(CGFloat.self), + m43: container.decode(CGFloat.self), + m44: container.decode(CGFloat.self)) } /// Encodes this value into the given encoder. @@ -72,7 +73,7 @@ extension CATransform3D: Codable { /// This function throws an error if any values are invalid for the given encoder’s format. /// - Parameter encoder: The encoder to write data to. @inlinable - public func encode(to encoder: Encoder) throws { + public func encode(to encoder: any Encoder) throws { var container = encoder.unkeyedContainer() try container.encode(m11) try container.encode(m12) @@ -270,3 +271,5 @@ public extension CATransform3D { #endif #endif + +// swiftlint:enable identifier_name diff --git a/Sources/SwifterSwift/CoreGraphics/CGAffineTransformExtensions.swift b/Sources/SwifterSwift/CoreGraphics/CGAffineTransformExtensions.swift index 868f7a191..ba85882b8 100644 --- a/Sources/SwifterSwift/CoreGraphics/CGAffineTransformExtensions.swift +++ b/Sources/SwifterSwift/CoreGraphics/CGAffineTransformExtensions.swift @@ -1,4 +1,4 @@ -// CGAffineTransformExtensions.swift - Copyright 2020 SwifterSwift +// CGAffineTransformExtensions.swift - Copyright 2023 SwifterSwift #if canImport(CoreGraphics) import CoreGraphics diff --git a/Sources/SwifterSwift/CoreGraphics/CGColorExtensions.swift b/Sources/SwifterSwift/CoreGraphics/CGColorExtensions.swift index b74522001..e9dcb6ac4 100644 --- a/Sources/SwifterSwift/CoreGraphics/CGColorExtensions.swift +++ b/Sources/SwifterSwift/CoreGraphics/CGColorExtensions.swift @@ -1,4 +1,4 @@ -// CGColorExtensions.swift - Copyright 2020 SwifterSwift +// CGColorExtensions.swift - Copyright 2023 SwifterSwift #if canImport(CoreGraphics) import CoreGraphics diff --git a/Sources/SwifterSwift/CoreGraphics/CGFloatExtensions.swift b/Sources/SwifterSwift/CoreGraphics/CGFloatExtensions.swift index 7420c91b2..7622eeb83 100644 --- a/Sources/SwifterSwift/CoreGraphics/CGFloatExtensions.swift +++ b/Sources/SwifterSwift/CoreGraphics/CGFloatExtensions.swift @@ -1,4 +1,4 @@ -// CGFloatExtensions.swift - Copyright 2020 SwifterSwift +// CGFloatExtensions.swift - Copyright 2023 SwifterSwift #if canImport(CoreGraphics) import CoreGraphics diff --git a/Sources/SwifterSwift/CoreGraphics/CGPointExtensions.swift b/Sources/SwifterSwift/CoreGraphics/CGPointExtensions.swift index d52e37632..686d3bbf5 100644 --- a/Sources/SwifterSwift/CoreGraphics/CGPointExtensions.swift +++ b/Sources/SwifterSwift/CoreGraphics/CGPointExtensions.swift @@ -1,4 +1,4 @@ -// CGPointExtensions.swift - Copyright 2020 SwifterSwift +// CGPointExtensions.swift - Copyright 2023 SwifterSwift #if canImport(CoreGraphics) import CoreGraphics @@ -62,11 +62,11 @@ public extension CGPoint { /// // point1 = CGPoint(x: 40, y: 40) /// /// - Parameters: - /// - lhs: self + /// - lhs: `self`. /// - rhs: CGPoint to add. static func += (lhs: inout CGPoint, rhs: CGPoint) { - // swiftlint:disable:next shorthand_operator - lhs = lhs + rhs + lhs.x += rhs.x + lhs.y += rhs.y } /// SwifterSwift: Subtract two CGPoints. @@ -92,14 +92,14 @@ public extension CGPoint { /// // point1 = CGPoint(x: -20, y: -20) /// /// - Parameters: - /// - lhs: self + /// - lhs: `self`. /// - rhs: CGPoint to subtract. static func -= (lhs: inout CGPoint, rhs: CGPoint) { - // swiftlint:disable:next shorthand_operator - lhs = lhs - rhs + lhs.x -= rhs.x + lhs.y -= rhs.y } - /// SwifterSwift: Multiply a CGPoint with a scalar + /// SwifterSwift: Multiply a CGPoint with a scalar. /// /// let point1 = CGPoint(x: 10, y: 10) /// let scalar = point1 * 5 @@ -113,22 +113,22 @@ public extension CGPoint { return CGPoint(x: point.x * scalar, y: point.y * scalar) } - /// SwifterSwift: Multiply self with a scalar + /// SwifterSwift: Multiply self with a scalar. /// /// let point1 = CGPoint(x: 10, y: 10) /// point *= 5 /// // point1 = CGPoint(x: 50, y: 50) /// /// - Parameters: - /// - point: self. + /// - point: `self`. /// - scalar: scalar value. /// - Returns: result of multiplication of the given CGPoint with the scalar. static func *= (point: inout CGPoint, scalar: CGFloat) { - // swiftlint:disable:next shorthand_operator - point = point * scalar + point.x *= scalar + point.y *= scalar } - /// SwifterSwift: Multiply a CGPoint with a scalar + /// SwifterSwift: Multiply a CGPoint with a scalar. /// /// let point1 = CGPoint(x: 10, y: 10) /// let scalar = 5 * point1 diff --git a/Sources/SwifterSwift/CoreGraphics/CGRectExtensions.swift b/Sources/SwifterSwift/CoreGraphics/CGRectExtensions.swift index 3a9afbf7f..2783a561e 100644 --- a/Sources/SwifterSwift/CoreGraphics/CGRectExtensions.swift +++ b/Sources/SwifterSwift/CoreGraphics/CGRectExtensions.swift @@ -1,4 +1,4 @@ -// CGRectExtensions.swift - Copyright 2020 SwifterSwift +// CGRectExtensions.swift - Copyright 2023 SwifterSwift #if canImport(CoreGraphics) import CoreGraphics @@ -6,17 +6,17 @@ import CoreGraphics // MARK: - Properties public extension CGRect { - /// SwifterSwift: Return center of rect + /// SwifterSwift: Return center of rect. var center: CGPoint { CGPoint(x: midX, y: midY) } } // MARK: - Initializers public extension CGRect { - /// SwifterSwift: Create a `CGRect` instance with center and size + /// SwifterSwift: Create a `CGRect` instance with center and size. /// - Parameters: - /// - center: center of the new rect - /// - size: size of the new rect + /// - center: center of the new rect. + /// - size: size of the new rect. init(center: CGPoint, size: CGSize) { let origin = CGPoint(x: center.x - size.width / 2.0, y: center.y - size.height / 2.0) self.init(origin: origin, size: size) @@ -26,12 +26,12 @@ public extension CGRect { // MARK: - Methods public extension CGRect { - /// SwifterSwift: Create a new `CGRect` by resizing with specified anchor + /// SwifterSwift: Create a new `CGRect` by resizing with specified anchor. /// - Parameters: - /// - size: new size to be applied + /// - size: new size to be applied. /// - anchor: specified anchor, a point in normalized coordinates - /// '(0, 0)' is the top left corner of rect,'(1, 1)' is the bottom right corner of rect, - /// defaults to '(0.5, 0.5)'. excample: + /// defaults to '(0.5, 0.5)'. Example: /// /// anchor = CGPoint(x: 0.0, y: 1.0): /// diff --git a/Sources/SwifterSwift/CoreGraphics/CGSizeExtensions.swift b/Sources/SwifterSwift/CoreGraphics/CGSizeExtensions.swift index 56efc3778..c0b70edac 100644 --- a/Sources/SwifterSwift/CoreGraphics/CGSizeExtensions.swift +++ b/Sources/SwifterSwift/CoreGraphics/CGSizeExtensions.swift @@ -1,4 +1,4 @@ -// CGSizeExtensions.swift - Copyright 2020 SwifterSwift +// CGSizeExtensions.swift - Copyright 2023 SwifterSwift #if canImport(CoreGraphics) import CoreGraphics @@ -34,7 +34,7 @@ public extension CGSize { /// // newRect.width = 75 , newRect = 50 /// /// - Parameter boundingSize: bounding size to fit self to. - /// - Returns: self fitted into given bounding size + /// - Returns: self fitted into given bounding size. func aspectFit(to boundingSize: CGSize) -> CGSize { let minRatio = min(boundingSize.width / width, boundingSize.height / height) return CGSize(width: width * minRatio, height: height * minRatio) @@ -48,7 +48,7 @@ public extension CGSize { /// // newRect.width = 100 , newRect = 60 /// /// - Parameter boundingSize: bounding size to fill self to. - /// - Returns: self filled into given bounding size + /// - Returns: self filled into given bounding size. func aspectFill(to boundingSize: CGSize) -> CGSize { let minRatio = max(boundingSize.width / width, boundingSize.height / height) let aWidth = min(width * minRatio, boundingSize.width) @@ -60,7 +60,7 @@ public extension CGSize { // MARK: - Operators public extension CGSize { - /// SwifterSwift: Add two CGSize + /// SwifterSwift: Add two CGSize. /// /// let sizeA = CGSize(width: 5, height: 10) /// let sizeB = CGSize(width: 3, height: 4) @@ -97,7 +97,7 @@ public extension CGSize { /// // sizeA = CGPoint(width: 8, height: 14) /// /// - Parameters: - /// - lhs: self + /// - lhs: `self`. /// - rhs: CGSize to add. static func += (lhs: inout CGSize, rhs: CGSize) { lhs.width += rhs.width @@ -111,14 +111,14 @@ public extension CGSize { /// // result = CGSize(width: 8, height: 14) /// /// - Parameters: - /// - lhs: self. + /// - lhs: `self`. /// - tuple: tuple value. static func += (lhs: inout CGSize, tuple: (width: CGFloat, height: CGFloat)) { lhs.width += tuple.width lhs.height += tuple.height } - /// SwifterSwift: Subtract two CGSize + /// SwifterSwift: Subtract two CGSize. /// /// let sizeA = CGSize(width: 5, height: 10) /// let sizeB = CGSize(width: 3, height: 4) @@ -155,7 +155,7 @@ public extension CGSize { /// // sizeA = CGPoint(width: 2, height: 6) /// /// - Parameters: - /// - lhs: self + /// - lhs: `self`. /// - rhs: CGSize to subtract. static func -= (lhs: inout CGSize, rhs: CGSize) { lhs.width -= rhs.width @@ -169,14 +169,14 @@ public extension CGSize { /// // result = CGSize(width: 3, height: 6) /// /// - Parameters: - /// - lhs: self. + /// - lhs: `self`. /// - tuple: tuple value. static func -= (lhs: inout CGSize, tuple: (width: CGFloat, height: CGFloat)) { lhs.width -= tuple.width lhs.height -= tuple.height } - /// SwifterSwift: Multiply two CGSize + /// SwifterSwift: Multiply two CGSize. /// /// let sizeA = CGSize(width: 5, height: 10) /// let sizeB = CGSize(width: 3, height: 4) @@ -227,7 +227,7 @@ public extension CGSize { /// // result = CGSize(width: 15, height: 40) /// /// - Parameters: - /// - lhs: self. + /// - lhs: `self`. /// - rhs: CGSize to multiply. static func *= (lhs: inout CGSize, rhs: CGSize) { lhs.width *= rhs.width @@ -241,7 +241,7 @@ public extension CGSize { /// // result = CGSize(width: 15, height: 30) /// /// - Parameters: - /// - lhs: self. + /// - lhs: `self`. /// - scalar: scalar value. static func *= (lhs: inout CGSize, scalar: CGFloat) { lhs.width *= scalar diff --git a/Sources/SwifterSwift/CoreGraphics/CGVectorExtensions.swift b/Sources/SwifterSwift/CoreGraphics/CGVectorExtensions.swift index ce5d63d89..0327d958d 100644 --- a/Sources/SwifterSwift/CoreGraphics/CGVectorExtensions.swift +++ b/Sources/SwifterSwift/CoreGraphics/CGVectorExtensions.swift @@ -1,4 +1,4 @@ -// CGVectorExtensions.swift - Copyright 2020 SwifterSwift +// CGVectorExtensions.swift - Copyright 2023 SwifterSwift #if canImport(CoreGraphics) import CoreGraphics @@ -6,7 +6,8 @@ import CoreGraphics // MARK: - Properties public extension CGVector { - /// SwifterSwift: The angle of rotation (in radians) of the vector. The range of the angle is -π to π; an angle of 0 points to the right. + /// SwifterSwift: The angle of rotation (in radians) of the vector. The range of the angle is -π to π; an angle of 0 + /// points to the right. /// /// https://en.wikipedia.org/wiki/Atan2 var angle: CGFloat { @@ -30,7 +31,7 @@ public extension CGVector { /// /// - Parameters: /// - angle: The angle of rotation (in radians) counterclockwise from the positive x-axis. - /// - magnitude: The lenth of the vector. + /// - magnitude: The length of the vector. /// init(angle: CGFloat, magnitude: CGFloat) { // https://www.grc.nasa.gov/WWW/K-12/airplane/vectpart.html @@ -47,9 +48,9 @@ public extension CGVector { /// let largerVector = vector * 2 /// /// - Parameters: - /// - vector: The vector to be multiplied - /// - scalar: The scale by which the vector will be multiplied - /// - Returns: The vector with its magnitude scaled + /// - vector: The vector to be multiplied. + /// - scalar: The scale by which the vector will be multiplied. + /// - Returns: The vector with its magnitude scaled. static func * (vector: CGVector, scalar: CGFloat) -> CGVector { return CGVector(dx: vector.dx * scalar, dy: vector.dy * scalar) } @@ -60,24 +61,24 @@ public extension CGVector { /// let largerVector = 2 * vector /// /// - Parameters: - /// - scalar: The scalar by which the vector will be multiplied - /// - vector: The vector to be multiplied - /// - Returns: The vector with its magnitude scaled + /// - scalar: The scalar by which the vector will be multiplied. + /// - vector: The vector to be multiplied. + /// - Returns: The vector with its magnitude scaled. static func * (scalar: CGFloat, vector: CGVector) -> CGVector { return CGVector(dx: scalar * vector.dx, dy: scalar * vector.dy) } - /// SwifterSwift: Compound assignment operator for vector-scalr multiplication + /// SwifterSwift: Compound assignment operator for vector-scalar multiplication. /// /// var vector = CGVector(dx: 1, dy: 1) /// vector *= 2 /// /// - Parameters: - /// - vector: The vector to be multiplied - /// - scalar: The scale by which the vector will be multiplied + /// - vector: The vector to be multiplied. + /// - scalar: The scale by which the vector will be multiplied. static func *= (vector: inout CGVector, scalar: CGFloat) { - // swiftlint:disable:next shorthand_operator - vector = vector * scalar + vector.dx *= scalar + vector.dy *= scalar } /// SwifterSwift: Negates the vector. The direction is reversed, but magnitude remains the same. @@ -85,8 +86,8 @@ public extension CGVector { /// let vector = CGVector(dx: 1, dy: 1) /// let reversedVector = -vector /// - /// - Parameter vector: The vector to be negated - /// - Returns: The negated vector + /// - Parameter vector: The vector to be negated. + /// - Returns: The negated vector. static prefix func - (vector: CGVector) -> CGVector { return CGVector(dx: -vector.dx, dy: -vector.dy) } diff --git a/Sources/SwifterSwift/CoreLocation/CLLocationArrayExtensions.swift b/Sources/SwifterSwift/CoreLocation/CLLocationArrayExtensions.swift index db8c13d11..c407444bd 100644 --- a/Sources/SwifterSwift/CoreLocation/CLLocationArrayExtensions.swift +++ b/Sources/SwifterSwift/CoreLocation/CLLocationArrayExtensions.swift @@ -1,4 +1,4 @@ -// CLLocationArrayExtensions.swift - Copyright 2020 SwifterSwift +// CLLocationArrayExtensions.swift - Copyright 2023 SwifterSwift #if canImport(CoreLocation) import CoreLocation @@ -6,11 +6,11 @@ import CoreLocation // MARK: - Methods public extension Array where Element: CLLocation { - /// SwifterSwift: Calculates the sum of distances between each location in the array based on the curvature of the earth. + /// SwifterSwift: Calculates the sum of distances between each location in the array based on the curvature of the + /// earth. /// - /// - Parameter unitLength: The unit of length to return the distance in. + /// - Parameter unit: The unit of length to return the distance in. /// - Returns: The distance in the specified unit. - @available(tvOS 10.0, macOS 10.12, watchOS 3.0, *) func distance(unitLength unit: UnitLength) -> Measurement { guard count > 1 else { return Measurement(value: 0.0, unit: unit) diff --git a/Sources/SwifterSwift/CoreLocation/CLLocationExtensions.swift b/Sources/SwifterSwift/CoreLocation/CLLocationExtensions.swift index 07d717203..1f02d294a 100644 --- a/Sources/SwifterSwift/CoreLocation/CLLocationExtensions.swift +++ b/Sources/SwifterSwift/CoreLocation/CLLocationExtensions.swift @@ -1,4 +1,4 @@ -// CLLocationExtensions.swift - Copyright 2020 SwifterSwift +// CLLocationExtensions.swift - Copyright 2023 SwifterSwift #if canImport(CoreLocation) import CoreLocation @@ -63,6 +63,19 @@ public extension CLLocation { return (degrees + 360).truncatingRemainder(dividingBy: 360) } + + /// SwifterSwift: Check the distance to `location` is less than or equal to `radius`. + /// + /// - Parameters: + /// - location: End location. + /// - radius: Range limit distance. + /// - unit: The unit of length. Default value is `.meters`. + /// - Returns: `true` if the distance between the receiver and `location` is less than or equal to the given + /// `radius`. + func isInRange(of location: CLLocation, radius: Double, unitLength unit: UnitLength = .meters) -> Bool { + let distance = Measurement(value: radius, unit: unit).converted(to: .meters).value + return self.distance(from: location) <= distance + } } #endif diff --git a/Sources/SwifterSwift/CoreLocation/CLVisitExtensions.swift b/Sources/SwifterSwift/CoreLocation/CLVisitExtensions.swift index ab2738437..74c27da2a 100644 --- a/Sources/SwifterSwift/CoreLocation/CLVisitExtensions.swift +++ b/Sources/SwifterSwift/CoreLocation/CLVisitExtensions.swift @@ -1,4 +1,4 @@ -// CLVisitExtensions.swift - Copyright 2020 SwifterSwift +// CLVisitExtensions.swift - Copyright 2023 SwifterSwift #if canImport(CoreLocation) && (os(iOS) || targetEnvironment(macCatalyst)) import CoreLocation @@ -8,7 +8,7 @@ import CoreLocation public extension CLVisit { /// SwifterSwift: Retrieves a visit's location. /// - /// - Returns: CLLocation + /// - Returns: CLLocation. var location: CLLocation { return CLLocation(latitude: coordinate.latitude, longitude: coordinate.longitude) } diff --git a/Sources/SwifterSwift/CryptoKit/DigestExtensions.swift b/Sources/SwifterSwift/CryptoKit/DigestExtensions.swift new file mode 100644 index 000000000..328ba2a29 --- /dev/null +++ b/Sources/SwifterSwift/CryptoKit/DigestExtensions.swift @@ -0,0 +1,19 @@ +// DigestExtensions.swift - Copyright 2023 SwifterSwift + +#if canImport(CryptoKit) +import CryptoKit + +@available(iOS 13.0, macOS 10.15, watchOS 6.0, tvOS 13.0, *) +public extension Digest { + // MARK: - Properties + + /// SwifterSwift: Hexadecimal value string (read-only, Complexity: O(N), _N_ being the amount of bytes.) + var hexString: String { + var result = "" + for byte in makeIterator() { + result += String(format: "%02X", byte) + } + return result + } +} +#endif diff --git a/Sources/SwifterSwift/Dispatch/DispatchQueueExtensions.swift b/Sources/SwifterSwift/Dispatch/DispatchQueueExtensions.swift index a1e67b689..d42635b2b 100644 --- a/Sources/SwifterSwift/Dispatch/DispatchQueueExtensions.swift +++ b/Sources/SwifterSwift/Dispatch/DispatchQueueExtensions.swift @@ -1,4 +1,4 @@ -// DispatchQueueExtensions.swift - Copyright 2020 SwifterSwift +// DispatchQueueExtensions.swift - Copyright 2023 SwifterSwift #if canImport(Dispatch) import Dispatch @@ -35,10 +35,10 @@ public extension DispatchQueue { return DispatchQueue.getSpecific(key: key) != nil } - /// SwifterSwift: Runs passed closure asynchronous after certain time interval + /// SwifterSwift: Runs passed closure asynchronous after certain time interval. /// /// - Parameters: - /// - delay: The time inverval after which the closure will run. + /// - delay: The time interval after which the closure will run. /// - qos: Quality of service at which the work item should be executed. /// - flags: Flags that control the execution environment of the work item. /// - work: The closure to run after certain time interval. diff --git a/Sources/SwifterSwift/Foundation/CalendarExtensions.swift b/Sources/SwifterSwift/Foundation/CalendarExtensions.swift index bdda3a4f9..763fb7e17 100644 --- a/Sources/SwifterSwift/Foundation/CalendarExtensions.swift +++ b/Sources/SwifterSwift/Foundation/CalendarExtensions.swift @@ -1,4 +1,4 @@ -// CalendarExtensions.swift - Copyright 2020 SwifterSwift +// CalendarExtensions.swift - Copyright 2023 SwifterSwift #if canImport(Foundation) import Foundation diff --git a/Sources/SwifterSwift/Foundation/DataExtensions.swift b/Sources/SwifterSwift/Foundation/DataExtensions.swift index dbab5a7fb..5800ab913 100644 --- a/Sources/SwifterSwift/Foundation/DataExtensions.swift +++ b/Sources/SwifterSwift/Foundation/DataExtensions.swift @@ -1,4 +1,4 @@ -// DataExtensions.swift - Copyright 2020 SwifterSwift +// DataExtensions.swift - Copyright 2023 SwifterSwift #if canImport(Foundation) import Foundation @@ -29,8 +29,8 @@ public extension Data { /// - Parameter options: Options for reading the JSON data and creating the Foundation object. /// /// For possible values, see `JSONSerialization.ReadingOptions`. - /// - Returns: A Foundation object from the JSON data in the receiver, or `nil` if an error occurs. /// - Throws: An `NSError` if the receiver does not represent a valid JSON object. + /// - Returns: A Foundation object from the JSON data in the receiver, or `nil` if an error occurs. func jsonObject(options: JSONSerialization.ReadingOptions = []) throws -> Any { return try JSONSerialization.jsonObject(with: self, options: options) } diff --git a/Sources/SwifterSwift/Foundation/DateExtensions.swift b/Sources/SwifterSwift/Foundation/DateExtensions.swift index 6600b3a2b..237d58a5c 100755 --- a/Sources/SwifterSwift/Foundation/DateExtensions.swift +++ b/Sources/SwifterSwift/Foundation/DateExtensions.swift @@ -1,4 +1,4 @@ -// DateExtensions.swift - Copyright 2020 SwifterSwift +// DateExtensions.swift - Copyright 2023 SwifterSwift #if canImport(Foundation) import Foundation @@ -49,10 +49,7 @@ public extension Date { public extension Date { /// SwifterSwift: User’s current calendar. - var calendar: Calendar { - // Workaround to segfault on corelibs foundation https://bugs.swift.org/browse/SR-10147 - return Calendar(identifier: Calendar.current.identifier) - } + var calendar: Calendar { Calendar.current } /// SwifterSwift: Era. /// @@ -160,10 +157,12 @@ public extension Date { /// SwifterSwift: Weekday. /// - /// Date().weekday -> 5 // fifth day in the current week. + /// The weekday units are the numbers 1 through N (where for the Gregorian calendar N=7 and 1 is Sunday). + /// + /// Date().weekday -> 5 // fifth day in the current week, e.g. Thursday in the Gregorian calendar /// var weekday: Int { - return calendar.component(.weekday, from: self) + calendar.component(.weekday, from: self) } /// SwifterSwift: Hour. @@ -248,7 +247,8 @@ public extension Date { } set { #if targetEnvironment(macCatalyst) - // The `Calendar` implementation in `macCatalyst` does not know that a nanosecond is 1/1,000,000,000th of a second + // The `Calendar` implementation in `macCatalyst` does not know that a nanosecond is 1/1,000,000,000th of a + // second let allowedRange = 0..<1_000_000_000 #else let allowedRange = calendar.range(of: .nanosecond, in: .second, for: self)! @@ -278,7 +278,8 @@ public extension Date { set { let nanoSeconds = newValue * 1_000_000 #if targetEnvironment(macCatalyst) - // The `Calendar` implementation in `macCatalyst` does not know that a nanosecond is 1/1,000,000,000th of a second + // The `Calendar` implementation in `macCatalyst` does not know that a nanosecond is 1/1,000,000,000th of a + // second let allowedRange = 0..<1_000_000_000 #else let allowedRange = calendar.range(of: .nanosecond, in: .second, for: self)! @@ -404,7 +405,7 @@ public extension Date { [.year, .month, .day, .hour, .minute, .second, .nanosecond], from: self) let min = components.minute! - components.minute? = min % 10 < 6 ? min - min % 10 : min + 10 - (min % 10) + components.minute? = min % 10 < 5 ? min - min % 10 : min + 10 - (min % 10) components.second = 0 components.nanosecond = 0 return calendar.date(from: components)! @@ -525,31 +526,32 @@ public extension Date { /// /// - Parameters: /// - component: component type. - /// - value: multiples of compnenet to add. + /// - value: multiples of component to add. mutating func add(_ component: Calendar.Component, value: Int) { if let date = calendar.date(byAdding: component, value: value, to: self) { self = date } } - // swiftlint:disable cyclomatic_complexity function_body_length + // swiftlint:disable cyclomatic_complexity /// SwifterSwift: Date by changing value of calendar component. /// /// let date = Date() // "Jan 12, 2017, 7:07 PM" - /// let date2 = date.changing(.minute, value: 10) // "Jan 12, 2017, 6:10 PM" + /// let date2 = date.changing(.minute, value: 10) // "Jan 12, 2017, 7:10 PM" /// let date3 = date.changing(.day, value: 4) // "Jan 4, 2017, 7:07 PM" /// let date4 = date.changing(.month, value: 2) // "Feb 12, 2017, 7:07 PM" /// let date5 = date.changing(.year, value: 2000) // "Jan 12, 2000, 7:07 PM" /// /// - Parameters: /// - component: component type. - /// - value: new value of compnenet to change. + /// - value: new value of component to change. /// - Returns: original date after changing given component to given value. func changing(_ component: Calendar.Component, value: Int) -> Date? { switch component { case .nanosecond: #if targetEnvironment(macCatalyst) - // The `Calendar` implementation in `macCatalyst` does not know that a nanosecond is 1/1,000,000,000th of a second + // The `Calendar` implementation in `macCatalyst` does not know that a nanosecond is 1/1,000,000,000th of a + // second let allowedRange = 0..<1_000_000_000 #else let allowedRange = calendar.range(of: .nanosecond, in: .second, for: self)! @@ -605,8 +607,9 @@ public extension Date { } } + // swiftlint:enable cyclomatic_complexity + #if !os(Linux) - // swiftlint:enable cyclomatic_complexity, function_body_length /// SwifterSwift: Data at the beginning of calendar component. /// @@ -652,7 +655,6 @@ public extension Date { } #endif - // swiftlint:disable function_body_length /// SwifterSwift: Date at the end of calendar component. /// /// let date = Date() // "Jan 12, 2017, 7:27 PM" @@ -717,8 +719,6 @@ public extension Date { } } - // swiftlint:enable function_body_length - /// SwifterSwift: Check if date is in current given calendar component. /// /// Date().isInCurrent(.day) -> true @@ -776,7 +776,7 @@ public extension Date { return dateFormatter.string(from: self) } - /// SwifterSwift: Time string from date + /// SwifterSwift: Time string from date. /// /// Date().timeString(ofStyle: .short) -> "7:37 PM" /// Date().timeString(ofStyle: .medium) -> "7:37:02 PM" @@ -844,7 +844,7 @@ public extension Date { /// SwifterSwift: get number of seconds between two date /// - /// - Parameter date: date to compate self to. + /// - Parameter date: date to compare self to. /// - Returns: number of seconds between self and given date. func secondsSince(_ date: Date) -> Double { return timeIntervalSince(date) @@ -852,7 +852,7 @@ public extension Date { /// SwifterSwift: get number of minutes between two date /// - /// - Parameter date: date to compate self to. + /// - Parameter date: date to compare self to. /// - Returns: number of minutes between self and given date. func minutesSince(_ date: Date) -> Double { return timeIntervalSince(date) / 60 @@ -860,7 +860,7 @@ public extension Date { /// SwifterSwift: get number of hours between two date /// - /// - Parameter date: date to compate self to. + /// - Parameter date: date to compare self to. /// - Returns: number of hours between self and given date. func hoursSince(_ date: Date) -> Double { return timeIntervalSince(date) / 3600 @@ -868,18 +868,18 @@ public extension Date { /// SwifterSwift: get number of days between two date /// - /// - Parameter date: date to compate self to. + /// - Parameter date: date to compare self to. /// - Returns: number of days between self and given date. func daysSince(_ date: Date) -> Double { return timeIntervalSince(date) / (3600 * 24) } - /// SwifterSwift: check if a date is between two other dates + /// SwifterSwift: check if a date is between two other dates. /// /// - Parameters: /// - startDate: start date to compare self to. /// - endDate: endDate date to compare self to. - /// - includeBounds: true if the start and end date should be included (default is false) + /// - includeBounds: true if the start and end date should be included (default is false). /// - Returns: true if the date is between the two given dates. func isBetween(_ startDate: Date, _ endDate: Date, includeBounds: Bool = false) -> Bool { if includeBounds { @@ -888,16 +888,16 @@ public extension Date { return startDate.compare(self).rawValue * compare(endDate).rawValue > 0 } - /// SwifterSwift: check if a date is a number of date components of another date + /// SwifterSwift: check if a date is a number of date components of another date. /// /// - Parameters: - /// - value: number of times component is used in creating range + /// - value: number of times component is used in creating range. /// - component: Calendar.Component to use. /// - date: Date to compare self to. - /// - Returns: true if the date is within a number of components of another date + /// - Returns: true if the date is within a number of components of another date. func isWithin(_ value: UInt, _ component: Calendar.Component, of date: Date) -> Bool { let components = calendar.dateComponents([component], from: self, to: date) - let componentValue = components.value(for: component)! + guard let componentValue = components.value(for: component) else { return false } return abs(componentValue) <= value } @@ -923,7 +923,8 @@ public extension Date { .timeIntervalSinceReferenceDate)) } - /// SwifterSwift: Returns a random date within the specified range, using the given generator as a source for randomness. + /// SwifterSwift: Returns a random date within the specified range, using the given generator as a source for + /// randomness. /// /// - Parameters: /// - range: The range in which to create a random date. `range` must not be empty. @@ -936,7 +937,8 @@ public extension Date { using: &generator)) } - /// SwifterSwift: Returns a random date within the specified range, using the given generator as a source for randomness. + /// SwifterSwift: Returns a random date within the specified range, using the given generator as a source for + /// randomness. /// /// - Parameters: /// - range: The range in which to create a random date. @@ -1020,7 +1022,7 @@ public extension Date { self.init(timeIntervalSince1970: unixTimestamp) } - /// SwifterSwift: Create date object from Int literal + /// SwifterSwift: Create date object from Int literal. /// /// let date = Date(integerLiteral: 2017_12_25) // "2017-12-25 00:00:00 +0000" /// - Parameter value: Int value, e.g. 20171225, or 2017_12_25 etc. diff --git a/Sources/SwifterSwift/Foundation/FileManagerExtensions.swift b/Sources/SwifterSwift/Foundation/FileManagerExtensions.swift index 1e26e4964..b7d23577f 100644 --- a/Sources/SwifterSwift/Foundation/FileManagerExtensions.swift +++ b/Sources/SwifterSwift/Foundation/FileManagerExtensions.swift @@ -1,4 +1,4 @@ -// FileManagerExtensions.swift - Copyright 2020 SwifterSwift +// FileManagerExtensions.swift - Copyright 2023 SwifterSwift #if canImport(Foundation) import Foundation @@ -9,8 +9,8 @@ public extension FileManager { /// - Parameters: /// - path: JSON file path. /// - readingOptions: JSONSerialization reading options. - /// - Returns: Optional dictionary. /// - Throws: Throws any errors thrown by Data creation or JSON serialization. + /// - Returns: Optional dictionary. func jsonFromFile( atPath path: String, readingOptions: JSONSerialization.ReadingOptions = .allowFragments) throws -> [String: Any]? { @@ -27,8 +27,8 @@ public extension FileManager { /// - filename: File to read. /// - bundleClass: Bundle where the file is associated. /// - readingOptions: JSONSerialization reading options. - /// - Returns: Optional dictionary. /// - Throws: Throws any errors thrown by Data creation or JSON serialization. + /// - Returns: Optional dictionary. func jsonFromFile( withFilename filename: String, at bundleClass: AnyClass? = nil, @@ -50,25 +50,20 @@ public extension FileManager { } #endif - /// SwifterSwift: Creates a unique directory for saving temporary files. The directory can be used to create multiple temporary files used for a common purpose. + /// SwifterSwift: Creates a unique directory for saving temporary files. The directory can be used to create + /// multiple temporary files used for a common purpose. /// /// let tempDirectory = try fileManager.createTemporaryDirectory() /// let tempFile1URL = tempDirectory.appendingPathComponent(ProcessInfo().globallyUniqueString) /// let tempFile2URL = tempDirectory.appendingPathComponent(ProcessInfo().globallyUniqueString) /// - /// - Returns: A URL to a new directory for saving temporary files. /// - Throws: An error if a temporary directory cannot be found or created. + /// - Returns: A URL to a new directory for saving temporary files. func createTemporaryDirectory() throws -> URL { #if !os(Linux) - let temporaryDirectoryURL: URL - if #available(OSX 10.12, tvOS 10.0, watchOS 3.0, *) { - temporaryDirectoryURL = temporaryDirectory - } else { - temporaryDirectoryURL = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true) - } return try url(for: .itemReplacementDirectory, in: .userDomainMask, - appropriateFor: temporaryDirectoryURL, + appropriateFor: temporaryDirectory, create: true) #else let envs = ProcessInfo.processInfo.environment diff --git a/Sources/SwifterSwift/Foundation/LocaleExtensions.swift b/Sources/SwifterSwift/Foundation/LocaleExtensions.swift index e3a470527..68991d6ab 100644 --- a/Sources/SwifterSwift/Foundation/LocaleExtensions.swift +++ b/Sources/SwifterSwift/Foundation/LocaleExtensions.swift @@ -1,4 +1,4 @@ -// LocaleExtensions.swift - Copyright 2020 SwifterSwift +// LocaleExtensions.swift - Copyright 2023 SwifterSwift #if canImport(Foundation) import Foundation @@ -29,6 +29,7 @@ public extension Locale { /// - Parameter isoRegionCode: The IOS region code. /// /// Adapted from https://stackoverflow.com/a/30403199/1627511 + /// - Returns: A flag emoji string for the given region code (optional). static func flagEmoji(forRegionCode isoRegionCode: String) -> String? { #if !os(Linux) guard isoRegionCodes.contains(isoRegionCode) else { return nil } diff --git a/Sources/SwifterSwift/Foundation/MeasurementExtensions.swift b/Sources/SwifterSwift/Foundation/MeasurementExtensions.swift new file mode 100644 index 000000000..02b9579af --- /dev/null +++ b/Sources/SwifterSwift/Foundation/MeasurementExtensions.swift @@ -0,0 +1,52 @@ +// MeasurementExtensions.swift - Copyright 2023 SwifterSwift + +#if canImport(Foundation) +import Foundation + +// MARK: - Methods + +public extension Measurement where UnitType == UnitAngle { + /// SwifterSwift: Create a `Measurement` for an angle with a specified value in degrees. + /// - Parameter value: The quantity of the angle in degree. + /// - Returns: Measurement for an angle with unit degrees. + static func degrees(_ value: Double) -> Measurement { + return Measurement(value: value, unit: .degrees) + } + + /// SwifterSwift: Create a Measurement for an angle with a specified value in arc minutes. + /// - Parameter value: The quantity of the angle in arc minutes. + /// - Returns: Measurement for an angle with unit arc minutes. + static func arcMinutes(_ value: Double) -> Measurement { + return Measurement(value: value, unit: .arcMinutes) + } + + /// SwifterSwift: Create a Measurement for an angle with a specified value in arc seconds. + /// - Parameter value: The quantity of the angle in arc seconds. + /// - Returns: Measurement for an angle with unit arc seconds. + static func arcSeconds(_ value: Double) -> Measurement { + return Measurement(value: value, unit: .arcSeconds) + } + + /// SwifterSwift: Create a Measurement for an angle with a specified value in radians. + /// - Parameter value: The quantity of the angle in radians. + /// - Returns: Measurement for an angle with unit radians. + static func radians(_ value: Double) -> Measurement { + return Measurement(value: value, unit: .radians) + } + + /// SwifterSwift: Create a Measurement for an angle with a specified value in gradians. + /// - Parameter value: The quantity of the angle in gradians. + /// - Returns: Measurement for an angle with unit gradians. + static func gradians(_ value: Double) -> Measurement { + return Measurement(value: value, unit: .gradians) + } + + /// SwifterSwift: Create a Measurement for an angle with a specified value in revolutions. + /// - Parameter value: The quantity of the angle in revolutions. + /// - Returns: Measurement for an angle with unit revolutions. + static func revolutions(_ value: Double) -> Measurement { + return Measurement(value: value, unit: .revolutions) + } +} + +#endif diff --git a/Sources/SwifterSwift/Foundation/NSAttributedStringExtensions.swift b/Sources/SwifterSwift/Foundation/NSAttributedStringExtensions.swift index 6213024b7..093951d4b 100644 --- a/Sources/SwifterSwift/Foundation/NSAttributedStringExtensions.swift +++ b/Sources/SwifterSwift/Foundation/NSAttributedStringExtensions.swift @@ -1,4 +1,4 @@ -// NSAttributedStringExtensions.swift - Copyright 2020 SwifterSwift +// NSAttributedStringExtensions.swift - Copyright 2023 SwifterSwift #if canImport(Foundation) import Foundation @@ -20,16 +20,16 @@ public extension NSAttributedString { guard !string.isEmpty else { return self } let pointSize: CGFloat - if let font = attribute(.font, at: 0, effectiveRange: nil) as? Font { + if let font = attribute(.font, at: 0, effectiveRange: nil) as? SFFont { pointSize = font.pointSize } else { #if os(tvOS) || os(watchOS) - pointSize = Font.preferredFont(forTextStyle: .headline).pointSize + pointSize = SFFont.preferredFont(forTextStyle: .headline).pointSize #else - pointSize = Font.systemFontSize + pointSize = SFFont.systemFontSize #endif } - return applying(attributes: [.font: Font.boldSystemFont(ofSize: pointSize)]) + return applying(attributes: [.font: SFFont.boldSystemFont(ofSize: pointSize)]) } #endif @@ -66,7 +66,7 @@ public extension NSAttributedString { } #endif - /// SwifterSwift: Dictionary of the attributes applied across the whole string + /// SwifterSwift: Dictionary of the attributes applied across the whole string. var attributes: [Key: Any] { guard length > 0 else { return [:] } return attributes(at: 0, effectiveRange: nil) @@ -76,10 +76,10 @@ public extension NSAttributedString { // MARK: - Methods public extension NSAttributedString { - /// SwifterSwift: Applies given attributes to the new instance of NSAttributedString initialized with self object + /// SwifterSwift: Applies given attributes to the new instance of NSAttributedString initialized with self object. /// - /// - Parameter attributes: Dictionary of attributes - /// - Returns: NSAttributedString with applied attributes + /// - Parameter attributes: Dictionary of attributes. + /// - Returns: NSAttributedString with applied attributes. func applying(attributes: [Key: Any]) -> NSAttributedString { guard !string.isEmpty else { return self } @@ -93,18 +93,19 @@ public extension NSAttributedString { /// /// - Parameter color: text color. /// - Returns: a NSAttributedString colored with given color. - func colored(with color: Color) -> NSAttributedString { + func colored(with color: SFColor) -> NSAttributedString { return applying(attributes: [.foregroundColor: color]) } #endif - /// SwifterSwift: Apply attributes to substrings matching a regular expression + /// SwifterSwift: Apply attributes to substrings matching a regular expression. /// /// - Parameters: - /// - attributes: Dictionary of attributes - /// - pattern: a regular expression to target - /// - options: The regular expression options that are applied to the expression during matching. See NSRegularExpression.Options for possible values. - /// - Returns: An NSAttributedString with attributes applied to substrings matching the pattern + /// - attributes: Dictionary of attributes. + /// - pattern: a regular expression to target. + /// - options: The regular expression options that are applied to the expression during matching. See + /// NSRegularExpression.Options for possible values. + /// - Returns: An NSAttributedString with attributes applied to substrings matching the pattern. func applying(attributes: [Key: Any], toRangesMatching pattern: String, options: NSRegularExpression.Options = []) -> NSAttributedString { @@ -120,12 +121,12 @@ public extension NSAttributedString { return result } - /// SwifterSwift: Apply attributes to occurrences of a given string + /// SwifterSwift: Apply attributes to occurrences of a given string. /// /// - Parameters: - /// - attributes: Dictionary of attributes - /// - target: a subsequence string for the attributes to be applied to - /// - Returns: An NSAttributedString with attributes applied on the target string + /// - attributes: Dictionary of attributes. + /// - target: a subsequence string for the attributes to be applied to. + /// - Returns: An NSAttributedString with attributes applied on the target string. func applying(attributes: [Key: Any], toOccurrencesOf target: T) -> NSAttributedString { let pattern = "\\Q\(target)\\E" @@ -148,7 +149,8 @@ public extension NSAttributedString { lhs = string } - /// SwifterSwift: Add a NSAttributedString to another NSAttributedString and return a new NSAttributedString instance. + /// SwifterSwift: Add a NSAttributedString to another NSAttributedString and return a new NSAttributedString + /// instance. /// /// - Parameters: /// - lhs: NSAttributedString to add. @@ -169,7 +171,8 @@ public extension NSAttributedString { lhs += NSAttributedString(string: rhs) } - /// SwifterSwift: Add a NSAttributedString to another NSAttributedString and return a new NSAttributedString instance. + /// SwifterSwift: Add a NSAttributedString to another NSAttributedString and return a new NSAttributedString + /// instance. /// /// - Parameters: /// - lhs: NSAttributedString to add. @@ -180,4 +183,29 @@ public extension NSAttributedString { } } +public extension Array where Element: NSAttributedString { + /// SwifterSwift: Returns a new `NSAttributedString` by concatenating the elements of the sequence, adding the given + /// separator between each element. + /// + /// [Is there joinWithSeparator for attributed strings](https://stackoverflow.com/q/32830519/1627511) + /// + /// - Parameter separator: An `NSAttributedString` to add between the elements of the sequence. + /// - Returns: NSAttributedString with applied attributes. + func joined(separator: NSAttributedString) -> NSAttributedString { + guard let firstElement = first else { return NSMutableAttributedString(string: "") } + return dropFirst().reduce(into: NSMutableAttributedString(attributedString: firstElement)) { result, element in + result.append(separator) + result.append(element) + } + } + + func joined(separator: String) -> NSAttributedString { + guard let firstElement = first else { return NSMutableAttributedString(string: "") } + let attributedStringSeparator = NSAttributedString(string: separator) + return dropFirst().reduce(into: NSMutableAttributedString(attributedString: firstElement)) { result, element in + result.append(attributedStringSeparator) + result.append(element) + } + } +} #endif diff --git a/Sources/SwifterSwift/Foundation/NSPredicateExtensions.swift b/Sources/SwifterSwift/Foundation/NSPredicateExtensions.swift index c56bad76b..c76507e9c 100644 --- a/Sources/SwifterSwift/Foundation/NSPredicateExtensions.swift +++ b/Sources/SwifterSwift/Foundation/NSPredicateExtensions.swift @@ -1,4 +1,4 @@ -// NSPredicateExtensions.swift - Copyright 2020 SwifterSwift +// NSPredicateExtensions.swift - Copyright 2023 SwifterSwift #if canImport(Foundation) import Foundation @@ -17,16 +17,16 @@ public extension NSPredicate { public extension NSPredicate { /// SwifterSwift: Returns a new predicate formed by AND-ing the argument to the predicate. /// - /// - Parameter predicate: NSPredicate - /// - Returns: NSCompoundPredicate + /// - Parameter predicate: NSPredicate. + /// - Returns: NSCompoundPredicate. func and(_ predicate: NSPredicate) -> NSCompoundPredicate { return NSCompoundPredicate(andPredicateWithSubpredicates: [self, predicate]) } /// SwifterSwift: Returns a new predicate formed by OR-ing the argument to the predicate. /// - /// - Parameter predicate: NSPredicate - /// - Returns: NSCompoundPredicate + /// - Parameter predicate: NSPredicate. + /// - Returns: NSCompoundPredicate. func or(_ predicate: NSPredicate) -> NSCompoundPredicate { return NSCompoundPredicate(orPredicateWithSubpredicates: [self, predicate]) } diff --git a/Sources/SwifterSwift/Foundation/NSRegularExpressionExtensions.swift b/Sources/SwifterSwift/Foundation/NSRegularExpressionExtensions.swift index 045eafc67..7f94b9ffe 100644 --- a/Sources/SwifterSwift/Foundation/NSRegularExpressionExtensions.swift +++ b/Sources/SwifterSwift/Foundation/NSRegularExpressionExtensions.swift @@ -1,4 +1,4 @@ -// NSRegularExpressionExtensions.swift - Copyright 2020 SwifterSwift +// NSRegularExpressionExtensions.swift - Copyright 2023 SwifterSwift #if canImport(Foundation) import Foundation @@ -13,11 +13,14 @@ public extension NSRegularExpression { /// - block: The Block enumerates the matches of the regular expression in the string. /// The block takes three arguments and returns `Void`: /// - result: - /// An `NSTextCheckingResult` specifying the match. This result gives the overall matched range via its `range` property, and the range of each individual capture group via its `range(at:)` method. The range {NSNotFound, 0} is returned if one of the capture groups did not participate in this particular match. + /// An `NSTextCheckingResult` specifying the match. This result gives the overall matched range via its `range` + /// property, and the range of each individual capture group via its `range(at:)` method. The range {NSNotFound, 0} + /// is returned if one of the capture groups did not participate in this particular match. /// - flags: /// The current state of the matching progress. See `NSRegularExpression.MatchingFlags` for the possible values. /// - stop: - /// A reference to a Boolean value. The Block can set the value to true to stop further processing of the array. The stop argument is an out-only argument. You should only ever set this Boolean to true within the Block. + /// A reference to a Boolean value. The Block can set the value to true to stop further processing of the array. + /// The stop argument is an out-only argument. You should only ever set this Boolean to true within the Block. #if os(Linux) func enumerateMatches(in string: String, options: MatchingOptions = [], @@ -29,11 +32,11 @@ public extension NSRegularExpression { enumerateMatches(in: string, options: options, range: NSRange(range, in: string)) { result, flags, stop in - var shouldStop = false - block(result, flags, &shouldStop) - if shouldStop { - stop.pointee = true - } + var shouldStop = false + block(result, flags, &shouldStop) + if shouldStop { + stop.pointee = true + } } } #else @@ -45,11 +48,11 @@ public extension NSRegularExpression { enumerateMatches(in: string, options: options, range: NSRange(range, in: string)) { result, flags, stop in - var shouldStop = false - block(result, flags, &shouldStop) - if shouldStop { - stop.pointee = true - } + var shouldStop = false + block(result, flags, &shouldStop) + if shouldStop { + stop.pointee = true + } } } #endif @@ -60,7 +63,9 @@ public extension NSRegularExpression { /// - string: The string to search. /// - options: The matching options to use. See NSRegularExpression.MatchingOptions for possible values. /// - range: The range of the string to search. - /// - Returns: An array of `NSTextCheckingResult` objects. Each result gives the overall matched range via its `range` property, and the range of each individual capture group via its `range(at:)` method. The range {NSNotFound, 0} is returned if one of the capture groups did not participate in this particular match. + /// - Returns: An array of `NSTextCheckingResult` objects. Each result gives the overall matched range via its + /// `range` property, and the range of each individual capture group via its `range(at:)` method. The range + /// {NSNotFound, 0} is returned if one of the capture groups did not participate in this particular match. func matches(in string: String, options: MatchingOptions = [], range: Range) -> [NSTextCheckingResult] { @@ -90,7 +95,9 @@ public extension NSRegularExpression { /// - string: The string to search. /// - options: The matching options to use. See `NSRegularExpression.MatchingOptions` for possible values. /// - range: The range of the string to search. - /// - Returns: An `NSTextCheckingResult` object. This result gives the overall matched range via its `range` property, and the range of each individual capture group via its `range(at:)` method. The range {NSNotFound, 0} is returned if one of the capture groups did not participate in this particular match. + /// - Returns: An `NSTextCheckingResult` object. This result gives the overall matched range via its `range` + /// property, and the range of each individual capture group via its `range(at:)` method. The range {NSNotFound, 0} + /// is returned if one of the capture groups did not participate in this particular match. func firstMatch(in string: String, options: MatchingOptions = [], range: Range) -> NSTextCheckingResult? { @@ -99,7 +106,8 @@ public extension NSRegularExpression { range: NSRange(range, in: string)) } - /// SwifterSwift: Returns the range of the first match of the regular expression within the specified range of the string. + /// SwifterSwift: Returns the range of the first match of the regular expression within the specified range of the + /// string. /// /// - Parameters: /// - string: The string to search. diff --git a/Sources/SwifterSwift/Foundation/NotificationCenterExtensions.swift b/Sources/SwifterSwift/Foundation/NotificationCenterExtensions.swift index 5d5ef6bed..5a2df00f8 100644 --- a/Sources/SwifterSwift/Foundation/NotificationCenterExtensions.swift +++ b/Sources/SwifterSwift/Foundation/NotificationCenterExtensions.swift @@ -1,23 +1,29 @@ -// NotificationCenterExtensions.swift - Copyright 2020 SwifterSwift +// NotificationCenterExtensions.swift - Copyright 2023 SwifterSwift #if canImport(Foundation) import Foundation public extension NotificationCenter { - /// SwifterSwift: Adds a one-time entry to the notification center's dispatch table that includes a notification queue and a block to add to the queue, and an optional notification name and sender. + /// SwifterSwift: Adds a one-time entry to the notification center's dispatch table that includes a notification + /// queue and a block to add to the queue, and an optional notification name and sender. /// - Parameters: - /// - name: The name of the notification for which to register the observer; that is, only notifications with this name are used to add the block to the operation queue. + /// - name: The name of the notification for which to register the observer; that is, only notifications with this + /// name are used to add the block to the operation queue. /// - /// If you pass `nil`, the notification center doesn’t use a notification’s name to decide whether to add the block to the operation queue. - /// - obj: The object whose notifications the observer wants to receive; that is, only notifications sent by this sender are delivered to the observer. + /// If you pass `nil`, the notification center doesn’t use a notification’s name to decide whether to add the + /// block to the operation queue. + /// - obj: The object whose notifications the observer wants to receive; that is, only notifications sent by this + /// sender are delivered to the observer. /// - /// If you pass `nil`, the notification center doesn’t use a notification’s sender to decide whether to deliver it to the observer. + /// If you pass `nil`, the notification center doesn’t use a notification’s sender to decide whether to deliver + /// it to the observer. /// - queue: The operation queue to which block should be added. /// /// If you pass `nil`, the block is run synchronously on the posting thread. /// - block: The block to be executed when the notification is received. /// - /// The block is copied by the notification center and (the copy) held until the observer registration is removed. + /// The block is copied by the notification center and (the copy) held until the observer registration is + /// removed. /// /// The block takes one argument: /// - notification: The notification. @@ -25,9 +31,12 @@ public extension NotificationCenter { object obj: Any? = nil, queue: OperationQueue? = nil, using block: @escaping (_ notification: Notification) -> Void) { - var handler: NSObjectProtocol! - handler = addObserver(forName: name, object: obj, queue: queue) { [unowned self] in + var handler: (any NSObjectProtocol)! + let removeObserver = { [unowned self] in self.removeObserver(handler!) + } + handler = addObserver(forName: name, object: obj, queue: queue) { + removeObserver() block($0) } } diff --git a/Sources/SwifterSwift/Foundation/URLExtensions.swift b/Sources/SwifterSwift/Foundation/URLExtensions.swift index e4ce0651c..f2b53ddbd 100644 --- a/Sources/SwifterSwift/Foundation/URLExtensions.swift +++ b/Sources/SwifterSwift/Foundation/URLExtensions.swift @@ -1,4 +1,4 @@ -// URLExtensions.swift - Copyright 2020 SwifterSwift +// URLExtensions.swift - Copyright 2023 SwifterSwift #if canImport(Foundation) import Foundation @@ -11,32 +11,47 @@ import UIKit // MARK: - Properties public extension URL { - /// SwifterSwift: Dictionary of the URL's query parameters + /// SwifterSwift: Dictionary of the URL's query parameters that have values. + /// + /// Duplicated query keys are ignored, taking only the first instance. var queryParameters: [String: String]? { - guard let components = URLComponents(url: self, resolvingAgainstBaseURL: false), - let queryItems = components.queryItems else { return nil } - - var items: [String: String] = [:] - - for queryItem in queryItems { - items[queryItem.name] = queryItem.value + guard let queryItems = URLComponents(url: self, resolvingAgainstBaseURL: false)?.queryItems else { + return nil } - return items + return Dictionary(queryItems.lazy.compactMap { + guard let value = $0.value else { return nil } + return ($0.name, value) + }) { first, _ in first } + } + + /// SwifterSwift: Array of the URL's query parameters. + var allQueryParameters: [URLQueryItem]? { + URLComponents(url: self, resolvingAgainstBaseURL: false)?.queryItems } } // MARK: - Initializers public extension URL { - /// SwifterSwift: Initializes an `URL` object with a base URL and a relative string. If `string` was malformed, returns `nil`. + /// SwifterSwift: Initializes an `URL` object with a base URL and a relative string. If `string` was malformed, + /// returns `nil`. /// - Parameters: - /// - string: The URL string with which to initialize the `URL` object. Must conform to RFC 2396. `string` is interpreted relative to `url`. + /// - string: The URL string with which to initialize the `URL` object. Must conform to RFC 2396. `string` is + /// interpreted relative to `url`. /// - url: The base URL for the `URL` object. init?(string: String?, relativeTo url: URL? = nil) { guard let string = string else { return nil } self.init(string: string, relativeTo: url) } + + /** + SwifterSwift: Initializes a forced unwrapped `URL` from string. Can potentially crash if string is invalid. + - Parameter unsafeString: The URL string used to initialize the `URL`object. + */ + init(unsafeString: String) { + self.init(string: unsafeString)! + } } // MARK: - Methods @@ -57,6 +72,20 @@ public extension URL { return urlComponents.url! } + /// SwifterSwift: URL with appending query parameters. + /// + /// let url = URL(string: "https://google.com")! + /// let param = [URLQueryItem(name: "q", value: "Swifter Swift")] + /// url.appendingQueryParameters(params) -> "https://google.com?q=Swifter%20Swift" + /// + /// - Parameter parameters: parameters dictionary. + /// - Returns: URL with appending given query parameters. + func appendingQueryParameters(_ parameters: [URLQueryItem]) -> URL { + var urlComponents = URLComponents(url: self, resolvingAgainstBaseURL: false)! + urlComponents.queryItems = (urlComponents.queryItems ?? []) + parameters + return urlComponents.url! + } + /// SwifterSwift: Append query parameters to URL. /// /// var url = URL(string: "https://google.com")! @@ -66,7 +95,24 @@ public extension URL { /// /// - Parameter parameters: parameters dictionary. mutating func appendQueryParameters(_ parameters: [String: String]) { - self = appendingQueryParameters(parameters) + var urlComponents = URLComponents(url: self, resolvingAgainstBaseURL: false)! + urlComponents.queryItems = (urlComponents.queryItems ?? []) + parameters + .map { URLQueryItem(name: $0, value: $1) } + self = urlComponents.url! + } + + /// SwifterSwift: Append query parameters to URL. + /// + /// var url = URL(string: "https://google.com")! + /// let param = [URLQueryItem(name: "q", value: "Swifter Swift")] + /// url.appendQueryParameters(params) + /// print(url) // prints "https://google.com?q=Swifter%20Swift" + /// + /// - Parameter parameters: parameters dictionary. + mutating func appendQueryParameters(_ parameters: [URLQueryItem]) { + var urlComponents = URLComponents(url: self, resolvingAgainstBaseURL: false)! + urlComponents.queryItems = (urlComponents.queryItems ?? []) + parameters + self = urlComponents.url! } /// SwifterSwift: Get value of a query key. @@ -76,9 +122,9 @@ public extension URL { /// /// - Parameter key: The key of a query value. func queryValue(for key: String) -> String? { - return URLComponents(string: absoluteString)? + URLComponents(url: self, resolvingAgainstBaseURL: false)? .queryItems? - .first(where: { $0.name == key })? + .first { $0.name == key }? .value } @@ -89,6 +135,8 @@ public extension URL { /// /// - Returns: URL with all path components removed. func deletingAllPathComponents() -> URL { + guard !pathComponents.isEmpty else { return self } + var url: URL = self for _ in 0.. Self { + var request = self + request.httpMethod = methodString.uppercased() + return request + } + + /// SwifterSwift: Duplicates the request and set a header with key and value + /// + /// let request = URLRequest(url: url) + /// .header(name: "Content-Type", value: "application/json") + /// + /// - Parameters: + /// - name: The name of the header + /// - value: The value of the header + /// - Returns: The modified request + func header(name: String, value: String) -> Self { + var request = self + request.setValue(value, forHTTPHeaderField: name) + return request + } +} + #endif diff --git a/Sources/SwifterSwift/Foundation/URLSessionExtensions.swift b/Sources/SwifterSwift/Foundation/URLSessionExtensions.swift new file mode 100644 index 000000000..8bfd97b89 --- /dev/null +++ b/Sources/SwifterSwift/Foundation/URLSessionExtensions.swift @@ -0,0 +1,38 @@ +// URLSessionExtensions.swift - Copyright 2023 SwifterSwift + +#if canImport(Foundation) +import Foundation + +#if canImport(FoundationNetworking) +import FoundationNetworking +#endif + +// MARK: - Methods + +public extension URLSession { + /// SwifterSwift: synchronous version of `dataTask(with:completionHandler:)` + /// + /// - Parameter request: A URL request object that provides the URL, cache policy, request type, body data or body + /// stream, and so on. + /// - Returns: The data returned by the server; An object that provides response metadata, such as HTTP headers and + /// status code. + func dataSync(with request: URLRequest) throws -> (Data, URLResponse) { + var data: Data! + var response: URLResponse! + var error: (any Error)? + let semaphore = DispatchSemaphore(value: 0) + dataTask(with: request) { receivedData, receivedResponse, receivedError in + data = receivedData + response = receivedResponse + error = receivedError + semaphore.signal() + }.resume() + _ = semaphore.wait(timeout: .distantFuture) + if let error = error { + throw error + } + return (data, response) + } +} + +#endif diff --git a/Sources/SwifterSwift/Foundation/UserDefaultsExtensions.swift b/Sources/SwifterSwift/Foundation/UserDefaultsExtensions.swift index 243bf4e97..b4e02f4dd 100644 --- a/Sources/SwifterSwift/Foundation/UserDefaultsExtensions.swift +++ b/Sources/SwifterSwift/Foundation/UserDefaultsExtensions.swift @@ -1,4 +1,4 @@ -// UserDefaultsExtensions.swift - Copyright 2020 SwifterSwift +// UserDefaultsExtensions.swift - Copyright 2023 SwifterSwift #if canImport(Foundation) && !os(Linux) import Foundation @@ -6,7 +6,7 @@ import Foundation // MARK: - Methods public extension UserDefaults { - /// SwifterSwift: get object from UserDefaults by using subscript + /// SwifterSwift: get object from UserDefaults by using subscript. /// /// - Parameter key: key in the current user's defaults database. subscript(key: String) -> Any? { @@ -20,7 +20,7 @@ public extension UserDefaults { /// SwifterSwift: Float from UserDefaults. /// - /// - Parameter forKey: key to find float for. + /// - Parameter key: key to find float for. /// - Returns: Float object for key (if exists). func float(forKey key: String) -> Float? { return object(forKey: key) as? Float @@ -28,7 +28,7 @@ public extension UserDefaults { /// SwifterSwift: Date from UserDefaults. /// - /// - Parameter forKey: key to find date for. + /// - Parameter key: key to find date for. /// - Returns: Date object for key (if exists). func date(forKey key: String) -> Date? { return object(forKey: key) as? Date diff --git a/Sources/SwifterSwift/HealthKit/HKActivitySummaryExtensions.swift b/Sources/SwifterSwift/HealthKit/HKActivitySummaryExtensions.swift index d92aab980..8d27c1166 100644 --- a/Sources/SwifterSwift/HealthKit/HKActivitySummaryExtensions.swift +++ b/Sources/SwifterSwift/HealthKit/HKActivitySummaryExtensions.swift @@ -1,20 +1,35 @@ -// HKActivitySummaryExtensions.swift - Copyright 2020 SwifterSwift +// HKActivitySummaryExtensions.swift - Copyright 2023 SwifterSwift +// HeathKit is not available for tvOS, available only on iOS 8.0+ Mac Catalyst 13.0+ watchOS 2.0+ +// See: https://developer.apple.com/documentation/healthkit +#if !os(tvOS) #if canImport(HealthKit) import HealthKit // MARK: - Properties -@available(watchOS 2.2, *) +@available(macOS 13.0, *) public extension HKActivitySummary { - /// SwifterSwift: Check if stand goal is met - var isStandGoalMet: Bool { appleStandHoursGoal.compare(appleStandHours) != .orderedDescending } + /// SwifterSwift: Check if stand goal is met. + var isStandGoalMet: Bool { + if #available(iOS 16.0, watchOS 9.0, *) { + return standHoursGoal == nil || standHoursGoal!.compare(appleStandHours) != .orderedDescending + } else { + return appleStandHoursGoal.compare(appleStandHours) != .orderedDescending + } + } - /// SwifterSwift: Check if exercise time goal is met - var isExerciseTimeGoalMet: Bool { appleExerciseTimeGoal.compare(appleExerciseTime) != .orderedDescending } + /// SwifterSwift: Check if exercise time goal is met. + var isExerciseTimeGoalMet: Bool { + if #available(iOS 16.0, watchOS 9.0, *) { + return exerciseTimeGoal == nil || exerciseTimeGoal!.compare(appleExerciseTime) != .orderedDescending + } else { + return appleExerciseTimeGoal.compare(appleExerciseTime) != .orderedDescending + } + } - /// SwifterSwift: Check if active energy goal is met + /// SwifterSwift: Check if active energy goal is met. var isEnergyBurnedGoalMet: Bool { activeEnergyBurnedGoal.compare(activeEnergyBurned) != .orderedDescending } } - +#endif #endif diff --git a/Sources/SwifterSwift/MapKit/MKMapViewExtensions.swift b/Sources/SwifterSwift/MapKit/MKMapViewExtensions.swift index ae62d6d1d..fc30e7a60 100644 --- a/Sources/SwifterSwift/MapKit/MKMapViewExtensions.swift +++ b/Sources/SwifterSwift/MapKit/MKMapViewExtensions.swift @@ -1,12 +1,10 @@ -// MKMapViewExtensions.swift - Copyright 2020 SwifterSwift +// MKMapViewExtensions.swift - Copyright 2023 SwifterSwift -#if canImport(MapKit) +#if canImport(MapKit) && !os(watchOS) import MapKit -#if !os(watchOS) -@available(tvOS 9.2, *) public extension MKMapView { - /// SwifterSwift: Dequeue reusable MKAnnotationView using class type + /// SwifterSwift: Dequeue reusable MKAnnotationView using class type. /// /// - Parameters: /// - name: MKAnnotationView type. @@ -15,23 +13,21 @@ public extension MKMapView { return dequeueReusableAnnotationView(withIdentifier: String(describing: name)) as? T } - /// SwifterSwift: Register MKAnnotationView using class type + /// SwifterSwift: Register MKAnnotationView using class type. /// /// - Parameter name: MKAnnotationView type. - @available(iOS 11.0, tvOS 11.0, macOS 10.13, *) func register(annotationViewWithClass name: T.Type) { register(T.self, forAnnotationViewWithReuseIdentifier: String(describing: name)) } - /// SwifterSwift: Dequeue reusable MKAnnotationView using class type + /// SwifterSwift: Dequeue reusable MKAnnotationView using class type. /// /// - Parameters: /// - name: MKAnnotationView type. /// - annotation: annotation of the mapView. /// - Returns: optional MKAnnotationView object. - @available(iOS 11.0, tvOS 11.0, macOS 10.13, *) func dequeueReusableAnnotationView(withClass name: T.Type, - for annotation: MKAnnotation) -> T? { + for annotation: any MKAnnotation) -> T? { guard let annotationView = dequeueReusableAnnotationView( withIdentifier: String(describing: name), for: annotation) as? T else { @@ -45,10 +41,13 @@ public extension MKMapView { /// /// - Parameters: /// - coordinates: Gets the array of type CLLocationCoordinate2D. - /// - meter: If arrays have a single item, they take the value of meters (Double). The map zooms in at the given meters. - /// - edgePadding: The amount of additional space (measured in screen points) to make visible around the specified rectangle - /// - animated: The animation control takes the Boolean value. Enter the true value for zooming with the animation. - func zoom(to coordinates: [CLLocationCoordinate2D], meter: Double, edgePadding: EdgeInsets, animated: Bool) { + /// - meter: If arrays have a single item, they take the value of meters (Double). The map zooms in at the given + /// meters. + /// - edgePadding: The amount of additional space (measured in screen points) to make visible around the specified + /// rectangle + /// - animated: The animation control takes the Boolean value. Enter the true value for zooming with the + /// animation. + func zoom(to coordinates: [CLLocationCoordinate2D], meter: Double, edgePadding: SFEdgeInsets, animated: Bool) { guard !coordinates.isEmpty else { return } if coordinates.count == 1 { @@ -65,5 +64,3 @@ public extension MKMapView { } #endif - -#endif diff --git a/Sources/SwifterSwift/MapKit/MKMultiPointExtensions.swift b/Sources/SwifterSwift/MapKit/MKMultiPointExtensions.swift new file mode 100644 index 000000000..c95638d50 --- /dev/null +++ b/Sources/SwifterSwift/MapKit/MKMultiPointExtensions.swift @@ -0,0 +1,17 @@ +// MKMultiPointExtensions.swift - Copyright 2023 SwifterSwift + +#if canImport(MapKit) && !os(watchOS) +import MapKit + +// MARK: - Properties + +public extension MKMultiPoint { + /// SwifterSwift: Return an Array of coordinates representing the provided multi point. + var coordinates: [CLLocationCoordinate2D] { + var coords = [CLLocationCoordinate2D](repeating: kCLLocationCoordinate2DInvalid, count: pointCount) + getCoordinates(&coords, range: NSRange(location: 0, length: pointCount)) + return coords + } +} + +#endif diff --git a/Sources/SwifterSwift/MapKit/MKPolylineExtensions.swift b/Sources/SwifterSwift/MapKit/MKPolylineExtensions.swift index 6a7af2f39..59bd163bf 100644 --- a/Sources/SwifterSwift/MapKit/MKPolylineExtensions.swift +++ b/Sources/SwifterSwift/MapKit/MKPolylineExtensions.swift @@ -1,11 +1,10 @@ -// MKPolylineExtensions.swift - Copyright 2020 SwifterSwift +// MKPolylineExtensions.swift - Copyright 2023 SwifterSwift #if canImport(MapKit) && !os(watchOS) import MapKit // MARK: - Initializers -@available(tvOS 9.2, *) public extension MKPolyline { /// SwifterSwift: Create a new MKPolyline from a provided Array of coordinates. /// @@ -16,16 +15,4 @@ public extension MKPolyline { } } -// MARK: - Properties - -@available(tvOS 9.2, *) -public extension MKPolyline { - /// SwifterSwift: Return an Array of coordinates representing the provided polyline. - var coordinates: [CLLocationCoordinate2D] { - var coords = [CLLocationCoordinate2D](repeating: kCLLocationCoordinate2DInvalid, count: pointCount) - getCoordinates(&coords, range: NSRange(location: 0, length: pointCount)) - return coords - } -} - #endif diff --git a/Sources/SwifterSwift/SceneKit/SCNBoxExtensions.swift b/Sources/SwifterSwift/SceneKit/SCNBoxExtensions.swift index fb8ebe01a..067acc208 100644 --- a/Sources/SwifterSwift/SceneKit/SCNBoxExtensions.swift +++ b/Sources/SwifterSwift/SceneKit/SCNBoxExtensions.swift @@ -1,4 +1,4 @@ -// SCNBoxExtensions.swift - Copyright 2020 SwifterSwift +// SCNBoxExtensions.swift - Copyright 2023 SwifterSwift #if canImport(SceneKit) import SceneKit @@ -54,7 +54,8 @@ public extension SCNBox { materials = [material] } - /// SwifterSwift: Creates a box geometry with the specified width, height, length, chamfer radius, and material color. + /// SwifterSwift: Creates a box geometry with the specified width, height, length, chamfer radius, and material + /// color. /// /// - Parameters: /// - width: The width of the box along the x-axis of its local coordinate space. @@ -62,7 +63,7 @@ public extension SCNBox { /// - length: The length of the box along the z-axis of its local coordinate space. /// - chamferRadius: The radius of curvature for the edges and corners of the box. /// - color: The color of the geometry's material. - convenience init(width: CGFloat, height: CGFloat, length: CGFloat, chamferRadius: CGFloat = 0, color: Color) { + convenience init(width: CGFloat, height: CGFloat, length: CGFloat, chamferRadius: CGFloat = 0, color: SFColor) { self.init(width: width, height: height, length: length, chamferRadius: chamferRadius) materials = [SCNMaterial(color: color)] } @@ -73,7 +74,7 @@ public extension SCNBox { /// - sideLength: The width, height, and length of the box in its local coordinate space. /// - chamferRadius: The radius of curvature for the edges and corners of the box. /// - color: The color of the geometry's material. - convenience init(sideLength: CGFloat, chamferRadius: CGFloat = 0, color: Color) { + convenience init(sideLength: CGFloat, chamferRadius: CGFloat = 0, color: SFColor) { self.init(width: sideLength, height: sideLength, length: sideLength, chamferRadius: chamferRadius) materials = [SCNMaterial(color: color)] } diff --git a/Sources/SwifterSwift/SceneKit/SCNCapsuleExtensions.swift b/Sources/SwifterSwift/SceneKit/SCNCapsuleExtensions.swift index 98b7561c4..565c1fefb 100644 --- a/Sources/SwifterSwift/SceneKit/SCNCapsuleExtensions.swift +++ b/Sources/SwifterSwift/SceneKit/SCNCapsuleExtensions.swift @@ -1,4 +1,4 @@ -// SCNCapsuleExtensions.swift - Copyright 2020 SwifterSwift +// SCNCapsuleExtensions.swift - Copyright 2023 SwifterSwift #if canImport(SceneKit) import SceneKit @@ -43,7 +43,7 @@ public extension SCNCapsule { /// - capRadius: The radius both of the capsule’s cylindrical body and of its hemispherical ends. /// - height: The height of the capsule along the y-axis of its local coordinate space. /// - material: The material of the geometry. - convenience init(capRadius: CGFloat, height: CGFloat, color: Color) { + convenience init(capRadius: CGFloat, height: CGFloat, color: SFColor) { self.init(capRadius: capRadius, height: height) materials = [SCNMaterial(color: color)] } @@ -54,7 +54,7 @@ public extension SCNCapsule { /// - capDiameter: The diameter both of the capsule’s cylindrical body and of its hemispherical ends. /// - height: The height of the capsule along the y-axis of its local coordinate space. /// - material: The material of the geometry. - convenience init(capDiameter: CGFloat, height: CGFloat, color: Color) { + convenience init(capDiameter: CGFloat, height: CGFloat, color: SFColor) { self.init(capRadius: capDiameter / 2, height: height) materials = [SCNMaterial(color: color)] } diff --git a/Sources/SwifterSwift/SceneKit/SCNConeExtensions.swift b/Sources/SwifterSwift/SceneKit/SCNConeExtensions.swift index dc323a7ae..a65e2aea4 100644 --- a/Sources/SwifterSwift/SceneKit/SCNConeExtensions.swift +++ b/Sources/SwifterSwift/SceneKit/SCNConeExtensions.swift @@ -1,4 +1,4 @@ -// SCNConeExtensions.swift - Copyright 2020 SwifterSwift +// SCNConeExtensions.swift - Copyright 2023 SwifterSwift #if canImport(SceneKit) import SceneKit @@ -9,8 +9,10 @@ public extension SCNCone { /// SwifterSwift: Creates a cone geometry with the given top diameter, bottom diameter, and height. /// /// - Parameters: - /// - topDiameter: The diameter of the cone’s top, forming a circle in the x- and z-axis dimensions of its local coordinate space. - /// - bottomDiameter: The diameter of the cone’s base, forming a circle in the x- and z-axis dimensions of its local coordinate space. + /// - topDiameter: The diameter of the cone’s top, forming a circle in the x- and z-axis dimensions of its local + /// coordinate space. + /// - bottomDiameter: The diameter of the cone’s base, forming a circle in the x- and z-axis dimensions of its + /// local coordinate space. /// - height: The height of the cone along the y-axis of its local coordinate space. convenience init(topDiameter: CGFloat, bottomDiameter: CGFloat, height: CGFloat) { self.init(topRadius: topDiameter / 2, bottomRadius: bottomDiameter / 2, height: height) @@ -19,8 +21,10 @@ public extension SCNCone { /// SwifterSwift: Creates a cone geometry with the given top radius, bottom radius, height, and material. /// /// - Parameters: - /// - topRadius: The radius of the cone’s top, forming a circle in the x- and z-axis dimensions of its local coordinate space. - /// - bottomRadius: The radius of the cone’s base, forming a circle in the x- and z-axis dimensions of its local coordinate space. + /// - topRadius: The radius of the cone’s top, forming a circle in the x- and z-axis dimensions of its local + /// coordinate space. + /// - bottomRadius: The radius of the cone’s base, forming a circle in the x- and z-axis dimensions of its local + /// coordinate space. /// - height: The height of the cone along the y-axis of its local coordinate space. /// - material: The material of the geometry. convenience init(topRadius: CGFloat, bottomRadius: CGFloat, height: CGFloat, material: SCNMaterial) { @@ -31,8 +35,10 @@ public extension SCNCone { /// SwifterSwift: Creates a cone geometry with the given top diameter, bottom diameter, height, and material. /// /// - Parameters: - /// - topDiameter: The diameter of the cone’s top, forming a circle in the x- and z-axis dimensions of its local coordinate space. - /// - bottomDiameter: The diameter of the cone’s base, forming a circle in the x- and z-axis dimensions of its local coordinate space. + /// - topDiameter: The diameter of the cone’s top, forming a circle in the x- and z-axis dimensions of its local + /// coordinate space. + /// - bottomDiameter: The diameter of the cone’s base, forming a circle in the x- and z-axis dimensions of its + /// local coordinate space. /// - height: The height of the cone along the y-axis of its local coordinate space. /// - material: The material of the geometry. convenience init(topDiameter: CGFloat, bottomDiameter: CGFloat, height: CGFloat, material: SCNMaterial) { @@ -43,11 +49,13 @@ public extension SCNCone { /// SwifterSwift: Creates a cone geometry with the given top radius, bottom radius, height, and material. /// /// - Parameters: - /// - topRadius: The radius of the cone’s top, forming a circle in the x- and z-axis dimensions of its local coordinate space. - /// - bottomRadius: The radius of the cone’s base, forming a circle in the x- and z-axis dimensions of its local coordinate space. + /// - topRadius: The radius of the cone’s top, forming a circle in the x- and z-axis dimensions of its local + /// coordinate space. + /// - bottomRadius: The radius of the cone’s base, forming a circle in the x- and z-axis dimensions of its local + /// coordinate space. /// - height: The height of the cone along the y-axis of its local coordinate space. /// - color: The color of the geometry's material. - convenience init(topRadius: CGFloat, bottomRadius: CGFloat, height: CGFloat, color: Color) { + convenience init(topRadius: CGFloat, bottomRadius: CGFloat, height: CGFloat, color: SFColor) { self.init(topRadius: topRadius, bottomRadius: bottomRadius, height: height) materials = [SCNMaterial(color: color)] } @@ -55,11 +63,13 @@ public extension SCNCone { /// SwifterSwift: Creates a cone geometry with the given top diameter, bottom diameter, height, and material. /// /// - Parameters: - /// - topDiameter: The diameter of the cone’s top, forming a circle in the x- and z-axis dimensions of its local coordinate space. - /// - bottomDiameter: The diameter of the cone’s base, forming a circle in the x- and z-axis dimensions of its local coordinate space. + /// - topDiameter: The diameter of the cone’s top, forming a circle in the x- and z-axis dimensions of its local + /// coordinate space. + /// - bottomDiameter: The diameter of the cone’s base, forming a circle in the x- and z-axis dimensions of its + /// local coordinate space. /// - height: The height of the cone along the y-axis of its local coordinate space. /// - color: The color of the geometry's material. - convenience init(topDiameter: CGFloat, bottomDiameter: CGFloat, height: CGFloat, color: Color) { + convenience init(topDiameter: CGFloat, bottomDiameter: CGFloat, height: CGFloat, color: SFColor) { self.init(topRadius: topDiameter / 2, bottomRadius: bottomDiameter / 2, height: height) materials = [SCNMaterial(color: color)] } diff --git a/Sources/SwifterSwift/SceneKit/SCNCylinderExtensions.swift b/Sources/SwifterSwift/SceneKit/SCNCylinderExtensions.swift index b33c8313e..8a4befedf 100644 --- a/Sources/SwifterSwift/SceneKit/SCNCylinderExtensions.swift +++ b/Sources/SwifterSwift/SceneKit/SCNCylinderExtensions.swift @@ -1,4 +1,4 @@ -// SCNCylinderExtensions.swift - Copyright 2020 SwifterSwift +// SCNCylinderExtensions.swift - Copyright 2023 SwifterSwift #if canImport(SceneKit) import SceneKit @@ -9,9 +9,9 @@ public extension SCNCylinder { /// SwifterSwift: Creates a cylinder geometry with the specified diameter and height. /// /// - Parameters: - /// - radius: The radius of the cylinder’s circular cross section in the x- and z-axis dimensions of its local coordinate space. + /// - diameter: The diameter of the cylinder’s circular cross section in the x- and z-axis dimensions of its local + /// coordinate space. /// - height: The height of the cylinder along the y-axis of its local coordinate space. - /// - material: The material of the geometry. convenience init(diameter: CGFloat, height: CGFloat) { self.init(radius: diameter / 2, height: height) } @@ -19,7 +19,8 @@ public extension SCNCylinder { /// SwifterSwift: Creates a cylinder geometry with the specified radius, height and material. /// /// - Parameters: - /// - radius: The radius of the cylinder’s circular cross section in the x- and z-axis dimensions of its local coordinate space. + /// - radius: The radius of the cylinder’s circular cross section in the x- and z-axis dimensions of its local + /// coordinate space. /// - height: The height of the cylinder along the y-axis of its local coordinate space. /// - material: The material of the geometry. convenience init(radius: CGFloat, height: CGFloat, material: SCNMaterial) { @@ -30,7 +31,8 @@ public extension SCNCylinder { /// SwifterSwift: Creates a cylinder geometry with the specified diameter, height and material. /// /// - Parameters: - /// - radius: The radius of the cylinder’s circular cross section in the x- and z-axis dimensions of its local coordinate space. + /// - diameter: The diameter of the cylinder’s circular cross section in the x- and z-axis dimensions of its local + /// coordinate space. /// - height: The height of the cylinder along the y-axis of its local coordinate space. /// - material: The material of the geometry. convenience init(diameter: CGFloat, height: CGFloat, material: SCNMaterial) { @@ -41,10 +43,11 @@ public extension SCNCylinder { /// SwifterSwift: Creates a cylinder geometry with the specified radius, height, and material color. /// /// - Parameters: - /// - radius: The radius of the cylinder’s circular cross section in the x- and z-axis dimensions of its local coordinate space. + /// - radius: The radius of the cylinder’s circular cross section in the x- and z-axis dimensions of its local + /// coordinate space. /// - height: The height of the cylinder along the y-axis of its local coordinate space. /// - color: The color of the geometry's material. - convenience init(radius: CGFloat, height: CGFloat, color: Color) { + convenience init(radius: CGFloat, height: CGFloat, color: SFColor) { self.init(radius: radius, height: height) materials = [SCNMaterial(color: color)] } @@ -52,10 +55,11 @@ public extension SCNCylinder { /// SwifterSwift: Creates a cylinder geometry with the specified diameter, height, and material color. /// /// - Parameters: - /// - diameter: The diameter of the cylinder’s circular cross section in the x- and z-axis dimensions of its local coordinate space. + /// - diameter: The diameter of the cylinder’s circular cross section in the x- and z-axis dimensions of its local + /// coordinate space. /// - height: The height of the cylinder along the y-axis of its local coordinate space. /// - color: The color of the geometry's material. - convenience init(diameter: CGFloat, height: CGFloat, color: Color) { + convenience init(diameter: CGFloat, height: CGFloat, color: SFColor) { self.init(radius: diameter / 2, height: height) materials = [SCNMaterial(color: color)] } diff --git a/Sources/SwifterSwift/SceneKit/SCNGeometryExtensions.swift b/Sources/SwifterSwift/SceneKit/SCNGeometryExtensions.swift index e0b430d7f..cd61e2d99 100644 --- a/Sources/SwifterSwift/SceneKit/SCNGeometryExtensions.swift +++ b/Sources/SwifterSwift/SceneKit/SCNGeometryExtensions.swift @@ -1,4 +1,4 @@ -// SCNGeometryExtensions.swift - Copyright 2020 SwifterSwift +// SCNGeometryExtensions.swift - Copyright 2023 SwifterSwift #if canImport(SceneKit) import SceneKit diff --git a/Sources/SwifterSwift/SceneKit/SCNMaterialExtensions.swift b/Sources/SwifterSwift/SceneKit/SCNMaterialExtensions.swift index 69da4fa6f..0220c0266 100644 --- a/Sources/SwifterSwift/SceneKit/SCNMaterialExtensions.swift +++ b/Sources/SwifterSwift/SceneKit/SCNMaterialExtensions.swift @@ -1,4 +1,4 @@ -// SCNMaterialExtensions.swift - Copyright 2020 SwifterSwift +// SCNMaterialExtensions.swift - Copyright 2023 SwifterSwift #if canImport(SceneKit) import SceneKit @@ -6,10 +6,10 @@ import SceneKit // MARK: - Methods public extension SCNMaterial { - /// SwifterSwift: Initializes a SCNMaterial with a specific diffuse color + /// SwifterSwift: Initializes a SCNMaterial with a specific diffuse color. /// - /// - Parameter color: diffuse color - convenience init(color: Color) { + /// - Parameter color: diffuse color. + convenience init(color: SFColor) { self.init() diffuse.contents = color } diff --git a/Sources/SwifterSwift/SceneKit/SCNPlaneExtensions.swift b/Sources/SwifterSwift/SceneKit/SCNPlaneExtensions.swift index 14be1383b..ab95b5d62 100644 --- a/Sources/SwifterSwift/SceneKit/SCNPlaneExtensions.swift +++ b/Sources/SwifterSwift/SceneKit/SCNPlaneExtensions.swift @@ -1,4 +1,4 @@ -// SCNPlaneExtensions.swift - Copyright 2020 SwifterSwift +// SCNPlaneExtensions.swift - Copyright 2023 SwifterSwift #if canImport(SceneKit) import SceneKit @@ -40,7 +40,7 @@ public extension SCNPlane { /// - width: The width of the plane along the x-axis of its local coordinate space. /// - height: The height of the plane along the y-axis of its local coordinate space. /// - color: The color of the geometry's material. - convenience init(width: CGFloat, height: CGFloat, color: Color) { + convenience init(width: CGFloat, height: CGFloat, color: SFColor) { self.init(width: width, height: height) materials = [SCNMaterial(color: color)] } @@ -50,7 +50,7 @@ public extension SCNPlane { /// - Parameters: /// - width: The width and height of the plane along the x-axis and y-axis of its local coordinate space. /// - color: The color of the geometry's material. - convenience init(width: CGFloat, color: Color) { + convenience init(width: CGFloat, color: SFColor) { self.init(width: width, height: width) materials = [SCNMaterial(color: color)] } diff --git a/Sources/SwifterSwift/SceneKit/SCNShapeExtensions.swift b/Sources/SwifterSwift/SceneKit/SCNShapeExtensions.swift index 7fce39c63..77b3d83c5 100644 --- a/Sources/SwifterSwift/SceneKit/SCNShapeExtensions.swift +++ b/Sources/SwifterSwift/SceneKit/SCNShapeExtensions.swift @@ -1,4 +1,4 @@ -// SCNShapeExtensions.swift - Copyright 2020 SwifterSwift +// SCNShapeExtensions.swift - Copyright 2023 SwifterSwift #if canImport(SceneKit) import SceneKit @@ -29,7 +29,7 @@ public extension SCNShape { /// - path: The two-dimensional path forming the basis of the shape. /// - extrusionDepth: The thickness of the extruded shape along the z-axis. /// - color: The color of the geometry's material. - convenience init(path: UIBezierPath, extrusionDepth: CGFloat, color: Color) { + convenience init(path: UIBezierPath, extrusionDepth: CGFloat, color: SFColor) { self.init(path: path, extrusionDepth: extrusionDepth) materials = [SCNMaterial(color: color)] } diff --git a/Sources/SwifterSwift/SceneKit/SCNSphereExtensions.swift b/Sources/SwifterSwift/SceneKit/SCNSphereExtensions.swift index d1e61d83e..6b6dd471e 100644 --- a/Sources/SwifterSwift/SceneKit/SCNSphereExtensions.swift +++ b/Sources/SwifterSwift/SceneKit/SCNSphereExtensions.swift @@ -1,4 +1,4 @@ -// SCNSphereExtensions.swift - Copyright 2020 SwifterSwift +// SCNSphereExtensions.swift - Copyright 2023 SwifterSwift #if canImport(SceneKit) import SceneKit @@ -28,7 +28,7 @@ public extension SCNSphere { /// - Parameters: /// - radius: The radius of the sphere in its local coordinate space. /// - color: The color of the geometry's material. - convenience init(radius: CGFloat, color: Color) { + convenience init(radius: CGFloat, color: SFColor) { self.init(radius: radius, material: SCNMaterial(color: color)) } @@ -47,7 +47,7 @@ public extension SCNSphere { /// - Parameters: /// - diameter: The diameter of the sphere in its local coordinate space. /// - color: The color of the geometry's material. - convenience init(diameter: CGFloat, color: Color) { + convenience init(diameter: CGFloat, color: SFColor) { self.init(diameter: diameter, material: SCNMaterial(color: color)) } } diff --git a/Sources/SwifterSwift/SceneKit/SCNVector3Extensions.swift b/Sources/SwifterSwift/SceneKit/SCNVector3Extensions.swift index 9cede47c2..c0f8e3114 100644 --- a/Sources/SwifterSwift/SceneKit/SCNVector3Extensions.swift +++ b/Sources/SwifterSwift/SceneKit/SCNVector3Extensions.swift @@ -1,4 +1,4 @@ -// SCNVector3Extensions.swift - Copyright 2020 SwifterSwift +// SCNVector3Extensions.swift - Copyright 2023 SwifterSwift #if os(OSX) /// SwifterSwift: CGFloat. @@ -29,7 +29,7 @@ public extension SCNVector3 { var length: SceneKitFloat { return sqrt(pow(x, 2) + pow(y, 2) + pow(z, 2)) } - + /// SwifterSwift: Returns the unit or normalized vector where `length = 1`. /// /// SCNVector3(2, 3, 6).normalized -> SCNVector3(2/7, 3/7, 6/7) @@ -60,11 +60,12 @@ public extension SCNVector3 { /// SCNVector3(10, 10, 10) += SCNVector3(10, 20, -30) -> SCNVector3(20, 30, -20) /// /// - Parameters: - /// - lhs: self + /// - lhs: `self`. /// - rhs: SCNVector3 to add. static func += (lhs: inout SCNVector3, rhs: SCNVector3) { - // swiftlint:disable:next shorthand_operator - lhs = lhs + rhs + lhs.x += rhs.x + lhs.y += rhs.y + lhs.z += rhs.z } /// SwifterSwift: Subtract two SCNVector3s. @@ -84,11 +85,12 @@ public extension SCNVector3 { /// SCNVector3(10, 10, 10) -= SCNVector3(10, 20, -30) -> SCNVector3(0, -10, 40) /// /// - Parameters: - /// - lhs: self + /// - lhs: `self`. /// - rhs: SCNVector3 to subtract. static func -= (lhs: inout SCNVector3, rhs: SCNVector3) { - // swiftlint:disable:next shorthand_operator - lhs = lhs - rhs + lhs.x -= rhs.x + lhs.y -= rhs.y + lhs.z -= rhs.z } /// SwifterSwift: Multiply a SCNVector3 with a scalar @@ -108,15 +110,16 @@ public extension SCNVector3 { /// SCNVector3(10, 20, -30) *= 3 -> SCNVector3(30, 60, -90) /// /// - Parameters: - /// - vector: self. + /// - vector: `self`. /// - scalar: scalar value. /// - Returns: result of multiplication of the given CGPoint with the scalar. static func *= (vector: inout SCNVector3, scalar: SceneKitFloat) { - // swiftlint:disable:next shorthand_operator - vector = vector * scalar + vector.x *= scalar + vector.y *= scalar + vector.z *= scalar } - /// SwifterSwift: Multiply a scalar with a SCNVector3 + /// SwifterSwift: Multiply a scalar with a SCNVector3. /// /// 3 * SCNVector3(10, 20, -30) -> SCNVector3(30, 60, -90) /// @@ -127,8 +130,8 @@ public extension SCNVector3 { static func * (scalar: SceneKitFloat, vector: SCNVector3) -> SCNVector3 { return SCNVector3(vector.x * scalar, vector.y * scalar, vector.z * scalar) } - - /// SwifterSwift: Divide a SCNVector3 with a scalar + + /// SwifterSwift: Divide a SCNVector3 with a scalar. /// /// SCNVector3(10, 20, -30) / 3 -> SCNVector3(3/10, 0.15, -30) /// @@ -140,12 +143,12 @@ public extension SCNVector3 { return SCNVector3(vector.x / scalar, vector.y / scalar, vector.z / scalar) } - /// SwifterSwift: Divide self with a scalar + /// SwifterSwift: Divide self with a scalar. /// /// SCNVector3(10, 20, -30) /= 3 -> SCNVector3(3/10, 0.15, -30) /// /// - Parameters: - /// - vector: self. + /// - vector: `self`. /// - scalar: scalar value. /// - Returns: result of division of the given CGPoint with the scalar. static func /= (vector: inout SCNVector3, scalar: SceneKitFloat) { diff --git a/Sources/SwifterSwift/Shared/ColorExtensions.swift b/Sources/SwifterSwift/Shared/ColorExtensions.swift index d30c92c45..837787a00 100644 --- a/Sources/SwifterSwift/Shared/ColorExtensions.swift +++ b/Sources/SwifterSwift/Shared/ColorExtensions.swift @@ -1,17 +1,17 @@ -// ColorExtensions.swift - Copyright 2020 SwifterSwift +// ColorExtensions.swift - Copyright 2023 SwifterSwift #if !os(Linux) #if canImport(UIKit) import UIKit /// SwifterSwift: Color -public typealias Color = UIColor +public typealias SFColor = UIColor #endif #if canImport(AppKit) && !targetEnvironment(macCatalyst) import AppKit /// SwifterSwift: Color -public typealias Color = NSColor +public typealias SFColor = NSColor #endif #if !os(watchOS) @@ -20,13 +20,13 @@ import CoreImage // MARK: - Properties -public extension Color { +public extension SFColor { /// SwifterSwift: Random color. - static var random: Color { + static var random: SFColor { let red = Int.random(in: 0...255) let green = Int.random(in: 0...255) let blue = Int.random(in: 0...255) - return Color(red: red, green: green, blue: blue)! + return SFColor(red: red, green: green, blue: blue)! } // swiftlint:disable large_tuple @@ -51,7 +51,7 @@ public extension Color { // swiftlint:enable large_tuple // swiftlint:disable large_tuple - /// SwifterSwift: RGB components for a Color represented as CGFloat numbers (between 0 and 1) + /// SwifterSwift: RGB components for a Color represented as CGFloat numbers (between 0 and 1). /// /// UIColor.red.rgbComponents.red -> 1.0 /// NSColor.green.rgbComponents.green -> 1.0 @@ -123,7 +123,7 @@ public extension Color { } #if !os(watchOS) - /// SwifterSwift: CoreImage.CIColor (read-only) + /// SwifterSwift: CoreImage.CIColor (read-only). var coreImageColor: CoreImage.CIColor? { return CoreImage.CIColor(color: self) } @@ -146,14 +146,14 @@ public extension Color { } /// SwifterSwift: Get color complementary (read-only, if applicable). - var complementary: Color? { + var complementary: SFColor? { let colorSpaceRGB = CGColorSpaceCreateDeviceRGB() - let convertColorToRGBSpace: ((_ color: Color) -> Color?) = { _ -> Color? in + let convertColorToRGBSpace: ((_ color: SFColor) -> SFColor?) = { _ -> SFColor? in if self.cgColor.colorSpace!.model == CGColorSpaceModel.monochrome { let oldComponents = self.cgColor.components let components: [CGFloat] = [oldComponents![0], oldComponents![0], oldComponents![0], oldComponents![1]] let colorRef = CGColor(colorSpace: colorSpaceRGB, components: components) - let colorOut = Color(cgColor: colorRef!) + let colorOut = SFColor(cgColor: colorRef!) return colorOut } else { return self @@ -167,23 +167,23 @@ public extension Color { let green: CGFloat = sqrt(pow(255.0, 2.0) - pow(componentColors[1] * 255, 2.0)) / 255 let blue: CGFloat = sqrt(pow(255.0, 2.0) - pow(componentColors[2] * 255, 2.0)) / 255 - return Color(red: red, green: green, blue: blue, alpha: 1.0) + return SFColor(red: red, green: green, blue: blue, alpha: 1.0) } } // MARK: - Methods -public extension Color { - /// SwifterSwift: Blend two Colors +public extension SFColor { + /// SwifterSwift: Blend two Colors. /// /// - Parameters: /// - color1: first color to blend /// - intensity1: intensity of first color (default is 0.5) /// - color2: second color to blend /// - intensity2: intensity of second color (default is 0.5) - /// - Returns: Color created by blending first and seond colors. - static func blend(_ color1: Color, intensity1: CGFloat = 0.5, with color2: Color, - intensity2: CGFloat = 0.5) -> Color { + /// - Returns: Color created by blending first and second colors. + static func blend(_ color1: SFColor, intensity1: CGFloat = 0.5, with color2: SFColor, + intensity2: CGFloat = 0.5) -> SFColor { // http://stackoverflow.com/questions/27342715/blend-uicolors-in-swift let total = intensity1 + intensity2 @@ -222,47 +222,47 @@ public extension Color { let blue = level1 * blue1 + level2 * blue2 let alpha = level1 * alpha1 + level2 * alpha2 - return Color(red: red, green: green, blue: blue, alpha: alpha) + return SFColor(red: red, green: green, blue: blue, alpha: alpha) } - /// SwifterSwift: Lighten a color + /// SwifterSwift: Lighten a color. /// - /// let color = Color(red: r, green: g, blue: b, alpha: a) + /// let color = SFColor(red: r, green: g, blue: b, alpha: a) /// let lighterColor: Color = color.lighten(by: 0.2) /// - /// - Parameter percentage: Percentage by which to lighten the color - /// - Returns: A lightened color - func lighten(by percentage: CGFloat = 0.2) -> Color { + /// - Parameter percentage: Percentage by which to lighten the color. + /// - Returns: A lightened color. + func lighten(by percentage: CGFloat = 0.2) -> SFColor { // https://stackoverflow.com/questions/38435308/swift-get-lighter-and-darker-color-variations-for-a-given-uicolor var red: CGFloat = 0, green: CGFloat = 0, blue: CGFloat = 0, alpha: CGFloat = 0 getRed(&red, green: &green, blue: &blue, alpha: &alpha) - return Color(red: min(red + percentage, 1.0), - green: min(green + percentage, 1.0), - blue: min(blue + percentage, 1.0), - alpha: alpha) + return SFColor(red: min(red + percentage, 1.0), + green: min(green + percentage, 1.0), + blue: min(blue + percentage, 1.0), + alpha: alpha) } - /// SwifterSwift: Darken a color + /// SwifterSwift: Darken a color. /// - /// let color = Color(red: r, green: g, blue: b, alpha: a) + /// let color = SFColor(red: r, green: g, blue: b, alpha: a) /// let darkerColor: Color = color.darken(by: 0.2) /// - /// - Parameter percentage: Percentage by which to darken the color - /// - Returns: A darkened color - func darken(by percentage: CGFloat = 0.2) -> Color { + /// - Parameter percentage: Percentage by which to darken the color. + /// - Returns: A darkened color. + func darken(by percentage: CGFloat = 0.2) -> SFColor { // https://stackoverflow.com/questions/38435308/swift-get-lighter-and-darker-color-variations-for-a-given-uicolor var red: CGFloat = 0, green: CGFloat = 0, blue: CGFloat = 0, alpha: CGFloat = 0 getRed(&red, green: &green, blue: &blue, alpha: &alpha) - return Color(red: max(red - percentage, 0), - green: max(green - percentage, 0), - blue: max(blue - percentage, 0), - alpha: alpha) + return SFColor(red: max(red - percentage, 0), + green: max(green - percentage, 0), + blue: max(blue - percentage, 0), + alpha: alpha) } } // MARK: - Initializers -public extension Color { +public extension SFColor { /// SwifterSwift: Create Color from RGB values with optional transparency. /// /// - Parameters: @@ -305,8 +305,9 @@ public extension Color { /// - transparency: optional transparency value (default is 1). convenience init?(hexString: String, transparency: CGFloat = 1) { var string = "" - if hexString.lowercased().hasPrefix("0x") { - string = hexString.replacingOccurrences(of: "0x", with: "") + let lowercaseHexString = hexString.lowercased() + if lowercaseHexString.hasPrefix("0x") { + string = lowercaseHexString.replacingOccurrences(of: "0x", with: "") } else if hexString.hasPrefix("#") { string = hexString.replacingOccurrences(of: "#", with: "") } else { @@ -331,17 +332,44 @@ public extension Color { self.init(red: red, green: green, blue: blue, transparency: trans) } + /// SwifterSwift: Create Color from hexadecimal string in the format ARGB (alpha-red-green-blue). + /// + /// - Parameters: + /// - argbHexString: hexadecimal string (examples: 7FEDE7F6, 0x7FEDE7F6, #7FEDE7F6, #f0ff, 0xFF0F, ..). + convenience init?(argbHexString: String) { + var string = argbHexString.replacingOccurrences(of: "0x", with: "").replacingOccurrences(of: "#", with: "") + + if string.count <= 4 { // convert hex to long format if in short format + var str = "" + for character in string { + str.append(String(repeating: String(character), count: 2)) + } + string = str + } + + guard let hexValue = Int(string, radix: 16) else { return nil } + + let hasAlpha = string.count == 8 + + let alpha = hasAlpha ? (hexValue >> 24) & 0xFF : 0xFF + let red = (hexValue >> 16) & 0xFF + let green = (hexValue >> 8) & 0xFF + let blue = hexValue & 0xFF + + self.init(red: red, green: green, blue: blue, transparency: CGFloat(alpha) / 255) + } + /// SwifterSwift: Create Color from a complementary of a Color (if applicable). /// /// - Parameter color: color of which opposite color is desired. - convenience init?(complementaryFor color: Color) { + convenience init?(complementaryFor color: SFColor) { let colorSpaceRGB = CGColorSpaceCreateDeviceRGB() - let convertColorToRGBSpace: ((_ color: Color) -> Color?) = { color -> Color? in + let convertColorToRGBSpace: ((_ color: SFColor) -> SFColor?) = { color -> SFColor? in if color.cgColor.colorSpace!.model == CGColorSpaceModel.monochrome { let oldComponents = color.cgColor.components let components: [CGFloat] = [oldComponents![0], oldComponents![0], oldComponents![0], oldComponents![1]] let colorRef = CGColor(colorSpace: colorSpaceRGB, components: components) - let colorOut = Color(cgColor: colorRef!) + let colorOut = SFColor(cgColor: colorRef!) return colorOut } else { return color @@ -360,97 +388,97 @@ public extension Color { // MARK: - Social -public extension Color { +public extension SFColor { /// SwifterSwift: Brand identity color of popular social media platform. enum Social { // https://www.lockedowndesign.com/social-media-colors/ /// SwifterSwift: red: 59, green: 89, blue: 152 - public static let facebook = Color(red: 59, green: 89, blue: 152)! + public static let facebook = SFColor(red: 59, green: 89, blue: 152)! /// SwifterSwift: red: 0, green: 182, blue: 241 - public static let twitter = Color(red: 0, green: 182, blue: 241)! + public static let twitter = SFColor(red: 0, green: 182, blue: 241)! /// SwifterSwift: red: 223, green: 74, blue: 50 - public static let googlePlus = Color(red: 223, green: 74, blue: 50)! + public static let googlePlus = SFColor(red: 223, green: 74, blue: 50)! /// SwifterSwift: red: 0, green: 123, blue: 182 - public static let linkedIn = Color(red: 0, green: 123, blue: 182)! + public static let linkedIn = SFColor(red: 0, green: 123, blue: 182)! /// SwifterSwift: red: 69, green: 187, blue: 255 - public static let vimeo = Color(red: 69, green: 187, blue: 255)! + public static let vimeo = SFColor(red: 69, green: 187, blue: 255)! /// SwifterSwift: red: 179, green: 18, blue: 23 - public static let youtube = Color(red: 179, green: 18, blue: 23)! + public static let youtube = SFColor(red: 179, green: 18, blue: 23)! /// SwifterSwift: red: 195, green: 42, blue: 163 - public static let instagram = Color(red: 195, green: 42, blue: 163)! + public static let instagram = SFColor(red: 195, green: 42, blue: 163)! /// SwifterSwift: red: 203, green: 32, blue: 39 - public static let pinterest = Color(red: 203, green: 32, blue: 39)! + public static let pinterest = SFColor(red: 203, green: 32, blue: 39)! /// SwifterSwift: red: 244, green: 0, blue: 131 - public static let flickr = Color(red: 244, green: 0, blue: 131)! + public static let flickr = SFColor(red: 244, green: 0, blue: 131)! /// SwifterSwift: red: 67, green: 2, blue: 151 - public static let yahoo = Color(red: 67, green: 2, blue: 151)! + public static let yahoo = SFColor(red: 67, green: 2, blue: 151)! /// SwifterSwift: red: 67, green: 2, blue: 151 - public static let soundCloud = Color(red: 67, green: 2, blue: 151)! + public static let soundCloud = SFColor(red: 67, green: 2, blue: 151)! /// SwifterSwift: red: 44, green: 71, blue: 98 - public static let tumblr = Color(red: 44, green: 71, blue: 98)! + public static let tumblr = SFColor(red: 44, green: 71, blue: 98)! /// SwifterSwift: red: 252, green: 69, blue: 117 - public static let foursquare = Color(red: 252, green: 69, blue: 117)! + public static let foursquare = SFColor(red: 252, green: 69, blue: 117)! /// SwifterSwift: red: 255, green: 176, blue: 0 - public static let swarm = Color(red: 255, green: 176, blue: 0)! + public static let swarm = SFColor(red: 255, green: 176, blue: 0)! /// SwifterSwift: red: 234, green: 76, blue: 137 - public static let dribbble = Color(red: 234, green: 76, blue: 137)! + public static let dribbble = SFColor(red: 234, green: 76, blue: 137)! /// SwifterSwift: red: 255, green: 87, blue: 0 - public static let reddit = Color(red: 255, green: 87, blue: 0)! + public static let reddit = SFColor(red: 255, green: 87, blue: 0)! /// SwifterSwift: red: 74, green: 93, blue: 78 - public static let devianArt = Color(red: 74, green: 93, blue: 78)! + public static let devianArt = SFColor(red: 74, green: 93, blue: 78)! /// SwifterSwift: red: 238, green: 64, blue: 86 - public static let pocket = Color(red: 238, green: 64, blue: 86)! + public static let pocket = SFColor(red: 238, green: 64, blue: 86)! /// SwifterSwift: red: 170, green: 34, blue: 182 - public static let quora = Color(red: 170, green: 34, blue: 182)! + public static let quora = SFColor(red: 170, green: 34, blue: 182)! /// SwifterSwift: red: 247, green: 146, blue: 30 - public static let slideShare = Color(red: 247, green: 146, blue: 30)! + public static let slideShare = SFColor(red: 247, green: 146, blue: 30)! /// SwifterSwift: red: 0, green: 153, blue: 229 - public static let px500 = Color(red: 0, green: 153, blue: 229)! + public static let px500 = SFColor(red: 0, green: 153, blue: 229)! /// SwifterSwift: red: 223, green: 109, blue: 70 - public static let listly = Color(red: 223, green: 109, blue: 70)! + public static let listly = SFColor(red: 223, green: 109, blue: 70)! /// SwifterSwift: red: 0, green: 180, blue: 137 - public static let vine = Color(red: 0, green: 180, blue: 137)! + public static let vine = SFColor(red: 0, green: 180, blue: 137)! /// SwifterSwift: red: 0, green: 175, blue: 240 - public static let skype = Color(red: 0, green: 175, blue: 240)! + public static let skype = SFColor(red: 0, green: 175, blue: 240)! /// SwifterSwift: red: 235, green: 73, blue: 36 - public static let stumbleUpon = Color(red: 235, green: 73, blue: 36)! + public static let stumbleUpon = SFColor(red: 235, green: 73, blue: 36)! /// SwifterSwift: red: 255, green: 252, blue: 0 - public static let snapchat = Color(red: 255, green: 252, blue: 0)! + public static let snapchat = SFColor(red: 255, green: 252, blue: 0)! /// SwifterSwift: red: 37, green: 211, blue: 102 - public static let whatsApp = Color(red: 37, green: 211, blue: 102)! + public static let whatsApp = SFColor(red: 37, green: 211, blue: 102)! } } // MARK: - Material colors -public extension Color { +public extension SFColor { // swiftlint:disable type_body_length /// SwifterSwift: Google Material design colors palette. enum Material { @@ -460,1348 +488,1348 @@ public extension Color { public static let red = red500 /// SwifterSwift: hex #FFEBEE - public static let red50 = Color(hex: 0xFFEBEE)! + public static let red50 = SFColor(hex: 0xFFEBEE)! /// SwifterSwift: hex #FFCDD2 - public static let red100 = Color(hex: 0xFFCDD2)! + public static let red100 = SFColor(hex: 0xFFCDD2)! /// SwifterSwift: hex #EF9A9A - public static let red200 = Color(hex: 0xEF9A9A)! + public static let red200 = SFColor(hex: 0xEF9A9A)! /// SwifterSwift: hex #E57373 - public static let red300 = Color(hex: 0xE57373)! + public static let red300 = SFColor(hex: 0xE57373)! /// SwifterSwift: hex #EF5350 - public static let red400 = Color(hex: 0xEF5350)! + public static let red400 = SFColor(hex: 0xEF5350)! /// SwifterSwift: hex #F44336 - public static let red500 = Color(hex: 0xF44336)! + public static let red500 = SFColor(hex: 0xF44336)! /// SwifterSwift: hex #E53935 - public static let red600 = Color(hex: 0xE53935)! + public static let red600 = SFColor(hex: 0xE53935)! /// SwifterSwift: hex #D32F2F - public static let red700 = Color(hex: 0xD32F2F)! + public static let red700 = SFColor(hex: 0xD32F2F)! /// SwifterSwift: hex #C62828 - public static let red800 = Color(hex: 0xC62828)! + public static let red800 = SFColor(hex: 0xC62828)! /// SwifterSwift: hex #B71C1C - public static let red900 = Color(hex: 0xB71C1C)! + public static let red900 = SFColor(hex: 0xB71C1C)! /// SwifterSwift: hex #FF8A80 - public static let redA100 = Color(hex: 0xFF8A80)! + public static let redA100 = SFColor(hex: 0xFF8A80)! /// SwifterSwift: hex #FF5252 - public static let redA200 = Color(hex: 0xFF5252)! + public static let redA200 = SFColor(hex: 0xFF5252)! /// SwifterSwift: hex #FF1744 - public static let redA400 = Color(hex: 0xFF1744)! + public static let redA400 = SFColor(hex: 0xFF1744)! /// SwifterSwift: hex #D50000 - public static let redA700 = Color(hex: 0xD50000)! + public static let redA700 = SFColor(hex: 0xD50000)! /// SwifterSwift: color pink500 public static let pink = pink500 /// SwifterSwift: hex #FCE4EC - public static let pink50 = Color(hex: 0xFCE4EC)! + public static let pink50 = SFColor(hex: 0xFCE4EC)! /// SwifterSwift: hex #F8BBD0 - public static let pink100 = Color(hex: 0xF8BBD0)! + public static let pink100 = SFColor(hex: 0xF8BBD0)! /// SwifterSwift: hex #F48FB1 - public static let pink200 = Color(hex: 0xF48FB1)! + public static let pink200 = SFColor(hex: 0xF48FB1)! /// SwifterSwift: hex #F06292 - public static let pink300 = Color(hex: 0xF06292)! + public static let pink300 = SFColor(hex: 0xF06292)! /// SwifterSwift: hex #EC407A - public static let pink400 = Color(hex: 0xEC407A)! + public static let pink400 = SFColor(hex: 0xEC407A)! /// SwifterSwift: hex #E91E63 - public static let pink500 = Color(hex: 0xE91E63)! + public static let pink500 = SFColor(hex: 0xE91E63)! /// SwifterSwift: hex #D81B60 - public static let pink600 = Color(hex: 0xD81B60)! + public static let pink600 = SFColor(hex: 0xD81B60)! /// SwifterSwift: hex #C2185B - public static let pink700 = Color(hex: 0xC2185B)! + public static let pink700 = SFColor(hex: 0xC2185B)! /// SwifterSwift: hex #AD1457 - public static let pink800 = Color(hex: 0xAD1457)! + public static let pink800 = SFColor(hex: 0xAD1457)! /// SwifterSwift: hex #880E4F - public static let pink900 = Color(hex: 0x880E4F)! + public static let pink900 = SFColor(hex: 0x880E4F)! /// SwifterSwift: hex #FF80AB - public static let pinkA100 = Color(hex: 0xFF80AB)! + public static let pinkA100 = SFColor(hex: 0xFF80AB)! /// SwifterSwift: hex #FF4081 - public static let pinkA200 = Color(hex: 0xFF4081)! + public static let pinkA200 = SFColor(hex: 0xFF4081)! /// SwifterSwift: hex #F50057 - public static let pinkA400 = Color(hex: 0xF50057)! + public static let pinkA400 = SFColor(hex: 0xF50057)! /// SwifterSwift: hex #C51162 - public static let pinkA700 = Color(hex: 0xC51162)! + public static let pinkA700 = SFColor(hex: 0xC51162)! /// SwifterSwift: color purple500 public static let purple = purple500 /// SwifterSwift: hex #F3E5F5 - public static let purple50 = Color(hex: 0xF3E5F5)! + public static let purple50 = SFColor(hex: 0xF3E5F5)! /// SwifterSwift: hex #E1BEE7 - public static let purple100 = Color(hex: 0xE1BEE7)! + public static let purple100 = SFColor(hex: 0xE1BEE7)! /// SwifterSwift: hex #CE93D8 - public static let purple200 = Color(hex: 0xCE93D8)! + public static let purple200 = SFColor(hex: 0xCE93D8)! /// SwifterSwift: hex #BA68C8 - public static let purple300 = Color(hex: 0xBA68C8)! + public static let purple300 = SFColor(hex: 0xBA68C8)! /// SwifterSwift: hex #AB47BC - public static let purple400 = Color(hex: 0xAB47BC)! + public static let purple400 = SFColor(hex: 0xAB47BC)! /// SwifterSwift: hex #9C27B0 - public static let purple500 = Color(hex: 0x9C27B0)! + public static let purple500 = SFColor(hex: 0x9C27B0)! /// SwifterSwift: hex #8E24AA - public static let purple600 = Color(hex: 0x8E24AA)! + public static let purple600 = SFColor(hex: 0x8E24AA)! /// SwifterSwift: hex #7B1FA2 - public static let purple700 = Color(hex: 0x7B1FA2)! + public static let purple700 = SFColor(hex: 0x7B1FA2)! /// SwifterSwift: hex #6A1B9A - public static let purple800 = Color(hex: 0x6A1B9A)! + public static let purple800 = SFColor(hex: 0x6A1B9A)! /// SwifterSwift: hex #4A148C - public static let purple900 = Color(hex: 0x4A148C)! + public static let purple900 = SFColor(hex: 0x4A148C)! /// SwifterSwift: hex #EA80FC - public static let purpleA100 = Color(hex: 0xEA80FC)! + public static let purpleA100 = SFColor(hex: 0xEA80FC)! /// SwifterSwift: hex #E040FB - public static let purpleA200 = Color(hex: 0xE040FB)! + public static let purpleA200 = SFColor(hex: 0xE040FB)! /// SwifterSwift: hex #D500F9 - public static let purpleA400 = Color(hex: 0xD500F9)! + public static let purpleA400 = SFColor(hex: 0xD500F9)! /// SwifterSwift: hex #AA00FF - public static let purpleA700 = Color(hex: 0xAA00FF)! + public static let purpleA700 = SFColor(hex: 0xAA00FF)! /// SwifterSwift: color deepPurple500 public static let deepPurple = deepPurple500 /// SwifterSwift: hex #EDE7F6 - public static let deepPurple50 = Color(hex: 0xEDE7F6)! + public static let deepPurple50 = SFColor(hex: 0xEDE7F6)! /// SwifterSwift: hex #D1C4E9 - public static let deepPurple100 = Color(hex: 0xD1C4E9)! + public static let deepPurple100 = SFColor(hex: 0xD1C4E9)! /// SwifterSwift: hex #B39DDB - public static let deepPurple200 = Color(hex: 0xB39DDB)! + public static let deepPurple200 = SFColor(hex: 0xB39DDB)! /// SwifterSwift: hex #9575CD - public static let deepPurple300 = Color(hex: 0x9575CD)! + public static let deepPurple300 = SFColor(hex: 0x9575CD)! /// SwifterSwift: hex #7E57C2 - public static let deepPurple400 = Color(hex: 0x7E57C2)! + public static let deepPurple400 = SFColor(hex: 0x7E57C2)! /// SwifterSwift: hex #673AB7 - public static let deepPurple500 = Color(hex: 0x673AB7)! + public static let deepPurple500 = SFColor(hex: 0x673AB7)! /// SwifterSwift: hex #5E35B1 - public static let deepPurple600 = Color(hex: 0x5E35B1)! + public static let deepPurple600 = SFColor(hex: 0x5E35B1)! /// SwifterSwift: hex #512DA8 - public static let deepPurple700 = Color(hex: 0x512DA8)! + public static let deepPurple700 = SFColor(hex: 0x512DA8)! /// SwifterSwift: hex #4527A0 - public static let deepPurple800 = Color(hex: 0x4527A0)! + public static let deepPurple800 = SFColor(hex: 0x4527A0)! /// SwifterSwift: hex #311B92 - public static let deepPurple900 = Color(hex: 0x311B92)! + public static let deepPurple900 = SFColor(hex: 0x311B92)! /// SwifterSwift: hex #B388FF - public static let deepPurpleA100 = Color(hex: 0xB388FF)! + public static let deepPurpleA100 = SFColor(hex: 0xB388FF)! /// SwifterSwift: hex #7C4DFF - public static let deepPurpleA200 = Color(hex: 0x7C4DFF)! + public static let deepPurpleA200 = SFColor(hex: 0x7C4DFF)! /// SwifterSwift: hex #651FFF - public static let deepPurpleA400 = Color(hex: 0x651FFF)! + public static let deepPurpleA400 = SFColor(hex: 0x651FFF)! /// SwifterSwift: hex #6200EA - public static let deepPurpleA700 = Color(hex: 0x6200EA)! + public static let deepPurpleA700 = SFColor(hex: 0x6200EA)! /// SwifterSwift: color indigo500 public static let indigo = indigo500 /// SwifterSwift: hex #E8EAF6 - public static let indigo50 = Color(hex: 0xE8EAF6)! + public static let indigo50 = SFColor(hex: 0xE8EAF6)! /// SwifterSwift: hex #C5CAE9 - public static let indigo100 = Color(hex: 0xC5CAE9)! + public static let indigo100 = SFColor(hex: 0xC5CAE9)! /// SwifterSwift: hex #9FA8DA - public static let indigo200 = Color(hex: 0x9FA8DA)! + public static let indigo200 = SFColor(hex: 0x9FA8DA)! /// SwifterSwift: hex #7986CB - public static let indigo300 = Color(hex: 0x7986CB)! + public static let indigo300 = SFColor(hex: 0x7986CB)! /// SwifterSwift: hex #5C6BC0 - public static let indigo400 = Color(hex: 0x5C6BC0)! + public static let indigo400 = SFColor(hex: 0x5C6BC0)! /// SwifterSwift: hex #3F51B5 - public static let indigo500 = Color(hex: 0x3F51B5)! + public static let indigo500 = SFColor(hex: 0x3F51B5)! /// SwifterSwift: hex #3949AB - public static let indigo600 = Color(hex: 0x3949AB)! + public static let indigo600 = SFColor(hex: 0x3949AB)! /// SwifterSwift: hex #303F9F - public static let indigo700 = Color(hex: 0x303F9F)! + public static let indigo700 = SFColor(hex: 0x303F9F)! /// SwifterSwift: hex #283593 - public static let indigo800 = Color(hex: 0x283593)! + public static let indigo800 = SFColor(hex: 0x283593)! /// SwifterSwift: hex #1A237E - public static let indigo900 = Color(hex: 0x1A237E)! + public static let indigo900 = SFColor(hex: 0x1A237E)! /// SwifterSwift: hex #8C9EFF - public static let indigoA100 = Color(hex: 0x8C9EFF)! + public static let indigoA100 = SFColor(hex: 0x8C9EFF)! /// SwifterSwift: hex #536DFE - public static let indigoA200 = Color(hex: 0x536DFE)! + public static let indigoA200 = SFColor(hex: 0x536DFE)! /// SwifterSwift: hex #3D5AFE - public static let indigoA400 = Color(hex: 0x3D5AFE)! + public static let indigoA400 = SFColor(hex: 0x3D5AFE)! /// SwifterSwift: hex #304FFE - public static let indigoA700 = Color(hex: 0x304FFE)! + public static let indigoA700 = SFColor(hex: 0x304FFE)! /// SwifterSwift: color blue500 public static let blue = blue500 /// SwifterSwift: hex #E3F2FD - public static let blue50 = Color(hex: 0xE3F2FD)! + public static let blue50 = SFColor(hex: 0xE3F2FD)! /// SwifterSwift: hex #BBDEFB - public static let blue100 = Color(hex: 0xBBDEFB)! + public static let blue100 = SFColor(hex: 0xBBDEFB)! /// SwifterSwift: hex #90CAF9 - public static let blue200 = Color(hex: 0x90CAF9)! + public static let blue200 = SFColor(hex: 0x90CAF9)! /// SwifterSwift: hex #64B5F6 - public static let blue300 = Color(hex: 0x64B5F6)! + public static let blue300 = SFColor(hex: 0x64B5F6)! /// SwifterSwift: hex #42A5F5 - public static let blue400 = Color(hex: 0x42A5F5)! + public static let blue400 = SFColor(hex: 0x42A5F5)! /// SwifterSwift: hex #2196F3 - public static let blue500 = Color(hex: 0x2196F3)! + public static let blue500 = SFColor(hex: 0x2196F3)! /// SwifterSwift: hex #1E88E5 - public static let blue600 = Color(hex: 0x1E88E5)! + public static let blue600 = SFColor(hex: 0x1E88E5)! /// SwifterSwift: hex #1976D2 - public static let blue700 = Color(hex: 0x1976D2)! + public static let blue700 = SFColor(hex: 0x1976D2)! /// SwifterSwift: hex #1565C0 - public static let blue800 = Color(hex: 0x1565C0)! + public static let blue800 = SFColor(hex: 0x1565C0)! /// SwifterSwift: hex #0D47A1 - public static let blue900 = Color(hex: 0x0D47A1)! + public static let blue900 = SFColor(hex: 0x0D47A1)! /// SwifterSwift: hex #82B1FF - public static let blueA100 = Color(hex: 0x82B1FF)! + public static let blueA100 = SFColor(hex: 0x82B1FF)! /// SwifterSwift: hex #448AFF - public static let blueA200 = Color(hex: 0x448AFF)! + public static let blueA200 = SFColor(hex: 0x448AFF)! /// SwifterSwift: hex #2979FF - public static let blueA400 = Color(hex: 0x2979FF)! + public static let blueA400 = SFColor(hex: 0x2979FF)! /// SwifterSwift: hex #2962FF - public static let blueA700 = Color(hex: 0x2962FF)! + public static let blueA700 = SFColor(hex: 0x2962FF)! /// SwifterSwift: color lightBlue500 public static let lightBlue = lightBlue500 /// SwifterSwift: hex #E1F5FE - public static let lightBlue50 = Color(hex: 0xE1F5FE)! + public static let lightBlue50 = SFColor(hex: 0xE1F5FE)! /// SwifterSwift: hex #B3E5FC - public static let lightBlue100 = Color(hex: 0xB3E5FC)! + public static let lightBlue100 = SFColor(hex: 0xB3E5FC)! /// SwifterSwift: hex #81D4FA - public static let lightBlue200 = Color(hex: 0x81D4FA)! + public static let lightBlue200 = SFColor(hex: 0x81D4FA)! /// SwifterSwift: hex #4FC3F7 - public static let lightBlue300 = Color(hex: 0x4FC3F7)! + public static let lightBlue300 = SFColor(hex: 0x4FC3F7)! /// SwifterSwift: hex #29B6F6 - public static let lightBlue400 = Color(hex: 0x29B6F6)! + public static let lightBlue400 = SFColor(hex: 0x29B6F6)! /// SwifterSwift: hex #03A9F4 - public static let lightBlue500 = Color(hex: 0x03A9F4)! + public static let lightBlue500 = SFColor(hex: 0x03A9F4)! /// SwifterSwift: hex #039BE5 - public static let lightBlue600 = Color(hex: 0x039BE5)! + public static let lightBlue600 = SFColor(hex: 0x039BE5)! /// SwifterSwift: hex #0288D1 - public static let lightBlue700 = Color(hex: 0x0288D1)! + public static let lightBlue700 = SFColor(hex: 0x0288D1)! /// SwifterSwift: hex #0277BD - public static let lightBlue800 = Color(hex: 0x0277BD)! + public static let lightBlue800 = SFColor(hex: 0x0277BD)! /// SwifterSwift: hex #01579B - public static let lightBlue900 = Color(hex: 0x01579B)! + public static let lightBlue900 = SFColor(hex: 0x01579B)! /// SwifterSwift: hex #80D8FF - public static let lightBlueA100 = Color(hex: 0x80D8FF)! + public static let lightBlueA100 = SFColor(hex: 0x80D8FF)! /// SwifterSwift: hex #40C4FF - public static let lightBlueA200 = Color(hex: 0x40C4FF)! + public static let lightBlueA200 = SFColor(hex: 0x40C4FF)! /// SwifterSwift: hex #00B0FF - public static let lightBlueA400 = Color(hex: 0x00B0FF)! + public static let lightBlueA400 = SFColor(hex: 0x00B0FF)! /// SwifterSwift: hex #0091EA - public static let lightBlueA700 = Color(hex: 0x0091EA)! + public static let lightBlueA700 = SFColor(hex: 0x0091EA)! /// SwifterSwift: color cyan500 public static let cyan = cyan500 /// SwifterSwift: hex #E0F7FA - public static let cyan50 = Color(hex: 0xE0F7FA)! + public static let cyan50 = SFColor(hex: 0xE0F7FA)! /// SwifterSwift: hex #B2EBF2 - public static let cyan100 = Color(hex: 0xB2EBF2)! + public static let cyan100 = SFColor(hex: 0xB2EBF2)! /// SwifterSwift: hex #80DEEA - public static let cyan200 = Color(hex: 0x80DEEA)! + public static let cyan200 = SFColor(hex: 0x80DEEA)! /// SwifterSwift: hex #4DD0E1 - public static let cyan300 = Color(hex: 0x4DD0E1)! + public static let cyan300 = SFColor(hex: 0x4DD0E1)! /// SwifterSwift: hex #26C6DA - public static let cyan400 = Color(hex: 0x26C6DA)! + public static let cyan400 = SFColor(hex: 0x26C6DA)! /// SwifterSwift: hex #00BCD4 - public static let cyan500 = Color(hex: 0x00BCD4)! + public static let cyan500 = SFColor(hex: 0x00BCD4)! /// SwifterSwift: hex #00ACC1 - public static let cyan600 = Color(hex: 0x00ACC1)! + public static let cyan600 = SFColor(hex: 0x00ACC1)! /// SwifterSwift: hex #0097A7 - public static let cyan700 = Color(hex: 0x0097A7)! + public static let cyan700 = SFColor(hex: 0x0097A7)! /// SwifterSwift: hex #00838F - public static let cyan800 = Color(hex: 0x00838F)! + public static let cyan800 = SFColor(hex: 0x00838F)! /// SwifterSwift: hex #006064 - public static let cyan900 = Color(hex: 0x006064)! + public static let cyan900 = SFColor(hex: 0x006064)! /// SwifterSwift: hex #84FFFF - public static let cyanA100 = Color(hex: 0x84FFFF)! + public static let cyanA100 = SFColor(hex: 0x84FFFF)! /// SwifterSwift: hex #18FFFF - public static let cyanA200 = Color(hex: 0x18FFFF)! + public static let cyanA200 = SFColor(hex: 0x18FFFF)! /// SwifterSwift: hex #00E5FF - public static let cyanA400 = Color(hex: 0x00E5FF)! + public static let cyanA400 = SFColor(hex: 0x00E5FF)! /// SwifterSwift: hex #00B8D4 - public static let cyanA700 = Color(hex: 0x00B8D4)! + public static let cyanA700 = SFColor(hex: 0x00B8D4)! /// SwifterSwift: color teal500 public static let teal = teal500 /// SwifterSwift: hex #E0F2F1 - public static let teal50 = Color(hex: 0xE0F2F1)! + public static let teal50 = SFColor(hex: 0xE0F2F1)! /// SwifterSwift: hex #B2DFDB - public static let teal100 = Color(hex: 0xB2DFDB)! + public static let teal100 = SFColor(hex: 0xB2DFDB)! /// SwifterSwift: hex #80CBC4 - public static let teal200 = Color(hex: 0x80CBC4)! + public static let teal200 = SFColor(hex: 0x80CBC4)! /// SwifterSwift: hex #4DB6AC - public static let teal300 = Color(hex: 0x4DB6AC)! + public static let teal300 = SFColor(hex: 0x4DB6AC)! /// SwifterSwift: hex #26A69A - public static let teal400 = Color(hex: 0x26A69A)! + public static let teal400 = SFColor(hex: 0x26A69A)! /// SwifterSwift: hex #009688 - public static let teal500 = Color(hex: 0x009688)! + public static let teal500 = SFColor(hex: 0x009688)! /// SwifterSwift: hex #00897B - public static let teal600 = Color(hex: 0x00897B)! + public static let teal600 = SFColor(hex: 0x00897B)! /// SwifterSwift: hex #00796B - public static let teal700 = Color(hex: 0x00796B)! + public static let teal700 = SFColor(hex: 0x00796B)! /// SwifterSwift: hex #00695C - public static let teal800 = Color(hex: 0x00695C)! + public static let teal800 = SFColor(hex: 0x00695C)! /// SwifterSwift: hex #004D40 - public static let teal900 = Color(hex: 0x004D40)! + public static let teal900 = SFColor(hex: 0x004D40)! /// SwifterSwift: hex #A7FFEB - public static let tealA100 = Color(hex: 0xA7FFEB)! + public static let tealA100 = SFColor(hex: 0xA7FFEB)! /// SwifterSwift: hex #64FFDA - public static let tealA200 = Color(hex: 0x64FFDA)! + public static let tealA200 = SFColor(hex: 0x64FFDA)! /// SwifterSwift: hex #1DE9B6 - public static let tealA400 = Color(hex: 0x1DE9B6)! + public static let tealA400 = SFColor(hex: 0x1DE9B6)! /// SwifterSwift: hex #00BFA5 - public static let tealA700 = Color(hex: 0x00BFA5)! + public static let tealA700 = SFColor(hex: 0x00BFA5)! /// SwifterSwift: color green500 public static let green = green500 /// SwifterSwift: hex #E8F5E9 - public static let green50 = Color(hex: 0xE8F5E9)! + public static let green50 = SFColor(hex: 0xE8F5E9)! /// SwifterSwift: hex #C8E6C9 - public static let green100 = Color(hex: 0xC8E6C9)! + public static let green100 = SFColor(hex: 0xC8E6C9)! /// SwifterSwift: hex #A5D6A7 - public static let green200 = Color(hex: 0xA5D6A7)! + public static let green200 = SFColor(hex: 0xA5D6A7)! /// SwifterSwift: hex #81C784 - public static let green300 = Color(hex: 0x81C784)! + public static let green300 = SFColor(hex: 0x81C784)! /// SwifterSwift: hex #66BB6A - public static let green400 = Color(hex: 0x66BB6A)! + public static let green400 = SFColor(hex: 0x66BB6A)! /// SwifterSwift: hex #4CAF50 - public static let green500 = Color(hex: 0x4CAF50)! + public static let green500 = SFColor(hex: 0x4CAF50)! /// SwifterSwift: hex #43A047 - public static let green600 = Color(hex: 0x43A047)! + public static let green600 = SFColor(hex: 0x43A047)! /// SwifterSwift: hex #388E3C - public static let green700 = Color(hex: 0x388E3C)! + public static let green700 = SFColor(hex: 0x388E3C)! /// SwifterSwift: hex #2E7D32 - public static let green800 = Color(hex: 0x2E7D32)! + public static let green800 = SFColor(hex: 0x2E7D32)! /// SwifterSwift: hex #1B5E20 - public static let green900 = Color(hex: 0x1B5E20)! + public static let green900 = SFColor(hex: 0x1B5E20)! /// SwifterSwift: hex #B9F6CA - public static let greenA100 = Color(hex: 0xB9F6CA)! + public static let greenA100 = SFColor(hex: 0xB9F6CA)! /// SwifterSwift: hex #69F0AE - public static let greenA200 = Color(hex: 0x69F0AE)! + public static let greenA200 = SFColor(hex: 0x69F0AE)! /// SwifterSwift: hex #00E676 - public static let greenA400 = Color(hex: 0x00E676)! + public static let greenA400 = SFColor(hex: 0x00E676)! /// SwifterSwift: hex #00C853 - public static let greenA700 = Color(hex: 0x00C853)! + public static let greenA700 = SFColor(hex: 0x00C853)! /// SwifterSwift: color lightGreen500 public static let lightGreen = lightGreen500 /// SwifterSwift: hex #F1F8E9 - public static let lightGreen50 = Color(hex: 0xF1F8E9)! + public static let lightGreen50 = SFColor(hex: 0xF1F8E9)! /// SwifterSwift: hex #DCEDC8 - public static let lightGreen100 = Color(hex: 0xDCEDC8)! + public static let lightGreen100 = SFColor(hex: 0xDCEDC8)! /// SwifterSwift: hex #C5E1A5 - public static let lightGreen200 = Color(hex: 0xC5E1A5)! + public static let lightGreen200 = SFColor(hex: 0xC5E1A5)! /// SwifterSwift: hex #AED581 - public static let lightGreen300 = Color(hex: 0xAED581)! + public static let lightGreen300 = SFColor(hex: 0xAED581)! /// SwifterSwift: hex #9CCC65 - public static let lightGreen400 = Color(hex: 0x9CCC65)! + public static let lightGreen400 = SFColor(hex: 0x9CCC65)! /// SwifterSwift: hex #8BC34A - public static let lightGreen500 = Color(hex: 0x8BC34A)! + public static let lightGreen500 = SFColor(hex: 0x8BC34A)! /// SwifterSwift: hex #7CB342 - public static let lightGreen600 = Color(hex: 0x7CB342)! + public static let lightGreen600 = SFColor(hex: 0x7CB342)! /// SwifterSwift: hex #689F38 - public static let lightGreen700 = Color(hex: 0x689F38)! + public static let lightGreen700 = SFColor(hex: 0x689F38)! /// SwifterSwift: hex #558B2F - public static let lightGreen800 = Color(hex: 0x558B2F)! + public static let lightGreen800 = SFColor(hex: 0x558B2F)! /// SwifterSwift: hex #33691E - public static let lightGreen900 = Color(hex: 0x33691E)! + public static let lightGreen900 = SFColor(hex: 0x33691E)! /// SwifterSwift: hex #CCFF90 - public static let lightGreenA100 = Color(hex: 0xCCFF90)! + public static let lightGreenA100 = SFColor(hex: 0xCCFF90)! /// SwifterSwift: hex #B2FF59 - public static let lightGreenA200 = Color(hex: 0xB2FF59)! + public static let lightGreenA200 = SFColor(hex: 0xB2FF59)! /// SwifterSwift: hex #76FF03 - public static let lightGreenA400 = Color(hex: 0x76FF03)! + public static let lightGreenA400 = SFColor(hex: 0x76FF03)! /// SwifterSwift: hex #64DD17 - public static let lightGreenA700 = Color(hex: 0x64DD17)! + public static let lightGreenA700 = SFColor(hex: 0x64DD17)! /// SwifterSwift: color lime500 public static let lime = lime500 /// SwifterSwift: hex #F9FBE7 - public static let lime50 = Color(hex: 0xF9FBE7)! + public static let lime50 = SFColor(hex: 0xF9FBE7)! /// SwifterSwift: hex #F0F4C3 - public static let lime100 = Color(hex: 0xF0F4C3)! + public static let lime100 = SFColor(hex: 0xF0F4C3)! /// SwifterSwift: hex #E6EE9C - public static let lime200 = Color(hex: 0xE6EE9C)! + public static let lime200 = SFColor(hex: 0xE6EE9C)! /// SwifterSwift: hex #DCE775 - public static let lime300 = Color(hex: 0xDCE775)! + public static let lime300 = SFColor(hex: 0xDCE775)! /// SwifterSwift: hex #D4E157 - public static let lime400 = Color(hex: 0xD4E157)! + public static let lime400 = SFColor(hex: 0xD4E157)! /// SwifterSwift: hex #CDDC39 - public static let lime500 = Color(hex: 0xCDDC39)! + public static let lime500 = SFColor(hex: 0xCDDC39)! /// SwifterSwift: hex #C0CA33 - public static let lime600 = Color(hex: 0xC0CA33)! + public static let lime600 = SFColor(hex: 0xC0CA33)! /// SwifterSwift: hex #AFB42B - public static let lime700 = Color(hex: 0xAFB42B)! + public static let lime700 = SFColor(hex: 0xAFB42B)! /// SwifterSwift: hex #9E9D24 - public static let lime800 = Color(hex: 0x9E9D24)! + public static let lime800 = SFColor(hex: 0x9E9D24)! /// SwifterSwift: hex #827717 - public static let lime900 = Color(hex: 0x827717)! + public static let lime900 = SFColor(hex: 0x827717)! /// SwifterSwift: hex #F4FF81 - public static let limeA100 = Color(hex: 0xF4FF81)! + public static let limeA100 = SFColor(hex: 0xF4FF81)! /// SwifterSwift: hex #EEFF41 - public static let limeA200 = Color(hex: 0xEEFF41)! + public static let limeA200 = SFColor(hex: 0xEEFF41)! /// SwifterSwift: hex #C6FF00 - public static let limeA400 = Color(hex: 0xC6FF00)! + public static let limeA400 = SFColor(hex: 0xC6FF00)! /// SwifterSwift: hex #AEEA00 - public static let limeA700 = Color(hex: 0xAEEA00)! + public static let limeA700 = SFColor(hex: 0xAEEA00)! /// SwifterSwift: color yellow500 public static let yellow = yellow500 /// SwifterSwift: hex #FFFDE7 - public static let yellow50 = Color(hex: 0xFFFDE7)! + public static let yellow50 = SFColor(hex: 0xFFFDE7)! /// SwifterSwift: hex #FFF9C4 - public static let yellow100 = Color(hex: 0xFFF9C4)! + public static let yellow100 = SFColor(hex: 0xFFF9C4)! /// SwifterSwift: hex #FFF59D - public static let yellow200 = Color(hex: 0xFFF59D)! + public static let yellow200 = SFColor(hex: 0xFFF59D)! /// SwifterSwift: hex #FFF176 - public static let yellow300 = Color(hex: 0xFFF176)! + public static let yellow300 = SFColor(hex: 0xFFF176)! /// SwifterSwift: hex #FFEE58 - public static let yellow400 = Color(hex: 0xFFEE58)! + public static let yellow400 = SFColor(hex: 0xFFEE58)! /// SwifterSwift: hex #FFEB3B - public static let yellow500 = Color(hex: 0xFFEB3B)! + public static let yellow500 = SFColor(hex: 0xFFEB3B)! /// SwifterSwift: hex #FDD835 - public static let yellow600 = Color(hex: 0xFDD835)! + public static let yellow600 = SFColor(hex: 0xFDD835)! /// SwifterSwift: hex #FBC02D - public static let yellow700 = Color(hex: 0xFBC02D)! + public static let yellow700 = SFColor(hex: 0xFBC02D)! /// SwifterSwift: hex #F9A825 - public static let yellow800 = Color(hex: 0xF9A825)! + public static let yellow800 = SFColor(hex: 0xF9A825)! /// SwifterSwift: hex #F57F17 - public static let yellow900 = Color(hex: 0xF57F17)! + public static let yellow900 = SFColor(hex: 0xF57F17)! /// SwifterSwift: hex #FFFF8D - public static let yellowA100 = Color(hex: 0xFFFF8D)! + public static let yellowA100 = SFColor(hex: 0xFFFF8D)! /// SwifterSwift: hex #FFFF00 - public static let yellowA200 = Color(hex: 0xFFFF00)! + public static let yellowA200 = SFColor(hex: 0xFFFF00)! /// SwifterSwift: hex #FFEA00 - public static let yellowA400 = Color(hex: 0xFFEA00)! + public static let yellowA400 = SFColor(hex: 0xFFEA00)! /// SwifterSwift: hex #FFD600 - public static let yellowA700 = Color(hex: 0xFFD600)! + public static let yellowA700 = SFColor(hex: 0xFFD600)! /// SwifterSwift: color amber500 public static let amber = amber500 /// SwifterSwift: hex #FFF8E1 - public static let amber50 = Color(hex: 0xFFF8E1)! + public static let amber50 = SFColor(hex: 0xFFF8E1)! /// SwifterSwift: hex #FFECB3 - public static let amber100 = Color(hex: 0xFFECB3)! + public static let amber100 = SFColor(hex: 0xFFECB3)! /// SwifterSwift: hex #FFE082 - public static let amber200 = Color(hex: 0xFFE082)! + public static let amber200 = SFColor(hex: 0xFFE082)! /// SwifterSwift: hex #FFD54F - public static let amber300 = Color(hex: 0xFFD54F)! + public static let amber300 = SFColor(hex: 0xFFD54F)! /// SwifterSwift: hex #FFCA28 - public static let amber400 = Color(hex: 0xFFCA28)! + public static let amber400 = SFColor(hex: 0xFFCA28)! /// SwifterSwift: hex #FFC107 - public static let amber500 = Color(hex: 0xFFC107)! + public static let amber500 = SFColor(hex: 0xFFC107)! /// SwifterSwift: hex #FFB300 - public static let amber600 = Color(hex: 0xFFB300)! + public static let amber600 = SFColor(hex: 0xFFB300)! /// SwifterSwift: hex #FFA000 - public static let amber700 = Color(hex: 0xFFA000)! + public static let amber700 = SFColor(hex: 0xFFA000)! /// SwifterSwift: hex #FF8F00 - public static let amber800 = Color(hex: 0xFF8F00)! + public static let amber800 = SFColor(hex: 0xFF8F00)! /// SwifterSwift: hex #FF6F00 - public static let amber900 = Color(hex: 0xFF6F00)! + public static let amber900 = SFColor(hex: 0xFF6F00)! /// SwifterSwift: hex #FFE57F - public static let amberA100 = Color(hex: 0xFFE57F)! + public static let amberA100 = SFColor(hex: 0xFFE57F)! /// SwifterSwift: hex #FFD740 - public static let amberA200 = Color(hex: 0xFFD740)! + public static let amberA200 = SFColor(hex: 0xFFD740)! /// SwifterSwift: hex #FFC400 - public static let amberA400 = Color(hex: 0xFFC400)! + public static let amberA400 = SFColor(hex: 0xFFC400)! /// SwifterSwift: hex #FFAB00 - public static let amberA700 = Color(hex: 0xFFAB00)! + public static let amberA700 = SFColor(hex: 0xFFAB00)! /// SwifterSwift: color orange500 public static let orange = orange500 /// SwifterSwift: hex #FFF3E0 - public static let orange50 = Color(hex: 0xFFF3E0)! + public static let orange50 = SFColor(hex: 0xFFF3E0)! /// SwifterSwift: hex #FFE0B2 - public static let orange100 = Color(hex: 0xFFE0B2)! + public static let orange100 = SFColor(hex: 0xFFE0B2)! /// SwifterSwift: hex #FFCC80 - public static let orange200 = Color(hex: 0xFFCC80)! + public static let orange200 = SFColor(hex: 0xFFCC80)! /// SwifterSwift: hex #FFB74D - public static let orange300 = Color(hex: 0xFFB74D)! + public static let orange300 = SFColor(hex: 0xFFB74D)! /// SwifterSwift: hex #FFA726 - public static let orange400 = Color(hex: 0xFFA726)! + public static let orange400 = SFColor(hex: 0xFFA726)! /// SwifterSwift: hex #FF9800 - public static let orange500 = Color(hex: 0xFF9800)! + public static let orange500 = SFColor(hex: 0xFF9800)! /// SwifterSwift: hex #FB8C00 - public static let orange600 = Color(hex: 0xFB8C00)! + public static let orange600 = SFColor(hex: 0xFB8C00)! /// SwifterSwift: hex #F57C00 - public static let orange700 = Color(hex: 0xF57C00)! + public static let orange700 = SFColor(hex: 0xF57C00)! /// SwifterSwift: hex #EF6C00 - public static let orange800 = Color(hex: 0xEF6C00)! + public static let orange800 = SFColor(hex: 0xEF6C00)! /// SwifterSwift: hex #E65100 - public static let orange900 = Color(hex: 0xE65100)! + public static let orange900 = SFColor(hex: 0xE65100)! /// SwifterSwift: hex #FFD180 - public static let orangeA100 = Color(hex: 0xFFD180)! + public static let orangeA100 = SFColor(hex: 0xFFD180)! /// SwifterSwift: hex #FFAB40 - public static let orangeA200 = Color(hex: 0xFFAB40)! + public static let orangeA200 = SFColor(hex: 0xFFAB40)! /// SwifterSwift: hex #FF9100 - public static let orangeA400 = Color(hex: 0xFF9100)! + public static let orangeA400 = SFColor(hex: 0xFF9100)! /// SwifterSwift: hex #FF6D00 - public static let orangeA700 = Color(hex: 0xFF6D00)! + public static let orangeA700 = SFColor(hex: 0xFF6D00)! /// SwifterSwift: color deepOrange500 public static let deepOrange = deepOrange500 /// SwifterSwift: hex #FBE9E7 - public static let deepOrange50 = Color(hex: 0xFBE9E7)! + public static let deepOrange50 = SFColor(hex: 0xFBE9E7)! /// SwifterSwift: hex #FFCCBC - public static let deepOrange100 = Color(hex: 0xFFCCBC)! + public static let deepOrange100 = SFColor(hex: 0xFFCCBC)! /// SwifterSwift: hex #FFAB91 - public static let deepOrange200 = Color(hex: 0xFFAB91)! + public static let deepOrange200 = SFColor(hex: 0xFFAB91)! /// SwifterSwift: hex #FF8A65 - public static let deepOrange300 = Color(hex: 0xFF8A65)! + public static let deepOrange300 = SFColor(hex: 0xFF8A65)! /// SwifterSwift: hex #FF7043 - public static let deepOrange400 = Color(hex: 0xFF7043)! + public static let deepOrange400 = SFColor(hex: 0xFF7043)! /// SwifterSwift: hex #FF5722 - public static let deepOrange500 = Color(hex: 0xFF5722)! + public static let deepOrange500 = SFColor(hex: 0xFF5722)! /// SwifterSwift: hex #F4511E - public static let deepOrange600 = Color(hex: 0xF4511E)! + public static let deepOrange600 = SFColor(hex: 0xF4511E)! /// SwifterSwift: hex #E64A19 - public static let deepOrange700 = Color(hex: 0xE64A19)! + public static let deepOrange700 = SFColor(hex: 0xE64A19)! /// SwifterSwift: hex #D84315 - public static let deepOrange800 = Color(hex: 0xD84315)! + public static let deepOrange800 = SFColor(hex: 0xD84315)! /// SwifterSwift: hex #BF360C - public static let deepOrange900 = Color(hex: 0xBF360C)! + public static let deepOrange900 = SFColor(hex: 0xBF360C)! /// SwifterSwift: hex #FF9E80 - public static let deepOrangeA100 = Color(hex: 0xFF9E80)! + public static let deepOrangeA100 = SFColor(hex: 0xFF9E80)! /// SwifterSwift: hex #FF6E40 - public static let deepOrangeA200 = Color(hex: 0xFF6E40)! + public static let deepOrangeA200 = SFColor(hex: 0xFF6E40)! /// SwifterSwift: hex #FF3D00 - public static let deepOrangeA400 = Color(hex: 0xFF3D00)! + public static let deepOrangeA400 = SFColor(hex: 0xFF3D00)! /// SwifterSwift: hex #DD2C00 - public static let deepOrangeA700 = Color(hex: 0xDD2C00)! + public static let deepOrangeA700 = SFColor(hex: 0xDD2C00)! /// SwifterSwift: color brown500 public static let brown = brown500 /// SwifterSwift: hex #EFEBE9 - public static let brown50 = Color(hex: 0xEFEBE9)! + public static let brown50 = SFColor(hex: 0xEFEBE9)! /// SwifterSwift: hex #D7CCC8 - public static let brown100 = Color(hex: 0xD7CCC8)! + public static let brown100 = SFColor(hex: 0xD7CCC8)! /// SwifterSwift: hex #BCAAA4 - public static let brown200 = Color(hex: 0xBCAAA4)! + public static let brown200 = SFColor(hex: 0xBCAAA4)! /// SwifterSwift: hex #A1887F - public static let brown300 = Color(hex: 0xA1887F)! + public static let brown300 = SFColor(hex: 0xA1887F)! /// SwifterSwift: hex #8D6E63 - public static let brown400 = Color(hex: 0x8D6E63)! + public static let brown400 = SFColor(hex: 0x8D6E63)! /// SwifterSwift: hex #795548 - public static let brown500 = Color(hex: 0x795548)! + public static let brown500 = SFColor(hex: 0x795548)! /// SwifterSwift: hex #6D4C41 - public static let brown600 = Color(hex: 0x6D4C41)! + public static let brown600 = SFColor(hex: 0x6D4C41)! /// SwifterSwift: hex #5D4037 - public static let brown700 = Color(hex: 0x5D4037)! + public static let brown700 = SFColor(hex: 0x5D4037)! /// SwifterSwift: hex #4E342E - public static let brown800 = Color(hex: 0x4E342E)! + public static let brown800 = SFColor(hex: 0x4E342E)! /// SwifterSwift: hex #3E2723 - public static let brown900 = Color(hex: 0x3E2723)! + public static let brown900 = SFColor(hex: 0x3E2723)! /// SwifterSwift: color grey500 public static let grey = grey500 /// SwifterSwift: hex #FAFAFA - public static let grey50 = Color(hex: 0xFAFAFA)! + public static let grey50 = SFColor(hex: 0xFAFAFA)! /// SwifterSwift: hex #F5F5F5 - public static let grey100 = Color(hex: 0xF5F5F5)! + public static let grey100 = SFColor(hex: 0xF5F5F5)! /// SwifterSwift: hex #EEEEEE - public static let grey200 = Color(hex: 0xEEEEEE)! + public static let grey200 = SFColor(hex: 0xEEEEEE)! /// SwifterSwift: hex #E0E0E0 - public static let grey300 = Color(hex: 0xE0E0E0)! + public static let grey300 = SFColor(hex: 0xE0E0E0)! /// SwifterSwift: hex #BDBDBD - public static let grey400 = Color(hex: 0xBDBDBD)! + public static let grey400 = SFColor(hex: 0xBDBDBD)! /// SwifterSwift: hex #9E9E9E - public static let grey500 = Color(hex: 0x9E9E9E)! + public static let grey500 = SFColor(hex: 0x9E9E9E)! /// SwifterSwift: hex #757575 - public static let grey600 = Color(hex: 0x757575)! + public static let grey600 = SFColor(hex: 0x757575)! /// SwifterSwift: hex #616161 - public static let grey700 = Color(hex: 0x616161)! + public static let grey700 = SFColor(hex: 0x616161)! /// SwifterSwift: hex #424242 - public static let grey800 = Color(hex: 0x424242)! + public static let grey800 = SFColor(hex: 0x424242)! /// SwifterSwift: hex #212121 - public static let grey900 = Color(hex: 0x212121)! + public static let grey900 = SFColor(hex: 0x212121)! /// SwifterSwift: color blueGrey500 public static let blueGrey = blueGrey500 /// SwifterSwift: hex #ECEFF1 - public static let blueGrey50 = Color(hex: 0xECEFF1)! + public static let blueGrey50 = SFColor(hex: 0xECEFF1)! /// SwifterSwift: hex #CFD8DC - public static let blueGrey100 = Color(hex: 0xCFD8DC)! + public static let blueGrey100 = SFColor(hex: 0xCFD8DC)! /// SwifterSwift: hex #B0BEC5 - public static let blueGrey200 = Color(hex: 0xB0BEC5)! + public static let blueGrey200 = SFColor(hex: 0xB0BEC5)! /// SwifterSwift: hex #90A4AE - public static let blueGrey300 = Color(hex: 0x90A4AE)! + public static let blueGrey300 = SFColor(hex: 0x90A4AE)! /// SwifterSwift: hex #78909C - public static let blueGrey400 = Color(hex: 0x78909C)! + public static let blueGrey400 = SFColor(hex: 0x78909C)! /// SwifterSwift: hex #607D8B - public static let blueGrey500 = Color(hex: 0x607D8B)! + public static let blueGrey500 = SFColor(hex: 0x607D8B)! /// SwifterSwift: hex #546E7A - public static let blueGrey600 = Color(hex: 0x546E7A)! + public static let blueGrey600 = SFColor(hex: 0x546E7A)! /// SwifterSwift: hex #455A64 - public static let blueGrey700 = Color(hex: 0x455A64)! + public static let blueGrey700 = SFColor(hex: 0x455A64)! /// SwifterSwift: hex #37474F - public static let blueGrey800 = Color(hex: 0x37474F)! + public static let blueGrey800 = SFColor(hex: 0x37474F)! /// SwifterSwift: hex #263238 - public static let blueGrey900 = Color(hex: 0x263238)! + public static let blueGrey900 = SFColor(hex: 0x263238)! /// SwifterSwift: hex #000000 - public static let black = Color(hex: 0x000000)! + public static let black = SFColor(hex: 0x000000)! /// SwifterSwift: hex #FFFFFF - public static let white = Color(hex: 0xFFFFFF)! + public static let white = SFColor(hex: 0xFFFFFF)! } } // MARK: - CSS colors -public extension Color { +public extension SFColor { /// SwifterSwift: CSS colors. enum CSS { // http://www.w3schools.com/colors/colors_names.asp /// SwifterSwift: hex #F0F8FF - public static let aliceBlue = Color(hex: 0xF0F8FF)! + public static let aliceBlue = SFColor(hex: 0xF0F8FF)! /// SwifterSwift: hex #FAEBD7 - public static let antiqueWhite = Color(hex: 0xFAEBD7)! + public static let antiqueWhite = SFColor(hex: 0xFAEBD7)! /// SwifterSwift: hex #00FFFF - public static let aqua = Color(hex: 0x00FFFF)! + public static let aqua = SFColor(hex: 0x00FFFF)! /// SwifterSwift: hex #7FFFD4 - public static let aquamarine = Color(hex: 0x7FFFD4)! + public static let aquamarine = SFColor(hex: 0x7FFFD4)! /// SwifterSwift: hex #F0FFFF - public static let azure = Color(hex: 0xF0FFFF)! + public static let azure = SFColor(hex: 0xF0FFFF)! /// SwifterSwift: hex #F5F5DC - public static let beige = Color(hex: 0xF5F5DC)! + public static let beige = SFColor(hex: 0xF5F5DC)! /// SwifterSwift: hex #FFE4C4 - public static let bisque = Color(hex: 0xFFE4C4)! + public static let bisque = SFColor(hex: 0xFFE4C4)! /// SwifterSwift: hex #000000 - public static let black = Color(hex: 0x000000)! + public static let black = SFColor(hex: 0x000000)! /// SwifterSwift: hex #FFEBCD - public static let blanchedAlmond = Color(hex: 0xFFEBCD)! + public static let blanchedAlmond = SFColor(hex: 0xFFEBCD)! /// SwifterSwift: hex #0000FF - public static let blue = Color(hex: 0x0000FF)! + public static let blue = SFColor(hex: 0x0000FF)! /// SwifterSwift: hex #8A2BE2 - public static let blueViolet = Color(hex: 0x8A2BE2)! + public static let blueViolet = SFColor(hex: 0x8A2BE2)! /// SwifterSwift: hex #A52A2A - public static let brown = Color(hex: 0xA52A2A)! + public static let brown = SFColor(hex: 0xA52A2A)! /// SwifterSwift: hex #DEB887 - public static let burlyWood = Color(hex: 0xDEB887)! + public static let burlyWood = SFColor(hex: 0xDEB887)! /// SwifterSwift: hex #5F9EA0 - public static let cadetBlue = Color(hex: 0x5F9EA0)! + public static let cadetBlue = SFColor(hex: 0x5F9EA0)! /// SwifterSwift: hex #7FFF00 - public static let chartreuse = Color(hex: 0x7FFF00)! + public static let chartreuse = SFColor(hex: 0x7FFF00)! /// SwifterSwift: hex #D2691E - public static let chocolate = Color(hex: 0xD2691E)! + public static let chocolate = SFColor(hex: 0xD2691E)! /// SwifterSwift: hex #FF7F50 - public static let coral = Color(hex: 0xFF7F50)! + public static let coral = SFColor(hex: 0xFF7F50)! /// SwifterSwift: hex #6495ED - public static let cornflowerBlue = Color(hex: 0x6495ED)! + public static let cornflowerBlue = SFColor(hex: 0x6495ED)! /// SwifterSwift: hex #FFF8DC - public static let cornsilk = Color(hex: 0xFFF8DC)! + public static let cornsilk = SFColor(hex: 0xFFF8DC)! /// SwifterSwift: hex #DC143C - public static let crimson = Color(hex: 0xDC143C)! + public static let crimson = SFColor(hex: 0xDC143C)! /// SwifterSwift: hex #00FFFF - public static let cyan = Color(hex: 0x00FFFF)! + public static let cyan = SFColor(hex: 0x00FFFF)! /// SwifterSwift: hex #00008B - public static let darkBlue = Color(hex: 0x00008B)! + public static let darkBlue = SFColor(hex: 0x00008B)! /// SwifterSwift: hex #008B8B - public static let darkCyan = Color(hex: 0x008B8B)! + public static let darkCyan = SFColor(hex: 0x008B8B)! /// SwifterSwift: hex #B8860B - public static let darkGoldenRod = Color(hex: 0xB8860B)! + public static let darkGoldenRod = SFColor(hex: 0xB8860B)! /// SwifterSwift: hex #A9A9A9 - public static let darkGray = Color(hex: 0xA9A9A9)! + public static let darkGray = SFColor(hex: 0xA9A9A9)! /// SwifterSwift: hex #A9A9A9 - public static let darkGrey = Color(hex: 0xA9A9A9)! + public static let darkGrey = SFColor(hex: 0xA9A9A9)! /// SwifterSwift: hex #006400 - public static let darkGreen = Color(hex: 0x006400)! + public static let darkGreen = SFColor(hex: 0x006400)! /// SwifterSwift: hex #BDB76B - public static let darkKhaki = Color(hex: 0xBDB76B)! + public static let darkKhaki = SFColor(hex: 0xBDB76B)! /// SwifterSwift: hex #8B008B - public static let darkMagenta = Color(hex: 0x8B008B)! + public static let darkMagenta = SFColor(hex: 0x8B008B)! /// SwifterSwift: hex #556B2F - public static let darkOliveGreen = Color(hex: 0x556B2F)! + public static let darkOliveGreen = SFColor(hex: 0x556B2F)! /// SwifterSwift: hex #FF8C00 - public static let darkOrange = Color(hex: 0xFF8C00)! + public static let darkOrange = SFColor(hex: 0xFF8C00)! /// SwifterSwift: hex #9932CC - public static let darkOrchid = Color(hex: 0x9932CC)! + public static let darkOrchid = SFColor(hex: 0x9932CC)! /// SwifterSwift: hex #8B0000 - public static let darkRed = Color(hex: 0x8B0000)! + public static let darkRed = SFColor(hex: 0x8B0000)! /// SwifterSwift: hex #E9967A - public static let darkSalmon = Color(hex: 0xE9967A)! + public static let darkSalmon = SFColor(hex: 0xE9967A)! /// SwifterSwift: hex #8FBC8F - public static let darkSeaGreen = Color(hex: 0x8FBC8F)! + public static let darkSeaGreen = SFColor(hex: 0x8FBC8F)! /// SwifterSwift: hex #483D8B - public static let darkSlateBlue = Color(hex: 0x483D8B)! + public static let darkSlateBlue = SFColor(hex: 0x483D8B)! /// SwifterSwift: hex #2F4F4F - public static let darkSlateGray = Color(hex: 0x2F4F4F)! + public static let darkSlateGray = SFColor(hex: 0x2F4F4F)! /// SwifterSwift: hex #2F4F4F - public static let darkSlateGrey = Color(hex: 0x2F4F4F)! + public static let darkSlateGrey = SFColor(hex: 0x2F4F4F)! /// SwifterSwift: hex #00CED1 - public static let darkTurquoise = Color(hex: 0x00CED1)! + public static let darkTurquoise = SFColor(hex: 0x00CED1)! /// SwifterSwift: hex #9400D3 - public static let darkViolet = Color(hex: 0x9400D3)! + public static let darkViolet = SFColor(hex: 0x9400D3)! /// SwifterSwift: hex #FF1493 - public static let deepPink = Color(hex: 0xFF1493)! + public static let deepPink = SFColor(hex: 0xFF1493)! /// SwifterSwift: hex #00BFFF - public static let deepSkyBlue = Color(hex: 0x00BFFF)! + public static let deepSkyBlue = SFColor(hex: 0x00BFFF)! /// SwifterSwift: hex #696969 - public static let dimGray = Color(hex: 0x696969)! + public static let dimGray = SFColor(hex: 0x696969)! /// SwifterSwift: hex #696969 - public static let dimGrey = Color(hex: 0x696969)! + public static let dimGrey = SFColor(hex: 0x696969)! /// SwifterSwift: hex #1E90FF - public static let dodgerBlue = Color(hex: 0x1E90FF)! + public static let dodgerBlue = SFColor(hex: 0x1E90FF)! /// SwifterSwift: hex #B22222 - public static let fireBrick = Color(hex: 0xB22222)! + public static let fireBrick = SFColor(hex: 0xB22222)! /// SwifterSwift: hex #FFFAF0 - public static let floralWhite = Color(hex: 0xFFFAF0)! + public static let floralWhite = SFColor(hex: 0xFFFAF0)! /// SwifterSwift: hex #228B22 - public static let forestGreen = Color(hex: 0x228B22)! + public static let forestGreen = SFColor(hex: 0x228B22)! /// SwifterSwift: hex #FF00FF - public static let fuchsia = Color(hex: 0xFF00FF)! + public static let fuchsia = SFColor(hex: 0xFF00FF)! /// SwifterSwift: hex #DCDCDC - public static let gainsboro = Color(hex: 0xDCDCDC)! + public static let gainsboro = SFColor(hex: 0xDCDCDC)! /// SwifterSwift: hex #F8F8FF - public static let ghostWhite = Color(hex: 0xF8F8FF)! + public static let ghostWhite = SFColor(hex: 0xF8F8FF)! /// SwifterSwift: hex #FFD700 - public static let gold = Color(hex: 0xFFD700)! + public static let gold = SFColor(hex: 0xFFD700)! /// SwifterSwift: hex #DAA520 - public static let goldenRod = Color(hex: 0xDAA520)! + public static let goldenRod = SFColor(hex: 0xDAA520)! /// SwifterSwift: hex #808080 - public static let gray = Color(hex: 0x808080)! + public static let gray = SFColor(hex: 0x808080)! /// SwifterSwift: hex #808080 - public static let grey = Color(hex: 0x808080)! + public static let grey = SFColor(hex: 0x808080)! /// SwifterSwift: hex #008000 - public static let green = Color(hex: 0x008000)! + public static let green = SFColor(hex: 0x008000)! /// SwifterSwift: hex #ADFF2F - public static let greenYellow = Color(hex: 0xADFF2F)! + public static let greenYellow = SFColor(hex: 0xADFF2F)! /// SwifterSwift: hex #F0FFF0 - public static let honeyDew = Color(hex: 0xF0FFF0)! + public static let honeyDew = SFColor(hex: 0xF0FFF0)! /// SwifterSwift: hex #FF69B4 - public static let hotPink = Color(hex: 0xFF69B4)! + public static let hotPink = SFColor(hex: 0xFF69B4)! /// SwifterSwift: hex #CD5C5C - public static let indianRed = Color(hex: 0xCD5C5C)! + public static let indianRed = SFColor(hex: 0xCD5C5C)! /// SwifterSwift: hex #4B0082 - public static let indigo = Color(hex: 0x4B0082)! + public static let indigo = SFColor(hex: 0x4B0082)! /// SwifterSwift: hex #FFFFF0 - public static let ivory = Color(hex: 0xFFFFF0)! + public static let ivory = SFColor(hex: 0xFFFFF0)! /// SwifterSwift: hex #F0E68C - public static let khaki = Color(hex: 0xF0E68C)! + public static let khaki = SFColor(hex: 0xF0E68C)! /// SwifterSwift: hex #E6E6FA - public static let lavender = Color(hex: 0xE6E6FA)! + public static let lavender = SFColor(hex: 0xE6E6FA)! /// SwifterSwift: hex #FFF0F5 - public static let lavenderBlush = Color(hex: 0xFFF0F5)! + public static let lavenderBlush = SFColor(hex: 0xFFF0F5)! /// SwifterSwift: hex #7CFC00 - public static let lawnGreen = Color(hex: 0x7CFC00)! + public static let lawnGreen = SFColor(hex: 0x7CFC00)! /// SwifterSwift: hex #FFFACD - public static let lemonChiffon = Color(hex: 0xFFFACD)! + public static let lemonChiffon = SFColor(hex: 0xFFFACD)! /// SwifterSwift: hex #ADD8E6 - public static let lightBlue = Color(hex: 0xADD8E6)! + public static let lightBlue = SFColor(hex: 0xADD8E6)! /// SwifterSwift: hex #F08080 - public static let lightCoral = Color(hex: 0xF08080)! + public static let lightCoral = SFColor(hex: 0xF08080)! /// SwifterSwift: hex #E0FFFF - public static let lightCyan = Color(hex: 0xE0FFFF)! + public static let lightCyan = SFColor(hex: 0xE0FFFF)! /// SwifterSwift: hex #FAFAD2 - public static let lightGoldenRodYellow = Color(hex: 0xFAFAD2)! + public static let lightGoldenRodYellow = SFColor(hex: 0xFAFAD2)! /// SwifterSwift: hex #D3D3D3 - public static let lightGray = Color(hex: 0xD3D3D3)! + public static let lightGray = SFColor(hex: 0xD3D3D3)! /// SwifterSwift: hex #D3D3D3 - public static let lightGrey = Color(hex: 0xD3D3D3)! + public static let lightGrey = SFColor(hex: 0xD3D3D3)! /// SwifterSwift: hex #90EE90 - public static let lightGreen = Color(hex: 0x90EE90)! + public static let lightGreen = SFColor(hex: 0x90EE90)! /// SwifterSwift: hex #FFB6C1 - public static let lightPink = Color(hex: 0xFFB6C1)! + public static let lightPink = SFColor(hex: 0xFFB6C1)! /// SwifterSwift: hex #FFA07A - public static let lightSalmon = Color(hex: 0xFFA07A)! + public static let lightSalmon = SFColor(hex: 0xFFA07A)! /// SwifterSwift: hex #20B2AA - public static let lightSeaGreen = Color(hex: 0x20B2AA)! + public static let lightSeaGreen = SFColor(hex: 0x20B2AA)! /// SwifterSwift: hex #87CEFA - public static let lightSkyBlue = Color(hex: 0x87CEFA)! + public static let lightSkyBlue = SFColor(hex: 0x87CEFA)! /// SwifterSwift: hex #778899 - public static let lightSlateGray = Color(hex: 0x778899)! + public static let lightSlateGray = SFColor(hex: 0x778899)! /// SwifterSwift: hex #778899 - public static let lightSlateGrey = Color(hex: 0x778899)! + public static let lightSlateGrey = SFColor(hex: 0x778899)! /// SwifterSwift: hex #B0C4DE - public static let lightSteelBlue = Color(hex: 0xB0C4DE)! + public static let lightSteelBlue = SFColor(hex: 0xB0C4DE)! /// SwifterSwift: hex #FFFFE0 - public static let lightYellow = Color(hex: 0xFFFFE0)! + public static let lightYellow = SFColor(hex: 0xFFFFE0)! /// SwifterSwift: hex #00FF00 - public static let lime = Color(hex: 0x00FF00)! + public static let lime = SFColor(hex: 0x00FF00)! /// SwifterSwift: hex #32CD32 - public static let limeGreen = Color(hex: 0x32CD32)! + public static let limeGreen = SFColor(hex: 0x32CD32)! /// SwifterSwift: hex #FAF0E6 - public static let linen = Color(hex: 0xFAF0E6)! + public static let linen = SFColor(hex: 0xFAF0E6)! /// SwifterSwift: hex #FF00FF - public static let magenta = Color(hex: 0xFF00FF)! + public static let magenta = SFColor(hex: 0xFF00FF)! /// SwifterSwift: hex #800000 - public static let maroon = Color(hex: 0x800000)! + public static let maroon = SFColor(hex: 0x800000)! /// SwifterSwift: hex #66CDAA - public static let mediumAquaMarine = Color(hex: 0x66CDAA)! + public static let mediumAquaMarine = SFColor(hex: 0x66CDAA)! /// SwifterSwift: hex #0000CD - public static let mediumBlue = Color(hex: 0x0000CD)! + public static let mediumBlue = SFColor(hex: 0x0000CD)! /// SwifterSwift: hex #BA55D3 - public static let mediumOrchid = Color(hex: 0xBA55D3)! + public static let mediumOrchid = SFColor(hex: 0xBA55D3)! /// SwifterSwift: hex #9370DB - public static let mediumPurple = Color(hex: 0x9370DB)! + public static let mediumPurple = SFColor(hex: 0x9370DB)! /// SwifterSwift: hex #3CB371 - public static let mediumSeaGreen = Color(hex: 0x3CB371)! + public static let mediumSeaGreen = SFColor(hex: 0x3CB371)! /// SwifterSwift: hex #7B68EE - public static let mediumSlateBlue = Color(hex: 0x7B68EE)! + public static let mediumSlateBlue = SFColor(hex: 0x7B68EE)! /// SwifterSwift: hex #00FA9A - public static let mediumSpringGreen = Color(hex: 0x00FA9A)! + public static let mediumSpringGreen = SFColor(hex: 0x00FA9A)! /// SwifterSwift: hex #48D1CC - public static let mediumTurquoise = Color(hex: 0x48D1CC)! + public static let mediumTurquoise = SFColor(hex: 0x48D1CC)! /// SwifterSwift: hex #C71585 - public static let mediumVioletRed = Color(hex: 0xC71585)! + public static let mediumVioletRed = SFColor(hex: 0xC71585)! /// SwifterSwift: hex #191970 - public static let midnightBlue = Color(hex: 0x191970)! + public static let midnightBlue = SFColor(hex: 0x191970)! /// SwifterSwift: hex #F5FFFA - public static let mintCream = Color(hex: 0xF5FFFA)! + public static let mintCream = SFColor(hex: 0xF5FFFA)! /// SwifterSwift: hex #FFE4E1 - public static let mistyRose = Color(hex: 0xFFE4E1)! + public static let mistyRose = SFColor(hex: 0xFFE4E1)! /// SwifterSwift: hex #FFE4B5 - public static let moccasin = Color(hex: 0xFFE4B5)! + public static let moccasin = SFColor(hex: 0xFFE4B5)! /// SwifterSwift: hex #FFDEAD - public static let navajoWhite = Color(hex: 0xFFDEAD)! + public static let navajoWhite = SFColor(hex: 0xFFDEAD)! /// SwifterSwift: hex #000080 - public static let navy = Color(hex: 0x000080)! + public static let navy = SFColor(hex: 0x000080)! /// SwifterSwift: hex #FDF5E6 - public static let oldLace = Color(hex: 0xFDF5E6)! + public static let oldLace = SFColor(hex: 0xFDF5E6)! /// SwifterSwift: hex #808000 - public static let olive = Color(hex: 0x808000)! + public static let olive = SFColor(hex: 0x808000)! /// SwifterSwift: hex #6B8E23 - public static let oliveDrab = Color(hex: 0x6B8E23)! + public static let oliveDrab = SFColor(hex: 0x6B8E23)! /// SwifterSwift: hex #FFA500 - public static let orange = Color(hex: 0xFFA500)! + public static let orange = SFColor(hex: 0xFFA500)! /// SwifterSwift: hex #FF4500 - public static let orangeRed = Color(hex: 0xFF4500)! + public static let orangeRed = SFColor(hex: 0xFF4500)! /// SwifterSwift: hex #DA70D6 - public static let orchid = Color(hex: 0xDA70D6)! + public static let orchid = SFColor(hex: 0xDA70D6)! /// SwifterSwift: hex #EEE8AA - public static let paleGoldenRod = Color(hex: 0xEEE8AA)! + public static let paleGoldenRod = SFColor(hex: 0xEEE8AA)! /// SwifterSwift: hex #98FB98 - public static let paleGreen = Color(hex: 0x98FB98)! + public static let paleGreen = SFColor(hex: 0x98FB98)! /// SwifterSwift: hex #AFEEEE - public static let paleTurquoise = Color(hex: 0xAFEEEE)! + public static let paleTurquoise = SFColor(hex: 0xAFEEEE)! /// SwifterSwift: hex #DB7093 - public static let paleVioletRed = Color(hex: 0xDB7093)! + public static let paleVioletRed = SFColor(hex: 0xDB7093)! /// SwifterSwift: hex #FFEFD5 - public static let papayaWhip = Color(hex: 0xFFEFD5)! + public static let papayaWhip = SFColor(hex: 0xFFEFD5)! /// SwifterSwift: hex #FFDAB9 - public static let peachPuff = Color(hex: 0xFFDAB9)! + public static let peachPuff = SFColor(hex: 0xFFDAB9)! /// SwifterSwift: hex #CD853F - public static let peru = Color(hex: 0xCD853F)! + public static let peru = SFColor(hex: 0xCD853F)! /// SwifterSwift: hex #FFC0CB - public static let pink = Color(hex: 0xFFC0CB)! + public static let pink = SFColor(hex: 0xFFC0CB)! /// SwifterSwift: hex #DDA0DD - public static let plum = Color(hex: 0xDDA0DD)! + public static let plum = SFColor(hex: 0xDDA0DD)! /// SwifterSwift: hex #B0E0E6 - public static let powderBlue = Color(hex: 0xB0E0E6)! + public static let powderBlue = SFColor(hex: 0xB0E0E6)! /// SwifterSwift: hex #800080 - public static let purple = Color(hex: 0x800080)! + public static let purple = SFColor(hex: 0x800080)! /// SwifterSwift: hex #663399 - public static let rebeccaPurple = Color(hex: 0x663399)! + public static let rebeccaPurple = SFColor(hex: 0x663399)! /// SwifterSwift: hex #FF0000 - public static let red = Color(hex: 0xFF0000)! + public static let red = SFColor(hex: 0xFF0000)! /// SwifterSwift: hex #BC8F8F - public static let rosyBrown = Color(hex: 0xBC8F8F)! + public static let rosyBrown = SFColor(hex: 0xBC8F8F)! /// SwifterSwift: hex #4169E1 - public static let royalBlue = Color(hex: 0x4169E1)! + public static let royalBlue = SFColor(hex: 0x4169E1)! /// SwifterSwift: hex #8B4513 - public static let saddleBrown = Color(hex: 0x8B4513)! + public static let saddleBrown = SFColor(hex: 0x8B4513)! /// SwifterSwift: hex #FA8072 - public static let salmon = Color(hex: 0xFA8072)! + public static let salmon = SFColor(hex: 0xFA8072)! /// SwifterSwift: hex #F4A460 - public static let sandyBrown = Color(hex: 0xF4A460)! + public static let sandyBrown = SFColor(hex: 0xF4A460)! /// SwifterSwift: hex #2E8B57 - public static let seaGreen = Color(hex: 0x2E8B57)! + public static let seaGreen = SFColor(hex: 0x2E8B57)! /// SwifterSwift: hex #FFF5EE - public static let seaShell = Color(hex: 0xFFF5EE)! + public static let seaShell = SFColor(hex: 0xFFF5EE)! /// SwifterSwift: hex #A0522D - public static let sienna = Color(hex: 0xA0522D)! + public static let sienna = SFColor(hex: 0xA0522D)! /// SwifterSwift: hex #C0C0C0 - public static let silver = Color(hex: 0xC0C0C0)! + public static let silver = SFColor(hex: 0xC0C0C0)! /// SwifterSwift: hex #87CEEB - public static let skyBlue = Color(hex: 0x87CEEB)! + public static let skyBlue = SFColor(hex: 0x87CEEB)! /// SwifterSwift: hex #6A5ACD - public static let slateBlue = Color(hex: 0x6A5ACD)! + public static let slateBlue = SFColor(hex: 0x6A5ACD)! /// SwifterSwift: hex #708090 - public static let slateGray = Color(hex: 0x708090)! + public static let slateGray = SFColor(hex: 0x708090)! /// SwifterSwift: hex #708090 - public static let slateGrey = Color(hex: 0x708090)! + public static let slateGrey = SFColor(hex: 0x708090)! /// SwifterSwift: hex #FFFAFA - public static let snow = Color(hex: 0xFFFAFA)! + public static let snow = SFColor(hex: 0xFFFAFA)! /// SwifterSwift: hex #00FF7F - public static let springGreen = Color(hex: 0x00FF7F)! + public static let springGreen = SFColor(hex: 0x00FF7F)! /// SwifterSwift: hex #4682B4 - public static let steelBlue = Color(hex: 0x4682B4)! + public static let steelBlue = SFColor(hex: 0x4682B4)! /// SwifterSwift: hex #D2B48C - public static let tan = Color(hex: 0xD2B48C)! + public static let tan = SFColor(hex: 0xD2B48C)! /// SwifterSwift: hex #008080 - public static let teal = Color(hex: 0x008080)! + public static let teal = SFColor(hex: 0x008080)! /// SwifterSwift: hex #D8BFD8 - public static let thistle = Color(hex: 0xD8BFD8)! + public static let thistle = SFColor(hex: 0xD8BFD8)! /// SwifterSwift: hex #FF6347 - public static let tomato = Color(hex: 0xFF6347)! + public static let tomato = SFColor(hex: 0xFF6347)! /// SwifterSwift: hex #40E0D0 - public static let turquoise = Color(hex: 0x40E0D0)! + public static let turquoise = SFColor(hex: 0x40E0D0)! /// SwifterSwift: hex #EE82EE - public static let violet = Color(hex: 0xEE82EE)! + public static let violet = SFColor(hex: 0xEE82EE)! /// SwifterSwift: hex #F5DEB3 - public static let wheat = Color(hex: 0xF5DEB3)! + public static let wheat = SFColor(hex: 0xF5DEB3)! /// SwifterSwift: hex #FFFFFF - public static let white = Color(hex: 0xFFFFFF)! + public static let white = SFColor(hex: 0xFFFFFF)! /// SwifterSwift: hex #F5F5F5 - public static let whiteSmoke = Color(hex: 0xF5F5F5)! + public static let whiteSmoke = SFColor(hex: 0xF5F5F5)! /// SwifterSwift: hex #FFFF00 - public static let yellow = Color(hex: 0xFFFF00)! + public static let yellow = SFColor(hex: 0xFFFF00)! /// SwifterSwift: hex #9ACD32 - public static let yellowGreen = Color(hex: 0x9ACD32)! + public static let yellowGreen = SFColor(hex: 0x9ACD32)! } } // MARK: - Flat UI colors -public extension Color { +public extension SFColor { /// SwifterSwift: Flat UI colors enum FlatUI { // http://flatuicolors.com. /// SwifterSwift: hex #1ABC9C - public static let turquoise = Color(hex: 0x1ABC9C)! + public static let turquoise = SFColor(hex: 0x1ABC9C)! /// SwifterSwift: hex #16A085 - public static let greenSea = Color(hex: 0x16A085)! + public static let greenSea = SFColor(hex: 0x16A085)! /// SwifterSwift: hex #2ECC71 - public static let emerald = Color(hex: 0x2ECC71)! + public static let emerald = SFColor(hex: 0x2ECC71)! /// SwifterSwift: hex #27AE60 - public static let nephritis = Color(hex: 0x27AE60)! + public static let nephritis = SFColor(hex: 0x27AE60)! /// SwifterSwift: hex #3498DB - public static let peterRiver = Color(hex: 0x3498DB)! + public static let peterRiver = SFColor(hex: 0x3498DB)! /// SwifterSwift: hex #2980B9 - public static let belizeHole = Color(hex: 0x2980B9)! + public static let belizeHole = SFColor(hex: 0x2980B9)! /// SwifterSwift: hex #9B59B6 - public static let amethyst = Color(hex: 0x9B59B6)! + public static let amethyst = SFColor(hex: 0x9B59B6)! /// SwifterSwift: hex #8E44AD - public static let wisteria = Color(hex: 0x8E44AD)! + public static let wisteria = SFColor(hex: 0x8E44AD)! /// SwifterSwift: hex #34495E - public static let wetAsphalt = Color(hex: 0x34495E)! + public static let wetAsphalt = SFColor(hex: 0x34495E)! /// SwifterSwift: hex #2C3E50 - public static let midnightBlue = Color(hex: 0x2C3E50)! + public static let midnightBlue = SFColor(hex: 0x2C3E50)! /// SwifterSwift: hex #F1C40F - public static let sunFlower = Color(hex: 0xF1C40F)! + public static let sunFlower = SFColor(hex: 0xF1C40F)! /// SwifterSwift: hex #F39C12 - public static let flatOrange = Color(hex: 0xF39C12)! + public static let flatOrange = SFColor(hex: 0xF39C12)! /// SwifterSwift: hex #E67E22 - public static let carrot = Color(hex: 0xE67E22)! + public static let carrot = SFColor(hex: 0xE67E22)! /// SwifterSwift: hex #D35400 - public static let pumkin = Color(hex: 0xD35400)! + public static let pumpkin = SFColor(hex: 0xD35400)! /// SwifterSwift: hex #E74C3C - public static let alizarin = Color(hex: 0xE74C3C)! + public static let alizarin = SFColor(hex: 0xE74C3C)! /// SwifterSwift: hex #C0392B - public static let pomegranate = Color(hex: 0xC0392B)! + public static let pomegranate = SFColor(hex: 0xC0392B)! /// SwifterSwift: hex #ECF0F1 - public static let clouds = Color(hex: 0xECF0F1)! + public static let clouds = SFColor(hex: 0xECF0F1)! /// SwifterSwift: hex #BDC3C7 - public static let silver = Color(hex: 0xBDC3C7)! + public static let silver = SFColor(hex: 0xBDC3C7)! /// SwifterSwift: hex #7F8C8D - public static let asbestos = Color(hex: 0x7F8C8D)! + public static let asbestos = SFColor(hex: 0x7F8C8D)! /// SwifterSwift: hex #95A5A6 - public static let concerte = Color(hex: 0x95A5A6)! + public static let concrete = SFColor(hex: 0x95A5A6)! } // swiftlint:enable type_body_length diff --git a/Sources/SwifterSwift/Shared/EdgeInsetsExtensions.swift b/Sources/SwifterSwift/Shared/EdgeInsetsExtensions.swift index 82b6133fe..706c9b6e4 100644 --- a/Sources/SwifterSwift/Shared/EdgeInsetsExtensions.swift +++ b/Sources/SwifterSwift/Shared/EdgeInsetsExtensions.swift @@ -1,13 +1,13 @@ -// EdgeInsetsExtensions.swift - Copyright 2020 SwifterSwift +// EdgeInsetsExtensions.swift - Copyright 2023 SwifterSwift -#if os(iOS) || os(tvOS) || os(watchOS) +#if os(visionOS) || os(iOS) || os(tvOS) || os(watchOS) import UIKit /// SwifterSwift: EdgeInsets -public typealias EdgeInsets = UIEdgeInsets +public typealias SFEdgeInsets = UIEdgeInsets #elseif os(macOS) import Foundation /// SwifterSwift: EdgeInsets -public typealias EdgeInsets = NSEdgeInsets +public typealias SFEdgeInsets = NSEdgeInsets public extension NSEdgeInsets { /// SwifterSwift: An edge insets struct whose top, left, bottom, and right fields are all set to 0. @@ -39,7 +39,7 @@ extension NSEdgeInsets: Equatable { // MARK: - Properties -public extension EdgeInsets { +public extension SFEdgeInsets { /// SwifterSwift: Return the vertical insets. The vertical insets is composed by top + bottom. /// var vertical: CGFloat { @@ -57,8 +57,8 @@ public extension EdgeInsets { // MARK: - Methods -public extension EdgeInsets { - /// SwifterSwift: Creates an `EdgeInsets` with the inset value applied to all (top, bottom, right, left) +public extension SFEdgeInsets { + /// SwifterSwift: Creates an `EdgeInsets` with the inset value applied to all (top, bottom, right, left). /// /// - Parameter inset: Inset to be applied in all the edges. init(inset: CGFloat) { @@ -80,8 +80,8 @@ public extension EdgeInsets { /// - Parameters: /// - top: Offset to be applied in to the top edge. /// - Returns: EdgeInsets offset with given offset. - func insetBy(top: CGFloat) -> EdgeInsets { - return EdgeInsets(top: self.top + top, left: left, bottom: bottom, right: right) + func insetBy(top: CGFloat) -> SFEdgeInsets { + return SFEdgeInsets(top: self.top + top, left: left, bottom: bottom, right: right) } /// SwifterSwift: Creates an `EdgeInsets` based on current value and left offset. @@ -89,8 +89,8 @@ public extension EdgeInsets { /// - Parameters: /// - left: Offset to be applied in to the left edge. /// - Returns: EdgeInsets offset with given offset. - func insetBy(left: CGFloat) -> EdgeInsets { - return EdgeInsets(top: top, left: self.left + left, bottom: bottom, right: right) + func insetBy(left: CGFloat) -> SFEdgeInsets { + return SFEdgeInsets(top: top, left: self.left + left, bottom: bottom, right: right) } /// SwifterSwift: Creates an `EdgeInsets` based on current value and bottom offset. @@ -98,8 +98,8 @@ public extension EdgeInsets { /// - Parameters: /// - bottom: Offset to be applied in to the bottom edge. /// - Returns: EdgeInsets offset with given offset. - func insetBy(bottom: CGFloat) -> EdgeInsets { - return EdgeInsets(top: top, left: left, bottom: self.bottom + bottom, right: right) + func insetBy(bottom: CGFloat) -> SFEdgeInsets { + return SFEdgeInsets(top: top, left: left, bottom: self.bottom + bottom, right: right) } /// SwifterSwift: Creates an `EdgeInsets` based on current value and right offset. @@ -107,43 +107,45 @@ public extension EdgeInsets { /// - Parameters: /// - right: Offset to be applied in to the right edge. /// - Returns: EdgeInsets offset with given offset. - func insetBy(right: CGFloat) -> EdgeInsets { - return EdgeInsets(top: top, left: left, bottom: bottom, right: self.right + right) + func insetBy(right: CGFloat) -> SFEdgeInsets { + return SFEdgeInsets(top: top, left: left, bottom: bottom, right: self.right + right) } - /// SwifterSwift: Creates an `EdgeInsets` based on current value and horizontal value equally divided and applied to right offset and left offset. + /// SwifterSwift: Creates an `EdgeInsets` based on current value and horizontal value equally divided and applied to + /// right offset and left offset. /// /// - Parameters: /// - horizontal: Offset to be applied to right and left. /// - Returns: EdgeInsets offset with given offset. - func insetBy(horizontal: CGFloat) -> EdgeInsets { - return EdgeInsets(top: top, left: left + horizontal / 2, bottom: bottom, right: right + horizontal / 2) + func insetBy(horizontal: CGFloat) -> SFEdgeInsets { + return SFEdgeInsets(top: top, left: left + horizontal / 2, bottom: bottom, right: right + horizontal / 2) } - /// SwifterSwift: Creates an `EdgeInsets` based on current value and vertical value equally divided and applied to top and bottom. + /// SwifterSwift: Creates an `EdgeInsets` based on current value and vertical value equally divided and applied to + /// top and bottom. /// /// - Parameters: /// - vertical: Offset to be applied to top and bottom. /// - Returns: EdgeInsets offset with given offset. - func insetBy(vertical: CGFloat) -> EdgeInsets { - return EdgeInsets(top: top + vertical / 2, left: left, bottom: bottom + vertical / 2, right: right) + func insetBy(vertical: CGFloat) -> SFEdgeInsets { + return SFEdgeInsets(top: top + vertical / 2, left: left, bottom: bottom + vertical / 2, right: right) } } // MARK: - Operators -public extension EdgeInsets { +public extension SFEdgeInsets { /// SwifterSwift: Add all the properties of two `EdgeInsets` to create their addition. /// /// - Parameters: /// - lhs: The left-hand expression /// - rhs: The right-hand expression /// - Returns: A new `EdgeInsets` instance where the values of `lhs` and `rhs` are added together. - static func + (_ lhs: EdgeInsets, _ rhs: EdgeInsets) -> EdgeInsets { - return EdgeInsets(top: lhs.top + rhs.top, - left: lhs.left + rhs.left, - bottom: lhs.bottom + rhs.bottom, - right: lhs.right + rhs.right) + static func + (_ lhs: SFEdgeInsets, _ rhs: SFEdgeInsets) -> SFEdgeInsets { + return SFEdgeInsets(top: lhs.top + rhs.top, + left: lhs.left + rhs.left, + bottom: lhs.bottom + rhs.bottom, + right: lhs.right + rhs.right) } /// SwifterSwift: Add all the properties of two `EdgeInsets` to the left-hand instance. @@ -151,7 +153,7 @@ public extension EdgeInsets { /// - Parameters: /// - lhs: The left-hand expression to be mutated /// - rhs: The right-hand expression - static func += (_ lhs: inout EdgeInsets, _ rhs: EdgeInsets) { + static func += (_ lhs: inout SFEdgeInsets, _ rhs: SFEdgeInsets) { lhs.top += rhs.top lhs.left += rhs.left lhs.bottom += rhs.bottom diff --git a/Sources/SwifterSwift/Shared/FontExtensions.swift b/Sources/SwifterSwift/Shared/FontExtensions.swift index 126beaaa5..9e2789345 100644 --- a/Sources/SwifterSwift/Shared/FontExtensions.swift +++ b/Sources/SwifterSwift/Shared/FontExtensions.swift @@ -1,13 +1,13 @@ -// FontExtensions.swift - Copyright 2020 SwifterSwift +// FontExtensions.swift - Copyright 2023 SwifterSwift #if canImport(UIKit) import UIKit /// SwifterSwift: Font -public typealias Font = UIFont +public typealias SFFont = UIFont #endif #if canImport(AppKit) && !targetEnvironment(macCatalyst) import AppKit /// SwifterSwift: Font -public typealias Font = NSFont +public typealias SFFont = NSFont #endif diff --git a/Sources/SwifterSwift/SpriteKit/SKNodeExtensions.swift b/Sources/SwifterSwift/SpriteKit/SKNodeExtensions.swift index 019a1687a..40a11306d 100644 --- a/Sources/SwifterSwift/SpriteKit/SKNodeExtensions.swift +++ b/Sources/SwifterSwift/SpriteKit/SKNodeExtensions.swift @@ -1,4 +1,4 @@ -// SKNodeExtensions.swift - Copyright 2020 SwifterSwift +// SKNodeExtensions.swift - Copyright 2023 SwifterSwift #if canImport(SpriteKit) import SpriteKit @@ -6,7 +6,7 @@ import SpriteKit // MARK: - Methods public extension SKNode { - /// SwifterSwift: Return an array of all SKNode descendants + /// SwifterSwift: Return an array of all SKNode descendants. /// /// mySKNode.descendants() -> [childNodeOne, childNodeTwo] /// diff --git a/Sources/SwifterSwift/SpriteKit/SKSpriteNodeExtensions.swift b/Sources/SwifterSwift/SpriteKit/SKSpriteNodeExtensions.swift new file mode 100644 index 000000000..7726e2d3a --- /dev/null +++ b/Sources/SwifterSwift/SpriteKit/SKSpriteNodeExtensions.swift @@ -0,0 +1,32 @@ +// SKSpriteNodeExtensions.swift - Copyright 2023 SwifterSwift + +#if canImport(SpriteKit) +import SpriteKit + +// MARK: - Methods + +public extension SKSpriteNode { + /// SwifterSwift: SKSpriteNode sized with respect to aspect ratio. + /// + /// node.aspectFill(to: CGSize(width: 300, height: 300) + /// + /// - Parameter fillSize: fill size to use for aspect ratio calculation. + func aspectFill(to fillSize: CGSize) { + if let textureSize = texture?.size() { + let width = textureSize.width + let height = textureSize.height + + // Avoid division by 0. + guard width > 0, height > 0 else { + return + } + + let horizontalRatio = fillSize.width / width + let verticalRatio = fillSize.height / height + let ratio = horizontalRatio < verticalRatio ? horizontalRatio : verticalRatio + size = CGSize(width: width * ratio, height: height * ratio) + } + } +} + +#endif diff --git a/Sources/SwifterSwift/StoreKit/SKProductExtensions.swift b/Sources/SwifterSwift/StoreKit/SKProductExtensions.swift index 0d2b30bbd..735f849cf 100644 --- a/Sources/SwifterSwift/StoreKit/SKProductExtensions.swift +++ b/Sources/SwifterSwift/StoreKit/SKProductExtensions.swift @@ -1,4 +1,4 @@ -// SKProductExtensions.swift - Copyright 2020 SwifterSwift +// SKProductExtensions.swift - Copyright 2023 SwifterSwift #if canImport(StoreKit) import StoreKit @@ -11,7 +11,7 @@ public extension SKProduct { return priceFormatter }() - /// SwifterSwift: Localized price of SKProduct + /// SwifterSwift: Localized price of SKProduct. var localizedPrice: String? { let formatter = SKProduct.priceFormatter formatter.locale = priceLocale diff --git a/Sources/SwifterSwift/SwiftStdlib/ArrayExtensions.swift b/Sources/SwifterSwift/SwiftStdlib/ArrayExtensions.swift index 51900d4df..3474e9337 100755 --- a/Sources/SwifterSwift/SwiftStdlib/ArrayExtensions.swift +++ b/Sources/SwifterSwift/SwiftStdlib/ArrayExtensions.swift @@ -1,4 +1,23 @@ -// ArrayExtensions.swift - Copyright 2020 SwifterSwift +// ArrayExtensions.swift - Copyright 2023 SwifterSwift + +// MARK: - Initializers + +public extension Array { + /// SwifterSwift: Creates an array with specified number of elements, for each element it calls specified closure. + /// - Parameters: + /// - count: The number of elements in the new array. + /// - element: A closure that initializes each element. + /// - Parameter *index*: An index of initialized element in the array. + /// - Returns: element of the array. + init(count: Int, element: (Int) throws -> Element) rethrows { + try self.init(unsafeUninitializedCapacity: count) { buffer, initializedCount in + for index in 0.. [MyStruct(x: 1), MyStruct(x: 2), MyStruct(x: 3)] /// /// - Parameters: /// - otherArray: array containing elements in the desired order. - /// - keyPath: keyPath indiciating the property that the array should be sorted by + /// - keyPath: keyPath indicating the property that the array should be sorted by /// - Returns: sorted array. func sorted(like otherArray: [T], keyPath: KeyPath) -> [Element] { let dict = otherArray.enumerated().reduce(into: [:]) { $0[$1.element] = $1.offset } diff --git a/Sources/SwifterSwift/SwiftStdlib/BidirectionalCollectionExtensions.swift b/Sources/SwifterSwift/SwiftStdlib/BidirectionalCollectionExtensions.swift index 94acedf24..e32e6ae5a 100644 --- a/Sources/SwifterSwift/SwiftStdlib/BidirectionalCollectionExtensions.swift +++ b/Sources/SwifterSwift/SwiftStdlib/BidirectionalCollectionExtensions.swift @@ -1,9 +1,10 @@ -// BidirectionalCollectionExtensions.swift - Copyright 2020 SwifterSwift +// BidirectionalCollectionExtensions.swift - Copyright 2023 SwifterSwift // MARK: - Methods public extension BidirectionalCollection { - /// SwifterSwift: Returns the element at the specified position. If offset is negative, the `n`th element from the end will be returned where `n` is the result of `abs(distance)`. + /// SwifterSwift: Returns the element at the specified position. If offset is negative, the `n`th element from the + /// end will be returned where `n` is the result of `abs(distance)`. /// /// let arr = [1, 2, 3, 4, 5] /// arr[offset: 1] -> 2 @@ -15,12 +16,14 @@ public extension BidirectionalCollection { return self[indices.index(index, offsetBy: distance)] } - /// SwifterSwift: Returns the last element of the sequence with having property by given key path equals to given `value`. + /// SwifterSwift: Returns the last element of the sequence with having property by given key path equals to given + /// `value`. /// /// - Parameters: /// - keyPath: The `KeyPath` of property for `Element` to compare. /// - value: The value to compare with `Element` property - /// - Returns: The last element of the collection that has property by given key path equals to given `value` or `nil` if there is no such element. + /// - Returns: The last element of the collection that has property by given key path equals to given `value` or + /// `nil` if there is no such element. func last(where keyPath: KeyPath, equals value: T) -> Element? { return last { $0[keyPath: keyPath] == value } } diff --git a/Sources/SwifterSwift/SwiftStdlib/BinaryFloatingPointExtensions.swift b/Sources/SwifterSwift/SwiftStdlib/BinaryFloatingPointExtensions.swift index 26d002c5c..2f2ad0bc8 100644 --- a/Sources/SwifterSwift/SwiftStdlib/BinaryFloatingPointExtensions.swift +++ b/Sources/SwifterSwift/SwiftStdlib/BinaryFloatingPointExtensions.swift @@ -1,4 +1,4 @@ -// BinaryFloatingPointExtensions.swift - Copyright 2020 SwifterSwift +// BinaryFloatingPointExtensions.swift - Copyright 2023 SwifterSwift #if canImport(Foundation) import Foundation @@ -7,7 +7,8 @@ import Foundation public extension BinaryFloatingPoint { #if canImport(Foundation) - /// SwifterSwift: Returns a rounded value with the specified number of decimal places and rounding rule. If `numberOfDecimalPlaces` is negative, `0` will be used. + /// SwifterSwift: Returns a rounded value with the specified number of decimal places and rounding rule. If + /// `numberOfDecimalPlaces` is negative, `0` will be used. /// /// let num = 3.1415927 /// num.rounded(numberOfDecimalPlaces: 3, rule: .up) -> 3.142 diff --git a/Sources/SwifterSwift/SwiftStdlib/BinaryIntegerExtensions.swift b/Sources/SwifterSwift/SwiftStdlib/BinaryIntegerExtensions.swift new file mode 100644 index 000000000..522a52606 --- /dev/null +++ b/Sources/SwifterSwift/SwiftStdlib/BinaryIntegerExtensions.swift @@ -0,0 +1,45 @@ +// BinaryIntegerExtensions.swift - Copyright 2023 SwifterSwift + +// MARK: - Properties + +public extension BinaryInteger { + /// SwifterSwift: The raw bytes of the integer. + /// + /// var number = Int16(-128) + /// print(number.bytes) + /// // prints "[255, 128]" + /// + var bytes: [UInt8] { + var result = [UInt8]() + result.reserveCapacity(MemoryLayout.size) + var value = self + for _ in 0...size { + result.append(UInt8(truncatingIfNeeded: value)) + value >>= 8 + } + return result.reversed() + } +} + +// MARK: - Initializers + +public extension BinaryInteger { + /// SwifterSwift: Creates a `BinaryInteger` from a raw byte representation. + /// + /// var number = Int16(bytes: [0xFF, 0b1111_1101]) + /// print(number!) + /// // prints "-3" + /// + /// - Parameter bytes: An array of bytes representing the value of the integer. + init?(bytes: [UInt8]) { + // https://stackoverflow.com/a/43518567/9506784 + precondition(bytes.count <= MemoryLayout.size, + "Integer with a \(bytes.count) byte binary representation of '\(bytes.map { String($0, radix: 2) }.joined(separator: " "))' overflows when stored into a \(MemoryLayout.size) byte '\(Self.self)'") + var value: Self = 0 + for byte in bytes { + value <<= 8 + value |= Self(byte) + } + self.init(exactly: value) + } +} diff --git a/Sources/SwifterSwift/SwiftStdlib/BoolExtensions.swift b/Sources/SwifterSwift/SwiftStdlib/BoolExtensions.swift index 1c31bd3da..326ca9340 100644 --- a/Sources/SwifterSwift/SwiftStdlib/BoolExtensions.swift +++ b/Sources/SwifterSwift/SwiftStdlib/BoolExtensions.swift @@ -1,4 +1,4 @@ -// BoolExtensions.swift - Copyright 2020 SwifterSwift +// BoolExtensions.swift - Copyright 2023 SwifterSwift // MARK: - Properties diff --git a/Sources/SwifterSwift/SwiftStdlib/CharacterExtensions.swift b/Sources/SwifterSwift/SwiftStdlib/CharacterExtensions.swift index 0753cd8cb..f66272958 100755 --- a/Sources/SwifterSwift/SwiftStdlib/CharacterExtensions.swift +++ b/Sources/SwifterSwift/SwiftStdlib/CharacterExtensions.swift @@ -1,4 +1,4 @@ -// CharacterExtensions.swift - Copyright 2020 SwifterSwift +// CharacterExtensions.swift - Copyright 2023 SwifterSwift // MARK: - Properties diff --git a/Sources/SwifterSwift/SwiftStdlib/CollectionExtensions.swift b/Sources/SwifterSwift/SwiftStdlib/CollectionExtensions.swift index e09eee22b..1da1fb14a 100644 --- a/Sources/SwifterSwift/SwiftStdlib/CollectionExtensions.swift +++ b/Sources/SwifterSwift/SwiftStdlib/CollectionExtensions.swift @@ -1,4 +1,4 @@ -// CollectionExtensions.swift - Copyright 2020 SwifterSwift +// CollectionExtensions.swift - Copyright 2023 SwifterSwift #if canImport(Dispatch) import Dispatch @@ -40,7 +40,8 @@ public extension Collection { return indices.contains(index) ? self[index] : nil } - /// SwifterSwift: Returns an array of slices of length "size" from the array. If array can't be split evenly, the final slice will be the remaining elements. + /// SwifterSwift: Returns an array of slices of length "size" from the array. If array can't be split evenly, the + /// final slice will be the remaining elements. /// /// [0, 2, 4, 7].group(by: 2) -> [[0, 2], [4, 7]] /// [0, 2, 4, 7, 6].group(by: 2) -> [[0, 2], [4, 7], [6]] @@ -65,7 +66,7 @@ public extension Collection { /// [1, 7, 1, 2, 4, 1, 8].indices(where: { $0 == 1 }) -> [0, 2, 5] /// /// - Parameter condition: condition to evaluate each element against. - /// - Returns: all indices where the specified condition evaluates to true. (optional) + /// - Returns: all indices where the specified condition evaluates to true (optional). func indices(where condition: (Element) throws -> Bool) rethrows -> [Index]? { let indices = try self.indices.filter { try condition(self[$0]) } return indices.isEmpty ? nil : indices @@ -82,11 +83,44 @@ public extension Collection { func forEach(slice: Int, body: ([Element]) throws -> Void) rethrows { var start = startIndex while case let end = index(start, offsetBy: slice, limitedBy: endIndex) ?? endIndex, - start != end { + start != end { try body(Array(self[start.. AnySequence<(Element, Element)> { + guard var index1 = index(startIndex, offsetBy: 0, limitedBy: endIndex), + var index2 = index(index1, offsetBy: 1, limitedBy: endIndex) else { + return AnySequence { + EmptyCollection.Iterator() + } + } + return AnySequence { + AnyIterator { + if index1 >= endIndex || index2 >= endIndex { + return nil + } + defer { + index2 = self.index(after: index2) + if index2 >= endIndex { + index1 = self.index(after: index1) + index2 = self.index(after: index1) + } + } + return (self[index1], self[index2]) + } + } + } } // MARK: - Methods (Equatable) diff --git a/Sources/SwifterSwift/SwiftStdlib/ComparableExtensions.swift b/Sources/SwifterSwift/SwiftStdlib/ComparableExtensions.swift index feb36bc58..f6e275acf 100644 --- a/Sources/SwifterSwift/SwiftStdlib/ComparableExtensions.swift +++ b/Sources/SwifterSwift/SwiftStdlib/ComparableExtensions.swift @@ -1,4 +1,4 @@ -// ComparableExtensions.swift - Copyright 2020 SwifterSwift +// ComparableExtensions.swift - Copyright 2023 SwifterSwift // MARK: - Methods @@ -11,10 +11,8 @@ public extension Comparable { /// "c".isBetween(a...d) // true /// 0.32.isBetween(0.31...0.33) // true /// - /// - parameter min: Minimum comparable value. - /// - parameter max: Maximum comparable value. - /// - /// - returns: `true` if value is between `min` and `max`, `false` otherwise. + /// - Parameter range: Closed range against which the value is checked to be included. + /// - Returns: `true` if the value is included in the range, `false` otherwise. func isBetween(_ range: ClosedRange) -> Bool { return range ~= self } @@ -26,10 +24,8 @@ public extension Comparable { /// "c".clamped(to: "e"..."g") // "e" /// 0.32.clamped(to: 0.1...0.29) // 0.29 /// - /// - parameter min: Lower bound to limit the value to. - /// - parameter max: Upper bound to limit the value to. - /// - /// - returns: A value limited to the range between `min` and `max`. + /// - Parameter range: Closed range that limits the value. + /// - Returns: A value limited to the range, i.e. between `range.lowerBound` and `range.upperBound`. func clamped(to range: ClosedRange) -> Self { return max(range.lowerBound, min(self, range.upperBound)) } diff --git a/Sources/SwifterSwift/SwiftStdlib/DecodableExtensions.swift b/Sources/SwifterSwift/SwiftStdlib/DecodableExtensions.swift index 004e82c15..bbc44ad87 100644 --- a/Sources/SwifterSwift/SwiftStdlib/DecodableExtensions.swift +++ b/Sources/SwifterSwift/SwiftStdlib/DecodableExtensions.swift @@ -1,4 +1,4 @@ -// DecodableExtensions.swift - Copyright 2020 SwifterSwift +// DecodableExtensions.swift - Copyright 2023 SwifterSwift #if canImport(Foundation) import Foundation @@ -6,10 +6,10 @@ import Foundation public extension Decodable { #if canImport(Foundation) - /// SwifterSwift: Parsing the model in Decodable type + /// SwifterSwift: Parsing the model in Decodable type. /// - Parameters: /// - data: Data. - /// - decoder: JSONDecoder. Initialized by default + /// - decoder: JSONDecoder. Initialized by default. init?(from data: Data, using decoder: JSONDecoder = .init()) { guard let parsed = try? decoder.decode(Self.self, from: data) else { return nil } self = parsed diff --git a/Sources/SwifterSwift/SwiftStdlib/DefaultStringInterpolationExtensions.swift b/Sources/SwifterSwift/SwiftStdlib/DefaultStringInterpolationExtensions.swift new file mode 100644 index 000000000..3821337ad --- /dev/null +++ b/Sources/SwifterSwift/SwiftStdlib/DefaultStringInterpolationExtensions.swift @@ -0,0 +1,41 @@ +// DefaultStringInterpolationExtensions.swift - Copyright 2023 SwifterSwift + +public extension DefaultStringInterpolation { + /// SwifterSwift: Interpolates the given value's textual representation + /// into the string literal being created if the value is not `nil` and the + /// optional predicate returns `true`, otherwise append a placeholder. + /// + /// Do not call this method directly. It is used by the compiler when + /// interpreting string interpolations. Instead, use string + /// interpolation to create a new string by including values, literals, + /// variables, or expressions enclosed in parentheses, prefixed by a + /// backslash (`\(`...`, placeholder: `...`)`). + /// + /// var token: Int? = nil + /// print("\(token, placeholder: "-")") + /// // Prints "-" + /// token = 0 + /// print("\(token, placeholder: "-")") + /// // Prints "0" + /// print("\(token, placeholder: "-", where: { $0 > 0} )") + /// // Prints "-" + /// + /// - Parameters: + /// - value: The values, literals, variables, or expressions to be interpolated. + /// - placeholder: The string that displays when `value` is `nil`. + /// - predicate: A closure that takes unwrapped `value` as its argument and returns `true` if the `value`'s + /// textual representation should be interpolated into the string literal or `false` if the + /// `placeholder` should be interpolated into the string literal. You can use a `nil` value for + /// this parameter. + mutating func appendInterpolation(_ value: T?, + placeholder: @autoclosure () -> String, + where predicate: ((T) throws -> Bool)? = nil) rethrows { + switch value { + case let .some(value) where try predicate?(value) != false: + appendInterpolation(value) + + default: + appendInterpolation(placeholder()) + } + } +} diff --git a/Sources/SwifterSwift/SwiftStdlib/Deprecated/StdlibDeprecated.swift b/Sources/SwifterSwift/SwiftStdlib/Deprecated/StdlibDeprecated.swift deleted file mode 100644 index c52f6fb8f..000000000 --- a/Sources/SwifterSwift/SwiftStdlib/Deprecated/StdlibDeprecated.swift +++ /dev/null @@ -1,116 +0,0 @@ -// StdlibDeprecated.swift - Copyright 2020 SwifterSwift - -private func optionalCompareAscending(path1: T?, path2: T?) -> Bool { - guard let path1 = path1, let path2 = path2 else { return false } - return path1 < path2 -} - -private func optionalCompareDescending(path1: T?, path2: T?) -> Bool { - guard let path1 = path1, let path2 = path2 else { return false } - return path1 > path2 -} - -public extension Array { - /// SwifterSwift: Returns a sorted array based on an optional keypath. - /// - /// - Parameter path: Key path to sort. The key path type must be Comparable. - /// - Parameter ascending: If order must be ascending. - /// - Returns: Sorted array based on keyPath. - @available(*, deprecated, message: "Use sorted(by:with:) instead.") - func sorted(by path: KeyPath, ascending: Bool) -> [Element] { - if ascending { - return sorted(by: path, with: optionalCompareAscending) - } - return sorted(by: path, with: optionalCompareDescending) - } - - /// SwifterSwift: Returns a sorted array based on a keypath. - /// - /// - Parameter path: Key path to sort. The key path type must be Comparable. - /// - Parameter ascending: If order must be ascending. - /// - Returns: Sorted array based on keyPath. - @available(*, deprecated, message: "Use sorted(by:with:) instead.") - func sorted(by path: KeyPath, ascending: Bool) -> [Element] { - if ascending { - return sorted(by: path, with: <) - } - return sorted(by: path, with: >) - } - - /// SwifterSwift: Sort the array based on an optional keypath. - /// - /// - Parameters: - /// - path: Key path to sort, must be Comparable. - /// - ascending: whether order is ascending or not. - /// - Returns: self after sorting. - @available(*, deprecated, message: "Use sort(by:with:) instead.") - @discardableResult - mutating func sort(by path: KeyPath, ascending: Bool) -> [Element] { - if ascending { - sort(by: path, with: optionalCompareAscending) - } else { - sort(by: path, with: optionalCompareDescending) - } - return self - } - - /// SwifterSwift: Sort the array based on a keypath. - /// - /// - Parameters: - /// - path: Key path to sort, must be Comparable. - /// - ascending: whether order is ascending or not. - /// - Returns: self after sorting. - @available(*, deprecated, message: "Use sort(by:with:) instead.") - @discardableResult - mutating func sort(by path: KeyPath, ascending: Bool) -> [Element] { - if ascending { - sort(by: path, with: <) - } else { - sort(by: path, with: >) - } - return self - } -} - -public extension Sequence { - /// SwifterSwift: Returns an array containing the results of mapping the given key path over the sequence’s elements. - /// - /// - Parameter keyPath: Key path to map. - /// - Returns: An array containing the results of mapping. - @available(*, deprecated, message: "Please use map() with a key path instead.") - func map(by keyPath: KeyPath) -> [T] { - return map { $0[keyPath: keyPath] } - } - - /// SwifterSwift: Returns an array containing the non-nil results of mapping the given key path over the sequence’s elements. - /// - /// - Parameter keyPath: Key path to map. - /// - Returns: An array containing the non-nil results of mapping. - @available(*, deprecated, message: "Please use compactMap() with a key path instead.") - func compactMap(by keyPath: KeyPath) -> [T] { - return compactMap { $0[keyPath: keyPath] } - } - - /// SwifterSwift: Returns an array containing the results of filtering the sequence’s elements by a boolean key path. - /// - /// - Parameter keyPath: Boolean key path. If it's value is `true` the element will be added to result. - /// - Returns: An array containing filtered elements. - @available(*, deprecated, message: "Please use filter() with a key path instead.") - func filter(by keyPath: KeyPath) -> [Element] { - return filter { $0[keyPath: keyPath] } - } - - /// SwifterSwift: Get last element that satisfies a conditon. - /// - /// [2, 2, 4, 7].last(where: {$0 % 2 == 0}) -> 4 - /// - /// - Parameter condition: condition to evaluate each element against. - /// - Returns: the last element in the array matching the specified condition. (optional) - @available(*, deprecated, message: "For an unordered sequence using `last` instead of `first` is equal.") - func last(where condition: (Element) throws -> Bool) rethrows -> Element? { - for element in reversed() { - if try condition(element) { return element } - } - return nil - } -} diff --git a/Sources/SwifterSwift/SwiftStdlib/DictionaryExtensions.swift b/Sources/SwifterSwift/SwiftStdlib/DictionaryExtensions.swift index 20ae02512..f05f77eef 100644 --- a/Sources/SwifterSwift/SwiftStdlib/DictionaryExtensions.swift +++ b/Sources/SwifterSwift/SwiftStdlib/DictionaryExtensions.swift @@ -1,4 +1,4 @@ -// DictionaryExtensions.swift - Copyright 2020 SwifterSwift +// DictionaryExtensions.swift - Copyright 2023 SwifterSwift #if canImport(Foundation) import Foundation @@ -10,7 +10,7 @@ public extension Dictionary { /// SwifterSwift: Creates a Dictionary from a given sequence grouped by a given key path. /// /// - Parameters: - /// - sequence: Sequence being grouped + /// - sequence: Sequence being grouped. /// - keypath: The key path to group by. init(grouping sequence: S, by keyPath: KeyPath) where Value == [S.Element] { self.init(grouping: sequence, by: { $0[keyPath: keyPath] }) @@ -22,7 +22,7 @@ public extension Dictionary { /// dict.has(key: "testKey") -> true /// dict.has(key: "anotherKey") -> false /// - /// - Parameter key: key to search for + /// - Parameter key: key to search for. /// - Returns: true if key exists in dictionary. func has(key: Key) -> Bool { return index(forKey: key) != nil @@ -36,7 +36,7 @@ public extension Dictionary { /// dict.keys.contains("key1") -> false /// dict.keys.contains("key2") -> false /// - /// - Parameter keys: keys to be removed + /// - Parameter keys: keys to be removed. mutating func removeAll(keys: S) where S.Element == Key { keys.forEach { removeValue(forKey: $0) } } @@ -96,22 +96,26 @@ public extension Dictionary { } #endif - /// SwifterSwift: Returns a dictionary containing the results of mapping the given closure over the sequence’s elements. - /// - Parameter transform: A mapping closure. `transform` accepts an element of this sequence as its parameter and returns a transformed value of the same or of a different type. + /// SwifterSwift: Returns a dictionary containing the results of mapping the given closure over the sequence’s + /// elements. + /// - Parameter transform: A mapping closure. `transform` accepts an element of this sequence as its parameter and + /// returns a transformed value of the same or of a different type. /// - Returns: A dictionary containing the transformed elements of this sequence. func mapKeysAndValues(_ transform: ((key: Key, value: Value)) throws -> (K, V)) rethrows -> [K: V] { - return [K: V](uniqueKeysWithValues: try map(transform)) + return try [K: V](uniqueKeysWithValues: map(transform)) } - /// SwifterSwift: Returns a dictionary containing the non-`nil` results of calling the given transformation with each element of this sequence. - /// - Parameter transform: A closure that accepts an element of this sequence as its argument and returns an optional value. + /// SwifterSwift: Returns a dictionary containing the non-`nil` results of calling the given transformation with + /// each element of this sequence. + /// - Parameter transform: A closure that accepts an element of this sequence as its argument and returns an + /// optional value. /// - Returns: A dictionary of the non-`nil` results of calling `transform` with each element of the sequence. /// - Complexity: *O(m + n)*, where _m_ is the length of this sequence and _n_ is the length of the result. func compactMapKeysAndValues(_ transform: ((key: Key, value: Value)) throws -> (K, V)?) rethrows -> [K: V] { - return [K: V](uniqueKeysWithValues: try compactMap(transform)) + return try [K: V](uniqueKeysWithValues: compactMap(transform)) } - /// SwifterSwift: Creates a new dictionary using specified keys + /// SwifterSwift: Creates a new dictionary using specified keys. /// /// var dict = ["key1": 1, "key2": 2, "key3": 3, "key4": 4] /// dict.pick(keys: ["key1", "key3", "key4"]) -> ["key1": 1, "key3": 3, "key4": 4] @@ -121,7 +125,8 @@ public extension Dictionary { /// /// - Parameter keys: An array of keys that will be the entries in the resulting dictionary. /// - /// - Returns: A new dictionary that contains the specified keys only. If none of the keys exist, an empty dictionary will be returned. + /// - Returns: A new dictionary that contains the specified keys only. If none of the keys exist, an empty + /// dictionary will be returned. func pick(keys: [Key]) -> [Key: Value] { keys.reduce(into: [Key: Value]()) { result, item in result[item] = self[item] @@ -220,8 +225,8 @@ public extension Dictionary { /// result["key2"] -> "value2" /// /// - Parameters: - /// - lhs: dictionary - /// - rhs: dictionary + /// - lhs: dictionary. + /// - rhs: dictionary. /// - Returns: An dictionary with keys and values from both. static func + (lhs: [Key: Value], rhs: [Key: Value]) -> [Key: Value] { var result = lhs @@ -240,13 +245,13 @@ public extension Dictionary { /// dict["key2"] -> "value2" /// /// - Parameters: - /// - lhs: dictionary - /// - rhs: dictionary + /// - lhs: dictionary. + /// - rhs: dictionary. static func += (lhs: inout [Key: Value], rhs: [Key: Value]) { rhs.forEach { lhs[$0] = $1 } } - /// SwifterSwift: Remove keys contained in the sequence from the dictionary + /// SwifterSwift: Remove keys contained in the sequence from the dictionary. /// /// let dict: [String: String] = ["key1": "value1", "key2": "value2", "key3": "value3"] /// let result = dict-["key1", "key2"] @@ -255,8 +260,8 @@ public extension Dictionary { /// result.keys.contains("key2") -> false /// /// - Parameters: - /// - lhs: dictionary - /// - rhs: array with the keys to be removed. + /// - lhs: dictionary. + /// - keys: array with the keys to be removed. /// - Returns: a new dictionary with keys removed. static func - (lhs: [Key: Value], keys: S) -> [Key: Value] where S.Element == Key { var result = lhs @@ -264,7 +269,7 @@ public extension Dictionary { return result } - /// SwifterSwift: Remove keys contained in the sequence from the dictionary + /// SwifterSwift: Remove keys contained in the sequence from the dictionary. /// /// var dict: [String: String] = ["key1": "value1", "key2": "value2", "key3": "value3"] /// dict-=["key1", "key2"] @@ -273,8 +278,8 @@ public extension Dictionary { /// dict.keys.contains("key2") -> false /// /// - Parameters: - /// - lhs: dictionary - /// - rhs: array with the keys to be removed. + /// - lhs: dictionary. + /// - keys: array with the keys to be removed. static func -= (lhs: inout [Key: Value], keys: S) where S.Element == Key { lhs.removeAll(keys: keys) } diff --git a/Sources/SwifterSwift/SwiftStdlib/DoubleExtensions.swift b/Sources/SwifterSwift/SwiftStdlib/DoubleExtensions.swift index 384fd1368..6f68e9924 100755 --- a/Sources/SwifterSwift/SwiftStdlib/DoubleExtensions.swift +++ b/Sources/SwifterSwift/SwiftStdlib/DoubleExtensions.swift @@ -1,4 +1,4 @@ -// DoubleExtensions.swift - Copyright 2020 SwifterSwift +// DoubleExtensions.swift - Copyright 2023 SwifterSwift #if canImport(CoreGraphics) import CoreGraphics @@ -41,7 +41,7 @@ infix operator **: PowerPrecedence /// - lhs: base double. /// - rhs: exponent double. /// - Returns: exponentiation result (example: 4.4 ** 0.5 = 2.0976176963). -func ** (lhs: Double, rhs: Double) -> Double { +public func ** (lhs: Double, rhs: Double) -> Double { // http://nshipster.com/swift-operators/ return pow(lhs, rhs) } diff --git a/Sources/SwifterSwift/SwiftStdlib/FloatExtensions.swift b/Sources/SwifterSwift/SwiftStdlib/FloatExtensions.swift index 5db970257..ae6026bbe 100755 --- a/Sources/SwifterSwift/SwiftStdlib/FloatExtensions.swift +++ b/Sources/SwifterSwift/SwiftStdlib/FloatExtensions.swift @@ -1,4 +1,4 @@ -// FloatExtensions.swift - Copyright 2020 SwifterSwift +// FloatExtensions.swift - Copyright 2023 SwifterSwift #if canImport(CoreGraphics) import CoreGraphics @@ -41,7 +41,7 @@ infix operator **: PowerPrecedence /// - lhs: base float. /// - rhs: exponent float. /// - Returns: exponentiation result (4.4 ** 0.5 = 2.0976176963). -func ** (lhs: Float, rhs: Float) -> Float { +public func ** (lhs: Float, rhs: Float) -> Float { // http://nshipster.com/swift-operators/ return pow(lhs, rhs) } diff --git a/Sources/SwifterSwift/SwiftStdlib/FloatingPointExtensions.swift b/Sources/SwifterSwift/SwiftStdlib/FloatingPointExtensions.swift index 8e24b352a..146b691a6 100644 --- a/Sources/SwifterSwift/SwiftStdlib/FloatingPointExtensions.swift +++ b/Sources/SwifterSwift/SwiftStdlib/FloatingPointExtensions.swift @@ -1,4 +1,4 @@ -// FloatingPointExtensions.swift - Copyright 2020 SwifterSwift +// FloatingPointExtensions.swift - Copyright 2023 SwifterSwift #if canImport(Foundation) import Foundation @@ -54,10 +54,10 @@ infix operator ± /// SwifterSwift: Tuple of plus-minus operation. /// /// - Parameters: -/// - lhs: number -/// - rhs: number +/// - lhs: number. +/// - rhs: number. /// - Returns: tuple of plus-minus operation ( 2.5 ± 1.5 -> (4, 1)). -func ± (lhs: T, rhs: T) -> (T, T) { +public func ± (lhs: T, rhs: T) -> (T, T) { // http://nshipster.com/swift-operators/ return (lhs + rhs, lhs - rhs) } @@ -68,7 +68,7 @@ func ± (lhs: T, rhs: T) -> (T, T) { prefix operator ± /// SwifterSwift: Tuple of plus-minus operation. /// -/// - Parameter int: number +/// - Parameter int: number. /// - Returns: tuple of plus-minus operation (± 2.5 -> (2.5, -2.5)). public prefix func ± (number: T) -> (T, T) { // http://nshipster.com/swift-operators/ @@ -81,7 +81,7 @@ public prefix func ± (number: T) -> (T, T) { prefix operator √ /// SwifterSwift: Square root of float. /// -/// - Parameter float: float value to find square root for +/// - Parameter float: float value to find square root for. /// - Returns: square root of given float. public prefix func √ (float: T) -> T where T: FloatingPoint { // http://nshipster.com/swift-operators/ diff --git a/Sources/SwifterSwift/SwiftStdlib/IntExtensions.swift b/Sources/SwifterSwift/SwiftStdlib/IntExtensions.swift index 03d7510f7..c9768bc9f 100755 --- a/Sources/SwifterSwift/SwiftStdlib/IntExtensions.swift +++ b/Sources/SwifterSwift/SwiftStdlib/IntExtensions.swift @@ -1,4 +1,4 @@ -// IntExtensions.swift - Copyright 2020 SwifterSwift +// IntExtensions.swift - Copyright 2023 SwifterSwift #if canImport(CoreGraphics) import CoreGraphics @@ -23,7 +23,7 @@ public extension Int { return Double.pi * Double(self) / 180.0 } - /// SwifterSwift: Degree value of radian input + /// SwifterSwift: Degree value of radian input. var radiansToDegrees: Double { return Double(self) * 180 / Double.pi } @@ -50,7 +50,7 @@ public extension Int { } #endif - /// SwifterSwift: String formatted for values over ±1000 (example: 1k, -2k, 100k, 1kk, -5kk..) + /// SwifterSwift: String formatted for values over ±1000 (example: 1k, -2k, 100k, 1kk, -5kk..). var kFormatted: String { var sign: String { return self >= 0 ? "" : "-" @@ -94,7 +94,7 @@ public extension Int { public extension Int { /// SwifterSwift: check if given integer prime or not. Warning: Using big numbers can be computationally expensive! - /// - Returns: true or false depending on prime-ness + /// - Returns: true or false depending on prime-ness. func isPrime() -> Bool { // To improve speed on latter loop :) if self == 2 { return true } @@ -139,7 +139,7 @@ public extension Int { return romanValue } - /// SwifterSwift: Rounds to the closest multiple of n + /// SwifterSwift: Rounds to the closest multiple of n. func roundToNearest(_ number: Int) -> Int { return number == 0 ? self : Int(round(Double(self) / Double(number))) * number } @@ -155,7 +155,7 @@ infix operator **: PowerPrecedence /// - lhs: base integer. /// - rhs: exponent integer. /// - Returns: exponentiation result (example: 2 ** 3 = 8). -func ** (lhs: Int, rhs: Int) -> Double { +public func ** (lhs: Int, rhs: Int) -> Double { // http://nshipster.com/swift-operators/ return pow(Double(lhs), Double(rhs)) } @@ -164,7 +164,7 @@ func ** (lhs: Int, rhs: Int) -> Double { prefix operator √ /// SwifterSwift: Square root of integer. /// -/// - Parameter int: integer value to find square root for +/// - Parameter int: integer value to find square root for. /// - Returns: square root of given integer. public prefix func √ (int: Int) -> Double { // http://nshipster.com/swift-operators/ @@ -181,7 +181,7 @@ infix operator ± /// - lhs: integer number. /// - rhs: integer number. /// - Returns: tuple of plus-minus operation (example: 2 ± 3 -> (5, -1)). -func ± (lhs: Int, rhs: Int) -> (Int, Int) { +public func ± (lhs: Int, rhs: Int) -> (Int, Int) { // http://nshipster.com/swift-operators/ return (lhs + rhs, lhs - rhs) } @@ -192,7 +192,7 @@ func ± (lhs: Int, rhs: Int) -> (Int, Int) { prefix operator ± /// SwifterSwift: Tuple of plus-minus operation. /// -/// - Parameter int: integer number +/// - Parameter int: integer number. /// - Returns: tuple of plus-minus operation (example: ± 2 -> (2, -2)). public prefix func ± (int: Int) -> (Int, Int) { // http://nshipster.com/swift-operators/ diff --git a/Sources/SwifterSwift/SwiftStdlib/KeyedDecodingContainerExtensions.swift b/Sources/SwifterSwift/SwiftStdlib/KeyedDecodingContainerExtensions.swift index 476b7a35f..09df10920 100644 --- a/Sources/SwifterSwift/SwiftStdlib/KeyedDecodingContainerExtensions.swift +++ b/Sources/SwifterSwift/SwiftStdlib/KeyedDecodingContainerExtensions.swift @@ -1,4 +1,4 @@ -// KeyedDecodingContainerExtensions.swift - Copyright 2020 SwifterSwift +// KeyedDecodingContainerExtensions.swift - Copyright 2023 SwifterSwift #if canImport(Foundation) import Foundation diff --git a/Sources/SwifterSwift/SwiftStdlib/MutableCollectionExtensions.swift b/Sources/SwifterSwift/SwiftStdlib/MutableCollectionExtensions.swift index eec6fc6d8..0147d1f9e 100644 --- a/Sources/SwifterSwift/SwiftStdlib/MutableCollectionExtensions.swift +++ b/Sources/SwifterSwift/SwiftStdlib/MutableCollectionExtensions.swift @@ -1,10 +1,10 @@ -// MutableCollectionExtensions.swift - Copyright 2020 SwifterSwift +// MutableCollectionExtensions.swift - Copyright 2023 SwifterSwift public extension MutableCollection where Self: RandomAccessCollection { /// SwifterSwift: Sort the collection based on a keypath and a compare function. /// /// - Parameter keyPath: Key path to sort by. The key path type must be Comparable. - /// - Parameter compare: Comparation function that will determine the ordering. + /// - Parameter compare: Comparison function that will determine the ordering. mutating func sort(by keyPath: KeyPath, with compare: (T, T) -> Bool) { sort { compare($0[keyPath: keyPath], $1[keyPath: keyPath]) } } @@ -16,7 +16,8 @@ public extension MutableCollection where Self: RandomAccessCollection { sort { $0[keyPath: keyPath] < $1[keyPath: keyPath] } } - /// SwifterSwift: Sort the collection based on two key paths. The second one will be used in case the values of the first one match. + /// SwifterSwift: Sort the collection based on two key paths. The second one will be used in case the values of the + /// first one match. /// /// - Parameters: /// - keyPath1: Key path to sort by. Must be Comparable. @@ -31,7 +32,8 @@ public extension MutableCollection where Self: RandomAccessCollection { } } - /// SwifterSwift: Sort the collection based on three key paths. Whenever the values of one key path match, the next one will be used. + /// SwifterSwift: Sort the collection based on three key paths. Whenever the values of one key path match, the next + /// one will be used. /// /// - Parameters: /// - keyPath1: Key path to sort by. Must be Comparable. @@ -55,8 +57,9 @@ public extension MutableCollection where Self: RandomAccessCollection { public extension MutableCollection { /// SwifterSwift: Assign a given value to a field `keyPath` of all elements in the collection. /// - /// - Parameter value: The new value of the field - /// - Parameter keyPath: The actual field of the element + /// - Parameters: + /// - value: The new value of the field. + /// - keyPath: The actual field of the element. mutating func assignToAll(value: Value, by keyPath: WritableKeyPath) { for idx in indices { self[idx][keyPath: keyPath] = value diff --git a/Sources/SwifterSwift/SwiftStdlib/OptionalExtensions.swift b/Sources/SwifterSwift/SwiftStdlib/OptionalExtensions.swift index cb425de59..7f7b07a3a 100644 --- a/Sources/SwifterSwift/SwiftStdlib/OptionalExtensions.swift +++ b/Sources/SwifterSwift/SwiftStdlib/OptionalExtensions.swift @@ -1,4 +1,4 @@ -// OptionalExtensions.swift - Copyright 2020 SwifterSwift +// OptionalExtensions.swift - Copyright 2023 SwifterSwift // MARK: - Methods @@ -27,24 +27,24 @@ public extension Optional { /// try print(bar.unwrapped(or: MyError.notFound)) -> "bar" /// /// - Parameter error: The error to throw if the optional is `nil`. - /// - Returns: The value wrapped by the optional. /// - Throws: The error passed in. - func unwrapped(or error: Error) throws -> Wrapped { + /// - Returns: The value wrapped by the optional. + func unwrapped(or error: any Error) throws -> Wrapped { guard let wrapped = self else { throw error } return wrapped } - /// SwifterSwift: Runs a block to Wrapped if not nil + /// SwifterSwift: Runs a block to Wrapped if not nil. /// /// let foo: String? = nil /// foo.run { unwrappedFoo in - /// // block will never run sice foo is nill + /// // block will never run since foo is nil /// print(unwrappedFoo) /// } /// /// let bar: String? = "bar" /// bar.run { unwrappedBar in - /// // block will run sice bar is not nill + /// // block will run since bar is not nil /// print(unwrappedBar) -> "bar" /// } /// @@ -91,15 +91,12 @@ public extension Optional { public extension Optional where Wrapped: Collection { /// SwifterSwift: Check if optional is nil or empty collection. var isNilOrEmpty: Bool { - guard let collection = self else { return true } - return collection.isEmpty + return self?.isEmpty ?? true } - /// SwifterSwift: Returns the collection only if it is not nill and not empty. + /// SwifterSwift: Returns the collection only if it is not nil and not empty. var nonEmpty: Wrapped? { - guard let collection = self else { return nil } - guard !collection.isEmpty else { return nil } - return collection + return (self?.isEmpty ?? true) ? nil : self } } diff --git a/Sources/SwifterSwift/SwiftStdlib/RangeReplaceableCollectionExtensions.swift b/Sources/SwifterSwift/SwiftStdlib/RangeReplaceableCollectionExtensions.swift index 8096b54cd..a5a6a9dc8 100644 --- a/Sources/SwifterSwift/SwiftStdlib/RangeReplaceableCollectionExtensions.swift +++ b/Sources/SwifterSwift/SwiftStdlib/RangeReplaceableCollectionExtensions.swift @@ -1,9 +1,10 @@ -// RangeReplaceableCollectionExtensions.swift - Copyright 2020 SwifterSwift +// RangeReplaceableCollectionExtensions.swift - Copyright 2023 SwifterSwift // MARK: - Initializers public extension RangeReplaceableCollection { - /// SwifterSwift: Creates a new collection of a given size where for each position of the collection the value will be the result of a call of the given expression. + /// SwifterSwift: Creates a new collection of a given size where for each position of the collection the value will + /// be the result of a call of the given expression. /// /// let values = Array(expression: "Value", count: 3) /// print(values) @@ -18,7 +19,7 @@ public extension RangeReplaceableCollection { if count > 0 { reserveCapacity(count) while self.count < count { - append(try expression()) + try append(expression()) } } } @@ -33,7 +34,8 @@ public extension RangeReplaceableCollection { /// [1, 2, 3, 4].rotated(by: 3) -> [2,3,4,1] /// [1, 2, 3, 4].rotated(by: -1) -> [2,3,4,1] /// - /// - Parameter places: Number of places that the array be rotated. If the value is positive the end becomes the start, if it negative it's that start becom the end. + /// - Parameter places: Number of places that the array be rotated. If the value is positive the end becomes the + /// start, if it negative it's that start become the end. /// - Returns: The new rotated collection. func rotated(by places: Int) -> Self { // Inspired by: https://ruby-doc.org/core-2.2.0/Array.html#method-i-rotate @@ -47,7 +49,8 @@ public extension RangeReplaceableCollection { /// [1, 2, 3, 4].rotate(by: 3) -> [2,3,4,1] /// [1, 2, 3, 4].rotated(by: -1) -> [2,3,4,1] /// - /// - Parameter places: The number of places that the array should be rotated. If the value is positive the end becomes the start, if it negative it's that start become the end. + /// - Parameter places: The number of places that the array should be rotated. If the value is positive the end + /// becomes the start, if it negative it's that start become the end. /// - Returns: self after rotating. @discardableResult mutating func rotate(by places: Int) -> Self { @@ -72,8 +75,10 @@ public extension RangeReplaceableCollection { /// [1, 2, 2, 3, 4, 2, 5].removeFirst { $0 % 2 == 0 } -> [1, 2, 3, 4, 2, 5] /// ["h", "e", "l", "l", "o"].removeFirst { $0 == "e" } -> ["h", "l", "l", "o"] /// - /// - Parameter predicate: A closure that takes an element as its argument and returns a Boolean value that indicates whether the passed element represents a match. - /// - Returns: The first element for which predicate returns true, after removing it. If no elements in the collection satisfy the given predicate, returns `nil`. + /// - Parameter predicate: A closure that takes an element as its argument and returns a Boolean value that + /// indicates whether the passed element represents a match. + /// - Returns: The first element for which predicate returns true, after removing it. If no elements in the + /// collection satisfy the given predicate, returns `nil`. @discardableResult mutating func removeFirst(where predicate: (Element) throws -> Bool) rethrows -> Element? { guard let index = try firstIndex(where: predicate) else { return nil } @@ -109,7 +114,7 @@ public extension RangeReplaceableCollection { /// - Parameter condition: condition to evaluate each element against. /// - Returns: All elements up until condition evaluates to false. func take(while condition: (Element) throws -> Bool) rethrows -> Self { - return Self(try prefix(while: condition)) + return try Self(prefix(while: condition)) } /// SwifterSwift: Skip elements of Array while condition is true. @@ -147,7 +152,8 @@ public extension RangeReplaceableCollection { /// SwifterSwift: Accesses the element at the specified position. /// - /// - Parameter offset: The offset position of the element to access. `offset` must be a valid index offset of the collection that is not equal to the `endIndex` property. + /// - Parameter offset: The offset position of the element to access. `offset` must be a valid index offset of the + /// collection that is not equal to the `endIndex` property. subscript(offset: Int) -> Element { get { return self[index(startIndex, offsetBy: offset)] @@ -160,7 +166,8 @@ public extension RangeReplaceableCollection { /// SwifterSwift: Accesses a contiguous subrange of the collection’s elements. /// - /// - Parameter range: A range of the collection’s indices offsets. The bounds of the range must be valid indices of the collection. + /// - Parameter range: A range of the collection’s indices offsets. The bounds of the range must be valid indices of + /// the collection. subscript(range: R) -> SubSequence where R: RangeExpression, R.Bound == Int { get { let indexRange = range.relative(to: 0..(contentsOf newElements: S?) where Element == S.Element, S: Sequence { + guard let newElements = newElements else { return } + append(contentsOf: newElements) + } } diff --git a/Sources/SwifterSwift/SwiftStdlib/SequenceExtensions.swift b/Sources/SwifterSwift/SwiftStdlib/SequenceExtensions.swift index ec935c5d2..b33f44109 100644 --- a/Sources/SwifterSwift/SwiftStdlib/SequenceExtensions.swift +++ b/Sources/SwifterSwift/SwiftStdlib/SequenceExtensions.swift @@ -1,7 +1,7 @@ -// SequenceExtensions.swift - Copyright 2020 SwifterSwift +// SequenceExtensions.swift - Copyright 2023 SwifterSwift public extension Sequence { - /// SwifterSwift: Check if all elements in collection match a conditon. + /// SwifterSwift: Check if all elements in collection match a condition. /// /// [2, 2, 4].all(matching: {$0 % 2 == 0}) -> true /// [1,2, 2, 4].all(matching: {$0 % 2 == 0}) -> false @@ -12,7 +12,7 @@ public extension Sequence { return try !contains { try !condition($0) } } - /// SwifterSwift: Check if no elements in collection match a conditon. + /// SwifterSwift: Check if no elements in collection match a condition. /// /// [2, 2, 4].none(matching: {$0 % 2 == 0}) -> false /// [1, 3, 5, 7].none(matching: {$0 % 2 == 0}) -> true @@ -23,7 +23,7 @@ public extension Sequence { return try !contains { try condition($0) } } - /// SwifterSwift: Check if any element in collection match a conditon. + /// SwifterSwift: Check if any element in collection match a condition. /// /// [2, 2, 4].any(matching: {$0 % 2 == 0}) -> false /// [1, 3, 5, 7].any(matching: {$0 % 2 == 0}) -> true @@ -114,7 +114,8 @@ public extension Sequence { /// [2, 2, 4, 7].single(where: {$0 % 2 == 0}) -> nil /// /// - Parameter condition: condition to evaluate each element against. - /// - Returns: The only element in the array matching the specified condition. If there are more matching elements, nil is returned. (optional) + /// - Returns: The only element in the array matching the specified condition. If there are more matching elements, + /// nil is returned. (optional) func single(where condition: (Element) throws -> Bool) rethrows -> Element? { var singleElement: Element? for element in self where try condition(element) { @@ -137,10 +138,11 @@ public extension Sequence { /// - Complexity: O(*n*), where *n* is the length of the sequence. func withoutDuplicates(transform: (Element) throws -> T) rethrows -> [Element] { var set = Set() - return try filter { set.insert(try transform($0)).inserted } + return try filter { try set.insert(transform($0)).inserted } } - ///  SwifterSwift: Separates all items into 2 lists based on a given predicate. The first list contains all items for which the specified condition evaluates to true. The second list contains those that don't. + ///  SwifterSwift: Separates all items into 2 lists based on a given predicate. The first list contains all items + /// for which the specified condition evaluates to true. The second list contains those that don't. /// /// let (even, odd) = [0, 1, 2, 3, 4, 5].divided { $0 % 2 == 0 } /// let (minors, adults) = people.divided { $0.age < 18 } @@ -153,6 +155,7 @@ public extension Sequence { var nonMatching = [Element]() for element in self { + // swiftlint:disable:next void_function_in_ternary try condition(element) ? matching.append(element) : nonMatching.append(element) } return (matching, nonMatching) @@ -161,7 +164,7 @@ public extension Sequence { /// SwifterSwift: Return a sorted array based on a key path and a compare function. /// /// - Parameter keyPath: Key path to sort by. - /// - Parameter compare: Comparation function that will determine the ordering. + /// - Parameter compare: Comparison function that will determine the ordering. /// - Returns: The sorted array. func sorted(by keyPath: KeyPath, with compare: (T, T) -> Bool) -> [Element] { return sorted { compare($0[keyPath: keyPath], $1[keyPath: keyPath]) } @@ -175,7 +178,8 @@ public extension Sequence { return sorted { $0[keyPath: keyPath] < $1[keyPath: keyPath] } } - /// SwifterSwift: Returns a sorted sequence based on two key paths. The second one will be used in case the values of the first one match. + /// SwifterSwift: Returns a sorted sequence based on two key paths. The second one will be used in case the values + /// of the first one match. /// /// - Parameters: /// - keyPath1: Key path to sort by. Must be Comparable. @@ -190,7 +194,8 @@ public extension Sequence { } } - /// SwifterSwift: Returns a sorted sequence based on three key paths. Whenever the values of one key path match, the next one will be used. + /// SwifterSwift: Returns a sorted sequence based on three key paths. Whenever the values of one key path match, the + /// next one will be used. /// /// - Parameters: /// - keyPath1: Key path to sort by. Must be Comparable. @@ -215,18 +220,20 @@ public extension Sequence { /// ["James", "Wade", "Bryant"].sum(for: \.count) -> 15 /// /// - Parameter keyPath: Key path of the `AdditiveArithmetic` property. - /// - Returns: The sum of the `AdditiveArithmetic` propertys at `keyPath`. + /// - Returns: The sum of the `AdditiveArithmetic` properties at `keyPath`. func sum(for keyPath: KeyPath) -> T { // Inspired by: https://swiftbysundell.com/articles/reducers-in-swift/ return reduce(.zero) { $0 + $1[keyPath: keyPath] } } - /// SwifterSwift: Returns the first element of the sequence with having property by given key path equals to given `value`. + /// SwifterSwift: Returns the first element of the sequence with having property by given key path equals to given + /// `value`. /// /// - Parameters: /// - keyPath: The `KeyPath` of property for `Element` to compare. - /// - value: The value to compare with `Element` property - /// - Returns: The first element of the collection that has property by given key path equals to given `value` or `nil` if there is no such element. + /// - value: The value to compare with `Element` property. + /// - Returns: The first element of the collection that has property by given key path equals to given `value` or + /// `nil` if there is no such element. func first(where keyPath: KeyPath, equals value: T) -> Element? { return first { $0[keyPath: keyPath] == value } } @@ -267,12 +274,7 @@ public extension Sequence where Element: Hashable { /// - Returns: true if the receiver contains duplicates. func containsDuplicates() -> Bool { var set = Set() - for element in self { - if !set.insert(element).inserted { - return true - } - } - return false + return contains { !set.insert($0).inserted } } /// SwifterSwift: Getting the duplicated elements in a sequence. diff --git a/Sources/SwifterSwift/SwiftStdlib/SignedIntegerExtensions.swift b/Sources/SwifterSwift/SwiftStdlib/SignedIntegerExtensions.swift index 56441f3aa..5e4498268 100644 --- a/Sources/SwifterSwift/SwiftStdlib/SignedIntegerExtensions.swift +++ b/Sources/SwifterSwift/SwiftStdlib/SignedIntegerExtensions.swift @@ -1,4 +1,4 @@ -// SignedIntegerExtensions.swift - Copyright 2020 SwifterSwift +// SignedIntegerExtensions.swift - Copyright 2023 SwifterSwift // // SignedIntegerExtensions.swift @@ -85,8 +85,8 @@ public extension SignedInteger { /// print((12).ordinalString()) // prints "12th" /// /// - Parameter locale: locale, default is .current. - /// - Returns: string ordinal representation of number in specified locale language. E.g. input 92, output in "en": "92nd". - @available(macOS 10.11, *) + /// - Returns: string ordinal representation of number in specified locale language. E.g. input 92, output in "en": + /// "92nd". func ordinalString(locale: Locale = .current) -> String? { let formatter = NumberFormatter() formatter.locale = locale diff --git a/Sources/SwifterSwift/SwiftStdlib/SignedNumericExtensions.swift b/Sources/SwifterSwift/SwiftStdlib/SignedNumericExtensions.swift index 88688aa0a..ac8e8e9a6 100644 --- a/Sources/SwifterSwift/SwiftStdlib/SignedNumericExtensions.swift +++ b/Sources/SwifterSwift/SwiftStdlib/SignedNumericExtensions.swift @@ -1,4 +1,4 @@ -// SignedNumericExtensions.swift - Copyright 2020 SwifterSwift +// SignedNumericExtensions.swift - Copyright 2023 SwifterSwift #if canImport(Foundation) import Foundation @@ -33,7 +33,8 @@ public extension SignedNumeric { /// print((12.32).spelledOutString()) // prints "twelve point three two" /// /// - Parameter locale: Locale, default is .current. - /// - Returns: String representation of number spelled in specified locale language. E.g. input 92, output in "en": "ninety-two" + /// - Returns: String representation of number spelled in specified locale language. E.g. input 92, output in "en": + /// "ninety-two". func spelledOutString(locale: Locale = .current) -> String? { let formatter = NumberFormatter() formatter.locale = locale diff --git a/Sources/SwifterSwift/SwiftStdlib/StringExtensions.swift b/Sources/SwifterSwift/SwiftStdlib/StringExtensions.swift index 365f7f4af..2e48034de 100755 --- a/Sources/SwifterSwift/SwiftStdlib/StringExtensions.swift +++ b/Sources/SwifterSwift/SwiftStdlib/StringExtensions.swift @@ -1,4 +1,4 @@ -// StringExtensions.swift - Copyright 2020 SwifterSwift +// StringExtensions.swift - Copyright 2023 SwifterSwift #if canImport(Foundation) import Foundation @@ -25,6 +25,11 @@ public extension String { /// "SGVsbG8gV29ybGQh".base64Decoded = Optional("Hello World!") /// var base64Decoded: String? { + if let data = Data(base64Encoded: self, + options: .ignoreUnknownCharacters) { + return String(data: data, encoding: .utf8) + } + let remainder = count % 4 var padding = "" @@ -164,7 +169,7 @@ public extension String { /// "Mama".isPalindrome -> false /// var isPalindrome: Bool { - let letters = filter { $0.isLetter } + let letters = filter(\.isLetter) guard !letters.isEmpty else { return false } let midIndex = letters.index(letters.startIndex, offsetBy: letters.count / 2) let firstHalf = letters[letters.startIndex.. true /// @@ -242,7 +248,8 @@ public extension String { #endif #if canImport(Foundation) - /// SwifterSwift: Check if string is a valid Swift number. Note: In North America, "." is the decimal separator, while in many parts of Europe "," is used, + /// SwifterSwift: Check if string is a valid Swift number. Note: In North America, "." is the decimal separator, + /// while in many parts of Europe "," is used. /// /// "123".isNumeric -> true /// "1.3".isNumeric -> true (en_US) @@ -407,7 +414,7 @@ public extension String { #endif #if canImport(Foundation) - /// SwifterSwift: Escaped string for inclusion in a regular expression pattern + /// SwifterSwift: Escaped string for inclusion in a regular expression pattern. /// /// "hello ^$ there" -> "hello \\^\\$ there" /// @@ -427,14 +434,14 @@ public extension String { #endif #if canImport(Foundation) - /// SwifterSwift: Check if the given string contains only white spaces + /// SwifterSwift: Check if the given string contains only white spaces. var isWhitespace: Bool { return trimmingCharacters(in: .whitespacesAndNewlines).isEmpty } #endif #if os(iOS) || os(tvOS) - /// SwifterSwift: Check if the given string spelled correctly + /// SwifterSwift: Check if the given string spelled correctly. var isSpelledCorrectly: Bool { let checker = UITextChecker() let range = NSRange(startIndex.. Hallo Welt + /// "Hello world".localized() -> Hallo Welt /// + /// - Parameter comment: Optional comment for translators. + /// - Returns: Localized string. func localized(comment: String = "") -> String { return NSLocalizedString(self, comment: comment) } #endif + #if canImport(Foundation) + /// SwifterSwift: Returns a format localized string. + /// + /// "%d Swift %d Objective-C".formatLocalized(1, 2) -> 1 Swift 2 Objective-C + /// + /// - Parameters: + /// - comment: Optional comment for translators. + /// - arguments: Arguments used by format. + /// - Returns: Format localized string. + func formatLocalized(comment: String = "", _ arguments: (any CVarArg)...) -> String { + let format = NSLocalizedString(self, comment: comment) + return String(format: format, arguments: arguments) + } + #endif + /// SwifterSwift: The most common character in string. /// /// "This is a test, since e is appearing everywhere e should be the common character".mostCommonCharacter() -> "e" @@ -541,15 +565,15 @@ public extension String { } #if canImport(Foundation) - /// SwifterSwift: an array of all words in a string + /// SwifterSwift: an array of all words in a string. /// /// "Swift is amazing".words() -> ["Swift", "is", "amazing"] /// /// - Returns: The words contained in a string. func words() -> [String] { // https://stackoverflow.com/questions/42822838 - let chararacterSet = CharacterSet.whitespacesAndNewlines.union(.punctuationCharacters) - let comps = components(separatedBy: chararacterSet) + let characterSet = CharacterSet.whitespacesAndNewlines.union(.punctuationCharacters) + let comps = components(separatedBy: characterSet) return comps.filter { !$0.isEmpty } } #endif @@ -562,8 +586,8 @@ public extension String { /// - Returns: The count of words contained in a string. func wordCount() -> Int { // https://stackoverflow.com/questions/42822838 - let chararacterSet = CharacterSet.whitespacesAndNewlines.union(.punctuationCharacters) - let comps = components(separatedBy: chararacterSet) + let characterSet = CharacterSet.whitespacesAndNewlines.union(.punctuationCharacters) + let comps = components(separatedBy: characterSet) let words = comps.filter { !$0.isEmpty } return words.count } @@ -615,19 +639,74 @@ public extension String { /// "Hello World!"[safe: 6..<11] -> "World" /// "Hello World!"[safe: 21..<110] -> nil /// + /// - Parameter range: Range expression. + subscript(safe range: Range) -> String? { + guard range.lowerBound >= 0, + range.upperBound <= count else { + return nil + } + + return String(self[range]) + } + + /// SwifterSwift: Safely subscript string within a given range. + /// /// "Hello World!"[safe: 6...11] -> "World!" /// "Hello World!"[safe: 21...110] -> nil /// /// - Parameter range: Range expression. - subscript(safe range: R) -> String? where R: RangeExpression, R.Bound == Int { - let range = range.relative(to: Int.min..) -> String? { + guard range.lowerBound >= 0, + range.upperBound < count else { + return nil + } + + return String(self[range]) + } + + /// SwifterSwift: Safely subscript string within a given range. + /// + /// "Hello World!"[safe: ..<5] -> "Hello" + /// "Hello World!"[safe: ..<(-110)] -> nil + /// + /// - Parameter range: Range expression. + subscript(safe range: PartialRangeUpTo) -> String? { + guard range.upperBound >= 0, + range.upperBound <= count else { + return nil + } + + return String(self[range]) + } + + /// SwifterSwift: Safely subscript string within a given range. + /// + /// "Hello World!"[safe: ...10] -> "Hello World" + /// "Hello World!"[safe: ...110] -> nil + /// + /// - Parameter range: Range expression. + subscript(safe range: PartialRangeThrough) -> String? { + guard range.upperBound >= 0, + range.upperBound < count else { + return nil + } + + return String(self[range]) + } + + /// SwifterSwift: Safely subscript string within a given range. + /// + /// "Hello World!"[safe: 6...] -> "World!" + /// "Hello World!"[safe: 50...] -> nil + /// + /// - Parameter range: Range expression. + subscript(safe range: PartialRangeFrom) -> String? { guard range.lowerBound >= 0, - let lowerIndex = index(startIndex, offsetBy: range.lowerBound, limitedBy: endIndex), - let upperIndex = index(startIndex, offsetBy: range.upperBound, limitedBy: endIndex) else { + range.lowerBound < count else { return nil } - return String(self[lowerIndex.. "World") + /// - Returns: sliced substring of length number of characters (if applicable) (example: "Hello World".slicing(from: + /// 6, length: 5) -> "World"). func slicing(from index: Int, length: Int) -> String? { guard length >= 0, index >= 0, index < count else { return nil } guard index.advanced(by: length) <= count else { @@ -970,8 +1050,9 @@ public extension String { #if canImport(Foundation) /// SwifterSwift: Verify if string matches the regex. /// - /// - Parameter regex: Regex to verify. - /// - Parameter options: The matching options to use. + /// - Parameters: + /// - regex: Regex to verify. + /// - options: The matching options to use. /// - Returns: `true` if string matches the regex. func matches(regex: NSRegularExpression, options: NSRegularExpression.MatchingOptions = []) -> Bool { let range = NSRange(startIndex.. Bool { - return lhs.range(of: rhs, options: .regularExpression) != nil + return rhs.range(of: lhs, options: .regularExpression) != nil } #endif #if canImport(Foundation) - /// SwifterSwift: Overload Swift's 'contains' operator for matching regex + /// SwifterSwift: Overload Swift's 'contains' operator for matching regex. /// - /// - Parameter lhs: String to check on regex. - /// - Parameter rhs: Regex to match against. + /// - Parameters: + /// - lhs: String to check on regex. + /// - rhs: Regex to match against. /// - Returns: `true` if there is at least one match for the regex in the string. - static func ~= (lhs: String, rhs: NSRegularExpression) -> Bool { - let range = NSRange(lhs.startIndex.. Bool { + let range = NSRange(rhs.startIndex.. " hue" /// "hue".padStart(10, with: "br") -> "brbrbrbhue" /// - /// - Parameter length: The target length to pad. - /// - Parameter string: Pad string. Default is " ". + /// - Parameters: + /// - length: The target length to pad. + /// - string: Pad string. Default is " ". @discardableResult mutating func padStart(_ length: Int, with string: String = " ") -> String { self = paddingStart(length, with: string) @@ -1037,8 +1124,9 @@ public extension String { /// "hue".paddingStart(10) -> " hue" /// "hue".paddingStart(10, with: "br") -> "brbrbrbhue" /// - /// - Parameter length: The target length to pad. - /// - Parameter string: Pad string. Default is " ". + /// - Parameters: + /// - length: The target length to pad. + /// - string: Pad string. Default is " ". /// - Returns: The string with the padding on the start. func paddingStart(_ length: Int, with string: String = " ") -> String { guard count < length else { return self } @@ -1060,8 +1148,9 @@ public extension String { /// "hue".padEnd(10) -> "hue " /// "hue".padEnd(10, with: "br") -> "huebrbrbrb" /// - /// - Parameter length: The target length to pad. - /// - Parameter string: Pad string. Default is " ". + /// - Parameters: + /// - length: The target length to pad. + /// - string: Pad string. Default is " ". @discardableResult mutating func padEnd(_ length: Int, with string: String = " ") -> String { self = paddingEnd(length, with: string) @@ -1073,8 +1162,9 @@ public extension String { /// "hue".paddingEnd(10) -> "hue " /// "hue".paddingEnd(10, with: "br") -> "huebrbrbrb" /// - /// - Parameter length: The target length to pad. - /// - Parameter string: Pad string. Default is " ". + /// - Parameters: + /// - length: The target length to pad. + /// - string: Pad string. Default is " ". /// - Returns: The string with the padding on the end. func paddingEnd(_ length: Int, with string: String = " ") -> String { guard count < length else { return self } @@ -1142,25 +1232,6 @@ public extension String { self.init(str) } #endif - - /// SwifterSwift: Create a new random string of given length. - /// - /// String(randomOfLength: 10) -> "gY8r3MHvlQ" - /// - /// - Parameter length: number of characters in string. - init(randomOfLength length: Int) { - guard length > 0 else { - self.init() - return - } - - let base = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" - var randomString = "" - for _ in 1...length { - randomString.append(base.randomElement()!) - } - self = randomString - } } #if !os(Linux) @@ -1173,7 +1244,7 @@ public extension String { var bold: NSAttributedString { return NSMutableAttributedString( string: self, - attributes: [.font: Font.boldSystemFont(ofSize: Font.systemFontSize)]) + attributes: [.font: SFFont.boldSystemFont(ofSize: SFFont.systemFontSize)]) } #endif @@ -1207,7 +1278,7 @@ public extension String { /// /// - Parameter color: text color. /// - Returns: a NSAttributedString versions of string colored with given color. - func colored(with color: Color) -> NSAttributedString { + func colored(with color: SFColor) -> NSAttributedString { return NSMutableAttributedString(string: self, attributes: [.foregroundColor: color]) } #endif @@ -1298,7 +1369,7 @@ public extension String { return NSRange(range, in: self) } - /// SwifterSwift: NSString appendingPathComponent(str: String) + /// SwifterSwift: NSString appendingPathComponent(str: String). /// /// - Note: This method only works with file paths (not, for example, string representations of URLs. /// See NSString [appendingPathComponent(_:)](https://developer.apple.com/documentation/foundation/nsstring/1417069-appendingpathcomponent) @@ -1308,16 +1379,18 @@ public extension String { return (self as NSString).appendingPathComponent(str) } - /// SwifterSwift: NSString appendingPathExtension(str: String) + /// SwifterSwift: NSString appendingPathExtension(str: String). /// /// - Parameter str: The extension to append to the receiver. - /// - Returns: a new string made by appending to the receiver an extension separator followed by ext (if applicable). + /// - Returns: a new string made by appending to the receiver an extension separator followed by ext (if + /// applicable). func appendingPathExtension(_ str: String) -> String? { return (self as NSString).appendingPathExtension(str) } /// SwifterSwift: Accesses a contiguous subrange of the collection’s elements. - /// - Parameter nsRange: A range of the collection’s indices. The bounds of the range must be valid indices of the collection. + /// - Parameter nsRange: A range of the collection’s indices. The bounds of the range must be valid indices of the + /// collection. /// - Returns: A slice of the receiving string. subscript(bounds: NSRange) -> Substring { guard let range = Range(bounds, in: self) else { fatalError("Failed to find range \(bounds) in \(self)") } diff --git a/Sources/SwifterSwift/SwiftStdlib/StringProtocolExtensions.swift b/Sources/SwifterSwift/SwiftStdlib/StringProtocolExtensions.swift index f4da50394..5c62a048c 100644 --- a/Sources/SwifterSwift/SwiftStdlib/StringProtocolExtensions.swift +++ b/Sources/SwifterSwift/SwiftStdlib/StringProtocolExtensions.swift @@ -1,4 +1,4 @@ -// StringProtocolExtensions.swift - Copyright 2020 SwifterSwift +// StringProtocolExtensions.swift - Copyright 2023 SwifterSwift import Foundation @@ -10,7 +10,7 @@ public extension StringProtocol { /// - Parameters: /// - Parameter aString: The string with which to compare the receiver. /// - Parameter options: Options for the comparison. - /// - Returns: The longest common suffix of the receiver and the given String + /// - Returns: The longest common suffix of the receiver and the given String. func commonSuffix(with aString: T, options: String.CompareOptions = []) -> String { return String(zip(reversed(), aString.reversed()) .lazy @@ -22,12 +22,16 @@ public extension StringProtocol { } #if canImport(Foundation) - /// SwifterSwift: Returns a new string in which all occurrences of a regex pattern in a specified range of the receiver are replaced by the template. - /// - Parameter ofPattern: Regex pattern to replace. - /// - Parameter template: The regex template to replace the pattern. - /// - Parameter options: Options to use when matching the regex. Only .regularExpression, .anchored .and caseInsensitive are supported. - /// - Parameter searchRange: The range in the receiver in which to search. - /// - Returns: A new string in which all occurrences of regex pattern in searchRange of the receiver are replaced by template. + /// SwifterSwift: Returns a new string in which all occurrences of a regex pattern in a specified range of the + /// receiver are replaced by the template. + /// - Parameters: + /// - pattern: Regex pattern to replace. + /// - template: The regex template to replace the pattern. + /// - options: Options to use when matching the regex. Only .regularExpression, .anchored .and caseInsensitive are + /// supported. + /// - searchRange: The range in the receiver in which to search. + /// - Returns: A new string in which all occurrences of regex pattern in searchRange of the receiver are replaced by + /// template. func replacingOccurrences( ofPattern pattern: Target, withTemplate template: Replacement, diff --git a/Sources/SwifterSwift/UIKit/UIActivityExtensions.swift b/Sources/SwifterSwift/UIKit/UIActivityExtensions.swift index 8e2fb9d6d..4f24697d7 100644 --- a/Sources/SwifterSwift/UIKit/UIActivityExtensions.swift +++ b/Sources/SwifterSwift/UIKit/UIActivityExtensions.swift @@ -1,4 +1,4 @@ -// UIActivityExtensions.swift - Copyright 2020 SwifterSwift +// UIActivityExtensions.swift - Copyright 2023 SwifterSwift #if canImport(UIKit) && os(iOS) import UIKit @@ -6,16 +6,16 @@ import UIKit // MARK: - ActivityType public extension UIActivity.ActivityType { - /// SwifterSwift: AddToiCloudDrive + /// SwifterSwift: AddToiCloudDrive. static let addToiCloudDrive = UIActivity.ActivityType("com.apple.CloudDocsUI.AddToiCloudDrive") - /// SwifterSwift: WhatsApp share extension + /// SwifterSwift: WhatsApp share extension. static let postToWhatsApp = UIActivity.ActivityType("net.whatsapp.WhatsApp.ShareExtension") - /// SwifterSwift: LinkedIn share extension + /// SwifterSwift: LinkedIn share extension. static let postToLinkedIn = UIActivity.ActivityType("com.linkedin.LinkedIn.ShareExtension") - /// SwifterSwift: XING share extension + /// SwifterSwift: XING share extension. static let postToXing = UIActivity.ActivityType("com.xing.XING.Xing-Share") } diff --git a/Sources/SwifterSwift/UIKit/UIAlertControllerExtensions.swift b/Sources/SwifterSwift/UIKit/UIAlertControllerExtensions.swift index d816fd75b..60fd20e77 100644 --- a/Sources/SwifterSwift/UIKit/UIAlertControllerExtensions.swift +++ b/Sources/SwifterSwift/UIKit/UIAlertControllerExtensions.swift @@ -1,4 +1,4 @@ -// UIAlertControllerExtensions.swift - Copyright 2020 SwifterSwift +// UIAlertControllerExtensions.swift - Copyright 2023 SwifterSwift #if canImport(UIKit) && !os(watchOS) import UIKit @@ -17,6 +17,7 @@ public extension UIAlertController { /// - vibrate: set true to vibrate the device while presenting the alert (default is false). /// - completion: an optional completion handler to be called after presenting alert controller (default is nil). @available(iOSApplicationExtension, unavailable) + @available(visionOS, unavailable) func show(animated: Bool = true, vibrate: Bool = false, completion: (() -> Void)? = nil) { #if targetEnvironment(macCatalyst) let window = UIApplication.shared.windows.last @@ -31,14 +32,14 @@ public extension UIAlertController { } } - /// SwifterSwift: Add an action to Alert + /// SwifterSwift: Add an action to Alert. /// /// - Parameters: - /// - title: action title - /// - style: action style (default is UIAlertActionStyle.default) - /// - isEnabled: isEnabled status for action (default is true) - /// - handler: optional action handler to be called when button is tapped (default is nil) - /// - Returns: action created by this method + /// - title: action title. + /// - style: action style (default is UIAlertActionStyle.default). + /// - isEnabled: isEnabled status for action (default is true). + /// - handler: optional action handler to be called when button is tapped (default is nil). + /// - Returns: action created by this method. @discardableResult func addAction( title: String, @@ -51,13 +52,13 @@ public extension UIAlertController { return action } - /// SwifterSwift: Add a text field to Alert + /// SwifterSwift: Add a text field to Alert. /// /// - Parameters: - /// - text: text field text (default is nil) - /// - placeholder: text field placeholder text (default is nil) - /// - editingChangedTarget: an optional target for text field's editingChanged - /// - editingChangedSelector: an optional selector for text field's editingChanged + /// - text: text field text (default is nil). + /// - placeholder: text field placeholder text (default is nil). + /// - editingChangedTarget: an optional target for text field's editingChanged. + /// - editingChangedSelector: an optional selector for text field's editingChanged. func addTextField( text: String? = nil, placeholder: String? = nil, @@ -81,8 +82,8 @@ public extension UIAlertController { /// - Parameters: /// - title: alert controller's title. /// - message: alert controller's message (default is nil). - /// - defaultActionButtonTitle: default action button title (default is "OK") - /// - tintColor: alert controller's tint color (default is nil) + /// - defaultActionButtonTitle: default action button title (default is "OK"). + /// - tintColor: alert controller's tint color (default is nil). convenience init( title: String, message: String? = nil, @@ -101,11 +102,12 @@ public extension UIAlertController { /// - Parameters: /// - title: alert controller's title (default is "Error"). /// - error: error to set alert controller's message to it's localizedDescription. - /// - defaultActionButtonTitle: default action button title (default is "OK") - /// - tintColor: alert controller's tint color (default is nil) + /// - defaultActionButtonTitle: default action button title (default is "OK"). + /// - preferredStyle: type of alert to display (default is .alert). + /// - tintColor: alert controller's tint color (default is nil). convenience init( title: String = "Error", - error: Error, + error: any Error, defaultActionButtonTitle: String = "OK", preferredStyle: UIAlertController.Style = .alert, tintColor: UIColor? = nil) { diff --git a/Sources/SwifterSwift/UIKit/UIApplicationExtensions.swift b/Sources/SwifterSwift/UIKit/UIApplicationExtensions.swift index 9ea04a9fc..11972c4d1 100644 --- a/Sources/SwifterSwift/UIKit/UIApplicationExtensions.swift +++ b/Sources/SwifterSwift/UIKit/UIApplicationExtensions.swift @@ -1,4 +1,4 @@ -// UIApplicationExtensions.swift - Copyright 2020 SwifterSwift +// UIApplicationExtensions.swift - Copyright 2023 SwifterSwift #if canImport(UIKit) import UIKit diff --git a/Sources/SwifterSwift/UIKit/UIBarButtonItemExtensions.swift b/Sources/SwifterSwift/UIKit/UIBarButtonItemExtensions.swift index 3c0414fa2..6ddd32222 100644 --- a/Sources/SwifterSwift/UIKit/UIBarButtonItemExtensions.swift +++ b/Sources/SwifterSwift/UIKit/UIBarButtonItemExtensions.swift @@ -1,4 +1,4 @@ -// UIBarButtonItemExtensions.swift - Copyright 2020 SwifterSwift +// UIBarButtonItemExtensions.swift - Copyright 2023 SwifterSwift #if canImport(UIKit) && !os(watchOS) import UIKit @@ -6,7 +6,7 @@ import UIKit // MARK: - Properties public extension UIBarButtonItem { - /// SwifterSwift: Creates a flexible space UIBarButtonItem + /// SwifterSwift: Creates a flexible space UIBarButtonItem. static var flexibleSpace: UIBarButtonItem { return UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil) } @@ -15,7 +15,7 @@ public extension UIBarButtonItem { // MARK: - Methods public extension UIBarButtonItem { - /// SwifterSwift: Add Target to UIBarButtonItem + /// SwifterSwift: Add Target to UIBarButtonItem. /// /// - Parameters: /// - target: target. @@ -25,9 +25,9 @@ public extension UIBarButtonItem { self.action = action } - /// SwifterSwift: Creates a fixed space UIBarButtonItem with a specific width + /// SwifterSwift: Creates a fixed space UIBarButtonItem with a specific width. /// - /// - Parameter width: Width of the UIBarButtonItem + /// - Parameter width: Width of the UIBarButtonItem. static func fixedSpace(width: CGFloat) -> UIBarButtonItem { let barButtonItem = UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil) barButtonItem.width = width diff --git a/Sources/SwifterSwift/UIKit/UIBezierPathExtensions.swift b/Sources/SwifterSwift/UIKit/UIBezierPathExtensions.swift index 12cab7f92..a86d19745 100644 --- a/Sources/SwifterSwift/UIKit/UIBezierPathExtensions.swift +++ b/Sources/SwifterSwift/UIKit/UIBezierPathExtensions.swift @@ -1,4 +1,4 @@ -// UIBezierPathExtensions.swift - Copyright 2020 SwifterSwift +// UIBezierPathExtensions.swift - Copyright 2023 SwifterSwift #if canImport(UIKit) import UIKit @@ -10,7 +10,7 @@ public extension UIBezierPath { /// /// - Parameters: /// - from: The point from which to path should start. - /// - to: The point where the path should end. + /// - otherPoint: The point where the path should end. convenience init(from: CGPoint, to otherPoint: CGPoint) { self.init() move(to: from) diff --git a/Sources/SwifterSwift/UIKit/UIButtonExtensions.swift b/Sources/SwifterSwift/UIKit/UIButtonExtensions.swift index a526cad1e..51a1e6fdd 100644 --- a/Sources/SwifterSwift/UIKit/UIButtonExtensions.swift +++ b/Sources/SwifterSwift/UIKit/UIButtonExtensions.swift @@ -1,4 +1,4 @@ -// UIButtonExtensions.swift - Copyright 2020 SwifterSwift +// UIButtonExtensions.swift - Copyright 2023 SwifterSwift #if canImport(UIKit) && !os(watchOS) import UIKit @@ -9,133 +9,136 @@ public extension UIButton { /// SwifterSwift: Image of disabled state for button; also inspectable from Storyboard. @IBInspectable var imageForDisabled: UIImage? { - get { - return image(for: .disabled) - } - set { - setImage(newValue, for: .disabled) - } + get { image(for: .disabled) } + set { setImage(newValue, for: .disabled) } } /// SwifterSwift: Image of highlighted state for button; also inspectable from Storyboard. @IBInspectable var imageForHighlighted: UIImage? { - get { - return image(for: .highlighted) - } - set { - setImage(newValue, for: .highlighted) - } + get { image(for: .highlighted) } + set { setImage(newValue, for: .highlighted) } } /// SwifterSwift: Image of normal state for button; also inspectable from Storyboard. @IBInspectable var imageForNormal: UIImage? { - get { - return image(for: .normal) - } - set { - setImage(newValue, for: .normal) - } + get { image(for: .normal) } + set { setImage(newValue, for: .normal) } } /// SwifterSwift: Image of selected state for button; also inspectable from Storyboard. @IBInspectable var imageForSelected: UIImage? { - get { - return image(for: .selected) - } - set { - setImage(newValue, for: .selected) - } + get { image(for: .selected) } + set { setImage(newValue, for: .selected) } + } + + /// SwifterSwift: Image of focused state for button; also inspectable from Storyboard. + @IBInspectable + var imageForFocused: UIImage? { + get { image(for: .focused) } + set { setImage(newValue, for: .focused) } } /// SwifterSwift: Title color of disabled state for button; also inspectable from Storyboard. @IBInspectable var titleColorForDisabled: UIColor? { - get { - return titleColor(for: .disabled) - } - set { - setTitleColor(newValue, for: .disabled) - } + get { titleColor(for: .disabled) } + set { setTitleColor(newValue, for: .disabled) } } /// SwifterSwift: Title color of highlighted state for button; also inspectable from Storyboard. @IBInspectable var titleColorForHighlighted: UIColor? { - get { - return titleColor(for: .highlighted) - } - set { - setTitleColor(newValue, for: .highlighted) - } + get { titleColor(for: .highlighted) } + set { setTitleColor(newValue, for: .highlighted) } } /// SwifterSwift: Title color of normal state for button; also inspectable from Storyboard. @IBInspectable var titleColorForNormal: UIColor? { - get { - return titleColor(for: .normal) - } - set { - setTitleColor(newValue, for: .normal) - } + get { titleColor(for: .normal) } + set { setTitleColor(newValue, for: .normal) } } /// SwifterSwift: Title color of selected state for button; also inspectable from Storyboard. @IBInspectable var titleColorForSelected: UIColor? { - get { - return titleColor(for: .selected) - } - set { - setTitleColor(newValue, for: .selected) - } + get { titleColor(for: .selected) } + set { setTitleColor(newValue, for: .selected) } + } + + /// SwifterSwift: Title color of focused state for button; also inspectable from Storyboard. + @IBInspectable + var titleColorForFocused: UIColor? { + get { titleColor(for: .focused) } + set { setTitleColor(newValue, for: .focused) } } /// SwifterSwift: Title of disabled state for button; also inspectable from Storyboard. @IBInspectable var titleForDisabled: String? { - get { - return title(for: .disabled) - } - set { - setTitle(newValue, for: .disabled) - } + get { title(for: .disabled) } + set { setTitle(newValue, for: .disabled) } } /// SwifterSwift: Title of highlighted state for button; also inspectable from Storyboard. @IBInspectable var titleForHighlighted: String? { - get { - return title(for: .highlighted) - } - set { - setTitle(newValue, for: .highlighted) - } + get { title(for: .highlighted) } + set { setTitle(newValue, for: .highlighted) } } /// SwifterSwift: Title of normal state for button; also inspectable from Storyboard. @IBInspectable var titleForNormal: String? { - get { - return title(for: .normal) - } - set { - setTitle(newValue, for: .normal) - } + get { title(for: .normal) } + set { setTitle(newValue, for: .normal) } } /// SwifterSwift: Title of selected state for button; also inspectable from Storyboard. @IBInspectable var titleForSelected: String? { - get { - return title(for: .selected) - } - set { - setTitle(newValue, for: .selected) - } + get { title(for: .selected) } + set { setTitle(newValue, for: .selected) } + } + + /// SwifterSwift: Title of focused state for button; also inspectable from Storyboard. + @IBInspectable + var titleForFocused: String? { + get { title(for: .focused) } + set { setTitle(newValue, for: .focused) } + } + + /// SwifterSwift: Attributed title of disabled state for button. + var attributedTitleForDisabled: NSAttributedString? { + get { attributedTitle(for: .disabled) } + set { setAttributedTitle(newValue, for: .disabled) } + } + + /// SwifterSwift: Attributed title of highlighted state for button. + var attributedTitleForHighlighted: NSAttributedString? { + get { attributedTitle(for: .highlighted) } + set { setAttributedTitle(newValue, for: .highlighted) } + } + + /// SwifterSwift: Attributed title of normal state for button. + var attributedTitleForNormal: NSAttributedString? { + get { attributedTitle(for: .normal) } + set { setAttributedTitle(newValue, for: .normal) } + } + + /// SwifterSwift: Attributed title of selected state for button. + var attributedTitleForSelected: NSAttributedString? { + get { attributedTitle(for: .selected) } + set { setAttributedTitle(newValue, for: .selected) } + } + + /// SwifterSwift: Attributed title of focused state for button. + var attributedTitleForFocused: NSAttributedString? { + get { attributedTitle(for: .focused) } + set { setAttributedTitle(newValue, for: .focused) } } } @@ -143,7 +146,7 @@ public extension UIButton { public extension UIButton { private var states: [UIControl.State] { - return [.normal, .selected, .highlighted, .disabled] + [.normal, .selected, .highlighted, .disabled, .focused] } /// SwifterSwift: Set image for all states. @@ -167,9 +170,16 @@ public extension UIButton { states.forEach { setTitle(title, for: $0) } } - /// SwifterSwift: Center align title text and image + /// SwifterSwift: Set attributed title for all states. + /// + /// - Parameter title: title string. + func setAttributedTitleForAllStates(_ title: NSAttributedString) { + states.forEach { setAttributedTitle(title, for: $0) } + } + + /// SwifterSwift: Center align title text and image. /// - Parameters: - /// - imageAboveText: set true to make image above title text, default is false, image on left of text + /// - imageAboveText: set true to make image above title text, default is false, image on left of text. /// - spacing: spacing between title text and image. func centerTextAndImage(imageAboveText: Bool = false, spacing: CGFloat) { if imageAboveText { @@ -196,6 +206,21 @@ public extension UIButton { contentEdgeInsets = UIEdgeInsets(top: 0, left: insetAmount, bottom: 0, right: insetAmount) } } + + /// SwifterSwift: Set background color for specified state. + /// - Parameters: + /// - color: The color of the image that will be set as background for the button in the given state. + /// - forState: set the UIControl.State for the desired color. + func setBackgroundColor(color: UIColor, forState: UIControl.State) { + clipsToBounds = true // maintain corner radius + + let colorImage = UIGraphicsImageRenderer(size: CGSize(width: 1, height: 1)).image { context in + color.setFill() + context.fill(CGRect(x: 0, y: 0, width: 1, height: 1)) + draw(.zero) + } + setBackgroundImage(colorImage, for: forState) + } } #endif diff --git a/Sources/SwifterSwift/UIKit/UICollectionViewExtensions.swift b/Sources/SwifterSwift/UIKit/UICollectionViewExtensions.swift index d19d30396..4fe856175 100644 --- a/Sources/SwifterSwift/UIKit/UICollectionViewExtensions.swift +++ b/Sources/SwifterSwift/UIKit/UICollectionViewExtensions.swift @@ -1,4 +1,4 @@ -// UICollectionViewExtensions.swift - Copyright 2020 SwifterSwift +// UICollectionViewExtensions.swift - Copyright 2023 SwifterSwift #if canImport(UIKit) && !os(watchOS) import UIKit @@ -147,26 +147,26 @@ public extension UICollectionView { register(UINib(nibName: identifier, bundle: bundle), forCellWithReuseIdentifier: identifier) } - /// SwifterSwift: Safely scroll to possibly invalid IndexPath + /// SwifterSwift: Safely scroll to possibly invalid IndexPath. /// /// - Parameters: - /// - indexPath: Target IndexPath to scroll to - /// - scrollPosition: Scroll position - /// - animated: Whether to animate or not + /// - indexPath: Target IndexPath to scroll to. + /// - scrollPosition: Scroll position. + /// - animated: Whether to animate or not. func safeScrollToItem(at indexPath: IndexPath, at scrollPosition: UICollectionView.ScrollPosition, animated: Bool) { guard indexPath.item >= 0, - indexPath.section >= 0, - indexPath.section < numberOfSections, - indexPath.item < numberOfItems(inSection: indexPath.section) else { + indexPath.section >= 0, + indexPath.section < numberOfSections, + indexPath.item < numberOfItems(inSection: indexPath.section) else { return } scrollToItem(at: indexPath, at: scrollPosition, animated: animated) } - /// SwifterSwift: Check whether IndexPath is valid within the CollectionView + /// SwifterSwift: Check whether IndexPath is valid within the CollectionView. /// - /// - Parameter indexPath: An IndexPath to check - /// - Returns: Boolean value for valid or invalid IndexPath + /// - Parameter indexPath: An IndexPath to check. + /// - Returns: Boolean value for valid or invalid IndexPath. func isValidIndexPath(_ indexPath: IndexPath) -> Bool { return indexPath.section >= 0 && indexPath.item >= 0 && diff --git a/Sources/SwifterSwift/UIKit/UIColorExtensions.swift b/Sources/SwifterSwift/UIKit/UIColorExtensions.swift index f2a56de0a..9673b6e59 100644 --- a/Sources/SwifterSwift/UIKit/UIColorExtensions.swift +++ b/Sources/SwifterSwift/UIKit/UIColorExtensions.swift @@ -1,4 +1,4 @@ -// UIColorExtensions.swift - Copyright 2020 SwifterSwift +// UIColorExtensions.swift - Copyright 2023 SwifterSwift #if canImport(UIKit) import UIKit diff --git a/Sources/SwifterSwift/UIKit/UIDatePickerExtensions.swift b/Sources/SwifterSwift/UIKit/UIDatePickerExtensions.swift deleted file mode 100644 index 67a36b98c..000000000 --- a/Sources/SwifterSwift/UIKit/UIDatePickerExtensions.swift +++ /dev/null @@ -1,22 +0,0 @@ -// UIDatePickerExtensions.swift - Copyright 2020 SwifterSwift - -#if canImport(UIKit) && os(iOS) -import UIKit - -// MARK: - Properties - -public extension UIDatePicker { - #if !targetEnvironment(macCatalyst) - /// SwifterSwift: Text color of UIDatePicker. - var textColor: UIColor? { - get { - value(forKeyPath: "textColor") as? UIColor - } - set { - setValue(newValue, forKeyPath: "textColor") - } - } - #endif -} - -#endif diff --git a/Sources/SwifterSwift/UIKit/UIFontExtensions.swift b/Sources/SwifterSwift/UIKit/UIFontExtensions.swift index 9df87c3d6..7674e3f4d 100644 --- a/Sources/SwifterSwift/UIKit/UIFontExtensions.swift +++ b/Sources/SwifterSwift/UIKit/UIFontExtensions.swift @@ -1,4 +1,4 @@ -// UIFontExtensions.swift - Copyright 2020 SwifterSwift +// UIFontExtensions.swift - Copyright 2023 SwifterSwift #if canImport(UIKit) import UIKit @@ -6,17 +6,17 @@ import UIKit // MARK: - Properties public extension UIFont { - /// SwifterSwift: Font as bold font + /// SwifterSwift: Font as bold font. var bold: UIFont { return UIFont(descriptor: fontDescriptor.withSymbolicTraits(.traitBold)!, size: 0) } - /// SwifterSwift: Font as italic font + /// SwifterSwift: Font as italic font. var italic: UIFont { return UIFont(descriptor: fontDescriptor.withSymbolicTraits(.traitItalic)!, size: 0) } - /// SwifterSwift: Font as monospaced font + /// SwifterSwift: Font as monospaced font. /// /// UIFont.preferredFont(forTextStyle: .body).monospaced /// diff --git a/Sources/SwifterSwift/UIKit/UIGestureRecognizerExtensions.swift b/Sources/SwifterSwift/UIKit/UIGestureRecognizerExtensions.swift index ae308391c..b8fdc5102 100644 --- a/Sources/SwifterSwift/UIKit/UIGestureRecognizerExtensions.swift +++ b/Sources/SwifterSwift/UIKit/UIGestureRecognizerExtensions.swift @@ -1,4 +1,4 @@ -// UIGestureRecognizerExtensions.swift - Copyright 2020 SwifterSwift +// UIGestureRecognizerExtensions.swift - Copyright 2023 SwifterSwift #if canImport(UIKit) && !os(watchOS) import UIKit diff --git a/Sources/SwifterSwift/UIKit/UIImageExtensions.swift b/Sources/SwifterSwift/UIKit/UIImageExtensions.swift index 69e8d3fc0..ed3f9699e 100644 --- a/Sources/SwifterSwift/UIKit/UIImageExtensions.swift +++ b/Sources/SwifterSwift/UIKit/UIImageExtensions.swift @@ -1,4 +1,4 @@ -// UIImageExtensions.swift - Copyright 2020 SwifterSwift +// UIImageExtensions.swift - Copyright 2023 SwifterSwift #if canImport(UIKit) import UIKit @@ -6,12 +6,12 @@ import UIKit // MARK: - Properties public extension UIImage { - /// SwifterSwift: Size in bytes of UIImage + /// SwifterSwift: Size in bytes of UIImage. var bytesSize: Int { return jpegData(compressionQuality: 1)?.count ?? 0 } - /// SwifterSwift: Size in kilo bytes of UIImage + /// SwifterSwift: Size in kilo bytes of UIImage. var kilobytesSize: Int { return (jpegData(compressionQuality: 1)?.count ?? 0) / 1024 } @@ -27,7 +27,7 @@ public extension UIImage { } #if canImport(CoreImage) - /// SwifterSwift: Average color for this image + /// SwifterSwift: Average color for this image. func averageColor() -> UIColor? { // https://stackoverflow.com/questions/26330924 guard let ciImage = ciImage ?? CIImage(image: self) else { return nil } @@ -63,7 +63,9 @@ public extension UIImage { public extension UIImage { /// SwifterSwift: Compressed UIImage from original UIImage. /// - /// - Parameter quality: The quality of the resulting JPEG image, expressed as a value from 0.0 to 1.0. The value 0.0 represents the maximum compression (or lowest quality) while the value 1.0 represents the least compression (or best quality), (default is 0.5). + /// - Parameter quality: The quality of the resulting JPEG image, expressed as a value from 0.0 to 1.0. The value + /// 0.0 represents the maximum compression (or lowest quality) while the value 1.0 represents the least compression + /// (or best quality), (default is 0.5). /// - Returns: optional UIImage (if applicable). func compressed(quality: CGFloat = 0.5) -> UIImage? { guard let data = jpegData(compressionQuality: quality) else { return nil } @@ -72,7 +74,9 @@ public extension UIImage { /// SwifterSwift: Compressed UIImage data from original UIImage. /// - /// - Parameter quality: The quality of the resulting JPEG image, expressed as a value from 0.0 to 1.0. The value 0.0 represents the maximum compression (or lowest quality) while the value 1.0 represents the least compression (or best quality), (default is 0.5). + /// - Parameter quality: The quality of the resulting JPEG image, expressed as a value from 0.0 to 1.0. The value + /// 0.0 represents the maximum compression (or lowest quality) while the value 1.0 represents the least compression + /// (or best quality), (default is 0.5). /// - Returns: optional Data (if applicable). func compressedData(quality: CGFloat = 0.5) -> Data? { return jpegData(compressionQuality: quality) @@ -98,11 +102,21 @@ public extension UIImage { func scaled(toHeight: CGFloat, opaque: Bool = false) -> UIImage? { let scale = toHeight / size.height let newWidth = size.width * scale - UIGraphicsBeginImageContextWithOptions(CGSize(width: newWidth, height: toHeight), opaque, self.scale) - draw(in: CGRect(x: 0, y: 0, width: newWidth, height: toHeight)) - let newImage = UIGraphicsGetImageFromCurrentImageContext() - UIGraphicsEndImageContext() - return newImage + let size = CGSize(width: newWidth, height: toHeight) + let rect = CGRect(origin: .zero, size: size) + + #if os(watchOS) + UIGraphicsBeginImageContextWithOptions(size, opaque, self.scale) + defer { UIGraphicsEndImageContext() } + draw(in: rect) + return UIGraphicsGetImageFromCurrentImageContext() + #else + let format = UIGraphicsImageRendererFormat() + format.scale = self.scale + return UIGraphicsImageRenderer(size: size, format: format).image { _ in + draw(in: rect) + } + #endif } /// SwifterSwift: UIImage scaled to width with respect to aspect ratio. @@ -114,11 +128,21 @@ public extension UIImage { func scaled(toWidth: CGFloat, opaque: Bool = false) -> UIImage? { let scale = toWidth / size.width let newHeight = size.height * scale - UIGraphicsBeginImageContextWithOptions(CGSize(width: toWidth, height: newHeight), opaque, self.scale) - draw(in: CGRect(x: 0, y: 0, width: toWidth, height: newHeight)) - let newImage = UIGraphicsGetImageFromCurrentImageContext() - UIGraphicsEndImageContext() - return newImage + let size = CGSize(width: toWidth, height: newHeight) + let rect = CGRect(origin: .zero, size: size) + + #if os(watchOS) + UIGraphicsBeginImageContextWithOptions(size, opaque, self.scale) + defer { UIGraphicsEndImageContext() } + draw(in: rect) + return UIGraphicsGetImageFromCurrentImageContext() + #else + let format = UIGraphicsImageRendererFormat() + format.scale = self.scale + return UIGraphicsImageRenderer(size: size, format: format).image { _ in + draw(in: rect) + } + #endif } /// SwifterSwift: Creates a copy of the receiver rotated by the given angle. @@ -128,7 +152,6 @@ public extension UIImage { /// /// - Parameter angle: The angle measurement by which to rotate the image. /// - Returns: A new image rotated by the given angle. - @available(tvOS 10.0, watchOS 3.0, *) func rotated(by angle: Measurement) -> UIImage? { let radians = CGFloat(angle.converted(to: .radians).value) @@ -139,19 +162,28 @@ public extension UIImage { width: destRect.width.rounded(), height: destRect.height.rounded()) - UIGraphicsBeginImageContextWithOptions(roundedDestRect.size, false, scale) - guard let contextRef = UIGraphicsGetCurrentContext() else { return nil } + let actions = { (contextRef: CGContext) in + contextRef.translateBy(x: roundedDestRect.width / 2, y: roundedDestRect.height / 2) + contextRef.rotate(by: radians) - contextRef.translateBy(x: roundedDestRect.width / 2, y: roundedDestRect.height / 2) - contextRef.rotate(by: radians) - - draw(in: CGRect(origin: CGPoint(x: -size.width / 2, - y: -size.height / 2), - size: size)) + self.draw(in: CGRect(origin: CGPoint(x: -self.size.width / 2, + y: -self.size.height / 2), + size: self.size)) + } - let newImage = UIGraphicsGetImageFromCurrentImageContext() - UIGraphicsEndImageContext() - return newImage + #if os(watchOS) + UIGraphicsBeginImageContextWithOptions(roundedDestRect.size, false, scale) + defer { UIGraphicsEndImageContext() } + guard let contextRef = UIGraphicsGetCurrentContext() else { return nil } + actions(contextRef) + return UIGraphicsGetImageFromCurrentImageContext() + #else + let format = UIGraphicsImageRendererFormat() + format.scale = scale + return UIGraphicsImageRenderer(size: roundedDestRect.size, format: format).image { + actions($0.cgContext) + } + #endif } /// SwifterSwift: Creates a copy of the receiver rotated by the given angle (in radians). @@ -169,19 +201,28 @@ public extension UIImage { width: destRect.width.rounded(), height: destRect.height.rounded()) - UIGraphicsBeginImageContextWithOptions(roundedDestRect.size, false, scale) - guard let contextRef = UIGraphicsGetCurrentContext() else { return nil } + let actions = { (contextRef: CGContext) in + contextRef.translateBy(x: roundedDestRect.width / 2, y: roundedDestRect.height / 2) + contextRef.rotate(by: radians) - contextRef.translateBy(x: roundedDestRect.width / 2, y: roundedDestRect.height / 2) - contextRef.rotate(by: radians) - - draw(in: CGRect(origin: CGPoint(x: -size.width / 2, - y: -size.height / 2), - size: size)) + self.draw(in: CGRect(origin: CGPoint(x: -self.size.width / 2, + y: -self.size.height / 2), + size: self.size)) + } - let newImage = UIGraphicsGetImageFromCurrentImageContext() - UIGraphicsEndImageContext() - return newImage + #if os(watchOS) + UIGraphicsBeginImageContextWithOptions(roundedDestRect.size, false, scale) + defer { UIGraphicsEndImageContext() } + guard let contextRef = UIGraphicsGetCurrentContext() else { return nil } + actions(contextRef) + return UIGraphicsGetImageFromCurrentImageContext() + #else + let format = UIGraphicsImageRendererFormat() + format.scale = scale + return UIGraphicsImageRenderer(size: roundedDestRect.size, format: format).image { + actions($0.cgContext) + } + #endif } /// SwifterSwift: UIImage filled with color @@ -189,19 +230,9 @@ public extension UIImage { /// - Parameter color: color to fill image with. /// - Returns: UIImage filled with given color. func filled(withColor color: UIColor) -> UIImage { - #if !os(watchOS) - if #available(tvOS 10.0, *) { - let format = UIGraphicsImageRendererFormat() - format.scale = scale - let renderer = UIGraphicsImageRenderer(size: size, format: format) - return renderer.image { context in - color.setFill() - context.fill(CGRect(origin: .zero, size: size)) - } - } - #endif - + #if os(watchOS) UIGraphicsBeginImageContextWithOptions(size, false, scale) + defer { UIGraphicsEndImageContext() } color.setFill() guard let context = UIGraphicsGetCurrentContext() else { return self } @@ -214,61 +245,54 @@ public extension UIImage { context.clip(to: rect, mask: mask) context.fill(rect) - let newImage = UIGraphicsGetImageFromCurrentImageContext()! - UIGraphicsEndImageContext() - return newImage + return UIGraphicsGetImageFromCurrentImageContext()! + #else + let format = UIGraphicsImageRendererFormat() + format.scale = scale + let renderer = UIGraphicsImageRenderer(size: size, format: format) + return renderer.image { context in + color.setFill() + context.fill(CGRect(origin: .zero, size: size)) + } + #endif } - /// SwifterSwift: UIImage tinted with color + /// SwifterSwift: UIImage tinted with color. /// /// - Parameters: /// - color: color to tint image with. - /// - blendMode: how to blend the tint + /// - blendMode: how to blend the tint. + /// - alpha: alpha value used to draw. /// - Returns: UIImage tinted with given color. func tint(_ color: UIColor, blendMode: CGBlendMode, alpha: CGFloat = 1.0) -> UIImage { let drawRect = CGRect(origin: .zero, size: size) - #if !os(watchOS) - if #available(tvOS 10.0, *) { - let format = UIGraphicsImageRendererFormat() - format.scale = scale - return UIGraphicsImageRenderer(size: size, format: format).image { context in - color.setFill() - context.fill(drawRect) - draw(in: drawRect, blendMode: blendMode, alpha: alpha) - } - } - #endif - + #if os(watchOS) UIGraphicsBeginImageContextWithOptions(size, false, scale) - defer { - UIGraphicsEndImageContext() - } + defer { UIGraphicsEndImageContext() } let context = UIGraphicsGetCurrentContext() color.setFill() context?.fill(drawRect) draw(in: drawRect, blendMode: blendMode, alpha: alpha) return UIGraphicsGetImageFromCurrentImageContext()! + #else + let format = UIGraphicsImageRendererFormat() + format.scale = scale + return UIGraphicsImageRenderer(size: size, format: format).image { context in + color.setFill() + context.fill(drawRect) + draw(in: drawRect, blendMode: blendMode, alpha: alpha) + } + #endif } - /// SwifterSwift: UImage with background color + /// SwifterSwift: UImage with background color. /// /// - Parameters: - /// - backgroundColor: Color to use as background color - /// - Returns: UIImage with a background color that is visible where alpha < 1 + /// - backgroundColor: Color to use as background color. + /// - Returns: UIImage with a background color that is visible where alpha < 1. func withBackgroundColor(_ backgroundColor: UIColor) -> UIImage { - #if !os(watchOS) - if #available(tvOS 10.0, *) { - let format = UIGraphicsImageRendererFormat() - format.scale = scale - return UIGraphicsImageRenderer(size: size, format: format).image { context in - backgroundColor.setFill() - context.fill(context.format.bounds) - draw(at: .zero) - } - } - #endif - + #if os(watchOS) UIGraphicsBeginImageContextWithOptions(size, false, scale) defer { UIGraphicsEndImageContext() } @@ -277,13 +301,22 @@ public extension UIImage { draw(at: .zero) return UIGraphicsGetImageFromCurrentImageContext()! + #else + let format = UIGraphicsImageRendererFormat() + format.scale = scale + return UIGraphicsImageRenderer(size: size, format: format).image { context in + backgroundColor.setFill() + context.fill(context.format.bounds) + draw(at: .zero) + } + #endif } - /// SwifterSwift: UIImage with rounded corners + /// SwifterSwift: UIImage with rounded corners. /// /// - Parameters: - /// - radius: corner radius (optional), resulting image will be round if unspecified - /// - Returns: UIImage with all corners rounded + /// - radius: corner radius (optional), resulting image will be round if unspecified. + /// - Returns: UIImage with all corners rounded. func withRoundedCorners(radius: CGFloat? = nil) -> UIImage? { let maxRadius = min(size.width, size.height) / 2 let cornerRadius: CGFloat @@ -293,31 +326,52 @@ public extension UIImage { cornerRadius = maxRadius } - UIGraphicsBeginImageContextWithOptions(size, false, scale) - - let rect = CGRect(origin: .zero, size: size) - UIBezierPath(roundedRect: rect, cornerRadius: cornerRadius).addClip() - draw(in: rect) + let actions = { + let rect = CGRect(origin: .zero, size: self.size) + UIBezierPath(roundedRect: rect, cornerRadius: cornerRadius).addClip() + self.draw(in: rect) + } - let image = UIGraphicsGetImageFromCurrentImageContext() - UIGraphicsEndImageContext() - return image + #if os(watchOS) + UIGraphicsBeginImageContextWithOptions(size, false, scale) + defer { UIGraphicsEndImageContext() } + actions() + return UIGraphicsGetImageFromCurrentImageContext() + #else + let format = UIGraphicsImageRendererFormat() + format.scale = scale + return UIGraphicsImageRenderer(size: size, format: format).image { _ in + actions() + } + #endif } /// SwifterSwift: Base 64 encoded PNG data of the image. /// - /// - returns: Base 64 encoded PNG data of the image as a String. + /// - Returns: Base 64 encoded PNG data of the image as a String. func pngBase64String() -> String? { return pngData()?.base64EncodedString() } /// SwifterSwift: Base 64 encoded JPEG data of the image. /// - /// - parameter compressionQuality: The quality of the resulting JPEG image, expressed as a value from 0.0 to 1.0. The value 0.0 represents the maximum compression (or lowest quality) while the value 1.0 represents the least compression (or best quality). - /// - returns: Base 64 encoded JPEG data of the image as a String. + /// - Parameter: compressionQuality: The quality of the resulting JPEG image, expressed as a value from 0.0 to 1.0. + /// The value 0.0 represents the maximum compression (or lowest quality) while the value 1.0 represents the least + /// compression (or best quality). + /// - Returns: Base 64 encoded JPEG data of the image as a String. func jpegBase64String(compressionQuality: CGFloat) -> String? { return jpegData(compressionQuality: compressionQuality)?.base64EncodedString() } + + /// SwifterSwift: UIImage with color uses .alwaysOriginal rendering mode. + /// + /// - Parameters: + /// - color: Color of image. + /// - Returns: UIImage with color. + @available(iOS 13.0, tvOS 13.0, watchOS 6.0, *) + func withAlwaysOriginalTintColor(_ color: UIColor) -> UIImage { + return withTintColor(color, renderingMode: .alwaysOriginal) + } } // MARK: - Initializers @@ -329,11 +383,9 @@ public extension UIImage { /// - color: image fill color. /// - size: image size. convenience init(color: UIColor, size: CGSize) { + #if os(watchOS) UIGraphicsBeginImageContextWithOptions(size, false, 1) - - defer { - UIGraphicsEndImageContext() - } + defer { UIGraphicsEndImageContext() } color.setFill() UIRectFill(CGRect(origin: .zero, size: size)) @@ -344,13 +396,27 @@ public extension UIImage { } self.init(cgImage: aCgImage) + #else + let format = UIGraphicsImageRendererFormat() + format.scale = 1 + guard let image = UIGraphicsImageRenderer(size: size, format: format).image(actions: { context in + color.setFill() + context.fill(context.format.bounds) + }).cgImage else { + self.init() + return + } + self.init(cgImage: image) + #endif } /// SwifterSwift: Create a new image from a base 64 string. /// /// - Parameters: /// - base64String: a base-64 `String`, representing the image - /// - scale: The scale factor to assume when interpreting the image data created from the base-64 string. Applying a scale factor of 1.0 results in an image whose size matches the pixel-based dimensions of the image. Applying a different scale factor changes the size of the image as reported by the `size` property. + /// - scale: The scale factor to assume when interpreting the image data created from the base-64 string. Applying + /// a scale factor of 1.0 results in an image whose size matches the pixel-based dimensions of the image. Applying a + /// different scale factor changes the size of the image as reported by the `size` property. convenience init?(base64String: String, scale: CGFloat = 1.0) { guard let data = Data(base64Encoded: base64String) else { return nil } self.init(data: data, scale: scale) @@ -360,11 +426,17 @@ public extension UIImage { /// /// - Important: /// Use this method to convert data:// URLs to UIImage objects. - /// Don't use this synchronous initializer to request network-based URLs. For network-based URLs, this method can block the current thread for tens of seconds on a slow network, resulting in a poor user experience, and in iOS, may cause your app to be terminated. - /// Instead, for non-file URLs, consider using this in an asynchronous way, using `dataTask(with:completionHandler:)` method of the URLSession class or a library such as `AlamofireImage`, `Kingfisher`, `SDWebImage`, or others to perform asynchronous network image loading. + /// Don't use this synchronous initializer to request network-based URLs. For network-based URLs, this method can + /// block the current thread for tens of seconds on a slow network, resulting in a poor user experience, and in iOS, + /// may cause your app to be terminated. + /// Instead, for non-file URLs, consider using this in an asynchronous way, using + /// `dataTask(with:completionHandler:)` method of the URLSession class or a library such as `AlamofireImage`, + /// `Kingfisher`, `SDWebImage`, or others to perform asynchronous network image loading. /// - Parameters: /// - url: a `URL`, representing the image location - /// - scale: The scale factor to assume when interpreting the image data created from the URL. Applying a scale factor of 1.0 results in an image whose size matches the pixel-based dimensions of the image. Applying a different scale factor changes the size of the image as reported by the `size` property. + /// - scale: The scale factor to assume when interpreting the image data created from the URL. Applying a scale + /// factor of 1.0 results in an image whose size matches the pixel-based dimensions of the image. Applying a + /// different scale factor changes the size of the image as reported by the `size` property. convenience init?(url: URL, scale: CGFloat = 1.0) throws { let data = try Data(contentsOf: url) self.init(data: data, scale: scale) diff --git a/Sources/SwifterSwift/UIKit/UIImageViewExtensions.swift b/Sources/SwifterSwift/UIKit/UIImageViewExtensions.swift index 4bef3cb03..5ef4a22e7 100644 --- a/Sources/SwifterSwift/UIKit/UIImageViewExtensions.swift +++ b/Sources/SwifterSwift/UIKit/UIImageViewExtensions.swift @@ -1,4 +1,4 @@ -// UIImageViewExtensions.swift - Copyright 2020 SwifterSwift +// UIImageViewExtensions.swift - Copyright 2023 SwifterSwift #if canImport(UIKit) && !os(watchOS) import UIKit @@ -12,7 +12,7 @@ public extension UIImageView { /// - url: URL of image. /// - contentMode: imageView content mode (default is .scaleAspectFit). /// - placeHolder: optional placeholder image - /// - completionHandler: optional completion handler to run when download finishs (default is nil). + /// - completionHandler: optional completion handler to run when download finishes (default is nil). func download( from url: URL, contentMode: UIView.ContentMode = .scaleAspectFit, @@ -36,7 +36,7 @@ public extension UIImageView { }.resume() } - /// SwifterSwift: Make image view blurry + /// SwifterSwift: Make image view blurry. /// /// - Parameter style: UIBlurEffectStyle (default is .light). func blur(withStyle style: UIBlurEffect.Style = .light) { @@ -48,7 +48,7 @@ public extension UIImageView { clipsToBounds = true } - /// SwifterSwift: Blurred version of an image view + /// SwifterSwift: Blurred version of an image view. /// /// - Parameter style: UIBlurEffectStyle (default is .light). /// - Returns: blurred version of self. diff --git a/Sources/SwifterSwift/UIKit/UILabelExtensions.swift b/Sources/SwifterSwift/UIKit/UILabelExtensions.swift index 85d99672a..2642ec578 100644 --- a/Sources/SwifterSwift/UIKit/UILabelExtensions.swift +++ b/Sources/SwifterSwift/UIKit/UILabelExtensions.swift @@ -1,4 +1,4 @@ -// UILabelExtensions.swift - Copyright 2020 SwifterSwift +// UILabelExtensions.swift - Copyright 2023 SwifterSwift #if canImport(UIKit) && !os(watchOS) import UIKit @@ -6,7 +6,7 @@ import UIKit // MARK: - Methods public extension UILabel { - /// SwifterSwift: Initialize a UILabel with text + /// SwifterSwift: Initialize a UILabel with text. convenience init(text: String?) { self.init() self.text = text @@ -23,7 +23,7 @@ public extension UILabel { self.text = text } - /// SwifterSwift: Required height for a label + /// SwifterSwift: Required height for a label. var requiredHeight: CGFloat { let label = UILabel(frame: CGRect(x: 0, y: 0, width: frame.width, height: CGFloat.greatestFiniteMagnitude)) label.numberOfLines = 0 diff --git a/Sources/SwifterSwift/UIKit/UILayoutPriorityExtensions.swift b/Sources/SwifterSwift/UIKit/UILayoutPriorityExtensions.swift index 058a52128..47d477258 100644 --- a/Sources/SwifterSwift/UIKit/UILayoutPriorityExtensions.swift +++ b/Sources/SwifterSwift/UIKit/UILayoutPriorityExtensions.swift @@ -1,4 +1,4 @@ -// UILayoutPriorityExtensions.swift - Copyright 2020 SwifterSwift +// UILayoutPriorityExtensions.swift - Copyright 2023 SwifterSwift #if os(iOS) || os(tvOS) import UIKit @@ -6,20 +6,20 @@ import UIKit extension UILayoutPriority: ExpressibleByFloatLiteral, ExpressibleByIntegerLiteral { // MARK: - Initializers - /// SwifterSwift: Initialize `UILayoutPriority` with a float literal + /// SwifterSwift: Initialize `UILayoutPriority` with a float literal. /// /// constraint.priority = 0.5 /// - /// - Parameter value: The float value of the constraint + /// - Parameter value: The float value of the constraint. public init(floatLiteral value: Float) { self.init(rawValue: value) } - /// SwifterSwift: Initialize `UILayoutPriority` with an integer literal + /// SwifterSwift: Initialize `UILayoutPriority` with an integer literal. /// /// constraint.priority = 5 /// - /// - Parameter value: The integer value of the constraint + /// - Parameter value: The integer value of the constraint. public init(integerLiteral value: Int) { self.init(rawValue: Float(value)) } diff --git a/Sources/SwifterSwift/UIKit/UINavigationBarExtensions.swift b/Sources/SwifterSwift/UIKit/UINavigationBarExtensions.swift index d89780066..c475e4afe 100644 --- a/Sources/SwifterSwift/UIKit/UINavigationBarExtensions.swift +++ b/Sources/SwifterSwift/UIKit/UINavigationBarExtensions.swift @@ -1,4 +1,4 @@ -// UINavigationBarExtensions.swift - Copyright 2020 SwifterSwift +// UINavigationBarExtensions.swift - Copyright 2023 SwifterSwift #if canImport(UIKit) && !os(watchOS) import UIKit @@ -9,40 +9,79 @@ public extension UINavigationBar { /// SwifterSwift: Set Navigation Bar title, title color and font. /// /// - Parameters: - /// - font: title font + /// - font: title font. /// - color: title text color (default is .black). func setTitleFont(_ font: UIFont, color: UIColor = .black) { var attrs = [NSAttributedString.Key: Any]() attrs[.font] = font attrs[.foregroundColor] = color - titleTextAttributes = attrs + if #available(iOS 13.0, tvOS 13.0, *) { + standardAppearance.titleTextAttributes = attrs + scrollEdgeAppearance?.titleTextAttributes = attrs + compactAppearance?.titleTextAttributes = attrs + } else { + titleTextAttributes = attrs + } } /// SwifterSwift: Make navigation bar transparent. /// /// - Parameter tint: tint color (default is .white). func makeTransparent(withTint tint: UIColor = .white) { - isTranslucent = true - backgroundColor = .clear - barTintColor = .clear - setBackgroundImage(UIImage(), for: .default) + let legacyAppearance = { [self] in + isTranslucent = true + backgroundColor = .clear + barTintColor = .clear + setBackgroundImage(UIImage(), for: .default) + titleTextAttributes = [.foregroundColor: tint] + shadowImage = UIImage() + } + #if os(tvOS) + legacyAppearance() + #else + if #available(iOS 13.0, *) { + let appearance = UINavigationBarAppearance() + appearance.configureWithTransparentBackground() + appearance.titleTextAttributes = [.foregroundColor: tint] + standardAppearance = appearance + scrollEdgeAppearance = appearance + compactAppearance = appearance + } else { + legacyAppearance() + } + #endif tintColor = tint - titleTextAttributes = [.foregroundColor: tint] - shadowImage = UIImage() } - /// SwifterSwift: Set navigationBar background and text colors + /// SwifterSwift: Set navigationBar background and text colors. /// /// - Parameters: - /// - background: backgound color - /// - text: text color + /// - background: background color. + /// - text: text color. func setColors(background: UIColor, text: UIColor) { - isTranslucent = false - backgroundColor = background - barTintColor = background - setBackgroundImage(UIImage(), for: .default) + let legacyAppearance = { [self] in + isTranslucent = false + backgroundColor = background + barTintColor = background + setBackgroundImage(UIImage(), for: .default) + titleTextAttributes = [.foregroundColor: text] + } + #if os(tvOS) + legacyAppearance() + #else + if #available(iOS 13.0, *) { + let appearance = UINavigationBarAppearance() + appearance.configureWithOpaqueBackground() + appearance.backgroundColor = background + appearance.titleTextAttributes = [.foregroundColor: text] + standardAppearance = appearance + scrollEdgeAppearance = appearance + compactAppearance = appearance + } else { + legacyAppearance() + } + #endif tintColor = text - titleTextAttributes = [.foregroundColor: text] } } diff --git a/Sources/SwifterSwift/UIKit/UINavigationControllerExtensions.swift b/Sources/SwifterSwift/UIKit/UINavigationControllerExtensions.swift index fa6c2bae5..396adad94 100755 --- a/Sources/SwifterSwift/UIKit/UINavigationControllerExtensions.swift +++ b/Sources/SwifterSwift/UIKit/UINavigationControllerExtensions.swift @@ -1,4 +1,4 @@ -// UINavigationControllerExtensions.swift - Copyright 2020 SwifterSwift +// UINavigationControllerExtensions.swift - Copyright 2023 SwifterSwift #if canImport(UIKit) && !os(watchOS) import UIKit @@ -32,6 +32,19 @@ public extension UINavigationController { CATransaction.commit() } + #if !os(tvOS) + /// SwifterSwift: Pushes a view controller while hiding or showing the bottom bar. + /// + /// - Parameters: + /// - viewController: The view controller to push. + /// - hidesBottomBar: If `true`, hides the bottom bar (e.g. tab bar). + /// - animated: Specify `true` to animate the transition. + func pushViewController(_ viewController: UIViewController, hidesBottomBar: Bool, animated: Bool) { + viewController.hidesBottomBarWhenPushed = hidesBottomBar + pushViewController(viewController, animated: animated) + } + #endif + /// SwifterSwift: Make navigation controller's navigation bar transparent. /// /// - Parameter tint: tint color (default is .white). diff --git a/Sources/SwifterSwift/UIKit/UINavigationItemExtensions.swift b/Sources/SwifterSwift/UIKit/UINavigationItemExtensions.swift index 8f127b2d7..4c3170a65 100644 --- a/Sources/SwifterSwift/UIKit/UINavigationItemExtensions.swift +++ b/Sources/SwifterSwift/UIKit/UINavigationItemExtensions.swift @@ -1,4 +1,4 @@ -// UINavigationItemExtensions.swift - Copyright 2020 SwifterSwift +// UINavigationItemExtensions.swift - Copyright 2023 SwifterSwift #if canImport(UIKit) && !os(watchOS) import UIKit diff --git a/Sources/SwifterSwift/UIKit/UIRefreshControlExtensions.swift b/Sources/SwifterSwift/UIKit/UIRefreshControlExtensions.swift index cfb9a95ff..4beffbc48 100644 --- a/Sources/SwifterSwift/UIKit/UIRefreshControlExtensions.swift +++ b/Sources/SwifterSwift/UIKit/UIRefreshControlExtensions.swift @@ -1,4 +1,4 @@ -// UIRefreshControlExtensions.swift - Copyright 2020 SwifterSwift +// UIRefreshControlExtensions.swift - Copyright 2023 SwifterSwift #if os(iOS) import UIKit @@ -6,12 +6,12 @@ import UIKit // MARK: - Methods public extension UIRefreshControl { - /// SwifterSwift: Programatically begin refresh control inside of UITableView. + /// SwifterSwift: Programmatically begin refresh control inside of UITableView. /// /// - Parameters: /// - tableView: UITableView instance, inside which the refresh control is contained. /// - animated: Boolean, indicates that is the content offset changing should be animated or not. - /// - sendAction: Boolean, indicates that should it fire sendActions method for valueChanged UIControlEvents + /// - sendAction: Boolean, indicates that should it fire sendActions method for valueChanged UIControlEvents. func beginRefreshing(in tableView: UITableView, animated: Bool, sendAction: Bool = false) { // https://stackoverflow.com/questions/14718850/14719658#14719658 assert(superview == tableView, "Refresh control does not belong to the receiving table view") @@ -24,6 +24,27 @@ public extension UIRefreshControl { sendActions(for: .valueChanged) } } + + /// SwifterSwift: Programmatically begin refresh control inside of UIScrollView. + /// + /// - Parameters: + /// - animated: Boolean, indicates that is the content offset changing should be animated or not. + /// - sendAction: Boolean, indicates that should it fire sendActions method for valueChanged UIControlEvents. + func beginRefreshing(animated: Bool, sendAction: Bool = false) { + // https://stackoverflow.com/questions/14718850/14719658#14719658 + guard let scrollView = superview as? UIScrollView else { + assertionFailure("Refresh control does not belong to a scroll view") + return + } + + beginRefreshing() + let offsetPoint = CGPoint(x: 0, y: -frame.height) + scrollView.setContentOffset(offsetPoint, animated: animated) + + if sendAction { + sendActions(for: .valueChanged) + } + } } #endif diff --git a/Sources/SwifterSwift/UIKit/UIScrollViewExtensions.swift b/Sources/SwifterSwift/UIKit/UIScrollViewExtensions.swift index ff64755f2..f2e85f5a2 100644 --- a/Sources/SwifterSwift/UIKit/UIScrollViewExtensions.swift +++ b/Sources/SwifterSwift/UIKit/UIScrollViewExtensions.swift @@ -1,4 +1,4 @@ -// UIScrollViewExtensions.swift - Copyright 2020 SwifterSwift +// UIScrollViewExtensions.swift - Copyright 2023 SwifterSwift #if canImport(UIKit) && !os(watchOS) import UIKit @@ -6,24 +6,23 @@ import UIKit // MARK: - Methods public extension UIScrollView { - /// SwifterSwift: Takes a snapshot of an entire ScrollView + /// SwifterSwift: Takes a snapshot of an entire ScrollView. /// - /// AnySubclassOfUIScroolView().snapshot + /// AnySubclassOfUIScrollView().snapshot /// UITableView().snapshot /// - /// - Returns: Snapshot as UIimage for rendered ScrollView + /// - Returns: Snapshot as UIImage for rendered ScrollView. var snapshot: UIImage? { + let size = contentSize + guard size != .zero else { return nil } + // Original Source: https://gist.github.com/thestoics/1204051 - UIGraphicsBeginImageContextWithOptions(contentSize, false, 0) - defer { - UIGraphicsEndImageContext() + return UIGraphicsImageRenderer(size: size).image { context in + let previousFrame = frame + frame = CGRect(origin: frame.origin, size: size) + layer.render(in: context.cgContext) + frame = previousFrame } - guard let context = UIGraphicsGetCurrentContext() else { return nil } - let previousFrame = frame - frame = CGRect(origin: frame.origin, size: contentSize) - layer.render(in: context) - frame = previousFrame - return UIGraphicsGetImageFromCurrentImageContext() } /// SwifterSwift: The currently visible region of the scroll view. @@ -38,19 +37,22 @@ public extension UIScrollView { public extension UIScrollView { /// SwifterSwift: Scroll to the top-most content offset. - /// - Parameter animated: `true` to animate the transition at a constant velocity to the new offset, `false` to make the transition immediate. + /// - Parameter animated: `true` to animate the transition at a constant velocity to the new offset, `false` to make + /// the transition immediate. func scrollToTop(animated: Bool = true) { setContentOffset(CGPoint(x: contentOffset.x, y: -contentInset.top), animated: animated) } /// SwifterSwift: Scroll to the left-most content offset. - /// - Parameter animated: `true` to animate the transition at a constant velocity to the new offset, `false` to make the transition immediate. + /// - Parameter animated: `true` to animate the transition at a constant velocity to the new offset, `false` to make + /// the transition immediate. func scrollToLeft(animated: Bool = true) { setContentOffset(CGPoint(x: -contentInset.left, y: contentOffset.y), animated: animated) } /// SwifterSwift: Scroll to the bottom-most content offset. - /// - Parameter animated: `true` to animate the transition at a constant velocity to the new offset, `false` to make the transition immediate. + /// - Parameter animated: `true` to animate the transition at a constant velocity to the new offset, `false` to make + /// the transition immediate. func scrollToBottom(animated: Bool = true) { setContentOffset( CGPoint(x: contentOffset.x, y: max(0, contentSize.height - bounds.height) + contentInset.bottom), @@ -58,7 +60,8 @@ public extension UIScrollView { } /// SwifterSwift: Scroll to the right-most content offset. - /// - Parameter animated: `true` to animate the transition at a constant velocity to the new offset, `false` to make the transition immediate. + /// - Parameter animated: `true` to animate the transition at a constant velocity to the new offset, `false` to make + /// the transition immediate. func scrollToRight(animated: Bool = true) { setContentOffset( CGPoint(x: max(0, contentSize.width - bounds.width) + contentInset.right, y: contentOffset.y), @@ -67,13 +70,14 @@ public extension UIScrollView { /// SwifterSwift: Scroll up one page of the scroll view. /// If `isPagingEnabled` is `true`, the previous page location is used. - /// - Parameter animated: `true` to animate the transition at a constant velocity to the new offset, `false` to make the transition immediate. + /// - Parameter animated: `true` to animate the transition at a constant velocity to the new offset, `false` to make + /// the transition immediate. func scrollUp(animated: Bool = true) { let minY = -contentInset.top var y = max(minY, contentOffset.y - bounds.height) #if !os(tvOS) if isPagingEnabled, - bounds.height != 0 { + bounds.height != 0 { let page = max(0, ((y + contentInset.top) / bounds.height).rounded(.down)) y = max(minY, page * bounds.height - contentInset.top) } @@ -83,13 +87,14 @@ public extension UIScrollView { /// SwifterSwift: Scroll left one page of the scroll view. /// If `isPagingEnabled` is `true`, the previous page location is used. - /// - Parameter animated: `true` to animate the transition at a constant velocity to the new offset, `false` to make the transition immediate. + /// - Parameter animated: `true` to animate the transition at a constant velocity to the new offset, `false` to make + /// the transition immediate. func scrollLeft(animated: Bool = true) { let minX = -contentInset.left var x = max(minX, contentOffset.x - bounds.width) #if !os(tvOS) if isPagingEnabled, - bounds.width != 0 { + bounds.width != 0 { let page = ((x + contentInset.left) / bounds.width).rounded(.down) x = max(minX, page * bounds.width - contentInset.left) } @@ -99,13 +104,14 @@ public extension UIScrollView { /// SwifterSwift: Scroll down one page of the scroll view. /// If `isPagingEnabled` is `true`, the next page location is used. - /// - Parameter animated: `true` to animate the transition at a constant velocity to the new offset, `false` to make the transition immediate. + /// - Parameter animated: `true` to animate the transition at a constant velocity to the new offset, `false` to make + /// the transition immediate. func scrollDown(animated: Bool = true) { let maxY = max(0, contentSize.height - bounds.height) + contentInset.bottom var y = min(maxY, contentOffset.y + bounds.height) #if !os(tvOS) if isPagingEnabled, - bounds.height != 0 { + bounds.height != 0 { let page = ((y + contentInset.top) / bounds.height).rounded(.down) y = min(maxY, page * bounds.height - contentInset.top) } @@ -115,13 +121,14 @@ public extension UIScrollView { /// SwifterSwift: Scroll right one page of the scroll view. /// If `isPagingEnabled` is `true`, the next page location is used. - /// - Parameter animated: `true` to animate the transition at a constant velocity to the new offset, `false` to make the transition immediate. + /// - Parameter animated: `true` to animate the transition at a constant velocity to the new offset, `false` to make + /// the transition immediate. func scrollRight(animated: Bool = true) { let maxX = max(0, contentSize.width - bounds.width) + contentInset.right var x = min(maxX, contentOffset.x + bounds.width) #if !os(tvOS) if isPagingEnabled, - bounds.width != 0 { + bounds.width != 0 { let page = ((x + contentInset.left) / bounds.width).rounded(.down) x = min(maxX, page * bounds.width - contentInset.left) } diff --git a/Sources/SwifterSwift/UIKit/UISearchBarExtensions.swift b/Sources/SwifterSwift/UIKit/UISearchBarExtensions.swift index 7e311ff5e..5b3c956ba 100644 --- a/Sources/SwifterSwift/UIKit/UISearchBarExtensions.swift +++ b/Sources/SwifterSwift/UIKit/UISearchBarExtensions.swift @@ -1,4 +1,4 @@ -// UISearchBarExtensions.swift - Copyright 2020 SwifterSwift +// UISearchBarExtensions.swift - Copyright 2023 SwifterSwift #if canImport(UIKit) && os(iOS) import UIKit diff --git a/Sources/SwifterSwift/UIKit/UISegmentedControlExtensions.swift b/Sources/SwifterSwift/UIKit/UISegmentedControlExtensions.swift index a757cfa15..1781f52f5 100644 --- a/Sources/SwifterSwift/UIKit/UISegmentedControlExtensions.swift +++ b/Sources/SwifterSwift/UIKit/UISegmentedControlExtensions.swift @@ -1,4 +1,4 @@ -// UISegmentedControlExtensions.swift - Copyright 2020 SwifterSwift +// UISegmentedControlExtensions.swift - Copyright 2023 SwifterSwift #if canImport(UIKit) && !os(watchOS) import UIKit diff --git a/Sources/SwifterSwift/UIKit/UISliderExtensions.swift b/Sources/SwifterSwift/UIKit/UISliderExtensions.swift index 725571f9e..42e7e9d2a 100644 --- a/Sources/SwifterSwift/UIKit/UISliderExtensions.swift +++ b/Sources/SwifterSwift/UIKit/UISliderExtensions.swift @@ -1,4 +1,4 @@ -// UISliderExtensions.swift - Copyright 2020 SwifterSwift +// UISliderExtensions.swift - Copyright 2023 SwifterSwift #if canImport(UIKit) && os(iOS) import UIKit @@ -12,7 +12,7 @@ public extension UISlider { /// - value: slider value. /// - animated: set true to animate value change (default is true). /// - duration: animation duration in seconds (default is 1 second). - /// - completion: an optional completion handler to run after value is changed (default is nil) + /// - completion: an optional completion handler to run after value is changed (default is nil). func setValue(_ value: Float, animated: Bool = true, duration: TimeInterval = 1, completion: (() -> Void)? = nil) { if animated { UIView.animate(withDuration: duration, animations: { diff --git a/Sources/SwifterSwift/UIKit/UIStackViewExtensions.swift b/Sources/SwifterSwift/UIKit/UIStackViewExtensions.swift index 58b7fa325..fb6fc8561 100644 --- a/Sources/SwifterSwift/UIKit/UIStackViewExtensions.swift +++ b/Sources/SwifterSwift/UIKit/UIStackViewExtensions.swift @@ -1,4 +1,4 @@ -// UIStackViewExtensions.swift - Copyright 2020 SwifterSwift +// UIStackViewExtensions.swift - Copyright 2023 SwifterSwift #if canImport(UIKit) && !os(watchOS) import UIKit @@ -6,6 +6,39 @@ import UIKit // MARK: - Initializers public extension UIStackView { + private class BackgroundView: UIView {} + + /// SwifterSwift: Add background color to UIStackView + var backgroundViewColor: UIColor? { + get { + if #available(iOS 14.0, *) { + return backgroundColor + } else { + return subviews.first(where: { $0 is BackgroundView })?.backgroundColor + } + } + set { + if #available(iOS 14.0, *) { + backgroundColor = newValue + } else { + if let existingBackgroundView = subviews.first(where: { $0 is BackgroundView }) { + existingBackgroundView.backgroundColor = newValue + } else { + let backgroundView = BackgroundView() + backgroundView.backgroundColor = newValue + insertSubview(backgroundView, at: 0) + backgroundView.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + backgroundView.topAnchor.constraint(equalTo: topAnchor), + backgroundView.leadingAnchor.constraint(equalTo: leadingAnchor), + backgroundView.bottomAnchor.constraint(equalTo: bottomAnchor), + backgroundView.trailingAnchor.constraint(equalTo: trailingAnchor) + ]) + } + } + } + } + /// SwifterSwift: Initialize an UIStackView with an array of UIView and common parameters. /// /// let stackView = UIStackView(arrangedSubviews: [UIView(), UIView()], axis: .vertical) @@ -13,9 +46,10 @@ public extension UIStackView { /// - Parameters: /// - arrangedSubviews: The UIViews to add to the stack. /// - axis: The axis along which the arranged views are laid out. - /// - spacing: The distance in points between the adjacent edges of the stack view’s arranged views.(default: 0.0) - /// - alignment: The alignment of the arranged subviews perpendicular to the stack view’s axis. (default: .fill) - /// - distribution: The distribution of the arranged views along the stack view’s axis.(default: .fill) + /// - spacing: The distance in points between the adjacent edges of the stack view’s arranged views (default: + /// 0.0). + /// - alignment: The alignment of the arranged subviews perpendicular to the stack view’s axis (default: .fill). + /// - distribution: The distribution of the arranged views along the stack view’s axis (default: .fill). convenience init( arrangedSubviews: [UIView], axis: NSLayoutConstraint.Axis, @@ -44,6 +78,40 @@ public extension UIStackView { removeArrangedSubview(view) } } + + /// SwifterSwift: Exchanges two views of the arranged subviews. + /// - Parameters: + /// - view1: first view to swap. + /// - view2: second view to swap. + /// - animated: set true to animate swap (default is true). + /// - duration: animation duration in seconds (default is 1 second). + /// - delay: animation delay in seconds (default is 1 second). + /// - options: animation options (default is AnimationOptions.curveLinear). + /// - completion: optional completion handler to run with animation finishes (default is nil). + func swap(_ view1: UIView, _ view2: UIView, + animated: Bool = false, + duration: TimeInterval = 0.25, + delay: TimeInterval = 0, + options: UIView.AnimationOptions = .curveLinear, + completion: ((Bool) -> Void)? = nil) { + func swapViews(_ view1: UIView, _ view2: UIView) { + guard let view1Index = arrangedSubviews.firstIndex(of: view1), + let view2Index = arrangedSubviews.firstIndex(of: view2) else { return } + removeArrangedSubview(view1) + insertArrangedSubview(view1, at: view2Index) + + removeArrangedSubview(view2) + insertArrangedSubview(view2, at: view1Index) + } + if animated { + UIView.animate(withDuration: duration, delay: delay, options: options, animations: { + swapViews(view1, view2) + self.layoutIfNeeded() + }, completion: completion) + } else { + swapViews(view1, view2) + } + } } #endif diff --git a/Sources/SwifterSwift/UIKit/UIStoryboardExtensions.swift b/Sources/SwifterSwift/UIKit/UIStoryboardExtensions.swift index 15f297380..8bbb3673e 100644 --- a/Sources/SwifterSwift/UIKit/UIStoryboardExtensions.swift +++ b/Sources/SwifterSwift/UIKit/UIStoryboardExtensions.swift @@ -1,4 +1,4 @@ -// UIStoryboardExtensions.swift - Copyright 2020 SwifterSwift +// UIStoryboardExtensions.swift - Copyright 2023 SwifterSwift #if canImport(UIKit) && !os(watchOS) import UIKit @@ -6,17 +6,17 @@ import UIKit // MARK: - Methods public extension UIStoryboard { - /// SwifterSwift: Get main storyboard for application + /// SwifterSwift: Get main storyboard for application. static var main: UIStoryboard? { let bundle = Bundle.main guard let name = bundle.object(forInfoDictionaryKey: "UIMainStoryboardFile") as? String else { return nil } return UIStoryboard(name: name, bundle: bundle) } - /// SwifterSwift: Instantiate a UIViewController using its class name + /// SwifterSwift: Instantiate a UIViewController using its class name. /// - /// - Parameter name: UIViewController type - /// - Returns: The view controller corresponding to specified class name + /// - Parameter name: UIViewController type. + /// - Returns: The view controller corresponding to specified class name. func instantiateViewController(withClass name: T.Type) -> T? { return instantiateViewController(withIdentifier: String(describing: name)) as? T } diff --git a/Sources/SwifterSwift/UIKit/UISwitchExtensions.swift b/Sources/SwifterSwift/UIKit/UISwitchExtensions.swift index 6cfd9b3a7..7cafeebca 100644 --- a/Sources/SwifterSwift/UIKit/UISwitchExtensions.swift +++ b/Sources/SwifterSwift/UIKit/UISwitchExtensions.swift @@ -1,4 +1,4 @@ -// UISwitchExtensions.swift - Copyright 2020 SwifterSwift +// UISwitchExtensions.swift - Copyright 2023 SwifterSwift #if canImport(UIKit) && os(iOS) import UIKit @@ -6,9 +6,9 @@ import UIKit // MARK: - Methods public extension UISwitch { - /// SwifterSwift: Toggle a UISwitch + /// SwifterSwift: Toggle a UISwitch. /// - /// - Parameter animated: set true to animate the change (default is true) + /// - Parameter animated: set true to animate the change (default is true). func toggle(animated: Bool = true) { setOn(!isOn, animated: animated) } diff --git a/Sources/SwifterSwift/UIKit/UITabBarExtensions.swift b/Sources/SwifterSwift/UIKit/UITabBarExtensions.swift index 380ee2068..c9d4cc8c6 100644 --- a/Sources/SwifterSwift/UIKit/UITabBarExtensions.swift +++ b/Sources/SwifterSwift/UIKit/UITabBarExtensions.swift @@ -1,4 +1,4 @@ -// UITabBarExtensions.swift - Copyright 2020 SwifterSwift +// UITabBarExtensions.swift - Copyright 2023 SwifterSwift #if canImport(UIKit) && !os(watchOS) import UIKit @@ -25,9 +25,11 @@ public extension UITabBar { tintColor = selectedItem ?? tintColor // shadowImage = UIImage() backgroundImage = UIImage() + #if !os(visionOS) isTranslucent = false + #endif - // selectedBackgoundColor + // selectedBackgroundColor guard let barItems = items else { return } @@ -35,13 +37,10 @@ public extension UITabBar { if let selectedbg = selectedBackground { let rect = CGSize(width: frame.width / CGFloat(barItems.count), height: frame.height) selectionIndicatorImage = { (color: UIColor, size: CGSize) -> UIImage in - UIGraphicsBeginImageContextWithOptions(size, false, 1) - color.setFill() - UIRectFill(CGRect(x: 0, y: 0, width: size.width, height: size.height)) - guard let image = UIGraphicsGetImageFromCurrentImageContext() else { return UIImage() } - UIGraphicsEndImageContext() - guard let aCgImage = image.cgImage else { return UIImage() } - return UIImage(cgImage: aCgImage) + return UIGraphicsImageRenderer(size: size).image { context in + color.setFill() + context.fill(context.format.bounds) + } }(selectedbg, rect) } @@ -51,24 +50,23 @@ public extension UITabBar { guard let image = barItem.image else { continue } barItem.image = { (image: UIImage, color: UIColor) -> UIImage in - UIGraphicsBeginImageContextWithOptions(image.size, false, image.scale) - color.setFill() - guard let context = UIGraphicsGetCurrentContext() else { - return image - } + guard let mask = image.cgImage else { return image } - context.translateBy(x: 0, y: image.size.height) - context.scaleBy(x: 1.0, y: -1.0) - context.setBlendMode(CGBlendMode.normal) + let format = UIGraphicsImageRendererFormat() + format.scale = image.scale + return UIGraphicsImageRenderer(size: image.size, format: format).image { + let context = $0.cgContext - let rect = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height) - guard let mask = image.cgImage else { return image } - context.clip(to: rect, mask: mask) - context.fill(rect) + color.setFill() - let newImage = UIGraphicsGetImageFromCurrentImageContext()! - UIGraphicsEndImageContext() - return newImage + context.translateBy(x: 0, y: image.size.height) + context.scaleBy(x: 1.0, y: -1.0) + context.setBlendMode(CGBlendMode.normal) + + let rect = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height) + context.clip(to: rect, mask: mask) + context.fill(rect) + } }(image, itemColor).withRenderingMode(.alwaysOriginal) barItem.setTitleTextAttributes([.foregroundColor: itemColor], for: .normal) diff --git a/Sources/SwifterSwift/UIKit/UITableViewExtensions.swift b/Sources/SwifterSwift/UIKit/UITableViewExtensions.swift index ff33aff45..9e71c7d60 100644 --- a/Sources/SwifterSwift/UIKit/UITableViewExtensions.swift +++ b/Sources/SwifterSwift/UIKit/UITableViewExtensions.swift @@ -1,4 +1,4 @@ -// UITableViewExtensions.swift - Copyright 2020 SwifterSwift +// UITableViewExtensions.swift - Copyright 2023 SwifterSwift #if canImport(UIKit) && !os(watchOS) import UIKit @@ -67,9 +67,9 @@ public extension UITableView { tableHeaderView = nil } - /// SwifterSwift: Dequeue reusable UITableViewCell using class name + /// SwifterSwift: Dequeue reusable UITableViewCell using class name. /// - /// - Parameter name: UITableViewCell type + /// - Parameter name: UITableViewCell type. /// - Returns: UITableViewCell object with associated class name. func dequeueReusableCell(withClass name: T.Type) -> T { guard let cell = dequeueReusableCell(withIdentifier: String(describing: name)) as? T else { @@ -79,7 +79,7 @@ public extension UITableView { return cell } - /// SwifterSwift: Dequeue reusable UITableViewCell using class name for indexPath + /// SwifterSwift: Dequeue reusable UITableViewCell using class name for indexPath. /// /// - Parameters: /// - name: UITableViewCell type. @@ -93,9 +93,9 @@ public extension UITableView { return cell } - /// SwifterSwift: Dequeue reusable UITableViewHeaderFooterView using class name + /// SwifterSwift: Dequeue reusable UITableViewHeaderFooterView using class name. /// - /// - Parameter name: UITableViewHeaderFooterView type + /// - Parameter name: UITableViewHeaderFooterView type. /// - Returns: UITableViewHeaderFooterView object with associated class name. func dequeueReusableHeaderFooterView(withClass name: T.Type) -> T { guard let headerFooterView = dequeueReusableHeaderFooterView(withIdentifier: String(describing: name)) as? T else { @@ -105,7 +105,7 @@ public extension UITableView { return headerFooterView } - /// SwifterSwift: Register UITableViewHeaderFooterView using class name + /// SwifterSwift: Register UITableViewHeaderFooterView using class name. /// /// - Parameters: /// - nib: Nib file used to create the header or footer view. @@ -114,21 +114,21 @@ public extension UITableView { register(nib, forHeaderFooterViewReuseIdentifier: String(describing: name)) } - /// SwifterSwift: Register UITableViewHeaderFooterView using class name + /// SwifterSwift: Register UITableViewHeaderFooterView using class name. /// - /// - Parameter name: UITableViewHeaderFooterView type + /// - Parameter name: UITableViewHeaderFooterView type. func register(headerFooterViewClassWith name: T.Type) { register(T.self, forHeaderFooterViewReuseIdentifier: String(describing: name)) } - /// SwifterSwift: Register UITableViewCell using class name + /// SwifterSwift: Register UITableViewCell using class name. /// - /// - Parameter name: UITableViewCell type + /// - Parameter name: UITableViewCell type. func register(cellWithClass name: T.Type) { register(T.self, forCellReuseIdentifier: String(describing: name)) } - /// SwifterSwift: Register UITableViewCell using class name + /// SwifterSwift: Register UITableViewCell using class name. /// /// - Parameters: /// - nib: Nib file used to create the tableView cell. @@ -154,10 +154,10 @@ public extension UITableView { register(UINib(nibName: identifier, bundle: bundle), forCellReuseIdentifier: identifier) } - /// SwifterSwift: Check whether IndexPath is valid within the tableView + /// SwifterSwift: Check whether IndexPath is valid within the tableView. /// - /// - Parameter indexPath: An IndexPath to check - /// - Returns: Boolean value for valid or invalid IndexPath + /// - Parameter indexPath: An IndexPath to check. + /// - Returns: Boolean value for valid or invalid IndexPath. func isValidIndexPath(_ indexPath: IndexPath) -> Bool { return indexPath.section >= 0 && indexPath.row >= 0 && @@ -165,12 +165,12 @@ public extension UITableView { indexPath.row < numberOfRows(inSection: indexPath.section) } - /// SwifterSwift: Safely scroll to possibly invalid IndexPath + /// SwifterSwift: Safely scroll to possibly invalid IndexPath. /// /// - Parameters: - /// - indexPath: Target IndexPath to scroll to - /// - scrollPosition: Scroll position - /// - animated: Whether to animate or not + /// - indexPath: Target IndexPath to scroll to. + /// - scrollPosition: Scroll position. + /// - animated: Whether to animate or not. func safeScrollToRow(at indexPath: IndexPath, at scrollPosition: UITableView.ScrollPosition, animated: Bool) { guard indexPath.section < numberOfSections else { return } guard indexPath.row < numberOfRows(inSection: indexPath.section) else { return } diff --git a/Sources/SwifterSwift/UIKit/UITextFieldExtensions.swift b/Sources/SwifterSwift/UIKit/UITextFieldExtensions.swift index 508279896..9f5681340 100755 --- a/Sources/SwifterSwift/UIKit/UITextFieldExtensions.swift +++ b/Sources/SwifterSwift/UIKit/UITextFieldExtensions.swift @@ -1,4 +1,4 @@ -// UITextFieldExtensions.swift - Copyright 2020 SwifterSwift +// UITextFieldExtensions.swift - Copyright 2023 SwifterSwift #if canImport(UIKit) && !os(watchOS) import UIKit @@ -146,8 +146,8 @@ public extension UITextField { /// SwifterSwift: Add padding to the left of the textfield rect. /// /// - Parameters: - /// - image: left image - /// - padding: amount of padding between icon and the left of textfield + /// - image: left image. + /// - padding: amount of padding between icon and the left of textfield. func addPaddingLeftIcon(_ image: UIImage, padding: CGFloat) { let iconView = UIView(frame: CGRect(x: 0, y: 0, width: image.size.width + padding, height: image.size.height)) let imageView = UIImageView(image: image) @@ -161,8 +161,8 @@ public extension UITextField { /// SwifterSwift: Add padding to the right of the textfield rect. /// /// - Parameters: - /// - image: right image - /// - padding: amount of padding between icon and the right of textfield + /// - image: right image. + /// - padding: amount of padding between icon and the right of textfield. func addPaddingRightIcon(_ image: UIImage, padding: CGFloat) { let iconView = UIView(frame: CGRect(x: 0, y: 0, width: image.size.width + padding, height: image.size.height)) let imageView = UIImageView(image: image) @@ -172,6 +172,20 @@ public extension UITextField { rightView = iconView rightViewMode = .always } + + /// SwifterSwift: Add tool bars to the textfield input accessory view. + /// - Parameters: + /// - items: The items to present in the toolbar. + /// - height: The height of the toolbar. + #if os(iOS) + @discardableResult + func addToolbar(items: [UIBarButtonItem]?, height: CGFloat = 44) -> UIToolbar { + let toolBar = UIToolbar(frame: CGRect(x: 0.0, y: 0.0, width: UIScreen.main.bounds.size.width, height: height)) + toolBar.setItems(items, animated: false) + inputAccessoryView = toolBar + return toolBar + } + #endif } #endif diff --git a/Sources/SwifterSwift/UIKit/UITextViewExtensions.swift b/Sources/SwifterSwift/UIKit/UITextViewExtensions.swift index c961e5eb3..2f1a80c51 100644 --- a/Sources/SwifterSwift/UIKit/UITextViewExtensions.swift +++ b/Sources/SwifterSwift/UIKit/UITextViewExtensions.swift @@ -1,4 +1,4 @@ -// UITextViewExtensions.swift - Copyright 2020 SwifterSwift +// UITextViewExtensions.swift - Copyright 2023 SwifterSwift #if canImport(UIKit) && !os(watchOS) import UIKit @@ -12,13 +12,13 @@ public extension UITextView { attributedText = NSAttributedString(string: "") } - /// SwifterSwift: Scroll to the bottom of text view + /// SwifterSwift: Scroll to the bottom of text view. func scrollToBottom() { let range = NSRange(location: (text as NSString).length - 1, length: 1) scrollRangeToVisible(range) } - /// SwifterSwift: Scroll to the top of text view + /// SwifterSwift: Scroll to the top of text view. func scrollToTop() { let range = NSRange(location: 0, length: 1) scrollRangeToVisible(range) @@ -26,6 +26,7 @@ public extension UITextView { /// SwifterSwift: Wrap to the content (Text / Attributed Text). func wrapToContent() { + isScrollEnabled = false contentInset = .zero scrollIndicatorInsets = .zero contentOffset = .zero diff --git a/Sources/SwifterSwift/UIKit/UIViewControllerExtensions.swift b/Sources/SwifterSwift/UIKit/UIViewControllerExtensions.swift index 5fde29d46..40b4732b7 100755 --- a/Sources/SwifterSwift/UIKit/UIViewControllerExtensions.swift +++ b/Sources/SwifterSwift/UIKit/UIViewControllerExtensions.swift @@ -1,4 +1,4 @@ -// UIViewControllerExtensions.swift - Copyright 2020 SwifterSwift +// UIViewControllerExtensions.swift - Copyright 2023 SwifterSwift #if canImport(UIKit) && !os(watchOS) import UIKit @@ -16,13 +16,13 @@ public extension UIViewController { // MARK: - Methods public extension UIViewController { - /// SwifterSwift: Instantiate UIViewController from storyboard + /// SwifterSwift: Instantiate UIViewController from storyboard. /// /// - Parameters: - /// - storyboard: Name of the storyboard where the UIViewController is located - /// - bundle: Bundle in which storyboard is located - /// - identifier: UIViewController's storyboard identifier - /// - Returns: Custom UIViewController instantiated from storyboard + /// - storyboard: Name of the storyboard where the UIViewController is located. + /// - bundle: Bundle in which storyboard is located. + /// - identifier: UIViewController's storyboard identifier. + /// - Returns: Custom UIViewController instantiated from storyboard. class func instantiate(from storyboard: String = "Main", bundle: Bundle? = nil, identifier: String? = nil) -> Self { let viewControllerIdentifier = identifier ?? String(describing: self) let storyboard = UIStoryboard(name: storyboard, bundle: bundle) @@ -52,17 +52,22 @@ public extension UIViewController { /// SwifterSwift: Unassign as listener from all notifications. func removeNotificationsObserver() { + // swiftlint:disable:next notification_center_detachment NotificationCenter.default.removeObserver(self) } - /// SwifterSwift: Helper method to display an alert on any UIViewController subclass. Uses UIAlertController to show an alert + /// SwifterSwift: Helper method to display an alert on any UIViewController subclass. Uses UIAlertController to show + /// an alert. /// /// - Parameters: - /// - title: title of the alert - /// - message: message/body of the alert - /// - buttonTitles: (Optional)list of button titles for the alert. Default button i.e "OK" will be shown if this paramter is nil - /// - highlightedButtonIndex: (Optional) index of the button from buttonTitles that should be highlighted. If this parameter is nil no button will be highlighted - /// - completion: (Optional) completion block to be invoked when any one of the buttons is tapped. It passes the index of the tapped button as an argument + /// - title: title of the alert. + /// - message: message/body of the alert. + /// - buttonTitles: (Optional)list of button titles for the alert. Default button i.e "OK" will be shown if this + /// parameter is nil. + /// - highlightedButtonIndex: (Optional) index of the button from buttonTitles that should be highlighted. If this + /// parameter is nil no button will be highlighted. + /// - completion: (Optional) completion block to be invoked when any one of the buttons is tapped. It passes the + /// index of the tapped button as an argument. /// - Returns: UIAlertController object (discardable). @discardableResult func showAlert( @@ -95,8 +100,8 @@ public extension UIViewController { /// SwifterSwift: Helper method to add a UIViewController as a childViewController. /// /// - Parameters: - /// - child: the view controller to add as a child - /// - containerView: the containerView for the child viewcontroller's root view. + /// - child: the view controller to add as a child. + /// - containerView: the containerView for the child viewController's root view. func addChildViewController(_ child: UIViewController, toContainerView containerView: UIView) { addChild(child) containerView.addSubview(child.view) @@ -126,7 +131,7 @@ public extension UIViewController { _ popoverContent: UIViewController, sourcePoint: CGPoint, size: CGSize? = nil, - delegate: UIPopoverPresentationControllerDelegate? = nil, + delegate: (any UIPopoverPresentationControllerDelegate)? = nil, animated: Bool = true, completion: (() -> Void)? = nil) { popoverContent.modalPresentationStyle = .popover diff --git a/Sources/SwifterSwift/UIKit/UIViewExtensions.swift b/Sources/SwifterSwift/UIKit/UIViewExtensions.swift index d2b6e47ac..62d8b63fa 100755 --- a/Sources/SwifterSwift/UIKit/UIViewExtensions.swift +++ b/Sources/SwifterSwift/UIKit/UIViewExtensions.swift @@ -1,4 +1,4 @@ -// UIViewExtensions.swift - Copyright 2020 SwifterSwift +// UIViewExtensions.swift - Copyright 2023 SwifterSwift #if canImport(UIKit) && !os(watchOS) import UIKit @@ -49,13 +49,33 @@ public extension UIView { /// SwifterSwift: easeInOut animation. case easeInOut } + + /// SwifterSwift: Add gradient directions + struct GradientDirection { + public static let topToBottom = GradientDirection(startPoint: CGPoint(x: 0.5, y: 0.0), + endPoint: CGPoint(x: 0.5, y: 1.0)) + public static let bottomToTop = GradientDirection(startPoint: CGPoint(x: 0.5, y: 1.0), + endPoint: CGPoint(x: 0.5, y: 0.0)) + public static let leftToRight = GradientDirection(startPoint: CGPoint(x: 0.0, y: 0.5), + endPoint: CGPoint(x: 1.0, y: 0.5)) + public static let rightToLeft = GradientDirection(startPoint: CGPoint(x: 1.0, y: 0.5), + endPoint: CGPoint(x: 0.0, y: 0.5)) + + public let startPoint: CGPoint + public let endPoint: CGPoint + + public init(startPoint: CGPoint, endPoint: CGPoint) { + self.startPoint = startPoint + self.endPoint = endPoint + } + } } // MARK: - Properties public extension UIView { /// SwifterSwift: Border color of view; also inspectable from Storyboard. - @IBInspectable var borderColor: UIColor? { + @IBInspectable var layerBorderColor: UIColor? { get { guard let color = layer.borderColor else { return nil } return UIColor(cgColor: color) @@ -72,7 +92,7 @@ public extension UIView { } /// SwifterSwift: Border width of view; also inspectable from Storyboard. - @IBInspectable var borderWidth: CGFloat { + @IBInspectable var layerBorderWidth: CGFloat { get { return layer.borderWidth } @@ -82,7 +102,7 @@ public extension UIView { } /// SwifterSwift: Corner radius of view; also inspectable from Storyboard. - @IBInspectable var cornerRadius: CGFloat { + @IBInspectable var layerCornerRadius: CGFloat { get { return layer.cornerRadius } @@ -104,22 +124,16 @@ public extension UIView { /// SwifterSwift: Check if view is in RTL format. var isRightToLeft: Bool { - if #available(iOS 10.0, macCatalyst 13.0, tvOS 10.0, *) { - return effectiveUserInterfaceLayoutDirection == .rightToLeft - } else { - return false - } + return effectiveUserInterfaceLayoutDirection == .rightToLeft } /// SwifterSwift: Take screenshot of view (if applicable). var screenshot: UIImage? { - UIGraphicsBeginImageContextWithOptions(layer.frame.size, false, 0) - defer { - UIGraphicsEndImageContext() + let size = layer.frame.size + guard size != .zero else { return nil } + return UIGraphicsImageRenderer(size: layer.frame.size).image { context in + layer.render(in: context.cgContext) } - guard let context = UIGraphicsGetCurrentContext() else { return nil } - layer.render(in: context) - return UIGraphicsGetImageFromCurrentImageContext() } /// SwifterSwift: Shadow color of view; also inspectable from Storyboard. @@ -270,7 +284,7 @@ public extension UIView { /// - color: shadow color (default is #137992). /// - radius: shadow radius (default is 3). /// - offset: shadow offset (default is .zero). - /// - opacity: shadow opacity (default is 0.5). It will also be affected by the `alpha` of `backgroundColor` + /// - opacity: shadow opacity (default is 0.5). It will also be affected by the `alpha` of `backgroundColor`. func addShadow( ofColor color: UIColor = UIColor(red: 0.07, green: 0.47, blue: 0.57, alpha: 1.0), radius: CGFloat = 3, @@ -294,7 +308,7 @@ public extension UIView { /// /// - Parameters: /// - duration: animation duration in seconds (default is 1 second). - /// - completion: optional completion handler to run with animation finishes (default is nil) + /// - completion: optional completion handler to run with animation finishes (default is nil). func fadeIn(duration: TimeInterval = 1, completion: ((Bool) -> Void)? = nil) { if isHidden { isHidden = false @@ -308,7 +322,7 @@ public extension UIView { /// /// - Parameters: /// - duration: animation duration in seconds (default is 1 second). - /// - completion: optional completion handler to run with animation finishes (default is nil) + /// - completion: optional completion handler to run with animation finishes (default is nil). func fadeOut(duration: TimeInterval = 1, completion: ((Bool) -> Void)? = nil) { if isHidden { isHidden = false @@ -352,7 +366,9 @@ public extension UIView { gestureRecognizers?.forEach(removeGestureRecognizer) } - /// SwifterSwift: Attaches gesture recognizers to the view. Attaching gesture recognizers to a view defines the scope of the represented gesture, causing it to receive touches hit-tested to that view and all of its subviews. The view establishes a strong reference to the gesture recognizers. + /// SwifterSwift: Attaches gesture recognizers to the view. Attaching gesture recognizers to a view defines the + /// scope of the represented gesture, causing it to receive touches hit-tested to that view and all of its subviews. + /// The view establishes a strong reference to the gesture recognizers. /// /// - Parameter gestureRecognizers: The array of gesture recognizers to be added to the view. func addGestureRecognizers(_ gestureRecognizers: [UIGestureRecognizer]) { @@ -361,7 +377,8 @@ public extension UIView { } } - /// SwifterSwift: Detaches gesture recognizers from the receiving view. This method releases gestureRecognizers in addition to detaching them from the view. + /// SwifterSwift: Detaches gesture recognizers from the receiving view. This method releases gestureRecognizers in + /// addition to detaching them from the view. /// /// - Parameter gestureRecognizers: The array of gesture recognizers to be removed from the view. func removeGestureRecognizers(_ gestureRecognizers: [UIGestureRecognizer]) { @@ -386,7 +403,7 @@ public extension UIView { completion: ((Bool) -> Void)? = nil) { let angleWithType = (type == .degrees) ? .pi * angle / 180.0 : angle let aDuration = animated ? duration : 0 - UIView.animate(withDuration: aDuration, delay: 0, options: .curveLinear, animations: { () -> Void in + UIView.animate(withDuration: aDuration, delay: 0, options: .curveLinear, animations: { () in self.transform = self.transform.rotated(by: angleWithType) }, completion: completion) } @@ -406,9 +423,10 @@ public extension UIView { duration: TimeInterval = 1, completion: ((Bool) -> Void)? = nil) { let angleWithType = (type == .degrees) ? .pi * angle / 180.0 : angle + let currentAngle = atan2(transform.b, transform.a) let aDuration = animated ? duration : 0 UIView.animate(withDuration: aDuration, animations: { - self.transform = self.transform.concatenating(CGAffineTransform(rotationAngle: angleWithType)) + self.transform = self.transform.rotated(by: angleWithType - currentAngle) }, completion: completion) } @@ -425,7 +443,7 @@ public extension UIView { duration: TimeInterval = 1, completion: ((Bool) -> Void)? = nil) { if animated { - UIView.animate(withDuration: duration, delay: 0, options: .curveLinear, animations: { () -> Void in + UIView.animate(withDuration: duration, delay: 0, options: .curveLinear, animations: { () in self.transform = self.transform.scaledBy(x: offset.x, y: offset.y) }, completion: completion) } else { @@ -437,7 +455,7 @@ public extension UIView { /// SwifterSwift: Shake view. /// /// - Parameters: - /// - direction: shake direction (horizontal or vertical), (default is .horizontal) + /// - direction: shake direction (horizontal or vertical), (default is .horizontal). /// - duration: animation duration in seconds (default is 1 second). /// - animationType: shake animation type (default is .easeOut). /// - completion: optional completion handler to run with animation finishes (default is nil). @@ -471,12 +489,37 @@ public extension UIView { CATransaction.commit() } + /// SwifterSwift: Add Gradient Colors. + /// + /// view.addGradient( + /// colors: [.red, .blue], + /// locations: [0.0, 1.0], + /// direction: .topToBottom + /// ) + /// + /// - Parameters: + /// - colors: An array of `SFColor` defining the color of each gradient stop. + /// - locations: An array of `CGFloat` defining the location of each + /// gradient stop as a value in the range [0, 1]. The values must be + /// monotonically increasing. + /// - direction: Struct type describing the direction of the gradient. + func addGradient(colors: [SFColor], locations: [CGFloat] = [0.0, 1.0], direction: GradientDirection) { + // + let gradientLayer = CAGradientLayer() + gradientLayer.frame = bounds + gradientLayer.colors = colors.map(\.cgColor) + gradientLayer.locations = locations.map { NSNumber(value: $0) } + gradientLayer.startPoint = direction.startPoint + gradientLayer.endPoint = direction.endPoint + layer.addSublayer(gradientLayer) + } + /// SwifterSwift: Add Visual Format constraints. /// /// - Parameters: - /// - withFormat: visual Format language - /// - views: array of views which will be accessed starting with index 0 (example: [v0], [v1], [v2]..) - @available(iOS 9, *) func addConstraints(withFormat: String, views: UIView...) { + /// - withFormat: visual Format language. + /// - views: array of views which will be accessed starting with index 0 (example: [v0], [v1], [v2]..). + func addConstraints(withFormat: String, views: UIView...) { // https://videos.letsbuildthatapp.com/ var viewsDictionary: [String: UIView] = [:] for (index, view) in views.enumerated() { @@ -492,7 +535,6 @@ public extension UIView { } /// SwifterSwift: Anchor all sides of the view into it's superview. - @available(iOS 9, *) func fillToSuperview() { // https://videos.letsbuildthatapp.com/ translatesAutoresizingMaskIntoConstraints = false @@ -505,21 +547,21 @@ public extension UIView { } } - /// SwifterSwift: Add anchors from any side of the current view into the specified anchors and returns the newly added constraints. + /// SwifterSwift: Add anchors from any side of the current view into the specified anchors and returns the newly + /// added constraints. /// /// - Parameters: - /// - top: current view's top anchor will be anchored into the specified anchor - /// - left: current view's left anchor will be anchored into the specified anchor - /// - bottom: current view's bottom anchor will be anchored into the specified anchor - /// - right: current view's right anchor will be anchored into the specified anchor - /// - topConstant: current view's top anchor margin - /// - leftConstant: current view's left anchor margin - /// - bottomConstant: current view's bottom anchor margin - /// - rightConstant: current view's right anchor margin - /// - widthConstant: current view's width - /// - heightConstant: current view's height + /// - top: current view's top anchor will be anchored into the specified anchor. + /// - left: current view's left anchor will be anchored into the specified anchor. + /// - bottom: current view's bottom anchor will be anchored into the specified anchor. + /// - right: current view's right anchor will be anchored into the specified anchor. + /// - topConstant: current view's top anchor margin. + /// - leftConstant: current view's left anchor margin. + /// - bottomConstant: current view's bottom anchor margin. + /// - rightConstant: current view's right anchor margin. + /// - widthConstant: current view's width. + /// - heightConstant: current view's height. /// - Returns: array of newly added constraints (if applicable). - @available(iOS 9, *) @discardableResult func anchor( top: NSLayoutYAxisAnchor? = nil, @@ -561,7 +603,7 @@ public extension UIView { anchors.append(heightAnchor.constraint(equalToConstant: heightConstant)) } - anchors.forEach { $0.isActive = true } + NSLayoutConstraint.activate(anchors) return anchors } @@ -569,7 +611,6 @@ public extension UIView { /// SwifterSwift: Anchor center X into current view's superview with a constant margin value. /// /// - Parameter constant: constant of the anchor constraint (default is 0). - @available(iOS 9, *) func anchorCenterXToSuperview(constant: CGFloat = 0) { // https://videos.letsbuildthatapp.com/ translatesAutoresizingMaskIntoConstraints = false @@ -581,7 +622,6 @@ public extension UIView { /// SwifterSwift: Anchor center Y into current view's superview with a constant margin value. /// /// - Parameter withConstant: constant of the anchor constraint (default is 0). - @available(iOS 9, *) func anchorCenterYToSuperview(constant: CGFloat = 0) { // https://videos.letsbuildthatapp.com/ translatesAutoresizingMaskIntoConstraints = false @@ -590,8 +630,7 @@ public extension UIView { } } - /// SwifterSwift: Anchor center X and Y into current view's superview - @available(iOS 9, *) + /// SwifterSwift: Anchor center X and Y into current view's superview. func anchorCenterSuperview() { // https://videos.letsbuildthatapp.com/ anchorCenterXToSuperview() @@ -614,6 +653,23 @@ public extension UIView { func ancestorView(withClass _: T.Type) -> T? { return ancestorView(where: { $0 is T }) as? T } + + /// SwifterSwift: Returns all the subviews of a given type recursively in the + /// view hierarchy rooted on the view it its called. + /// + /// - Parameter ofType: Class of the view to search. + /// - Returns: All subviews with a specified type. + func subviews(ofType _: T.Type) -> [T] { + var views = [T]() + for subview in subviews { + if let view = subview as? T { + views.append(view) + } else if !subview.subviews.isEmpty { + views.append(contentsOf: subview.subviews(ofType: T.self)) + } + } + return views + } } // MARK: - Constraints @@ -623,9 +679,9 @@ public extension UIView { /// and attribute. This will enumerate ancestors since constraints are /// always added to the common ancestor. /// - /// - Parameter attribute: the attribute to find - /// - Parameter at: the view to find - /// - Returns: matching constraint + /// - Parameter attribute: the attribute to find. + /// - Parameter at: the view to find. + /// - Returns: matching constraint. func findConstraint(attribute: NSLayoutConstraint.Attribute, for view: UIView) -> NSLayoutConstraint? { let constraint = constraints.first { ($0.firstAttribute == attribute && $0.firstItem as? UIView == view) || @@ -634,32 +690,32 @@ public extension UIView { return constraint ?? superview?.findConstraint(attribute: attribute, for: view) } - /// SwifterSwift: First width constraint for this view + /// SwifterSwift: First width constraint for this view. var widthConstraint: NSLayoutConstraint? { findConstraint(attribute: .width, for: self) } - /// SwifterSwift: First height constraint for this view + /// SwifterSwift: First height constraint for this view. var heightConstraint: NSLayoutConstraint? { findConstraint(attribute: .height, for: self) } - /// SwifterSwift: First leading constraint for this view + /// SwifterSwift: First leading constraint for this view. var leadingConstraint: NSLayoutConstraint? { findConstraint(attribute: .leading, for: self) } - /// SwifterSwift: First trailing constraint for this view + /// SwifterSwift: First trailing constraint for this view. var trailingConstraint: NSLayoutConstraint? { findConstraint(attribute: .trailing, for: self) } - /// SwifterSwift: First top constraint for this view + /// SwifterSwift: First top constraint for this view. var topConstraint: NSLayoutConstraint? { findConstraint(attribute: .top, for: self) } - /// SwifterSwift: First bottom constraint for this view + /// SwifterSwift: First bottom constraint for this view. var bottomConstraint: NSLayoutConstraint? { findConstraint(attribute: .bottom, for: self) } diff --git a/Sources/SwifterSwift/UIKit/UIWindowExtensions.swift b/Sources/SwifterSwift/UIKit/UIWindowExtensions.swift index 1b5d66b0b..00d43fccc 100644 --- a/Sources/SwifterSwift/UIKit/UIWindowExtensions.swift +++ b/Sources/SwifterSwift/UIKit/UIWindowExtensions.swift @@ -1,4 +1,4 @@ -// UIWindowExtensions.swift - Copyright 2020 SwifterSwift +// UIWindowExtensions.swift - Copyright 2023 SwifterSwift #if canImport(UIKit) && os(iOS) import UIKit diff --git a/Sources/SwifterSwift/WebKit/WKWebViewExtensions.swift b/Sources/SwifterSwift/WebKit/WKWebViewExtensions.swift index cd3ec5209..a30c8495a 100644 --- a/Sources/SwifterSwift/WebKit/WKWebViewExtensions.swift +++ b/Sources/SwifterSwift/WebKit/WKWebViewExtensions.swift @@ -1,16 +1,16 @@ -// WKWebViewExtensions.swift - Copyright 2020 SwifterSwift +// WKWebViewExtensions.swift - Copyright 2023 SwifterSwift #if canImport(WebKit) import WebKit // MARK: - Methods -extension WKWebView { +public extension WKWebView { /// SwifterSwift: Navigate to `url`. /// - Parameter url: URL to navigate. /// - Returns: A new navigation for given `url`. @discardableResult - public func loadURL(_ url: URL) -> WKNavigation? { + func loadURL(_ url: URL) -> WKNavigation? { return load(URLRequest(url: url)) } @@ -18,9 +18,13 @@ extension WKWebView { /// - Parameter urlString: The string specifying the URL to navigate to. /// - Returns: A new navigation for given `urlString`. @discardableResult - public func loadURLString(_ urlString: String) -> WKNavigation? { + func loadURLString(_ urlString: String, timeout: TimeInterval? = nil) -> WKNavigation? { guard let url = URL(string: urlString) else { return nil } - return load(URLRequest(url: url)) + var request = URLRequest(url: url) + if let timeout = timeout { + request.timeoutInterval = timeout + } + return load(request) } } diff --git a/SwifterSwift.podspec b/SwifterSwift.podspec index d87a2fae0..826d502e3 100644 --- a/SwifterSwift.podspec +++ b/SwifterSwift.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = 'SwifterSwift' - s.version = '5.2.0' + s.version = '6.0.0' s.summary = 'A handy collection of more than 500 native Swift extensions to boost your productivity.' s.description = <<-DESC SwifterSwift is a collection of over 500 native Swift extensions, with handy methods, syntactic sugar, and performance improvements for wide range of primitive data types, UIKit and Cocoa classes –over 500 in 1– for iOS, macOS, tvOS and watchOS. @@ -13,12 +13,12 @@ Pod::Spec.new do |s| s.screenshot = 'https://raw.githubusercontent.com/SwifterSwift/SwifterSwift/master/Assets/logo.png' s.documentation_url = 'http://swifterswift.com/docs' - s.ios.deployment_target = '10.0' - s.osx.deployment_target = '10.10' - s.tvos.deployment_target = '9.0' - s.watchos.deployment_target = '2.0' + s.ios.deployment_target = '12.0' + s.osx.deployment_target = '10.13' + s.tvos.deployment_target = '12.0' + s.watchos.deployment_target = '4.0' - s.swift_version = '5.1' + s.swift_version = '5.8' s.requires_arc = true s.source = { git: 'https://github.com/SwifterSwift/SwifterSwift.git', tag: s.version.to_s } s.source_files = 'Sources/SwifterSwift/**/*.swift' @@ -58,6 +58,11 @@ Pod::Spec.new do |s| sp.source_files = 'Sources/SwifterSwift/Shared/*.swift', 'Sources/SwifterSwift/CoreAnimation/*.swift' end + # CryptoKit Extensions + s.subspec 'CryptoKit' do |sp| + sp.source_files = 'Sources/SwifterSwift/Shared/*.swift', 'Sources/SwifterSwift/CryptoKit/*.swift' + end + # MapKit Extensions s.subspec 'MapKit' do |sp| sp.source_files = 'Sources/SwifterSwift/Shared/*.swift', 'Sources/SwifterSwift/MapKit/*.swift' @@ -92,4 +97,9 @@ Pod::Spec.new do |s| sp.source_files = 'Sources/SwifterSwift/HealthKit/*.swift' end + # Combine Extensions + s.subspec 'Combine' do |sp| + sp.source_files = 'Sources/SwifterSwift/Combine/*.swift' + end + end diff --git a/SwifterSwift.xcodeproj/project.pbxproj b/SwifterSwift.xcodeproj/project.pbxproj index 09799333a..8498ae589 100644 --- a/SwifterSwift.xcodeproj/project.pbxproj +++ b/SwifterSwift.xcodeproj/project.pbxproj @@ -3,7 +3,7 @@ archiveVersion = 1; classes = { }; - objectVersion = 48; + objectVersion = 54; objects = { /* Begin PBXBuildFile section */ @@ -21,8 +21,6 @@ 074EAF1D1F7BA68B00C74636 /* UIFontExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 074EAF1A1F7BA68B00C74636 /* UIFontExtensions.swift */; }; 074EAF1F1F7BA74700C74636 /* UIFontExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 074EAF1E1F7BA74600C74636 /* UIFontExtensionsTests.swift */; }; 074EAF201F7BA74700C74636 /* UIFontExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 074EAF1E1F7BA74600C74636 /* UIFontExtensionsTests.swift */; }; - 076AEC891FDB48580077D153 /* UIDatePickerExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 076AEC881FDB48580077D153 /* UIDatePickerExtensions.swift */; }; - 076AEC8D1FDB49480077D153 /* UIDatePickerExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 076AEC8C1FDB49480077D153 /* UIDatePickerExtensionsTests.swift */; }; 0770035920729B040095F09A /* MKPolylineExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 784C752E2051BD26001C48DD /* MKPolylineExtensions.swift */; }; 077BA08A1F6BE81F00D9C4AC /* URLRequestExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 077BA0891F6BE81F00D9C4AC /* URLRequestExtensions.swift */; }; 077BA08B1F6BE81F00D9C4AC /* URLRequestExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 077BA0891F6BE81F00D9C4AC /* URLRequestExtensions.swift */; }; @@ -338,6 +336,11 @@ 116090B424187D8100DDCD01 /* CGRectExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 116090B324187D8100DDCD01 /* CGRectExtensionsTests.swift */; }; 116090B524187D8100DDCD01 /* CGRectExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 116090B324187D8100DDCD01 /* CGRectExtensionsTests.swift */; }; 116090B624187D8100DDCD01 /* CGRectExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 116090B324187D8100DDCD01 /* CGRectExtensionsTests.swift */; }; + 17A4B79326CCFFAE007D299F /* SKSpriteNodeExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17A4B79226CCFFAE007D299F /* SKSpriteNodeExtensions.swift */; }; + 17A4B79426CCFFAF007D299F /* SKSpriteNodeExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17A4B79226CCFFAE007D299F /* SKSpriteNodeExtensions.swift */; }; + 17A4B79526CCFFAF007D299F /* SKSpriteNodeExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17A4B79226CCFFAE007D299F /* SKSpriteNodeExtensions.swift */; }; + 17A4B79626CCFFAF007D299F /* SKSpriteNodeExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17A4B79226CCFFAE007D299F /* SKSpriteNodeExtensions.swift */; }; + 17A4B79826CD0DA2007D299F /* SKSpriteNodeExtensionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 17A4B79726CD0DA2007D299F /* SKSpriteNodeExtensionTests.swift */; }; 182698AC1F8AB46E0052F21E /* CGColorExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 185BDE601F8AAEFD00140E19 /* CGColorExtensionsTests.swift */; }; 182698AD1F8AB46F0052F21E /* CGColorExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 185BDE601F8AAEFD00140E19 /* CGColorExtensionsTests.swift */; }; 182698AE1F8AB46F0052F21E /* CGColorExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 185BDE601F8AAEFD00140E19 /* CGColorExtensionsTests.swift */; }; @@ -358,7 +361,25 @@ 278CA0911F9A9679004918BD /* NSImageExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 278CA0901F9A9679004918BD /* NSImageExtensionsTests.swift */; }; 42F53FEC2039C5AC0070DC11 /* UIStackViewExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42F53FEB2039C5AC0070DC11 /* UIStackViewExtensions.swift */; }; 42F53FF02039C7140070DC11 /* UIStackViewExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 42F53FEF2039C7140070DC11 /* UIStackViewExtensionsTests.swift */; }; + 58253512259CF23B00407B78 /* MeasurementExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58253511259CF23B00407B78 /* MeasurementExtensions.swift */; }; + 58253513259CF23B00407B78 /* MeasurementExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58253511259CF23B00407B78 /* MeasurementExtensions.swift */; }; + 58253514259CF23B00407B78 /* MeasurementExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58253511259CF23B00407B78 /* MeasurementExtensions.swift */; }; + 58253515259CF23B00407B78 /* MeasurementExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58253511259CF23B00407B78 /* MeasurementExtensions.swift */; }; + 5898A74228D1D7C200261630 /* DefaultStringInterpolationExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58CAE63128CF2C53001935A2 /* DefaultStringInterpolationExtensionsTests.swift */; }; + 5898A74328D1D7C300261630 /* DefaultStringInterpolationExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58CAE63128CF2C53001935A2 /* DefaultStringInterpolationExtensionsTests.swift */; }; + 5898A74428D1D7C400261630 /* DefaultStringInterpolationExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58CAE63128CF2C53001935A2 /* DefaultStringInterpolationExtensionsTests.swift */; }; + 58CAE62928CF2935001935A2 /* DefaultStringInterpolationExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58CAE62828CF2935001935A2 /* DefaultStringInterpolationExtensions.swift */; }; + 58CAE62A28CF2935001935A2 /* DefaultStringInterpolationExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58CAE62828CF2935001935A2 /* DefaultStringInterpolationExtensions.swift */; }; + 58CAE62B28CF2935001935A2 /* DefaultStringInterpolationExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58CAE62828CF2935001935A2 /* DefaultStringInterpolationExtensions.swift */; }; + 58CAE62C28CF2935001935A2 /* DefaultStringInterpolationExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 58CAE62828CF2935001935A2 /* DefaultStringInterpolationExtensions.swift */; }; 5C88FBEC234CC1280065A942 /* NSColorExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C88FBEB234CC1280065A942 /* NSColorExtensionsTests.swift */; }; + 658A02F2270F01C500DBA951 /* MKMultiPointExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 658A02F1270F01C500DBA951 /* MKMultiPointExtensions.swift */; }; + 658A02F3270F01D200DBA951 /* MKMultiPointExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 658A02F1270F01C500DBA951 /* MKMultiPointExtensions.swift */; }; + 658A02F4270F01D200DBA951 /* MKMultiPointExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 658A02F1270F01C500DBA951 /* MKMultiPointExtensions.swift */; }; + 658A02F5270F01D300DBA951 /* MKMultiPointExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 658A02F1270F01C500DBA951 /* MKMultiPointExtensions.swift */; }; + 658A02F7270F024700DBA951 /* MKMultiPointExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 658A02F6270F024700DBA951 /* MKMultiPointExtensionsTests.swift */; }; + 658A02F8270F024B00DBA951 /* MKMultiPointExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 658A02F6270F024700DBA951 /* MKMultiPointExtensionsTests.swift */; }; + 658A02F9270F024C00DBA951 /* MKMultiPointExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 658A02F6270F024700DBA951 /* MKMultiPointExtensionsTests.swift */; }; 664CB96D2171863B00FC87B4 /* BidirectionalCollectionExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 664CB96C2171863B00FC87B4 /* BidirectionalCollectionExtensions.swift */; }; 664CB96E2171863B00FC87B4 /* BidirectionalCollectionExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 664CB96C2171863B00FC87B4 /* BidirectionalCollectionExtensions.swift */; }; 664CB96F2171863B00FC87B4 /* BidirectionalCollectionExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 664CB96C2171863B00FC87B4 /* BidirectionalCollectionExtensions.swift */; }; @@ -414,16 +435,23 @@ 784C752F2051BD26001C48DD /* MKPolylineExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 784C752E2051BD26001C48DD /* MKPolylineExtensions.swift */; }; 784C75302051BD4A001C48DD /* MKPolylineExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 784C752E2051BD26001C48DD /* MKPolylineExtensions.swift */; }; 784C75312051BD4B001C48DD /* MKPolylineExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 784C752E2051BD26001C48DD /* MKPolylineExtensions.swift */; }; - 784C75372051BE1D001C48DD /* MKPolylineTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 784C75362051BE1D001C48DD /* MKPolylineTests.swift */; }; - 784C75382051BE1D001C48DD /* MKPolylineTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 784C75362051BE1D001C48DD /* MKPolylineTests.swift */; }; - 784C75392051BE1D001C48DD /* MKPolylineTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 784C75362051BE1D001C48DD /* MKPolylineTests.swift */; }; + 784C75372051BE1D001C48DD /* MKPolylineExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 784C75362051BE1D001C48DD /* MKPolylineExtensionsTests.swift */; }; + 784C75382051BE1D001C48DD /* MKPolylineExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 784C75362051BE1D001C48DD /* MKPolylineExtensionsTests.swift */; }; + 784C75392051BE1D001C48DD /* MKPolylineExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 784C75362051BE1D001C48DD /* MKPolylineExtensionsTests.swift */; }; 86B3F7AC208D3D5C00BC297B /* UIScrollViewExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 86B3F7AB208D3D5C00BC297B /* UIScrollViewExtensions.swift */; }; 86B3F7AE208D3DF300BC297B /* UIScrollViewExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 86B3F7AD208D3DF300BC297B /* UIScrollViewExtensionsTests.swift */; }; + 8AC39B6328790A6B0041A56C /* DigestExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AC39B6228790A6B0041A56C /* DigestExtensions.swift */; }; + 8AC39B6428790A6B0041A56C /* DigestExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AC39B6228790A6B0041A56C /* DigestExtensions.swift */; }; + 8AC39B6528790A6B0041A56C /* DigestExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AC39B6228790A6B0041A56C /* DigestExtensions.swift */; }; + 8AC39B6628790A6B0041A56C /* DigestExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AC39B6228790A6B0041A56C /* DigestExtensions.swift */; }; + 8AC39B6928790B5F0041A56C /* DigestExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AC39B6828790B5F0041A56C /* DigestExtensionsTests.swift */; }; + 8AC39B6A28790B5F0041A56C /* DigestExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AC39B6828790B5F0041A56C /* DigestExtensionsTests.swift */; }; + 8AC39B6B28790B5F0041A56C /* DigestExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8AC39B6828790B5F0041A56C /* DigestExtensionsTests.swift */; }; 8D4B424C212972AE002A5923 /* UILayoutPriorityExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D4B424B212972AE002A5923 /* UILayoutPriorityExtensions.swift */; }; 8D4B424D212972AE002A5923 /* UILayoutPriorityExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D4B424B212972AE002A5923 /* UILayoutPriorityExtensions.swift */; }; 8D4B424F212972E7002A5923 /* UILayoutPriorityExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D4B424E212972E7002A5923 /* UILayoutPriorityExtensionsTests.swift */; }; 8D4B4250212972E7002A5923 /* UILayoutPriorityExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8D4B424E212972E7002A5923 /* UILayoutPriorityExtensionsTests.swift */; }; - 8D50E52224D0D76400972E2D /* UIImageView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 8D50E52024D0D73E00972E2D /* UIImageView.xib */; }; + 8D50E52224D0D76400972E2D /* UIImageViewTvOS.xib in Resources */ = {isa = PBXBuildFile; fileRef = 8D50E52024D0D73E00972E2D /* UIImageViewTvOS.xib */; }; 8D50E52324D0D76B00972E2D /* UIImageView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 8D50E51E24D0D71E00972E2D /* UIImageView.xib */; }; 90693551208B4F9400407C4D /* UIGestureRecognizerExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90693550208B4F9400407C4D /* UIGestureRecognizerExtensions.swift */; }; 90693555208B545100407C4D /* UIGestureRecognizerExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 90693554208B545100407C4D /* UIGestureRecognizerExtensionsTests.swift */; }; @@ -523,6 +551,23 @@ 9DC844A32349E1EE00E1571A /* UIColorExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DC844A22349E1EE00E1571A /* UIColorExtensions.swift */; }; 9DC844A62349E27600E1571A /* NSColorExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DC844A42349E24600E1571A /* NSColorExtensions.swift */; }; 9DC844A82349E28100E1571A /* UIColorExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9DC844A22349E1EE00E1571A /* UIColorExtensions.swift */; }; + 9E28344D25AEBD160093203B /* MeasurementExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E28344C25AEBD160093203B /* MeasurementExtensionsTests.swift */; }; + 9E28344E25AEBD160093203B /* MeasurementExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E28344C25AEBD160093203B /* MeasurementExtensionsTests.swift */; }; + 9E28344F25AEBD160093203B /* MeasurementExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E28344C25AEBD160093203B /* MeasurementExtensionsTests.swift */; }; + 9E3CA2672ABE0D7100FA4626 /* FutureExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E3CA2662ABE0D7100FA4626 /* FutureExtensions.swift */; }; + 9E3CA3772ABE103E00FA4626 /* FutureExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E3CA3752ABE0FEE00FA4626 /* FutureExtensionsTests.swift */; }; + 9E9F42B129872E9400A0BD60 /* URLSessionExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E9F42B029872E9300A0BD60 /* URLSessionExtensions.swift */; }; + 9E9F42B229872E9400A0BD60 /* URLSessionExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E9F42B029872E9300A0BD60 /* URLSessionExtensions.swift */; }; + 9E9F42B329872E9400A0BD60 /* URLSessionExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E9F42B029872E9300A0BD60 /* URLSessionExtensions.swift */; }; + 9E9F42B429872E9400A0BD60 /* URLSessionExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E9F42B029872E9300A0BD60 /* URLSessionExtensions.swift */; }; + 9E9F42BA298733DF00A0BD60 /* URLSessionExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E9F42B529872F5F00A0BD60 /* URLSessionExtensionsTests.swift */; }; + 9E9F42BB298733E000A0BD60 /* URLSessionExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E9F42B529872F5F00A0BD60 /* URLSessionExtensionsTests.swift */; }; + 9E9F42BC298733E000A0BD60 /* URLSessionExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E9F42B529872F5F00A0BD60 /* URLSessionExtensionsTests.swift */; }; + 9EA118102ABF2FEC00AD3916 /* FutureExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E3CA2662ABE0D7100FA4626 /* FutureExtensions.swift */; }; + 9EA118112ABF2FED00AD3916 /* FutureExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E3CA2662ABE0D7100FA4626 /* FutureExtensions.swift */; }; + 9EA118122ABF2FED00AD3916 /* FutureExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E3CA2662ABE0D7100FA4626 /* FutureExtensions.swift */; }; + 9EA118132ABF2FFE00AD3916 /* FutureExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E3CA3752ABE0FEE00FA4626 /* FutureExtensionsTests.swift */; }; + 9EA118142ABF2FFF00AD3916 /* FutureExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9E3CA3752ABE0FEE00FA4626 /* FutureExtensionsTests.swift */; }; A94AA78720280F9100E229A5 /* FileManagerExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = A94AA78620280F9100E229A5 /* FileManagerExtensions.swift */; }; A94AA78A202819B400E229A5 /* FileManagerExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A94AA7882028193A00E229A5 /* FileManagerExtensionsTests.swift */; }; A94AA78B202819B400E229A5 /* FileManagerExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = A94AA7882028193A00E229A5 /* FileManagerExtensionsTests.swift */; }; @@ -535,10 +580,6 @@ B22EB2BB20E9E81C001EAE70 /* RangeReplaceableCollectionExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = B22EB2B620E9E720001EAE70 /* RangeReplaceableCollectionExtensions.swift */; }; B22EB2BD20E9EA1F001EAE70 /* RangeReplaceableCollectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B22EB2B820E9E814001EAE70 /* RangeReplaceableCollectionTests.swift */; }; B22EB2BE20E9EA20001EAE70 /* RangeReplaceableCollectionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B22EB2B820E9E814001EAE70 /* RangeReplaceableCollectionTests.swift */; }; - B2A0DAB62336D5A6002B0BC5 /* StdlibDeprecated.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2A0DAB52336D5A6002B0BC5 /* StdlibDeprecated.swift */; }; - B2A0DAB72336D5A6002B0BC5 /* StdlibDeprecated.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2A0DAB52336D5A6002B0BC5 /* StdlibDeprecated.swift */; }; - B2A0DAB82336D5A6002B0BC5 /* StdlibDeprecated.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2A0DAB52336D5A6002B0BC5 /* StdlibDeprecated.swift */; }; - B2A0DAB92336D5A6002B0BC5 /* StdlibDeprecated.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2A0DAB52336D5A6002B0BC5 /* StdlibDeprecated.swift */; }; B2A0DAC02336DA87002B0BC5 /* MutableCollectionExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2A0DABF2336DA87002B0BC5 /* MutableCollectionExtensions.swift */; }; B2A0DAC12336DA87002B0BC5 /* MutableCollectionExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2A0DABF2336DA87002B0BC5 /* MutableCollectionExtensions.swift */; }; B2A0DAC22336DA87002B0BC5 /* MutableCollectionExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = B2A0DABF2336DA87002B0BC5 /* MutableCollectionExtensions.swift */; }; @@ -556,7 +597,7 @@ C7E027CA2360958B00F1061E /* KeyedDecodingContainerExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7E027C82360958B00F1061E /* KeyedDecodingContainerExtensionsTests.swift */; }; C7E027CB2360958B00F1061E /* KeyedDecodingContainerExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C7E027C82360958B00F1061E /* KeyedDecodingContainerExtensionsTests.swift */; }; CA1317542106D35E002F1B0D /* UIRefreshControlExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA1317532106D35E002F1B0D /* UIRefreshControlExtensions.swift */; }; - CA1317582106D4F5002F1B0D /* UIRefreshControlExntesionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA1317562106D4F5002F1B0D /* UIRefreshControlExntesionsTests.swift */; }; + CA1317582106D4F5002F1B0D /* UIRefreshControlExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = CA1317562106D4F5002F1B0D /* UIRefreshControlExtensionsTests.swift */; }; CAC5EBC62125270A00AB27EC /* TestImage.png in Resources */ = {isa = PBXBuildFile; fileRef = CAC5EBBF2125270A00AB27EC /* TestImage.png */; }; CAC5EBC72125270A00AB27EC /* TestStoryboard.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = CAC5EBC02125270A00AB27EC /* TestStoryboard.storyboard */; }; CAC5EBC82125270A00AB27EC /* UICollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = CAC5EBC12125270A00AB27EC /* UICollectionViewCell.xib */; }; @@ -570,6 +611,13 @@ CAC5EBD02125273100AB27EC /* TestImage.png in Resources */ = {isa = PBXBuildFile; fileRef = CAC5EBBF2125270A00AB27EC /* TestImage.png */; }; CAC5EBD12125273100AB27EC /* TestImage.png in Resources */ = {isa = PBXBuildFile; fileRef = CAC5EBBF2125270A00AB27EC /* TestImage.png */; }; CF30948A216AAC7A005609BC /* UIActivityExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = CF309489216AAC7A005609BC /* UIActivityExtensions.swift */; }; + D5FD5284270853B30073F831 /* BinaryIntegerExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5FD5283270853B30073F831 /* BinaryIntegerExtensions.swift */; }; + D5FD5285270853B30073F831 /* BinaryIntegerExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5FD5283270853B30073F831 /* BinaryIntegerExtensions.swift */; }; + D5FD5286270853B30073F831 /* BinaryIntegerExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5FD5283270853B30073F831 /* BinaryIntegerExtensions.swift */; }; + D5FD5287270853B30073F831 /* BinaryIntegerExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5FD5283270853B30073F831 /* BinaryIntegerExtensions.swift */; }; + D5FD528D27085D820073F831 /* BinaryIntegerExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5FD5288270858400073F831 /* BinaryIntegerExtensionsTests.swift */; }; + D5FD528E27085D820073F831 /* BinaryIntegerExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5FD5288270858400073F831 /* BinaryIntegerExtensionsTests.swift */; }; + D5FD528F27085D820073F831 /* BinaryIntegerExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D5FD5288270858400073F831 /* BinaryIntegerExtensionsTests.swift */; }; D951E8AA23D04DBE00F2CD0B /* DecodableExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D951E8A923D04DBE00F2CD0B /* DecodableExtensions.swift */; }; D951E8AB23D04DCA00F2CD0B /* DecodableExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D951E8A923D04DBE00F2CD0B /* DecodableExtensions.swift */; }; D951E8AC23D04DCA00F2CD0B /* DecodableExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = D951E8A923D04DBE00F2CD0B /* DecodableExtensions.swift */; }; @@ -577,7 +625,7 @@ D951E8AF23D04E4600F2CD0B /* DecodableExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D951E8AE23D04E4600F2CD0B /* DecodableExtensionsTests.swift */; }; D951E8B023D04E4600F2CD0B /* DecodableExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D951E8AE23D04E4600F2CD0B /* DecodableExtensionsTests.swift */; }; D951E8B123D04E4600F2CD0B /* DecodableExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D951E8AE23D04E4600F2CD0B /* DecodableExtensionsTests.swift */; }; - D9F36CF623521F440057258F /* MKMapViewTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9F36CF523521F440057258F /* MKMapViewTests.swift */; }; + D9F36CF623521F440057258F /* MKMapViewExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = D9F36CF523521F440057258F /* MKMapViewExtensionsTests.swift */; }; E5E5EB3A2350EED400B04C42 /* CAGradientLayerExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5E5EB392350EED400B04C42 /* CAGradientLayerExtensions.swift */; }; E5E5EB3D2350F40200B04C42 /* CAGradientLayerExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = E5E5EB3C2350F40200B04C42 /* CAGradientLayerExtensionsTests.swift */; }; F850972424AF72690007F74D /* MyViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5E36CB6424AC9909007727DA /* MyViewController.swift */; }; @@ -622,6 +670,8 @@ F8A710EF23BF3F8500112DAD /* EdgeInsetsExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A710ED23BF3F8200112DAD /* EdgeInsetsExtensionsTests.swift */; }; F8A710F023BF3F8500112DAD /* EdgeInsetsExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A710ED23BF3F8200112DAD /* EdgeInsetsExtensionsTests.swift */; }; F8A710F123BF3F8600112DAD /* EdgeInsetsExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8A710ED23BF3F8200112DAD /* EdgeInsetsExtensionsTests.swift */; }; + F8B4DFB3291EA77C0011BD90 /* HKActivitySummaryExtensionsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DAEBCE224B0079700F61684 /* HKActivitySummaryExtensionsTests.swift */; }; + F8B4DFB4291EA7880011BD90 /* HKActivitySummaryExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0DAEBCDF24B0045F00F61684 /* HKActivitySummaryExtensions.swift */; }; F8C1AE6F225B7F990045D5A0 /* NSRegularExpressionExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8C1AE6E225B7F990045D5A0 /* NSRegularExpressionExtensions.swift */; }; F8C1AE70225B7F990045D5A0 /* NSRegularExpressionExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8C1AE6E225B7F990045D5A0 /* NSRegularExpressionExtensions.swift */; }; F8C1AE71225B7F990045D5A0 /* NSRegularExpressionExtensions.swift in Sources */ = {isa = PBXBuildFile; fileRef = F8C1AE6E225B7F990045D5A0 /* NSRegularExpressionExtensions.swift */; }; @@ -667,8 +717,6 @@ 074C8D80224F86450050F040 /* MKMapViewExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MKMapViewExtensions.swift; sourceTree = ""; }; 074EAF1A1F7BA68B00C74636 /* UIFontExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIFontExtensions.swift; sourceTree = ""; }; 074EAF1E1F7BA74600C74636 /* UIFontExtensionsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIFontExtensionsTests.swift; sourceTree = ""; }; - 076AEC881FDB48580077D153 /* UIDatePickerExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIDatePickerExtensions.swift; sourceTree = ""; }; - 076AEC8C1FDB49480077D153 /* UIDatePickerExtensionsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIDatePickerExtensionsTests.swift; sourceTree = ""; }; 077BA0891F6BE81F00D9C4AC /* URLRequestExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLRequestExtensions.swift; sourceTree = ""; }; 077BA08E1F6BE83600D9C4AC /* UserDefaultsExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserDefaultsExtensions.swift; sourceTree = ""; }; 077BA0941F6BE98500D9C4AC /* URLRequestExtensionsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLRequestExtensionsTests.swift; sourceTree = ""; }; @@ -782,6 +830,8 @@ 0DAEBCE224B0079700F61684 /* HKActivitySummaryExtensionsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HKActivitySummaryExtensionsTests.swift; sourceTree = ""; }; 116090AE24187D5C00DDCD01 /* CGRectExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CGRectExtensions.swift; sourceTree = ""; }; 116090B324187D8100DDCD01 /* CGRectExtensionsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CGRectExtensionsTests.swift; sourceTree = ""; }; + 17A4B79226CCFFAE007D299F /* SKSpriteNodeExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SKSpriteNodeExtensions.swift; sourceTree = ""; }; + 17A4B79726CD0DA2007D299F /* SKSpriteNodeExtensionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SKSpriteNodeExtensionTests.swift; sourceTree = ""; }; 185BDE601F8AAEFD00140E19 /* CGColorExtensionsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CGColorExtensionsTests.swift; sourceTree = ""; }; 18C8E5DD2074C58100F8AF51 /* SequenceExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SequenceExtensions.swift; sourceTree = ""; }; 18C8E5E22074C67000F8AF51 /* SequenceExtensionsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SequenceExtensionsTests.swift; sourceTree = ""; }; @@ -790,8 +840,13 @@ 278CA0901F9A9679004918BD /* NSImageExtensionsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSImageExtensionsTests.swift; sourceTree = ""; }; 42F53FEB2039C5AC0070DC11 /* UIStackViewExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIStackViewExtensions.swift; sourceTree = ""; }; 42F53FEF2039C7140070DC11 /* UIStackViewExtensionsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIStackViewExtensionsTests.swift; sourceTree = ""; }; + 58253511259CF23B00407B78 /* MeasurementExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MeasurementExtensions.swift; sourceTree = ""; }; + 58CAE62828CF2935001935A2 /* DefaultStringInterpolationExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultStringInterpolationExtensions.swift; sourceTree = ""; }; + 58CAE63128CF2C53001935A2 /* DefaultStringInterpolationExtensionsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DefaultStringInterpolationExtensionsTests.swift; sourceTree = ""; }; 5C88FBEB234CC1280065A942 /* NSColorExtensionsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSColorExtensionsTests.swift; sourceTree = ""; }; 5E36CB6424AC9909007727DA /* MyViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyViewController.swift; sourceTree = ""; }; + 658A02F1270F01C500DBA951 /* MKMultiPointExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MKMultiPointExtensions.swift; sourceTree = ""; }; + 658A02F6270F024700DBA951 /* MKMultiPointExtensionsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MKMultiPointExtensionsTests.swift; sourceTree = ""; }; 664CB96C2171863B00FC87B4 /* BidirectionalCollectionExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BidirectionalCollectionExtensions.swift; sourceTree = ""; }; 664CB971217186E900FC87B4 /* BidirectionalCollectionExtensionsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BidirectionalCollectionExtensionsTests.swift; sourceTree = ""; }; 664CB9752171899800FC87B4 /* BinaryFloatingPointExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BinaryFloatingPointExtensions.swift; sourceTree = ""; }; @@ -810,13 +865,15 @@ 7832C2AE209BB19300224EED /* ComparableExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComparableExtensions.swift; sourceTree = ""; }; 7832C2B3209BB32500224EED /* ComparableExtensionsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComparableExtensionsTests.swift; sourceTree = ""; }; 784C752E2051BD26001C48DD /* MKPolylineExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MKPolylineExtensions.swift; sourceTree = ""; }; - 784C75362051BE1D001C48DD /* MKPolylineTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MKPolylineTests.swift; sourceTree = ""; }; + 784C75362051BE1D001C48DD /* MKPolylineExtensionsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MKPolylineExtensionsTests.swift; sourceTree = ""; }; 86B3F7AB208D3D5C00BC297B /* UIScrollViewExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIScrollViewExtensions.swift; sourceTree = ""; }; 86B3F7AD208D3DF300BC297B /* UIScrollViewExtensionsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIScrollViewExtensionsTests.swift; sourceTree = ""; }; + 8AC39B6228790A6B0041A56C /* DigestExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DigestExtensions.swift; sourceTree = ""; }; + 8AC39B6828790B5F0041A56C /* DigestExtensionsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DigestExtensionsTests.swift; sourceTree = ""; }; 8D4B424B212972AE002A5923 /* UILayoutPriorityExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UILayoutPriorityExtensions.swift; sourceTree = ""; }; 8D4B424E212972E7002A5923 /* UILayoutPriorityExtensionsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UILayoutPriorityExtensionsTests.swift; sourceTree = ""; }; 8D50E51E24D0D71E00972E2D /* UIImageView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = UIImageView.xib; sourceTree = ""; }; - 8D50E52024D0D73E00972E2D /* UIImageView.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = UIImageView.xib; sourceTree = ""; }; + 8D50E52024D0D73E00972E2D /* UIImageViewTvOS.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = UIImageViewTvOS.xib; sourceTree = ""; }; 90693550208B4F9400407C4D /* UIGestureRecognizerExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIGestureRecognizerExtensions.swift; sourceTree = ""; }; 90693554208B545100407C4D /* UIGestureRecognizerExtensionsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIGestureRecognizerExtensionsTests.swift; sourceTree = ""; }; 985C1B8E24797694008AAB0E /* WKWebViewExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WKWebViewExtensions.swift; sourceTree = ""; }; @@ -850,17 +907,21 @@ 9DC29CF32349E7E200F5CAAD /* UIColorExtensionsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIColorExtensionsTests.swift; sourceTree = ""; }; 9DC844A22349E1EE00E1571A /* UIColorExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIColorExtensions.swift; sourceTree = ""; }; 9DC844A42349E24600E1571A /* NSColorExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSColorExtensions.swift; sourceTree = ""; }; + 9E28344C25AEBD160093203B /* MeasurementExtensionsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MeasurementExtensionsTests.swift; sourceTree = ""; }; + 9E3CA2662ABE0D7100FA4626 /* FutureExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FutureExtensions.swift; sourceTree = ""; }; + 9E3CA3752ABE0FEE00FA4626 /* FutureExtensionsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FutureExtensionsTests.swift; sourceTree = ""; }; + 9E9F42B029872E9300A0BD60 /* URLSessionExtensions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = URLSessionExtensions.swift; sourceTree = ""; }; + 9E9F42B529872F5F00A0BD60 /* URLSessionExtensionsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = URLSessionExtensionsTests.swift; sourceTree = ""; }; A94AA78620280F9100E229A5 /* FileManagerExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileManagerExtensions.swift; sourceTree = ""; }; A94AA7882028193A00E229A5 /* FileManagerExtensionsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileManagerExtensionsTests.swift; sourceTree = ""; }; B22EB2B620E9E720001EAE70 /* RangeReplaceableCollectionExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RangeReplaceableCollectionExtensions.swift; sourceTree = ""; }; B22EB2B820E9E814001EAE70 /* RangeReplaceableCollectionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RangeReplaceableCollectionTests.swift; sourceTree = ""; }; - B2A0DAB52336D5A6002B0BC5 /* StdlibDeprecated.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StdlibDeprecated.swift; sourceTree = ""; }; B2A0DABF2336DA87002B0BC5 /* MutableCollectionExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MutableCollectionExtensions.swift; sourceTree = ""; }; B2A0DAC42336DCE5002B0BC5 /* MutableCollectionTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MutableCollectionTests.swift; sourceTree = ""; }; C7E027C32360942000F1061E /* KeyedDecodingContainerExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyedDecodingContainerExtensions.swift; sourceTree = ""; }; C7E027C82360958B00F1061E /* KeyedDecodingContainerExtensionsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyedDecodingContainerExtensionsTests.swift; sourceTree = ""; }; CA1317532106D35E002F1B0D /* UIRefreshControlExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIRefreshControlExtensions.swift; sourceTree = ""; }; - CA1317562106D4F5002F1B0D /* UIRefreshControlExntesionsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIRefreshControlExntesionsTests.swift; sourceTree = ""; }; + CA1317562106D4F5002F1B0D /* UIRefreshControlExtensionsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIRefreshControlExtensionsTests.swift; sourceTree = ""; }; CAC5EBBF2125270A00AB27EC /* TestImage.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = TestImage.png; sourceTree = ""; }; CAC5EBC02125270A00AB27EC /* TestStoryboard.storyboard */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.storyboard; path = TestStoryboard.storyboard; sourceTree = ""; }; CAC5EBC12125270A00AB27EC /* UICollectionViewCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = UICollectionViewCell.xib; sourceTree = ""; }; @@ -869,9 +930,11 @@ CAC5EBC42125270A00AB27EC /* UITableViewHeaderFooterView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = UITableViewHeaderFooterView.xib; sourceTree = ""; }; CAC5EBC52125270A00AB27EC /* big_buck_bunny_720p_1mb.mp4 */ = {isa = PBXFileReference; lastKnownFileType = file; path = big_buck_bunny_720p_1mb.mp4; sourceTree = ""; }; CF309489216AAC7A005609BC /* UIActivityExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIActivityExtensions.swift; sourceTree = ""; }; + D5FD5283270853B30073F831 /* BinaryIntegerExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BinaryIntegerExtensions.swift; sourceTree = ""; }; + D5FD5288270858400073F831 /* BinaryIntegerExtensionsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BinaryIntegerExtensionsTests.swift; sourceTree = ""; }; D951E8A923D04DBE00F2CD0B /* DecodableExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DecodableExtensions.swift; sourceTree = ""; }; D951E8AE23D04E4600F2CD0B /* DecodableExtensionsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DecodableExtensionsTests.swift; sourceTree = ""; }; - D9F36CF523521F440057258F /* MKMapViewTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MKMapViewTests.swift; sourceTree = ""; }; + D9F36CF523521F440057258F /* MKMapViewExtensionsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MKMapViewExtensionsTests.swift; sourceTree = ""; }; E5E5EB392350EED400B04C42 /* CAGradientLayerExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CAGradientLayerExtensions.swift; sourceTree = ""; }; E5E5EB3C2350F40200B04C42 /* CAGradientLayerExtensionsTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CAGradientLayerExtensionsTests.swift; sourceTree = ""; }; F854D2A42423AE92003A08A9 /* CGAffineTransformExtensions.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CGAffineTransformExtensions.swift; sourceTree = ""; }; @@ -959,14 +1022,16 @@ 077BA0871F6BE73000D9C4AC /* SwiftStdlib */ = { isa = PBXGroup; children = ( - B2A0DAB42336D580002B0BC5 /* Deprecated */, 07B7F1661F5EB41600E6F910 /* ArrayExtensions.swift */, 664CB96C2171863B00FC87B4 /* BidirectionalCollectionExtensions.swift */, 664CB9752171899800FC87B4 /* BinaryFloatingPointExtensions.swift */, + D5FD5283270853B30073F831 /* BinaryIntegerExtensions.swift */, 07B7F1671F5EB41600E6F910 /* BoolExtensions.swift */, 07B7F1681F5EB41600E6F910 /* CharacterExtensions.swift */, 07B7F1691F5EB41600E6F910 /* CollectionExtensions.swift */, 7832C2AE209BB19300224EED /* ComparableExtensions.swift */, + D951E8A923D04DBE00F2CD0B /* DecodableExtensions.swift */, + 58CAE62828CF2935001935A2 /* DefaultStringInterpolationExtensions.swift */, 07B7F16E1F5EB41600E6F910 /* DictionaryExtensions.swift */, 07B7F16F1F5EB41600E6F910 /* DoubleExtensions.swift */, 07B7F1701F5EB41600E6F910 /* FloatExtensions.swift */, @@ -981,7 +1046,6 @@ 9D9784DA1FCAE3D200D988E7 /* StringProtocolExtensions.swift */, B2A0DABF2336DA87002B0BC5 /* MutableCollectionExtensions.swift */, C7E027C32360942000F1061E /* KeyedDecodingContainerExtensions.swift */, - D951E8A923D04DBE00F2CD0B /* DecodableExtensions.swift */, ); path = SwiftStdlib; sourceTree = ""; @@ -992,11 +1056,13 @@ 07C50CFA1F5EB03200F46E5A /* ArrayExtensionsTests.swift */, 664CB971217186E900FC87B4 /* BidirectionalCollectionExtensionsTests.swift */, 664CB97A21718B1D00FC87B4 /* BinaryFloatingPointExtensionsTests.swift */, + D5FD5288270858400073F831 /* BinaryIntegerExtensionsTests.swift */, 07C50CFB1F5EB03200F46E5A /* BoolExtensionsTests.swift */, 07C50CFC1F5EB03200F46E5A /* CharacterExtensionsTests.swift */, 07C50CFD1F5EB03200F46E5A /* CollectionExtensionsTests.swift */, 7832C2B3209BB32500224EED /* ComparableExtensionsTests.swift */, D951E8AE23D04E4600F2CD0B /* DecodableExtensionsTests.swift */, + 58CAE63128CF2C53001935A2 /* DefaultStringInterpolationExtensionsTests.swift */, 07C50D001F5EB03200F46E5A /* DictionaryExtensionsTests.swift */, 07C50D011F5EB03200F46E5A /* DoubleExtensionsTests.swift */, 07C50D021F5EB03200F46E5A /* FloatExtensionsTests.swift */, @@ -1054,9 +1120,11 @@ isa = PBXGroup; children = ( 07B7F15C1F5EB41600E6F910 /* AppKit */, + 9E3CA2652ABE0D7100FA4626 /* Combine */, E5E5EB382350EE9A00B04C42 /* CoreAnimation */, 07D8960D1F5ED85400FC894D /* CoreGraphics */, 07D8960C1F5ED84400FC894D /* CoreLocation */, + 8AC39B6128790A150041A56C /* CryptoKit */, 664CB981217243BA00FC87B4 /* Dispatch */, 07B7F1651F5EB41600E6F910 /* Foundation */, 0DAEBCDE24B0041900F61684 /* HealthKit */, @@ -1090,12 +1158,14 @@ 07B7F16B1F5EB41600E6F910 /* DateExtensions.swift */, A94AA78620280F9100E229A5 /* FileManagerExtensions.swift */, 07B7F1731F5EB41600E6F910 /* LocaleExtensions.swift */, + 58253511259CF23B00407B78 /* MeasurementExtensions.swift */, F87AA5D4241257E3005F5B28 /* NotificationCenterExtensions.swift */, 07B7F1621F5EB41600E6F910 /* NSAttributedStringExtensions.swift */, 9D4914821F85138E00F3868F /* NSPredicateExtensions.swift */, F8C1AE6E225B7F990045D5A0 /* NSRegularExpressionExtensions.swift */, 07B7F1781F5EB41600E6F910 /* URLExtensions.swift */, 077BA0891F6BE81F00D9C4AC /* URLRequestExtensions.swift */, + 9E9F42B029872E9300A0BD60 /* URLSessionExtensions.swift */, 077BA08E1F6BE83600D9C4AC /* UserDefaultsExtensions.swift */, ); path = Foundation; @@ -1130,7 +1200,6 @@ 07B7F18F1F5EB41600E6F910 /* UITextViewExtensions.swift */, 07B7F1901F5EB41600E6F910 /* UIViewControllerExtensions.swift */, 07B7F1911F5EB41600E6F910 /* UIViewExtensions.swift */, - 076AEC881FDB48580077D153 /* UIDatePickerExtensions.swift */, 42F53FEB2039C5AC0070DC11 /* UIStackViewExtensions.swift */, 86B3F7AB208D3D5C00BC297B /* UIScrollViewExtensions.swift */, 0722CB3720C2E46B00C6C1B6 /* UIWindowExtensions.swift */, @@ -1146,9 +1215,11 @@ children = ( 07C50CCD1F5EAF2700F46E5A /* Info.plist */, 07C50D241F5EB03200F46E5A /* AppKitTests */, + 9E3CA3742ABE0FEE00FA4626 /* CombineTests */, E5E5EB3B2350F39E00B04C42 /* CoreAnimationTests */, 07D8960E1F5ED89A00FC894D /* CoreGraphicsTests */, 07D8960F1F5ED8AB00FC894D /* CoreLocationTests */, + 8AC39B6728790B330041A56C /* CryptoKitTests */, 664CB98821724A3A00FC87B4 /* DispatchTests */, 07C50CF91F5EB03200F46E5A /* FoundationTests */, 0DAEBCE124B0077000F61684 /* HealthKitTests */, @@ -1175,12 +1246,14 @@ 07C50CFF1F5EB03200F46E5A /* DateExtensionsTests.swift */, A94AA7882028193A00E229A5 /* FileManagerExtensionsTests.swift */, 07C50D041F5EB03200F46E5A /* LocaleExtensionsTests.swift */, + 9E28344C25AEBD160093203B /* MeasurementExtensionsTests.swift */, F87AA5D924125C19005F5B28 /* NotificationCenterExtensionsTests.swift */, 07C50D291F5EB03200F46E5A /* NSAttributedStringExtensionsTests.swift */, 9D4914881F8515D100F3868F /* NSPredicateExtensionsTests.swift */, F8C1AE73225B871F0045D5A0 /* NSRegularExpressionExtensionsTests.swift */, 07C50D071F5EB03200F46E5A /* URLExtensionsTests.swift */, 077BA0941F6BE98500D9C4AC /* URLRequestExtensionsTests.swift */, + 9E9F42B529872F5F00A0BD60 /* URLSessionExtensionsTests.swift */, 077BA0991F6BE99F00D9C4AC /* UserDefaultsExtensionsTests.swift */, ); path = FoundationTests; @@ -1195,7 +1268,6 @@ 07C50D101F5EB03200F46E5A /* UIButtonExtensionsTests.swift */, 07C50D111F5EB03200F46E5A /* UICollectionViewExtensionsTests.swift */, 9DC29CF32349E7E200F5CAAD /* UIColorExtensionsTests.swift */, - 076AEC8C1FDB49480077D153 /* UIDatePickerExtensionsTests.swift */, 074EAF1E1F7BA74600C74636 /* UIFontExtensionsTests.swift */, 90693554208B545100407C4D /* UIGestureRecognizerExtensionsTests.swift */, 07C50D131F5EB03200F46E5A /* UIImageExtensionsTests.swift */, @@ -1205,7 +1277,7 @@ 07C50D161F5EB03200F46E5A /* UINavigationBarExtensionTests.swift */, 07C50D171F5EB03200F46E5A /* UINavigationControllerExtensionsTests.swift */, 07C50D181F5EB03200F46E5A /* UINavigationItemExtensionsTests.swift */, - CA1317562106D4F5002F1B0D /* UIRefreshControlExntesionsTests.swift */, + CA1317562106D4F5002F1B0D /* UIRefreshControlExtensionsTests.swift */, 86B3F7AD208D3DF300BC297B /* UIScrollViewExtensionsTests.swift */, 07C50D191F5EB03200F46E5A /* UISearchBarExtensionsTests.swift */, 07C50D1A1F5EB03200F46E5A /* UISegmentedControlExtensionsTests.swift */, @@ -1334,6 +1406,7 @@ isa = PBXGroup; children = ( 752AC79F20BC975E00659E76 /* SKNodeExtensions.swift */, + 17A4B79226CCFFAE007D299F /* SKSpriteNodeExtensions.swift */, ); path = SpriteKit; sourceTree = ""; @@ -1342,6 +1415,7 @@ isa = PBXGroup; children = ( 752AC7A420BC9FF500659E76 /* SKNodeExtensionTests.swift */, + 17A4B79726CD0DA2007D299F /* SKSpriteNodeExtensionTests.swift */, ); path = SpriteKitTests; sourceTree = ""; @@ -1359,6 +1433,7 @@ children = ( 074C8D80224F86450050F040 /* MKMapViewExtensions.swift */, 784C752E2051BD26001C48DD /* MKPolylineExtensions.swift */, + 658A02F1270F01C500DBA951 /* MKMultiPointExtensions.swift */, ); path = MapKit; sourceTree = ""; @@ -1366,16 +1441,34 @@ 784C75322051BDCA001C48DD /* MapKitTests */ = { isa = PBXGroup; children = ( - D9F36CF523521F440057258F /* MKMapViewTests.swift */, - 784C75362051BE1D001C48DD /* MKPolylineTests.swift */, + D9F36CF523521F440057258F /* MKMapViewExtensionsTests.swift */, + 784C75362051BE1D001C48DD /* MKPolylineExtensionsTests.swift */, + 658A02F6270F024700DBA951 /* MKMultiPointExtensionsTests.swift */, ); path = MapKitTests; sourceTree = ""; }; + 8AC39B6128790A150041A56C /* CryptoKit */ = { + isa = PBXGroup; + children = ( + 8AC39B6228790A6B0041A56C /* DigestExtensions.swift */, + ); + path = CryptoKit; + sourceTree = ""; + }; + 8AC39B6728790B330041A56C /* CryptoKitTests */ = { + isa = PBXGroup; + children = ( + 8AC39B6828790B5F0041A56C /* DigestExtensionsTests.swift */, + ); + path = CryptoKitTests; + sourceTree = ""; + }; 8D50E51D24D0D6FB00972E2D /* tvOS */ = { isa = PBXGroup; children = ( - 8D50E52024D0D73E00972E2D /* UIImageView.xib */, + F88C491824AF73A400BA0503 /* TestStoryboard-tvOS.storyboard */, + 8D50E52024D0D73E00972E2D /* UIImageViewTvOS.xib */, ); path = tvOS; sourceTree = ""; @@ -1430,28 +1523,27 @@ path = SceneKitTests; sourceTree = ""; }; - B2A0DAB42336D580002B0BC5 /* Deprecated */ = { + 9E3CA2652ABE0D7100FA4626 /* Combine */ = { isa = PBXGroup; children = ( - B2A0DAB52336D5A6002B0BC5 /* StdlibDeprecated.swift */, + 9E3CA2662ABE0D7100FA4626 /* FutureExtensions.swift */, ); - path = Deprecated; + path = Combine; + sourceTree = ""; + }; + 9E3CA3742ABE0FEE00FA4626 /* CombineTests */ = { + isa = PBXGroup; + children = ( + 9E3CA3752ABE0FEE00FA4626 /* FutureExtensionsTests.swift */, + ); + path = CombineTests; sourceTree = ""; }; CAC5EBBE2125270A00AB27EC /* ResourcesTests */ = { isa = PBXGroup; children = ( - 8D50E51D24D0D6FB00972E2D /* tvOS */, - CAC5EBC52125270A00AB27EC /* big_buck_bunny_720p_1mb.mp4 */, - CAC5EBC22125270A00AB27EC /* test.json */, - CAC5EBBF2125270A00AB27EC /* TestImage.png */, - CAC5EBC02125270A00AB27EC /* TestStoryboard.storyboard */, - F88C491824AF73A400BA0503 /* TestStoryboard-tvOS.storyboard */, - CAC5EBC12125270A00AB27EC /* UICollectionViewCell.xib */, - 8D50E51E24D0D71E00972E2D /* UIImageView.xib */, - CAC5EBC32125270A00AB27EC /* UITableViewCell.xib */, - CAC5EBC42125270A00AB27EC /* UITableViewHeaderFooterView.xib */, - 5E36CB6424AC9909007727DA /* MyViewController.swift */, + F8830FC3285478EC00C1D802 /* Resources */, + F8830FC2285478EC00C1D802 /* Sources */, ); path = ResourcesTests; sourceTree = ""; @@ -1483,6 +1575,30 @@ path = CoreAnimationTests; sourceTree = ""; }; + F8830FC2285478EC00C1D802 /* Sources */ = { + isa = PBXGroup; + children = ( + 5E36CB6424AC9909007727DA /* MyViewController.swift */, + ); + path = Sources; + sourceTree = ""; + }; + F8830FC3285478EC00C1D802 /* Resources */ = { + isa = PBXGroup; + children = ( + 8D50E51D24D0D6FB00972E2D /* tvOS */, + CAC5EBC52125270A00AB27EC /* big_buck_bunny_720p_1mb.mp4 */, + CAC5EBC22125270A00AB27EC /* test.json */, + CAC5EBBF2125270A00AB27EC /* TestImage.png */, + CAC5EBC02125270A00AB27EC /* TestStoryboard.storyboard */, + CAC5EBC12125270A00AB27EC /* UICollectionViewCell.xib */, + 8D50E51E24D0D71E00972E2D /* UIImageView.xib */, + CAC5EBC32125270A00AB27EC /* UITableViewCell.xib */, + CAC5EBC42125270A00AB27EC /* UITableViewHeaderFooterView.xib */, + ); + path = Resources; + sourceTree = ""; + }; F8E62DA224D0184E00BF35AE /* XCTestTests */ = { isa = PBXGroup; children = ( @@ -1541,11 +1657,12 @@ isa = PBXNativeTarget; buildConfigurationList = 07898B631F278D7600558C97 /* Build configuration list for PBXNativeTarget "SwifterSwift-iOS" */; buildPhases = ( + E455DFD42AD0CBD4008A011E /* SwiftFormat */, + 07A1C43E224FBFC6003272E4 /* SwiftLint */, 07898B581F278D7600558C97 /* Sources */, 07898B591F278D7600558C97 /* Frameworks */, 07898B5A1F278D7600558C97 /* Headers */, 07898B5B1F278D7600558C97 /* Resources */, - 07A1C43E224FBFC6003272E4 /* SwiftLint */, ); buildRules = ( ); @@ -1560,11 +1677,12 @@ isa = PBXNativeTarget; buildConfigurationList = 07898B701F278D9700558C97 /* Build configuration list for PBXNativeTarget "SwifterSwift-tvOS" */; buildPhases = ( + E455DFD52AD0CC17008A011E /* SwiftFormat */, + 07A1C43F224FBFD5003272E4 /* SwiftLint */, 07898B661F278D9700558C97 /* Sources */, 07898B671F278D9700558C97 /* Frameworks */, 07898B681F278D9700558C97 /* Headers */, 07898B691F278D9700558C97 /* Resources */, - 07A1C43F224FBFD5003272E4 /* SwiftLint */, ); buildRules = ( ); @@ -1579,11 +1697,12 @@ isa = PBXNativeTarget; buildConfigurationList = 07898B7D1F278DBC00558C97 /* Build configuration list for PBXNativeTarget "SwifterSwift-watchOS" */; buildPhases = ( + E455DFD62AD0CC23008A011E /* SwiftFormat */, + 07A1C440224FBFDD003272E4 /* SwiftLint */, 07898B731F278DBC00558C97 /* Sources */, 07898B741F278DBC00558C97 /* Frameworks */, 07898B751F278DBC00558C97 /* Headers */, 07898B761F278DBC00558C97 /* Resources */, - 07A1C440224FBFDD003272E4 /* SwiftLint */, ); buildRules = ( ); @@ -1598,11 +1717,12 @@ isa = PBXNativeTarget; buildConfigurationList = 07898B8A1F278DD200558C97 /* Build configuration list for PBXNativeTarget "SwifterSwift-macOS" */; buildPhases = ( + E455DFD72AD0CC2F008A011E /* SwiftFormat */, + 07A1C441224FBFE7003272E4 /* SwiftLint */, 07898B801F278DD200558C97 /* Sources */, 07898B811F278DD200558C97 /* Frameworks */, 07898B821F278DD200558C97 /* Headers */, 07898B831F278DD200558C97 /* Resources */, - 07A1C441224FBFE7003272E4 /* SwiftLint */, ); buildRules = ( ); @@ -1617,10 +1737,11 @@ isa = PBXNativeTarget; buildConfigurationList = 07C50CD31F5EAF2700F46E5A /* Build configuration list for PBXNativeTarget "SwifterSwift-iOSTests" */; buildPhases = ( + E455DFD82AD0CC39008A011E /* SwiftFormat */, + 07A1C442224FC02E003272E4 /* SwiftLint */, 07C50CC51F5EAF2700F46E5A /* Sources */, 07C50CC61F5EAF2700F46E5A /* Frameworks */, 07C50CC71F5EAF2700F46E5A /* Resources */, - 07A1C442224FC02E003272E4 /* SwiftLint */, ); buildRules = ( ); @@ -1636,10 +1757,11 @@ isa = PBXNativeTarget; buildConfigurationList = 07C50CE01F5EAF4B00F46E5A /* Build configuration list for PBXNativeTarget "SwifterSwift-tvOSTests" */; buildPhases = ( + E455DFD92AD0CC46008A011E /* SwiftFormat */, + 07A1C443224FC034003272E4 /* SwiftLint */, 07C50CD41F5EAF4B00F46E5A /* Sources */, 07C50CD51F5EAF4B00F46E5A /* Frameworks */, 07C50CD61F5EAF4B00F46E5A /* Resources */, - 07A1C443224FC034003272E4 /* SwiftLint */, ); buildRules = ( ); @@ -1655,10 +1777,11 @@ isa = PBXNativeTarget; buildConfigurationList = 07C50CEF1F5EAF6300F46E5A /* Build configuration list for PBXNativeTarget "SwifterSwift-macOSTests" */; buildPhases = ( + E455DFDA2AD0CC57008A011E /* SwiftFormat */, + 07A1C444224FC043003272E4 /* SwiftLint */, 07C50CE31F5EAF6300F46E5A /* Sources */, 07C50CE41F5EAF6300F46E5A /* Frameworks */, 07C50CE51F5EAF6300F46E5A /* Resources */, - 07A1C444224FC043003272E4 /* SwiftLint */, ); buildRules = ( ); @@ -1676,8 +1799,9 @@ 07898B521F278CF900558C97 /* Project object */ = { isa = PBXProject; attributes = { + BuildIndependentTargetsInParallel = YES; LastSwiftUpdateCheck = 0900; - LastUpgradeCheck = 1200; + LastUpgradeCheck = 1500; ORGANIZATIONNAME = SwifterSwift; TargetAttributes = { 07898B5C1F278D7600558C97 = { @@ -1789,7 +1913,7 @@ files = ( CAC5EBD02125273100AB27EC /* TestImage.png in Resources */, CAC5EBCE2125272A00AB27EC /* test.json in Resources */, - 8D50E52224D0D76400972E2D /* UIImageView.xib in Resources */, + 8D50E52224D0D76400972E2D /* UIImageViewTvOS.xib in Resources */, CAC5EBCD2125272400AB27EC /* big_buck_bunny_720p_1mb.mp4 in Resources */, F88C491924AF73A400BA0503 /* TestStoryboard-tvOS.storyboard in Resources */, ); @@ -1809,6 +1933,7 @@ /* Begin PBXShellScriptBuildPhase section */ 07A1C43E224FBFC6003272E4 /* SwiftLint */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -1823,10 +1948,11 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "if [[ ${TRAVIS} == \"\" ]]; then\n if which swiftlint >/dev/null; then \n swiftlint \n else\n echo \"warning: SwiftLint is not installed\"\n fi\nfi\n"; + shellScript = "if [[ ${TRAVIS} == \"\" ]]; then\n if [[ \"$(uname -m)\" == arm64 ]]; then\n export PATH=\"/opt/homebrew/bin:$PATH\"\n fi\n\n if which swiftlint >/dev/null; then \n swiftlint\n else\n echo \"warning: SwiftLint is not installed\"\n fi\nfi\n"; }; 07A1C43F224FBFD5003272E4 /* SwiftLint */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -1841,10 +1967,11 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "if [[ ${TRAVIS} == \"\" ]]; then\n if which swiftlint >/dev/null; then \n swiftlint \n else\n echo \"warning: SwiftLint is not installed\"\n fi\nfi\n"; + shellScript = "if [[ ${TRAVIS} == \"\" ]]; then\n if [[ \"$(uname -m)\" == arm64 ]]; then\n export PATH=\"/opt/homebrew/bin:$PATH\"\n fi\n\n if which swiftlint >/dev/null; then \n swiftlint\n else\n echo \"warning: SwiftLint is not installed\"\n fi\nfi\n"; }; 07A1C440224FBFDD003272E4 /* SwiftLint */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -1859,10 +1986,11 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "if [[ ${TRAVIS} == \"\" ]]; then\n if which swiftlint >/dev/null; then \n swiftlint \n else\n echo \"warning: SwiftLint is not installed\"\n fi\nfi\n"; + shellScript = "if [[ ${TRAVIS} == \"\" ]]; then\n if [[ \"$(uname -m)\" == arm64 ]]; then\n export PATH=\"/opt/homebrew/bin:$PATH\"\n fi\n\n if which swiftlint >/dev/null; then \n swiftlint\n else\n echo \"warning: SwiftLint is not installed\"\n fi\nfi\n"; }; 07A1C441224FBFE7003272E4 /* SwiftLint */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -1877,10 +2005,11 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "if [[ ${TRAVIS} == \"\" ]]; then\n if which swiftlint >/dev/null; then \n swiftlint \n else\n echo \"warning: SwiftLint is not installed\"\n fi\nfi\n"; + shellScript = "if [[ ${TRAVIS} == \"\" ]]; then\n if [[ \"$(uname -m)\" == arm64 ]]; then\n export PATH=\"/opt/homebrew/bin:$PATH\"\n fi\n\n if which swiftlint >/dev/null; then \n swiftlint\n else\n echo \"warning: SwiftLint is not installed\"\n fi\nfi\n"; }; 07A1C442224FC02E003272E4 /* SwiftLint */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -1895,10 +2024,11 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "if [[ ${TRAVIS} == \"\" ]]; then\n if which swiftlint >/dev/null; then \n swiftlint \n else\n echo \"warning: SwiftLint is not installed\"\n fi\nfi\n"; + shellScript = "if [[ ${TRAVIS} == \"\" ]]; then\n if [[ \"$(uname -m)\" == arm64 ]]; then\n export PATH=\"/opt/homebrew/bin:$PATH\"\n fi\n\n if which swiftlint >/dev/null; then \n swiftlint\n else\n echo \"warning: SwiftLint is not installed\"\n fi\nfi\n"; }; 07A1C443224FC034003272E4 /* SwiftLint */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -1913,10 +2043,11 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "if [[ ${TRAVIS} == \"\" ]]; then\n if which swiftlint >/dev/null; then \n swiftlint \n else\n echo \"warning: SwiftLint is not installed\"\n fi\nfi\n\n"; + shellScript = "if [[ ${TRAVIS} == \"\" ]]; then\n if [[ \"$(uname -m)\" == arm64 ]]; then\n export PATH=\"/opt/homebrew/bin:$PATH\"\n fi\n\n if which swiftlint >/dev/null; then \n swiftlint\n else\n echo \"warning: SwiftLint is not installed\"\n fi\nfi\n\n"; }; 07A1C444224FC043003272E4 /* SwiftLint */ = { isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; buildActionMask = 2147483647; files = ( ); @@ -1931,7 +2062,140 @@ ); runOnlyForDeploymentPostprocessing = 0; shellPath = /bin/sh; - shellScript = "if [[ ${TRAVIS} == \"\" ]]; then\n if which swiftlint >/dev/null; then \n swiftlint \n else\n echo \"warning: SwiftLint is not installed\"\n fi\nfi\n"; + shellScript = "if [[ ${TRAVIS} == \"\" ]]; then\n if [[ \"$(uname -m)\" == arm64 ]]; then\n export PATH=\"/opt/homebrew/bin:$PATH\"\n fi\n\n if which swiftlint >/dev/null; then \n swiftlint\n else\n echo \"warning: SwiftLint is not installed\"\n fi\nfi\n"; + }; + E455DFD42AD0CBD4008A011E /* SwiftFormat */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = SwiftFormat; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "if [[ ${TRAVIS} == \"\" ]]; then\n if [[ \"$(uname -m)\" == arm64 ]]; then\n export PATH=\"/opt/homebrew/bin:$PATH\"\n fi\n\n if which swiftformat >/dev/null; then \n swiftformat \"$SRCROOT\"\n else\n echo \"warning: SwiftFormat is not installed\"\n fi\nfi\n"; + }; + E455DFD52AD0CC17008A011E /* SwiftFormat */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = SwiftFormat; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "if [[ ${TRAVIS} == \"\" ]]; then\n if [[ \"$(uname -m)\" == arm64 ]]; then\n export PATH=\"/opt/homebrew/bin:$PATH\"\n fi\n\n if which swiftformat >/dev/null; then \n swiftformat \"$SRCROOT\"\n else\n echo \"warning: SwiftFormat is not installed\"\n fi\nfi\n"; + }; + E455DFD62AD0CC23008A011E /* SwiftFormat */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = SwiftFormat; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "if [[ ${TRAVIS} == \"\" ]]; then\n if [[ \"$(uname -m)\" == arm64 ]]; then\n export PATH=\"/opt/homebrew/bin:$PATH\"\n fi\n\n if which swiftformat >/dev/null; then \n swiftformat \"$SRCROOT\"\n else\n echo \"warning: SwiftFormat is not installed\"\n fi\nfi\n"; + }; + E455DFD72AD0CC2F008A011E /* SwiftFormat */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = SwiftFormat; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "if [[ ${TRAVIS} == \"\" ]]; then\n if [[ \"$(uname -m)\" == arm64 ]]; then\n export PATH=\"/opt/homebrew/bin:$PATH\"\n fi\n\n if which swiftformat >/dev/null; then \n swiftformat \"$SRCROOT\"\n else\n echo \"warning: SwiftFormat is not installed\"\n fi\nfi\n"; + }; + E455DFD82AD0CC39008A011E /* SwiftFormat */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = SwiftFormat; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "if [[ ${TRAVIS} == \"\" ]]; then\n if [[ \"$(uname -m)\" == arm64 ]]; then\n export PATH=\"/opt/homebrew/bin:$PATH\"\n fi\n\n if which swiftformat >/dev/null; then \n swiftformat \"$SRCROOT\"\n else\n echo \"warning: SwiftFormat is not installed\"\n fi\nfi\n"; + }; + E455DFD92AD0CC46008A011E /* SwiftFormat */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = SwiftFormat; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "if [[ ${TRAVIS} == \"\" ]]; then\n if [[ \"$(uname -m)\" == arm64 ]]; then\n export PATH=\"/opt/homebrew/bin:$PATH\"\n fi\n\n if which swiftformat >/dev/null; then \n swiftformat \"$SRCROOT\"\n else\n echo \"warning: SwiftFormat is not installed\"\n fi\nfi\n"; + }; + E455DFDA2AD0CC57008A011E /* SwiftFormat */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = SwiftFormat; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "if [[ ${TRAVIS} == \"\" ]]; then\n if [[ \"$(uname -m)\" == arm64 ]]; then\n export PATH=\"/opt/homebrew/bin:$PATH\"\n fi\n\n if which swiftformat >/dev/null; then\n swiftformat \"$SRCROOT\"\n else\n echo \"warning: SwiftFormat is not installed\"\n fi\nfi\n"; }; /* End PBXShellScriptBuildPhase section */ @@ -1953,10 +2217,12 @@ 07B7F1A71F5EB42000E6F910 /* UIViewExtensions.swift in Sources */, 07B7F1991F5EB42000E6F910 /* UILabelExtensions.swift in Sources */, 752AC7A020BC975E00659E76 /* SKNodeExtensions.swift in Sources */, + 58CAE62928CF2935001935A2 /* DefaultStringInterpolationExtensions.swift in Sources */, 985C1B8F24797694008AAB0E /* WKWebViewExtensions.swift in Sources */, 9D9784DB1FCAE3D200D988E7 /* StringProtocolExtensions.swift in Sources */, F8A710E923BF3EF100112DAD /* EdgeInsetsExtensions.swift in Sources */, 07B7F20C1F5EB43C00E6F910 /* BoolExtensions.swift in Sources */, + 58253512259CF23B00407B78 /* MeasurementExtensions.swift in Sources */, 07B7F22F1F5EB45100E6F910 /* CGFloatExtensions.swift in Sources */, 9D806A602258D503008E500A /* SCNPlaneExtensions.swift in Sources */, CF30948A216AAC7A005609BC /* UIActivityExtensions.swift in Sources */, @@ -1972,15 +2238,17 @@ 9D806A862258F624008E500A /* SCNGeometryExtensions.swift in Sources */, C7E027C42360942000F1061E /* KeyedDecodingContainerExtensions.swift in Sources */, 07B7F2111F5EB43C00E6F910 /* DictionaryExtensions.swift in Sources */, - 076AEC891FDB48580077D153 /* UIDatePickerExtensions.swift in Sources */, + D5FD5284270853B30073F831 /* BinaryIntegerExtensions.swift in Sources */, 9D806A6A2258DC3E008E500A /* SCNShapeExtensions.swift in Sources */, F854D2A52423AE92003A08A9 /* CGAffineTransformExtensions.swift in Sources */, 07B7F2301F5EB45100E6F910 /* CGPointExtensions.swift in Sources */, 664CB96D2171863B00FC87B4 /* BidirectionalCollectionExtensions.swift in Sources */, 07B7F1951F5EB42000E6F910 /* UICollectionViewExtensions.swift in Sources */, 07B7F1A31F5EB42000E6F910 /* UITableViewExtensions.swift in Sources */, + 17A4B79326CCFFAE007D299F /* SKSpriteNodeExtensions.swift in Sources */, 077BA08F1F6BE83600D9C4AC /* UserDefaultsExtensions.swift in Sources */, 07B7F1A11F5EB42000E6F910 /* UISwitchExtensions.swift in Sources */, + 658A02F2270F01C500DBA951 /* MKMultiPointExtensions.swift in Sources */, 0722CB3820C2E46C00C6C1B6 /* UIWindowExtensions.swift in Sources */, 07B7F1A41F5EB42000E6F910 /* UITextFieldExtensions.swift in Sources */, 07B7F20B1F5EB43C00E6F910 /* ArrayExtensions.swift in Sources */, @@ -1990,6 +2258,7 @@ 07B7F1941F5EB42000E6F910 /* UIButtonExtensions.swift in Sources */, 07B7F21B1F5EB43C00E6F910 /* URLExtensions.swift in Sources */, 0782A5AF23DA985B00E562C0 /* CLLocationArrayExtensions.swift in Sources */, + 9E9F42B129872E9400A0BD60 /* URLSessionExtensions.swift in Sources */, 07B7F19B1F5EB42000E6F910 /* UINavigationControllerExtensions.swift in Sources */, 07B7F19F1F5EB42000E6F910 /* UISliderExtensions.swift in Sources */, 07B7F1931F5EB42000E6F910 /* UIBarButtonItemExtensions.swift in Sources */, @@ -2013,12 +2282,12 @@ E5E5EB3A2350EED400B04C42 /* CAGradientLayerExtensions.swift in Sources */, 07B7F2121F5EB43C00E6F910 /* DoubleExtensions.swift in Sources */, 07B7F2171F5EB43C00E6F910 /* OptionalExtensions.swift in Sources */, + 8AC39B6328790A6B0041A56C /* DigestExtensions.swift in Sources */, 9D806A792258E6A0008E500A /* SCNCapsuleExtensions.swift in Sources */, 074EAF1B1F7BA68B00C74636 /* UIFontExtensions.swift in Sources */, 8D4B424C212972AE002A5923 /* UILayoutPriorityExtensions.swift in Sources */, CA1317542106D35E002F1B0D /* UIRefreshControlExtensions.swift in Sources */, 07B7F1921F5EB42000E6F910 /* UIAlertControllerExtensions.swift in Sources */, - B2A0DAB62336D5A6002B0BC5 /* StdlibDeprecated.swift in Sources */, 07B7F22E1F5EB45100E6F910 /* CGColorExtensions.swift in Sources */, 70269A2B1FB478D100C6C2D0 /* CalendarExtensions.swift in Sources */, 07B7F2161F5EB43C00E6F910 /* LocaleExtensions.swift in Sources */, @@ -2035,6 +2304,7 @@ 07B7F19E1F5EB42000E6F910 /* UISegmentedControlExtensions.swift in Sources */, 07B7F20F1F5EB43C00E6F910 /* DataExtensions.swift in Sources */, 0DAEBCE024B0045F00F61684 /* HKActivitySummaryExtensions.swift in Sources */, + 9E3CA2672ABE0D7100FA4626 /* FutureExtensions.swift in Sources */, 07B7F19C1F5EB42000E6F910 /* UINavigationItemExtensions.swift in Sources */, 9D806A5B2258D2B8008E500A /* SCNMaterialExtensions.swift in Sources */, F87AA5D5241257E3005F5B28 /* NotificationCenterExtensions.swift in Sources */, @@ -2056,9 +2326,9 @@ 07B7F1AF1F5EB42000E6F910 /* UILabelExtensions.swift in Sources */, 664CB984217243D200FC87B4 /* DispatchQueueExtensions.swift in Sources */, 9D806A612258D503008E500A /* SCNPlaneExtensions.swift in Sources */, + 9E9F42B229872E9400A0BD60 /* URLSessionExtensions.swift in Sources */, 9D9784DC1FCAE42600D988E7 /* StringProtocolExtensions.swift in Sources */, 07B7F1FA1F5EB43C00E6F910 /* BoolExtensions.swift in Sources */, - B2A0DAB72336D5A6002B0BC5 /* StdlibDeprecated.swift in Sources */, A9AEB78620283ACC0021AACF /* FileManagerExtensions.swift in Sources */, 784C75312051BD4B001C48DD /* MKPolylineExtensions.swift in Sources */, 9DC844A82349E28100E1571A /* UIColorExtensions.swift in Sources */, @@ -2067,6 +2337,7 @@ 9D806A6B2258DC3E008E500A /* SCNShapeExtensions.swift in Sources */, 07B7F2381F5EB45200E6F910 /* CLLocationExtensions.swift in Sources */, 07B7F1FC1F5EB43C00E6F910 /* CollectionExtensions.swift in Sources */, + 58253513259CF23B00407B78 /* MeasurementExtensions.swift in Sources */, 74932D1623B8CEB800A56D81 /* SKProductExtensions.swift in Sources */, F854D2B62423E1F0003A08A9 /* CAGradientLayerExtensions.swift in Sources */, 07B7F2031F5EB43C00E6F910 /* IntExtensions.swift in Sources */, @@ -2077,6 +2348,7 @@ 07B7F2361F5EB45200E6F910 /* CGPointExtensions.swift in Sources */, 07B7F1AB1F5EB42000E6F910 /* UICollectionViewExtensions.swift in Sources */, 07B7F1B91F5EB42000E6F910 /* UITableViewExtensions.swift in Sources */, + D5FD5285270853B30073F831 /* BinaryIntegerExtensions.swift in Sources */, 116090B024187D5C00DDCD01 /* CGRectExtensions.swift in Sources */, B2EEA14B211A7ACA00EF7F8E /* RangeReplaceableCollectionExtensions.swift in Sources */, 7832C2B0209BB19300224EED /* ComparableExtensions.swift in Sources */, @@ -2095,6 +2367,7 @@ 07B7F1B51F5EB42000E6F910 /* UISliderExtensions.swift in Sources */, C7E027C52360942000F1061E /* KeyedDecodingContainerExtensions.swift in Sources */, 07A494E523C7CAAA00DFCBFC /* CLVisitExtensions.swift in Sources */, + 9EA118122ABF2FED00AD3916 /* FutureExtensions.swift in Sources */, F8C1AE70225B7F990045D5A0 /* NSRegularExpressionExtensions.swift in Sources */, 07B7F1A91F5EB42000E6F910 /* UIBarButtonItemExtensions.swift in Sources */, 07B7F1B81F5EB42000E6F910 /* UITabBarExtensions.swift in Sources */, @@ -2102,13 +2375,16 @@ 734308082108E4630029CD16 /* CGVectorExtensions.swift in Sources */, F8DED22624EAA2070068F758 /* UIScrollViewExtensions.swift in Sources */, 07B7F1BC1F5EB42000E6F910 /* UIViewControllerExtensions.swift in Sources */, + 58CAE62A28CF2935001935A2 /* DefaultStringInterpolationExtensions.swift in Sources */, F8A710EA23BF3F7300112DAD /* EdgeInsetsExtensions.swift in Sources */, 07B7F2371F5EB45200E6F910 /* CGSizeExtensions.swift in Sources */, 07B7F1FB1F5EB43C00E6F910 /* CharacterExtensions.swift in Sources */, 07B7F2081F5EB43C00E6F910 /* StringExtensions.swift in Sources */, 07B7F2391F5EB45200E6F910 /* NSAttributedStringExtensions.swift in Sources */, + 17A4B79426CCFFAF007D299F /* SKSpriteNodeExtensions.swift in Sources */, 07B7F1AD1F5EB42000E6F910 /* UIImageExtensions.swift in Sources */, 9D806A372258AE09008E500A /* UIBezierPathExtensions.swift in Sources */, + 8AC39B6428790A6B0041A56C /* DigestExtensions.swift in Sources */, 077BA08B1F6BE81F00D9C4AC /* URLRequestExtensions.swift in Sources */, 07A1C448224FFE16003272E4 /* UIApplicationExtensions.swift in Sources */, F854D2C42423ECF0003A08A9 /* CATransform3DExtensions.swift in Sources */, @@ -2133,6 +2409,7 @@ 0782A5B523DA98C900E562C0 /* CLLocationArrayExtensions.swift in Sources */, 9D806A422258B931008E500A /* SCNVector3Extensions.swift in Sources */, F887E93A2505AB87006CB4F7 /* FontExtensions.swift in Sources */, + 658A02F3270F01D200DBA951 /* MKMultiPointExtensions.swift in Sources */, 07B7F1B21F5EB42000E6F910 /* UINavigationItemExtensions.swift in Sources */, 9D806A5C2258D2B8008E500A /* SCNMaterialExtensions.swift in Sources */, 9D806A872258F624008E500A /* SCNGeometryExtensions.swift in Sources */, @@ -2160,6 +2437,7 @@ F887E93B2505AB87006CB4F7 /* FontExtensions.swift in Sources */, 07A1C449224FFE17003272E4 /* UIApplicationExtensions.swift in Sources */, 0770035920729B040095F09A /* MKPolylineExtensions.swift in Sources */, + D5FD5286270853B30073F831 /* BinaryIntegerExtensions.swift in Sources */, 9D806A7B2258E6A0008E500A /* SCNCapsuleExtensions.swift in Sources */, 07B7F2291F5EB45100E6F910 /* CGFloatExtensions.swift in Sources */, 07B7F1C41F5EB42200E6F910 /* UIImageViewExtensions.swift in Sources */, @@ -2170,12 +2448,14 @@ 07B7F1F11F5EB43B00E6F910 /* IntExtensions.swift in Sources */, 9D806A672258D75A008E500A /* SCNBoxExtensions.swift in Sources */, 664CB985217243D200FC87B4 /* DispatchQueueExtensions.swift in Sources */, + 9EA118112ABF2FED00AD3916 /* FutureExtensions.swift in Sources */, B2A0DAC22336DA87002B0BC5 /* MutableCollectionExtensions.swift in Sources */, 07B7F1ED1F5EB43B00E6F910 /* DictionaryExtensions.swift in Sources */, 07B7F22A1F5EB45100E6F910 /* CGPointExtensions.swift in Sources */, 734308092108E4640029CD16 /* CGVectorExtensions.swift in Sources */, 9D806A382258AE0A008E500A /* UIBezierPathExtensions.swift in Sources */, 07B7F1C11F5EB42200E6F910 /* UICollectionViewExtensions.swift in Sources */, + 17A4B79526CCFFAF007D299F /* SKSpriteNodeExtensions.swift in Sources */, 07B7F1CF1F5EB42200E6F910 /* UITableViewExtensions.swift in Sources */, 077BA0911F6BE83600D9C4AC /* UserDefaultsExtensions.swift in Sources */, 7832C2B1209BB19300224EED /* ComparableExtensions.swift in Sources */, @@ -2191,8 +2471,8 @@ 07B7F1C01F5EB42200E6F910 /* UIButtonExtensions.swift in Sources */, 07B7F1F71F5EB43B00E6F910 /* URLExtensions.swift in Sources */, 07B7F1C71F5EB42200E6F910 /* UINavigationControllerExtensions.swift in Sources */, + 58253514259CF23B00407B78 /* MeasurementExtensions.swift in Sources */, 07B7F1CB1F5EB42200E6F910 /* UISliderExtensions.swift in Sources */, - B2A0DAB82336D5A6002B0BC5 /* StdlibDeprecated.swift in Sources */, 07B7F1BF1F5EB42200E6F910 /* UIBarButtonItemExtensions.swift in Sources */, 07B7F1CE1F5EB42200E6F910 /* UITabBarExtensions.swift in Sources */, F8A710EB23BF3F7400112DAD /* EdgeInsetsExtensions.swift in Sources */, @@ -2215,9 +2495,12 @@ 07B7F1EE1F5EB43B00E6F910 /* DoubleExtensions.swift in Sources */, 9D806A762258E0D8008E500A /* SCNConeExtensions.swift in Sources */, 07B7F1F31F5EB43B00E6F910 /* OptionalExtensions.swift in Sources */, + 658A02F4270F01D200DBA951 /* MKMultiPointExtensions.swift in Sources */, 074EAF1D1F7BA68B00C74636 /* UIFontExtensions.swift in Sources */, 18C8E5E02074C60C00F8AF51 /* SequenceExtensions.swift in Sources */, + 58CAE62B28CF2935001935A2 /* DefaultStringInterpolationExtensions.swift in Sources */, 07B7F1BE1F5EB42200E6F910 /* UIAlertControllerExtensions.swift in Sources */, + 8AC39B6528790A6B0041A56C /* DigestExtensions.swift in Sources */, 07B7F2281F5EB45100E6F910 /* CGColorExtensions.swift in Sources */, 70269A2E1FB478D100C6C2D0 /* CalendarExtensions.swift in Sources */, 07B7F1F21F5EB43B00E6F910 /* LocaleExtensions.swift in Sources */, @@ -2226,6 +2509,7 @@ 07B7F1F01F5EB43B00E6F910 /* FloatingPointExtensions.swift in Sources */, 07B7F1CA1F5EB42200E6F910 /* UISegmentedControlExtensions.swift in Sources */, 07B7F1EB1F5EB43B00E6F910 /* DataExtensions.swift in Sources */, + 9E9F42B329872E9400A0BD60 /* URLSessionExtensions.swift in Sources */, 664CB96F2171863B00FC87B4 /* BidirectionalCollectionExtensions.swift in Sources */, 07B7F1C81F5EB42200E6F910 /* UINavigationItemExtensions.swift in Sources */, 07B7F1D11F5EB42200E6F910 /* UITextViewExtensions.swift in Sources */, @@ -2239,6 +2523,7 @@ files = ( B2EEA14A211A7AC900EF7F8E /* RangeReplaceableCollectionExtensions.swift in Sources */, 074C8D83224F86450050F040 /* MKMapViewExtensions.swift in Sources */, + F8B4DFB4291EA7880011BD90 /* HKActivitySummaryExtensions.swift in Sources */, 07B7F1E01F5EB43B00E6F910 /* LocaleExtensions.swift in Sources */, 9D806A5E2258D2B8008E500A /* SCNMaterialExtensions.swift in Sources */, 07B7F1DA1F5EB43B00E6F910 /* DateExtensions.swift in Sources */, @@ -2249,25 +2534,29 @@ 07B7F1DE1F5EB43B00E6F910 /* FloatingPointExtensions.swift in Sources */, 07B7F1DB1F5EB43B00E6F910 /* DictionaryExtensions.swift in Sources */, A9AEB78820283ACD0021AACF /* FileManagerExtensions.swift in Sources */, + 8AC39B6628790A6B0041A56C /* DigestExtensions.swift in Sources */, 07B7F1DC1F5EB43B00E6F910 /* DoubleExtensions.swift in Sources */, 07B7F1D71F5EB43B00E6F910 /* CharacterExtensions.swift in Sources */, 9DC844A62349E27600E1571A /* NSColorExtensions.swift in Sources */, + 9E9F42B429872E9400A0BD60 /* URLSessionExtensions.swift in Sources */, 07B7F2231F5EB44600E6F910 /* CGSizeExtensions.swift in Sources */, 664CB9792171899800FC87B4 /* BinaryFloatingPointExtensions.swift in Sources */, 9D806A632258D503008E500A /* SCNPlaneExtensions.swift in Sources */, 07B7F1E11F5EB43B00E6F910 /* OptionalExtensions.swift in Sources */, 9D9784DE1FCAE42600D988E7 /* StringProtocolExtensions.swift in Sources */, + 658A02F5270F01D300DBA951 /* MKMultiPointExtensions.swift in Sources */, 664CB9702171863B00FC87B4 /* BidirectionalCollectionExtensions.swift in Sources */, 07B7F2221F5EB44600E6F910 /* CGPointExtensions.swift in Sources */, 9D806A442258B931008E500A /* SCNVector3Extensions.swift in Sources */, D951E8AD23D04DCB00F2CD0B /* DecodableExtensions.swift in Sources */, + 17A4B79626CCFFAF007D299F /* SKSpriteNodeExtensions.swift in Sources */, 07B7F2251F5EB44600E6F910 /* NSAttributedStringExtensions.swift in Sources */, 077BA08D1F6BE81F00D9C4AC /* URLRequestExtensions.swift in Sources */, 9D806A772258E0D8008E500A /* SCNConeExtensions.swift in Sources */, 18C8E5E12074C60C00F8AF51 /* SequenceExtensions.swift in Sources */, 784C75302051BD4A001C48DD /* MKPolylineExtensions.swift in Sources */, 7343080A2108E4650029CD16 /* CGVectorExtensions.swift in Sources */, - B2A0DAB92336D5A6002B0BC5 /* StdlibDeprecated.swift in Sources */, + D5FD5287270853B30073F831 /* BinaryIntegerExtensions.swift in Sources */, 9D806A7C2258E6A0008E500A /* SCNCapsuleExtensions.swift in Sources */, 07B7F1E31F5EB43B00E6F910 /* SignedNumericExtensions.swift in Sources */, 9D806A722258DE23008E500A /* SCNCylinderExtensions.swift in Sources */, @@ -2276,6 +2565,7 @@ 9D806A682258D75A008E500A /* SCNBoxExtensions.swift in Sources */, F854D2C52423ECF0003A08A9 /* CATransform3DExtensions.swift in Sources */, F854D2B02423DF5A003A08A9 /* CGAffineTransformExtensions.swift in Sources */, + 9EA118102ABF2FEC00AD3916 /* FutureExtensions.swift in Sources */, 985C1B9024797694008AAB0E /* WKWebViewExtensions.swift in Sources */, 278CA08D1F9A9232004918BD /* NSImageExtensions.swift in Sources */, F8A710EC23BF3F7400112DAD /* EdgeInsetsExtensions.swift in Sources */, @@ -2290,6 +2580,7 @@ 0782A5B723DA98CA00E562C0 /* CLLocationArrayExtensions.swift in Sources */, 07A494E723C7CAAA00DFCBFC /* CLVisitExtensions.swift in Sources */, 07B7F2201F5EB44600E6F910 /* CGColorExtensions.swift in Sources */, + 58CAE62C28CF2935001935A2 /* DefaultStringInterpolationExtensions.swift in Sources */, 0726D77C1F7C24840028CAB5 /* ColorExtensions.swift in Sources */, 07B7F1D51F5EB43B00E6F910 /* ArrayExtensions.swift in Sources */, 7832C2B2209BB19300224EED /* ComparableExtensions.swift in Sources */, @@ -2303,6 +2594,7 @@ 116090B224187D5C00DDCD01 /* CGRectExtensions.swift in Sources */, 9D4914861F85138E00F3868F /* NSPredicateExtensions.swift in Sources */, 07B7F2271F5EB44600E6F910 /* NSViewExtensions.swift in Sources */, + 58253515259CF23B00407B78 /* MeasurementExtensions.swift in Sources */, C7E027C72360942000F1061E /* KeyedDecodingContainerExtensions.swift in Sources */, 07B7F2211F5EB44600E6F910 /* CGFloatExtensions.swift in Sources */, ); @@ -2316,6 +2608,7 @@ 9D806A3D2258AED2008E500A /* UIBezierPathExtensionsTests.swift in Sources */, 07C50D621F5EB05000F46E5A /* OptionalExtensionsTests.swift in Sources */, F854D2BB2423E7C8003A08A9 /* CGAffineTransformExtensionsTests.swift in Sources */, + 9E9F42BA298733DF00A0BD60 /* URLSessionExtensionsTests.swift in Sources */, 07C50D601F5EB05000F46E5A /* IntExtensionsTests.swift in Sources */, 07C50D631F5EB05000F46E5A /* StringExtensionsTests.swift in Sources */, 9D806A8B2258F892008E500A /* SCNGeometryExtensionsTests.swift in Sources */, @@ -2324,11 +2617,12 @@ 0DAEBCE324B0079700F61684 /* HKActivitySummaryExtensionsTests.swift in Sources */, 077BA09E1F6BEA4900D9C4AC /* URLRequestExtensionsTests.swift in Sources */, 9D806A932258FCCE008E500A /* SCNConeExtensionsTests.swift in Sources */, - D9F36CF623521F440057258F /* MKMapViewTests.swift in Sources */, + D9F36CF623521F440057258F /* MKMapViewExtensionsTests.swift in Sources */, 07C50D2E1F5EB04600F46E5A /* UICollectionViewExtensionsTests.swift in Sources */, 664CB98A21724A9100FC87B4 /* DispatchQueueExtensionsTests.swift in Sources */, 664CB97B21718B1D00FC87B4 /* BinaryFloatingPointExtensionsTests.swift in Sources */, 07C50D3C1F5EB04700F46E5A /* UITableViewExtensionsTests.swift in Sources */, + 9E28344D25AEBD160093203B /* MeasurementExtensionsTests.swift in Sources */, F87AA5DA24125C19005F5B28 /* NotificationCenterExtensionsTests.swift in Sources */, 985C1B932479795B008AAB0E /* WKWebViewExtensionsTests.swift in Sources */, 078916DE20760DA700AC0665 /* SignedIntegerExtensionsTests.swift in Sources */, @@ -2338,6 +2632,8 @@ 07C50D331F5EB04700F46E5A /* UINavigationBarExtensionTests.swift in Sources */, 07C50D401F5EB04700F46E5A /* UIViewExtensionsTests.swift in Sources */, E5E5EB3D2350F40200B04C42 /* CAGradientLayerExtensionsTests.swift in Sources */, + 9EA118142ABF2FFF00AD3916 /* FutureExtensionsTests.swift in Sources */, + 5898A74228D1D7C200261630 /* DefaultStringInterpolationExtensionsTests.swift in Sources */, 90693555208B545100407C4D /* UIGestureRecognizerExtensionsTests.swift in Sources */, 07C50D311F5EB04700F46E5A /* UIImageViewExtensionsTests.swift in Sources */, 07C50D2C1F5EB04600F46E5A /* UIBarButtonExtensionsTests.swift in Sources */, @@ -2357,9 +2653,10 @@ 07A494ED23C7D60400DFCBFC /* CLVisitExtensionsTests.swift in Sources */, 07FE50451F891C95000766AA /* SignedNumericExtensionsTests.swift in Sources */, 2141A352235F379600218109 /* TestHelpers.swift in Sources */, + D5FD528D27085D820073F831 /* BinaryIntegerExtensionsTests.swift in Sources */, 9D806A822258F56F008E500A /* SCNBoxExtensionsTests.swift in Sources */, 07C50D391F5EB04700F46E5A /* UIStoryboardExtensionsTests.swift in Sources */, - 784C75372051BE1D001C48DD /* MKPolylineTests.swift in Sources */, + 784C75372051BE1D001C48DD /* MKPolylineExtensionsTests.swift in Sources */, 0722CB3A20C2E49A00C6C1B6 /* UIWindowExtensionsTests.swift in Sources */, B22EB2B920E9E814001EAE70 /* RangeReplaceableCollectionTests.swift in Sources */, 8D4B424F212972E7002A5923 /* UILayoutPriorityExtensionsTests.swift in Sources */, @@ -2368,25 +2665,27 @@ 0782A5B223DA98C000E562C0 /* CLLocationArrayExtensionsTests.swift in Sources */, F850972424AF72690007F74D /* MyViewController.swift in Sources */, 9D806A472258B9BB008E500A /* SCNVector3ExtensionsTests.swift in Sources */, + 658A02F7270F024700DBA951 /* MKMultiPointExtensionsTests.swift in Sources */, 07C50D8C1F5EB06000F46E5A /* CGFloatExtensionsTests.swift in Sources */, 42F53FF02039C7140070DC11 /* UIStackViewExtensionsTests.swift in Sources */, 07C50D3E1F5EB04700F46E5A /* UITextViewExtensionsTests.swift in Sources */, 074EAF1F1F7BA74700C74636 /* UIFontExtensionsTests.swift in Sources */, 07C50D301F5EB04700F46E5A /* UIImageExtensionsTests.swift in Sources */, - CA1317582106D4F5002F1B0D /* UIRefreshControlExntesionsTests.swift in Sources */, + CA1317582106D4F5002F1B0D /* UIRefreshControlExtensionsTests.swift in Sources */, 07C50D3A1F5EB04700F46E5A /* UISwitchExtensionsTests.swift in Sources */, 07C50D341F5EB04700F46E5A /* UINavigationControllerExtensionsTests.swift in Sources */, F899FE7A24D01A2400F3BDE5 /* XCTestExtensionsTests.swift in Sources */, 752AC7A520BC9FF500659E76 /* SKNodeExtensionTests.swift in Sources */, F8C1AE74225B871F0045D5A0 /* NSRegularExpressionExtensionsTests.swift in Sources */, 07C50D5A1F5EB05000F46E5A /* CollectionExtensionsTests.swift in Sources */, - 076AEC8D1FDB49480077D153 /* UIDatePickerExtensionsTests.swift in Sources */, 07C50D351F5EB04700F46E5A /* UINavigationItemExtensionsTests.swift in Sources */, 07C50D2D1F5EB04600F46E5A /* UIButtonExtensionsTests.swift in Sources */, 70269A301FB47B0C00C6C2D0 /* CalendarExtensionTest.swift in Sources */, A94AA78A202819B400E229A5 /* FileManagerExtensionsTests.swift in Sources */, 7832C2B4209BB32500224EED /* ComparableExtensionsTests.swift in Sources */, 182698AC1F8AB46E0052F21E /* CGColorExtensionsTests.swift in Sources */, + 17A4B79826CD0DA2007D299F /* SKSpriteNodeExtensionTests.swift in Sources */, + 8AC39B6928790B5F0041A56C /* DigestExtensionsTests.swift in Sources */, 07C50D8F1F5EB06000F46E5A /* CLLocationExtensionsTests.swift in Sources */, 9D806A972258FD81008E500A /* SCNCylinderExtensionsTests.swift in Sources */, 07C50D8D1F5EB06000F46E5A /* CGPointExtensionsTests.swift in Sources */, @@ -2419,6 +2718,7 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + D5FD528E27085D820073F831 /* BinaryIntegerExtensionsTests.swift in Sources */, 9D806A9C2258FE9D008E500A /* SCNShapeExtensionsTests.swift in Sources */, 07C50D481F5EB04700F46E5A /* UILabelExtensionsTests.swift in Sources */, 9D806A942258FCCE008E500A /* SCNConeExtensionsTests.swift in Sources */, @@ -2434,10 +2734,12 @@ 07C50D521F5EB04700F46E5A /* UITableViewExtensionsTests.swift in Sources */, F854D2C02423EBD0003A08A9 /* CATransform3DExtensionsTests.swift in Sources */, 078916DB2076077000AC0665 /* FloatingPointExtensionsTests.swift in Sources */, + 9E9F42BB298733E000A0BD60 /* URLSessionExtensionsTests.swift in Sources */, F8C1AE75225B871F0045D5A0 /* NSRegularExpressionExtensionsTests.swift in Sources */, 07C50D4E1F5EB04700F46E5A /* UISliderExtensionsTests.swift in Sources */, + 658A02F8270F024B00DBA951 /* MKMultiPointExtensionsTests.swift in Sources */, 07C50D451F5EB04700F46E5A /* ColorExtensionsTests.swift in Sources */, - 784C75382051BE1D001C48DD /* MKPolylineTests.swift in Sources */, + 784C75382051BE1D001C48DD /* MKPolylineExtensionsTests.swift in Sources */, 07C50D491F5EB04700F46E5A /* UINavigationBarExtensionTests.swift in Sources */, B22EB2BD20E9EA1F001EAE70 /* RangeReplaceableCollectionTests.swift in Sources */, 07C50D561F5EB04700F46E5A /* UIViewExtensionsTests.swift in Sources */, @@ -2448,9 +2750,11 @@ 077BA0BA1F6BEA6A00D9C4AC /* UserDefaultsExtensionsTests.swift in Sources */, 9D806A832258F56F008E500A /* SCNBoxExtensionsTests.swift in Sources */, 07C50D671F5EB05100F46E5A /* CharacterExtensionsTests.swift in Sources */, + 9EA118132ABF2FFE00AD3916 /* FutureExtensionsTests.swift in Sources */, 9D49148A1F8515D100F3868F /* NSPredicateExtensionsTests.swift in Sources */, 07C50D651F5EB05100F46E5A /* ArrayExtensionsTests.swift in Sources */, F8885CF324D2ECB3009C33C0 /* WKWebViewExtensionsTests.swift in Sources */, + 9E28344E25AEBD160093203B /* MeasurementExtensionsTests.swift in Sources */, 748B192023B4F4FA0030FABB /* SKProductTests.swift in Sources */, 2141A353235F37C100218109 /* TestHelpers.swift in Sources */, 07C50D6D1F5EB05100F46E5A /* FloatExtensionsTests.swift in Sources */, @@ -2495,6 +2799,7 @@ 07C50D8A1F5EB06000F46E5A /* CLLocationExtensionsTests.swift in Sources */, 07C50D881F5EB06000F46E5A /* CGPointExtensionsTests.swift in Sources */, 07C50D551F5EB04700F46E5A /* UIViewControllerExtensionsTests.swift in Sources */, + 5898A74328D1D7C300261630 /* DefaultStringInterpolationExtensionsTests.swift in Sources */, 07C50D531F5EB04700F46E5A /* UITextFieldExtensionsTests.swift in Sources */, 07C50D721F5EB05100F46E5A /* URLExtensionsTests.swift in Sources */, 07C50D411F5EB04700F46E5A /* UIAlertControllerExtensionsTests.swift in Sources */, @@ -2511,6 +2816,7 @@ 07C50D8B1F5EB06000F46E5A /* NSAttributedStringExtensionsTests.swift in Sources */, 18C8E5E42074C67000F8AF51 /* SequenceExtensionsTests.swift in Sources */, 07A494EC23C7D60300DFCBFC /* CLVisitExtensionsTests.swift in Sources */, + 8AC39B6A28790B5F0041A56C /* DigestExtensionsTests.swift in Sources */, 07C50D511F5EB04700F46E5A /* UITabBarExtensionsTests.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; @@ -2523,6 +2829,7 @@ 734307FE2108C85E0029CD16 /* CGVectorExtensionsTests.swift in Sources */, 9D49148B1F8515D100F3868F /* NSPredicateExtensionsTests.swift in Sources */, 07C50D731F5EB05100F46E5A /* ArrayExtensionsTests.swift in Sources */, + 5898A74428D1D7C400261630 /* DefaultStringInterpolationExtensionsTests.swift in Sources */, 664CB97D21718B1D00FC87B4 /* BinaryFloatingPointExtensionsTests.swift in Sources */, 0782A5B423DA98C100E562C0 /* CLLocationArrayExtensionsTests.swift in Sources */, 07C50D7E1F5EB05100F46E5A /* OptionalExtensionsTests.swift in Sources */, @@ -2538,11 +2845,15 @@ F87AA5DC24125C19005F5B28 /* NotificationCenterExtensionsTests.swift in Sources */, 07C50D861F5EB05800F46E5A /* NSViewExtensionsTests.swift in Sources */, F854D2B92423E1FE003A08A9 /* CAGradientLayerExtensionsTests.swift in Sources */, + 9E28344F25AEBD160093203B /* MeasurementExtensionsTests.swift in Sources */, 07C50D851F5EB05800F46E5A /* NSAttributedStringExtensionsTests.swift in Sources */, + 9E9F42BC298733E000A0BD60 /* URLSessionExtensionsTests.swift in Sources */, 07C50D7C1F5EB05100F46E5A /* IntExtensionsTests.swift in Sources */, + F8B4DFB3291EA77C0011BD90 /* HKActivitySummaryExtensionsTests.swift in Sources */, F8C1AE76225B871F0045D5A0 /* NSRegularExpressionExtensionsTests.swift in Sources */, 07C50D791F5EB05100F46E5A /* DictionaryExtensionsTests.swift in Sources */, C7E027CB2360958B00F1061E /* KeyedDecodingContainerExtensionsTests.swift in Sources */, + 8AC39B6B28790B5F0041A56C /* DigestExtensionsTests.swift in Sources */, 07C50D7B1F5EB05100F46E5A /* FloatExtensionsTests.swift in Sources */, 07C50D7F1F5EB05100F46E5A /* StringExtensionsTests.swift in Sources */, 70269A311FB47B0C00C6C2D0 /* CalendarExtensionTest.swift in Sources */, @@ -2553,6 +2864,7 @@ 9D806A912258FAA0008E500A /* SCNCapsuleExtensionsTests.swift in Sources */, 07C50D7A1F5EB05100F46E5A /* DoubleExtensionsTests.swift in Sources */, 664CB98C21724A9100FC87B4 /* DispatchQueueExtensionsTests.swift in Sources */, + D5FD528F27085D820073F831 /* BinaryIntegerExtensionsTests.swift in Sources */, 18C8E5E52074C67000F8AF51 /* SequenceExtensionsTests.swift in Sources */, 9D806A492258B9BB008E500A /* SCNVector3ExtensionsTests.swift in Sources */, F854D2BD2423E7C8003A08A9 /* CGAffineTransformExtensionsTests.swift in Sources */, @@ -2566,7 +2878,7 @@ 748B192123B4F4FA0030FABB /* SKProductTests.swift in Sources */, 077BA0BB1F6BEA6B00D9C4AC /* UserDefaultsExtensionsTests.swift in Sources */, D951E8B123D04E4600F2CD0B /* DecodableExtensionsTests.swift in Sources */, - 784C75392051BE1D001C48DD /* MKPolylineTests.swift in Sources */, + 784C75392051BE1D001C48DD /* MKPolylineExtensionsTests.swift in Sources */, 07C50D751F5EB05100F46E5A /* CharacterExtensionsTests.swift in Sources */, 07FE50431F891B40000766AA /* ColorExtensionsTests.swift in Sources */, 07C50D7D1F5EB05100F46E5A /* LocaleExtensionsTests.swift in Sources */, @@ -2580,10 +2892,12 @@ F8A710F123BF3F8600112DAD /* EdgeInsetsExtensionsTests.swift in Sources */, 116090B624187D8100DDCD01 /* CGRectExtensionsTests.swift in Sources */, 07FE50471F891C95000766AA /* SignedNumericExtensionsTests.swift in Sources */, + 658A02F9270F024C00DBA951 /* MKMultiPointExtensionsTests.swift in Sources */, 985C1B942479795B008AAB0E /* WKWebViewExtensionsTests.swift in Sources */, A94AA78C202819B400E229A5 /* FileManagerExtensionsTests.swift in Sources */, 9D806AA12258FF1A008E500A /* SCNSphereExtensionsTests.swift in Sources */, 9D806A8D2258F892008E500A /* SCNGeometryExtensionsTests.swift in Sources */, + 9E3CA3772ABE103E00FA4626 /* FutureExtensionsTests.swift in Sources */, 078916DC2076077000AC0665 /* FloatingPointExtensionsTests.swift in Sources */, 9D806AA52258FF98008E500A /* SCNPlaneExtensionsTests.swift in Sources */, 07A494EB23C7CAC500DFCBFC /* CLVisitExtensionsTests.swift in Sources */, @@ -2632,6 +2946,7 @@ CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + DEAD_CODE_STRIPPING = YES; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_NO_COMMON_BLOCKS = YES; @@ -2641,10 +2956,13 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; - MACOSX_DEPLOYMENT_TARGET = 10.10; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MACOSX_DEPLOYMENT_TARGET = 10.13; ONLY_ACTIVE_ARCH = YES; + OTHER_SWIFT_FLAGS = "-enable-upcoming-feature ConciseMagicFile -enable-upcoming-feature ExistentialAny -enable-upcoming-feature ForwardTrailingClosures"; SWIFT_VERSION = 5.0; + TVOS_DEPLOYMENT_TARGET = 12.0; + WATCHOS_DEPLOYMENT_TARGET = 4.0; }; name = Debug; }; @@ -2669,6 +2987,7 @@ CLANG_WARN_SUSPICIOUS_MOVE = YES; CLANG_WARN_UNREACHABLE_CODE = YES; CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + DEAD_CODE_STRIPPING = YES; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_NO_COMMON_BLOCKS = YES; GCC_WARN_64_TO_32_BIT_CONVERSION = YES; @@ -2677,9 +2996,12 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; - MACOSX_DEPLOYMENT_TARGET = 10.10; + IPHONEOS_DEPLOYMENT_TARGET = 12.0; + MACOSX_DEPLOYMENT_TARGET = 10.13; + OTHER_SWIFT_FLAGS = "-enable-upcoming-feature ConciseMagicFile -enable-upcoming-feature ExistentialAny -enable-upcoming-feature ForwardTrailingClosures"; SWIFT_VERSION = 5.0; + TVOS_DEPLOYMENT_TARGET = 12.0; + WATCHOS_DEPLOYMENT_TARGET = 4.0; }; name = Release; }; @@ -2723,6 +3045,7 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -2741,9 +3064,14 @@ GCC_WARN_UNUSED_VARIABLE = YES; INFOPLIST_FILE = "$(SRCROOT)/Sources/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MARKETING_VERSION = 5.2.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 6.0.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++14"; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.swifterswift.SwifterSwift-iOS"; @@ -2800,6 +3128,7 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -2812,16 +3141,22 @@ GCC_WARN_UNUSED_VARIABLE = YES; INFOPLIST_FILE = "$(SRCROOT)/Sources/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - IPHONEOS_DEPLOYMENT_TARGET = 10.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MARKETING_VERSION = 5.2.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 6.0.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++14"; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_BUNDLE_IDENTIFIER = "com.swifterswift.SwifterSwift-iOS"; PRODUCT_NAME = SwifterSwift; PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = iphoneos; SKIP_INSTALL = YES; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; @@ -2870,6 +3205,7 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -2888,8 +3224,14 @@ GCC_WARN_UNUSED_VARIABLE = YES; INFOPLIST_FILE = "$(SRCROOT)/Sources/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MARKETING_VERSION = 5.2.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 6.0.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++14"; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.swifterswift.SwifterSwift-tvOS"; @@ -2901,7 +3243,6 @@ SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 3; - TVOS_DEPLOYMENT_TARGET = 9.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; }; @@ -2947,6 +3288,7 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -2959,18 +3301,24 @@ GCC_WARN_UNUSED_VARIABLE = YES; INFOPLIST_FILE = "$(SRCROOT)/Sources/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MARKETING_VERSION = 5.2.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 6.0.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++14"; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_BUNDLE_IDENTIFIER = "com.swifterswift.SwifterSwift-tvOS"; PRODUCT_NAME = SwifterSwift; PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = appletvos; SKIP_INSTALL = YES; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 3; - TVOS_DEPLOYMENT_TARGET = 9.0; VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -3018,6 +3366,7 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -3036,8 +3385,14 @@ GCC_WARN_UNUSED_VARIABLE = YES; INFOPLIST_FILE = "$(SRCROOT)/Sources/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MARKETING_VERSION = 5.2.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 6.0.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++14"; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.swifterswift.SwifterSwift-watchOS"; @@ -3051,7 +3406,6 @@ TARGETED_DEVICE_FAMILY = 4; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; - WATCHOS_DEPLOYMENT_TARGET = 2.0; }; name = Debug; }; @@ -3096,6 +3450,7 @@ DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; GCC_C_LANGUAGE_STANDARD = gnu11; @@ -3108,21 +3463,27 @@ GCC_WARN_UNUSED_VARIABLE = YES; INFOPLIST_FILE = "$(SRCROOT)/Sources/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; - MARKETING_VERSION = 5.2.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 6.0.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++14"; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_BUNDLE_IDENTIFIER = "com.swifterswift.SwifterSwift-watchOS"; PRODUCT_NAME = SwifterSwift; PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = watchos; SKIP_INSTALL = YES; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 4; VALIDATE_PRODUCT = YES; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; - WATCHOS_DEPLOYMENT_TARGET = 2.0; }; name = Release; }; @@ -3160,12 +3521,14 @@ COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; + DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; ENABLE_STRICT_OBJC_MSGSEND = YES; ENABLE_TESTABILITY = YES; FRAMEWORK_VERSION = A; @@ -3185,9 +3548,15 @@ GCC_WARN_UNUSED_VARIABLE = YES; INFOPLIST_FILE = "$(SRCROOT)/Sources/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.10; - MARKETING_VERSION = 5.2.0; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 6.0.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++14"; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.swifterswift.SwifterSwift-macOS"; @@ -3237,12 +3606,14 @@ COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; CURRENT_PROJECT_VERSION = 1; + DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEFINES_MODULE = YES; DEVELOPMENT_TEAM = ""; DYLIB_COMPATIBILITY_VERSION = 1; DYLIB_CURRENT_VERSION = 1; DYLIB_INSTALL_NAME_BASE = "@rpath"; + ENABLE_MODULE_VERIFIER = YES; ENABLE_NS_ASSERTIONS = NO; ENABLE_STRICT_OBJC_MSGSEND = YES; FRAMEWORK_VERSION = A; @@ -3256,16 +3627,23 @@ GCC_WARN_UNUSED_VARIABLE = YES; INFOPLIST_FILE = "$(SRCROOT)/Sources/Info.plist"; INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.10; - MARKETING_VERSION = 5.2.0; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/Frameworks", + ); + MARKETING_VERSION = 6.0.0; + MODULE_VERIFIER_SUPPORTED_LANGUAGES = "objective-c objective-c++"; + MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu11 gnu++14"; MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_BUNDLE_IDENTIFIER = "com.swifterswift.SwifterSwift-macOS"; PRODUCT_NAME = SwifterSwift; PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; SKIP_INSTALL = YES; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; VERSIONING_SYSTEM = "apple-generic"; VERSION_INFO_PREFIX = ""; @@ -3325,8 +3703,11 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; INFOPLIST_FILE = Tests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.swifterswift.SwifterSwift-iOSTests"; @@ -3387,14 +3768,18 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; INFOPLIST_FILE = Tests/Info.plist; - IPHONEOS_DEPLOYMENT_TARGET = 11.0; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_BUNDLE_IDENTIFIER = "com.swifterswift.SwifterSwift-iOSTests"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = iphoneos; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = "1,2"; VALIDATE_PRODUCT = YES; @@ -3454,7 +3839,12 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; INFOPLIST_FILE = Tests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.swifterswift.SwifterSwift-tvOSTests"; @@ -3465,7 +3855,6 @@ SWIFT_OPTIMIZATION_LEVEL = "-Onone"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 3; - TVOS_DEPLOYMENT_TARGET = 11.0; }; name = Debug; }; @@ -3516,16 +3905,21 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; INFOPLIST_FILE = Tests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_BUNDLE_IDENTIFIER = "com.swifterswift.SwifterSwift-tvOSTests"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = appletvos; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; TARGETED_DEVICE_FAMILY = 3; - TVOS_DEPLOYMENT_TARGET = 11.0; VALIDATE_PRODUCT = YES; }; name = Release; @@ -3564,6 +3958,7 @@ CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = dwarf; DEVELOPMENT_TEAM = ""; ENABLE_STRICT_OBJC_MSGSEND = YES; @@ -3583,8 +3978,12 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; INFOPLIST_FILE = Tests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.12; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/../Frameworks", + ); MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; PRODUCT_BUNDLE_IDENTIFIER = "com.swifterswift.SwifterSwift-macOSTests"; @@ -3631,6 +4030,7 @@ CODE_SIGN_STYLE = Automatic; COMBINE_HIDPI_IMAGES = YES; COPY_PHASE_STRIP = NO; + DEAD_CODE_STRIPPING = YES; DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; DEVELOPMENT_TEAM = ""; ENABLE_NS_ASSERTIONS = NO; @@ -3644,14 +4044,19 @@ GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; INFOPLIST_FILE = Tests/Info.plist; - LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks"; - MACOSX_DEPLOYMENT_TARGET = 10.12; + IPHONEOS_DEPLOYMENT_TARGET = 10.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/../Frameworks", + "@loader_path/../Frameworks", + ); MTL_ENABLE_DEBUG_INFO = NO; PRODUCT_BUNDLE_IDENTIFIER = "com.swifterswift.SwifterSwift-macOSTests"; PRODUCT_NAME = "$(TARGET_NAME)"; PROVISIONING_PROFILE_SPECIFIER = ""; SDKROOT = macosx; - SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule"; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; SWIFT_VERSION = 5.0; }; name = Release; diff --git a/SwifterSwift.xcodeproj/xcshareddata/xcschemes/SwifterSwift-iOS.xcscheme b/SwifterSwift.xcodeproj/xcshareddata/xcschemes/SwifterSwift-iOS.xcscheme index 1a633a6df..c77386aee 100644 --- a/SwifterSwift.xcodeproj/xcshareddata/xcschemes/SwifterSwift-iOS.xcscheme +++ b/SwifterSwift.xcodeproj/xcshareddata/xcschemes/SwifterSwift-iOS.xcscheme @@ -1,6 +1,6 @@ { + await self?.doSomething(value: value) ?? 0 + } + } + .sink { + XCTAssertEqual($0, 100) + expect.fulfill() + } + + waitForExpectations(timeout: 1) + } + + func testInitAsyncThrows() { + let expect = expectation(description: "testInitAsyncThrows") + + cancellable = Just(100) + .setFailureType(to: (any Error).self) + .flatMap { [weak self] value in + Future { + try await self?.doSomethingThrows(value: value, shouldThrow: true) ?? 0 + } + } + .catch { error in + XCTAssertEqual(error as? FutureExtensionsTestsError, FutureExtensionsTestsError.error) + return Just(200) + .setFailureType(to: (any Error).self) + } + .sink(receiveCompletion: { _ in + + }, receiveValue: { + XCTAssertEqual($0, 200) + expect.fulfill() + }) + + waitForExpectations(timeout: 1) + } + + private func doSomething(value: Int) async -> Int { + try? await Task.sleep(nanoseconds: 1_000_000 * 1) + return await withCheckedContinuation { continuation in + continuation.resume(returning: value) + } + } + + private func doSomethingThrows(value: Int, shouldThrow: Bool) async throws -> Int { + try? await Task.sleep(nanoseconds: 1_000_000 * 1) + return try await withCheckedThrowingContinuation { continuation in + continuation.resume(with: shouldThrow ? .failure(FutureExtensionsTestsError.error) : .success(value)) + } + } +} + +#endif diff --git a/Tests/CoreAnimationTests/CAGradientLayerExtensionsTests.swift b/Tests/CoreAnimationTests/CAGradientLayerExtensionsTests.swift index 937a95daa..e082a4db2 100644 --- a/Tests/CoreAnimationTests/CAGradientLayerExtensionsTests.swift +++ b/Tests/CoreAnimationTests/CAGradientLayerExtensionsTests.swift @@ -1,4 +1,4 @@ -// CAGradientLayerExtensionsTests.swift - Copyright 2020 SwifterSwift +// CAGradientLayerExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest @@ -7,7 +7,7 @@ import XCTest final class CAGradientLayerExtensionsTests: XCTestCase { func testInitWithGradientAttributes() { - let colors: [Color] = [.red, .blue, .orange, .yellow] + let colors: [SFColor] = [.red, .blue, .orange, .yellow] let locations: [CGFloat]? = [0, 0.3, 0.6, 1] let startPoint = CGPoint(x: 0.0, y: 0.5) let endPoint = CGPoint(x: 1.0, y: 0.5) diff --git a/Tests/CoreAnimationTests/CATransform3DExtensionsTests.swift b/Tests/CoreAnimationTests/CATransform3DExtensionsTests.swift index 4db85d838..8a59e4278 100644 --- a/Tests/CoreAnimationTests/CATransform3DExtensionsTests.swift +++ b/Tests/CoreAnimationTests/CATransform3DExtensionsTests.swift @@ -1,4 +1,4 @@ -// CATransform3DExtensionsTests.swift - Copyright 2020 SwifterSwift +// CATransform3DExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest diff --git a/Tests/CoreGraphicsTests/CGAffineTransformExtensionsTests.swift b/Tests/CoreGraphicsTests/CGAffineTransformExtensionsTests.swift index c16412fb8..1780f3fab 100644 --- a/Tests/CoreGraphicsTests/CGAffineTransformExtensionsTests.swift +++ b/Tests/CoreGraphicsTests/CGAffineTransformExtensionsTests.swift @@ -1,4 +1,4 @@ -// CGAffineTransformExtensionsTests.swift - Copyright 2020 SwifterSwift +// CGAffineTransformExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest diff --git a/Tests/CoreGraphicsTests/CGColorExtensionsTests.swift b/Tests/CoreGraphicsTests/CGColorExtensionsTests.swift index 2b0554a60..e24e46bc8 100644 --- a/Tests/CoreGraphicsTests/CGColorExtensionsTests.swift +++ b/Tests/CoreGraphicsTests/CGColorExtensionsTests.swift @@ -1,4 +1,4 @@ -// CGColorExtensionsTests.swift - Copyright 2020 SwifterSwift +// CGColorExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest diff --git a/Tests/CoreGraphicsTests/CGFloatExtensionsTests.swift b/Tests/CoreGraphicsTests/CGFloatExtensionsTests.swift index ddb8d1099..7f7567c9b 100644 --- a/Tests/CoreGraphicsTests/CGFloatExtensionsTests.swift +++ b/Tests/CoreGraphicsTests/CGFloatExtensionsTests.swift @@ -1,4 +1,4 @@ -// CGFloatExtensionsTests.swift - Copyright 2020 SwifterSwift +// CGFloatExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest diff --git a/Tests/CoreGraphicsTests/CGPointExtensionsTests.swift b/Tests/CoreGraphicsTests/CGPointExtensionsTests.swift index c160fd7a8..451f4edad 100644 --- a/Tests/CoreGraphicsTests/CGPointExtensionsTests.swift +++ b/Tests/CoreGraphicsTests/CGPointExtensionsTests.swift @@ -1,4 +1,4 @@ -// CGPointExtensionsTests.swift - Copyright 2020 SwifterSwift +// CGPointExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest diff --git a/Tests/CoreGraphicsTests/CGRectExtensionsTests.swift b/Tests/CoreGraphicsTests/CGRectExtensionsTests.swift index 2dd14b847..14dbba614 100644 --- a/Tests/CoreGraphicsTests/CGRectExtensionsTests.swift +++ b/Tests/CoreGraphicsTests/CGRectExtensionsTests.swift @@ -1,4 +1,4 @@ -// CGRectExtensionsTests.swift - Copyright 2020 SwifterSwift +// CGRectExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest diff --git a/Tests/CoreGraphicsTests/CGSizeExtensionsTests.swift b/Tests/CoreGraphicsTests/CGSizeExtensionsTests.swift index 2a51bb44c..66eaf474c 100644 --- a/Tests/CoreGraphicsTests/CGSizeExtensionsTests.swift +++ b/Tests/CoreGraphicsTests/CGSizeExtensionsTests.swift @@ -1,4 +1,4 @@ -// CGSizeExtensionsTests.swift - Copyright 2020 SwifterSwift +// CGSizeExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest diff --git a/Tests/CoreGraphicsTests/CGVectorExtensionsTests.swift b/Tests/CoreGraphicsTests/CGVectorExtensionsTests.swift index 1f5378a7b..0f7e87319 100644 --- a/Tests/CoreGraphicsTests/CGVectorExtensionsTests.swift +++ b/Tests/CoreGraphicsTests/CGVectorExtensionsTests.swift @@ -1,4 +1,4 @@ -// CGVectorExtensionsTests.swift - Copyright 2020 SwifterSwift +// CGVectorExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest diff --git a/Tests/CoreLocationTests/CLLocationArrayExtensionsTests.swift b/Tests/CoreLocationTests/CLLocationArrayExtensionsTests.swift index 21003cfbb..780e7f523 100644 --- a/Tests/CoreLocationTests/CLLocationArrayExtensionsTests.swift +++ b/Tests/CoreLocationTests/CLLocationArrayExtensionsTests.swift @@ -1,4 +1,4 @@ -// CLLocationArrayExtensionsTests.swift - Copyright 2020 SwifterSwift +// CLLocationArrayExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest @@ -7,7 +7,6 @@ import XCTest import CoreLocation final class CLLocationArrayExtensionsTests: XCTestCase { - @available(tvOS 10.0, macOS 10.12, watchOS 3.0, *) func testDistance() { let locations1 = [CLLocation]() let locations2 = [ diff --git a/Tests/CoreLocationTests/CLLocationExtensionsTests.swift b/Tests/CoreLocationTests/CLLocationExtensionsTests.swift index 3b0495ebe..d5daf4925 100644 --- a/Tests/CoreLocationTests/CLLocationExtensionsTests.swift +++ b/Tests/CoreLocationTests/CLLocationExtensionsTests.swift @@ -1,4 +1,4 @@ -// CLLocationExtensionsTests.swift - Copyright 2020 SwifterSwift +// CLLocationExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest @@ -26,6 +26,17 @@ final class CLLocationExtensionsTests: XCTestCase { XCTAssertEqual(bearing, 105.619, accuracy: 0.001) } + + func testInRange() { + let aLoc = CLLocation(latitude: 37.575803, longitude: 126.976807) + let bLoc = CLLocation(latitude: 37.572931, longitude: 126.976834) + + XCTAssert(aLoc.isInRange(of: bLoc, radius: 320)) + XCTAssert(aLoc.isInRange(of: bLoc, radius: 1050, unitLength: .feet)) + + XCTAssertFalse(aLoc.isInRange(of: bLoc, radius: 300)) + XCTAssertFalse(aLoc.isInRange(of: bLoc, radius: 1000, unitLength: .feet)) + } } #endif diff --git a/Tests/CoreLocationTests/CLVisitExtensionsTests.swift b/Tests/CoreLocationTests/CLVisitExtensionsTests.swift index 9c7ac4c29..2cb8dee6a 100644 --- a/Tests/CoreLocationTests/CLVisitExtensionsTests.swift +++ b/Tests/CoreLocationTests/CLVisitExtensionsTests.swift @@ -1,4 +1,4 @@ -// CLVisitExtensionsTests.swift - Copyright 2020 SwifterSwift +// CLVisitExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest diff --git a/Tests/CryptoKitTests/DigestExtensionsTests.swift b/Tests/CryptoKitTests/DigestExtensionsTests.swift new file mode 100644 index 000000000..7f2a37b83 --- /dev/null +++ b/Tests/CryptoKitTests/DigestExtensionsTests.swift @@ -0,0 +1,21 @@ +// DigestExtensionsTests.swift - Copyright 2023 SwifterSwift + +@testable import SwifterSwift +import XCTest + +#if canImport(CryptoKit) +import CryptoKit + +final class DigestExtensionsTests: XCTestCase { + @available(iOS 13.0, macOS 10.15, watchOS 6.0, tvOS 13.0, *) + func testHexString() { + guard let data = "Hello, World!".data(using: .utf8) else { return } + XCTAssertEqual( + SHA256.hash(data: data).hexString, + "DFFD6021BB2BD5B0AF676290809EC3A53191DD81C7F70A4B28688A362182986F") + XCTAssertEqual( + SHA512.hash(data: data).hexString, + "374D794A95CDCFD8B35993185FEF9BA368F160D8DAF432D08BA9F1ED1E5ABE6CC69291E0FA2FE0006A52570EF18C19DEF4E617C33CE52EF0A6E5FBE318CB0387") + } +} +#endif diff --git a/Tests/DispatchTests/DispatchQueueExtensionsTests.swift b/Tests/DispatchTests/DispatchQueueExtensionsTests.swift index 98f4474a1..17df982eb 100644 --- a/Tests/DispatchTests/DispatchQueueExtensionsTests.swift +++ b/Tests/DispatchTests/DispatchQueueExtensionsTests.swift @@ -1,4 +1,4 @@ -// DispatchQueueExtensionsTests.swift - Copyright 2020 SwifterSwift +// DispatchQueueExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest diff --git a/Tests/FoundationTests/CalendarExtensionTest.swift b/Tests/FoundationTests/CalendarExtensionTest.swift index 8c32e5adb..7a94e29f7 100644 --- a/Tests/FoundationTests/CalendarExtensionTest.swift +++ b/Tests/FoundationTests/CalendarExtensionTest.swift @@ -1,4 +1,4 @@ -// CalendarExtensionTest.swift - Copyright 2020 SwifterSwift +// CalendarExtensionTest.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest diff --git a/Tests/FoundationTests/DataExtensionsTests.swift b/Tests/FoundationTests/DataExtensionsTests.swift index 4e5557bdf..01d9b93e5 100644 --- a/Tests/FoundationTests/DataExtensionsTests.swift +++ b/Tests/FoundationTests/DataExtensionsTests.swift @@ -1,4 +1,4 @@ -// DataExtensionsTests.swift - Copyright 2020 SwifterSwift +// DataExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest diff --git a/Tests/FoundationTests/DateExtensionsTests.swift b/Tests/FoundationTests/DateExtensionsTests.swift index 0793da666..6a23b5f2d 100644 --- a/Tests/FoundationTests/DateExtensionsTests.swift +++ b/Tests/FoundationTests/DateExtensionsTests.swift @@ -1,4 +1,4 @@ -// DateExtensionsTests.swift - Copyright 2020 SwifterSwift +// DateExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest @@ -398,8 +398,11 @@ final class DateExtensionsTests: XCTestCase { let date3 = date.adding(.minute, value: 7) // adding 7 minutes XCTAssertEqual(date3.nearestFiveMinutes, date3.adding(.minute, value: -2)) - let date4 = date.adding(.hour, value: 1).adding(.minute, value: 2) // adding 1 hour and 2 minutes - XCTAssertEqual(date4.nearestFiveMinutes, date.adding(.hour, value: 1)) + let date4 = date.adding(.minute, value: 2) + XCTAssertEqual(date4.nearestFiveMinutes, date) + + let date5 = date.adding(.minute, value: 3) + XCTAssertEqual(date5.nearestFiveMinutes, date.adding(.minute, value: 5)) } func testNearestTenMinutes() { @@ -414,6 +417,9 @@ final class DateExtensionsTests: XCTestCase { let date4 = date.adding(.hour, value: 1).adding(.minute, value: 2) // adding 1 hour and 2 minutes XCTAssertEqual(date4.nearestTenMinutes, date.adding(.hour, value: 1)) + + let date5 = date.adding(.minute, value: 5) // adding 5 minutes + XCTAssertEqual(date5.nearestTenMinutes, date.adding(.minute, value: 10)) } func testNearestQuarterHour() { @@ -428,6 +434,12 @@ final class DateExtensionsTests: XCTestCase { let date4 = date.adding(.hour, value: 1).adding(.minute, value: 2) // adding 1 hour and 2 minutes XCTAssertEqual(date4.nearestQuarterHour, date.adding(.hour, value: 1)) + + let date5 = date.adding(.minute, value: 8) // adding 8 minutes + XCTAssertEqual(date5.nearestQuarterHour, date.adding(.minute, value: 15)) + + let date6 = date.adding(.minute, value: 7) // adding 7 minutes + XCTAssertEqual(date6.nearestQuarterHour, date) } func testNearestHalfHour() { @@ -442,6 +454,9 @@ final class DateExtensionsTests: XCTestCase { let date4 = date.adding(.hour, value: 1).adding(.minute, value: 2) // adding 1 hour and 2 minutes XCTAssertEqual(date4.nearestHalfHour, date.adding(.hour, value: 1)) + + let date5 = date.adding(.minute, value: 15) // adding 15 minutes + XCTAssertEqual(date5.nearestHalfHour, date.adding(.minute, value: 30)) } func testNearestHour() { @@ -453,6 +468,9 @@ final class DateExtensionsTests: XCTestCase { let date3 = date.adding(.minute, value: 34) // adding 34 minutes XCTAssertEqual(date3.nearestHour, date.adding(.hour, value: 1)) + + let date4 = date.adding(.minute, value: 30) // adding 30 minutes + XCTAssertEqual(date4.nearestHour, date.adding(.hour, value: 1)) } func testUnixTimestamp() { @@ -507,7 +525,6 @@ final class DateExtensionsTests: XCTestCase { XCTAssertEqual(date8.adding(.year, value: -4), date) } - // swiftlint:disable:next function_body_length func testAdd() { var date = Date(timeIntervalSince1970: 0) @@ -856,7 +873,7 @@ final class DateExtensionsTests: XCTestCase { XCTAssertFalse(Date().isWithin(1, .calendar, of: Date())) } - func testNewDateFromComponenets() { + func testNewDateFromComponents() { let date = Date( calendar: Date().calendar, timeZone: NSTimeZone.default, diff --git a/Tests/FoundationTests/FileManagerExtensionsTests.swift b/Tests/FoundationTests/FileManagerExtensionsTests.swift index 3f65573ee..e93f96779 100644 --- a/Tests/FoundationTests/FileManagerExtensionsTests.swift +++ b/Tests/FoundationTests/FileManagerExtensionsTests.swift @@ -1,4 +1,4 @@ -// FileManagerExtensionsTests.swift - Copyright 2020 SwifterSwift +// FileManagerExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest diff --git a/Tests/FoundationTests/LocaleExtensionsTests.swift b/Tests/FoundationTests/LocaleExtensionsTests.swift index ae95cae33..ffe5c38be 100644 --- a/Tests/FoundationTests/LocaleExtensionsTests.swift +++ b/Tests/FoundationTests/LocaleExtensionsTests.swift @@ -1,4 +1,4 @@ -// LocaleExtensionsTests.swift - Copyright 2020 SwifterSwift +// LocaleExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest diff --git a/Tests/FoundationTests/MeasurementExtensionsTests.swift b/Tests/FoundationTests/MeasurementExtensionsTests.swift new file mode 100644 index 000000000..e9a6fd786 --- /dev/null +++ b/Tests/FoundationTests/MeasurementExtensionsTests.swift @@ -0,0 +1,49 @@ +// MeasurementExtensionsTests.swift - Copyright 2023 SwifterSwift + +@testable import SwifterSwift +import XCTest + +#if canImport(Foundation) +import Foundation + +class MeasurementExtensionsTests: XCTestCase { + private let angleValue = 2.28 + + func testDegrees() { + let angle = Measurement.degrees(angleValue) + XCTAssertEqual(angle.unit, UnitAngle.degrees) + XCTAssertEqual(angle.value, angleValue) + } + + func testArcMinutes() { + let angle = Measurement.arcMinutes(angleValue) + XCTAssertEqual(angle.unit, UnitAngle.arcMinutes) + XCTAssertEqual(angle.value, angleValue) + } + + func testArcSeconds() { + let angle = Measurement.arcSeconds(angleValue) + XCTAssertEqual(angle.unit, UnitAngle.arcSeconds) + XCTAssertEqual(angle.value, angleValue) + } + + func testRadians() { + let angle = Measurement.radians(angleValue) + XCTAssertEqual(angle.unit, UnitAngle.radians) + XCTAssertEqual(angle.value, angleValue) + } + + func testGradians() { + let angle = Measurement.gradians(angleValue) + XCTAssertEqual(angle.unit, UnitAngle.gradians) + XCTAssertEqual(angle.value, angleValue) + } + + func testRevolutions() { + let angle = Measurement.revolutions(angleValue) + XCTAssertEqual(angle.unit, UnitAngle.revolutions) + XCTAssertEqual(angle.value, angleValue) + } +} + +#endif diff --git a/Tests/FoundationTests/NSAttributedStringExtensionsTests.swift b/Tests/FoundationTests/NSAttributedStringExtensionsTests.swift index d6507d090..2f0c64fe4 100644 --- a/Tests/FoundationTests/NSAttributedStringExtensionsTests.swift +++ b/Tests/FoundationTests/NSAttributedStringExtensionsTests.swift @@ -1,4 +1,4 @@ -// NSAttributedStringExtensionsTests.swift - Copyright 2020 SwifterSwift +// NSAttributedStringExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest @@ -7,7 +7,7 @@ import XCTest import Foundation #if canImport(UIKit) -private typealias Font = UIFont +private typealias SFFont = UIFont #endif #if canImport(AppKit) && !targetEnvironment(macCatalyst) @@ -19,10 +19,11 @@ final class NSAttributedStringExtensionsTests: XCTestCase { func testBolded() { #if !os(Linux) let unsizedAttributes = NSAttributedString(string: "Bolded").bolded.attributes - XCTAssertEqual((unsizedAttributes[.font] as? Font)?.fontName, Font.boldSystemFont(ofSize: 1).fontName) + XCTAssertEqual((unsizedAttributes[.font] as? SFFont)?.fontName, SFFont.boldSystemFont(ofSize: 1).fontName) - let sizedAttributes = NSAttributedString(string: "Bolded", attributes: [.font: Font.systemFont(ofSize: 12)]).bolded.attributes - XCTAssertEqual((sizedAttributes[.font] as? Font), Font.boldSystemFont(ofSize: 12)) + let sizedAttributes = NSAttributedString(string: "Bolded", attributes: [.font: SFFont.systemFont(ofSize: 12)]) + .bolded.attributes + XCTAssertEqual((sizedAttributes[.font] as? SFFont), SFFont.boldSystemFont(ofSize: 12)) #endif } @@ -38,7 +39,9 @@ final class NSAttributedStringExtensionsTests: XCTestCase { let unsizedAttributes = NSAttributedString(string: "Italicized").italicized.attributes XCTAssertEqual((unsizedAttributes[.font] as? UIFont)?.fontName, UIFont.italicSystemFont(ofSize: 1).fontName) - let sizedAttributes = NSAttributedString(string: "Italicized", attributes: [.font: Font.systemFont(ofSize: 12)]).italicized.attributes + let sizedAttributes = NSAttributedString( + string: "Italicized", + attributes: [.font: SFFont.systemFont(ofSize: 12)]).italicized.attributes XCTAssertEqual((sizedAttributes[.font] as? UIFont), UIFont.italicSystemFont(ofSize: 12)) #endif } @@ -46,7 +49,9 @@ final class NSAttributedStringExtensionsTests: XCTestCase { func testStruckthrough() { #if !os(macOS) && !os(Linux) let attributes = NSAttributedString(string: "Struck through").struckthrough.attributes - XCTAssertEqual((attributes[.strikethroughStyle] as? NSUnderlineStyle.RawValue), NSUnderlineStyle.single.rawValue) + XCTAssertEqual( + (attributes[.strikethroughStyle] as? NSUnderlineStyle.RawValue), + NSUnderlineStyle.single.rawValue) #endif } @@ -61,13 +66,13 @@ final class NSAttributedStringExtensionsTests: XCTestCase { out = string.applying(attributes: [ .strikethroughStyle: NSNumber(value: NSUnderlineStyle.single.rawValue), - .foregroundColor: Color.red + .foregroundColor: SFColor.red ]) attributes = out.attributes(at: 0, effectiveRange: nil) XCTAssertEqual(attributes.count, 2) XCTAssertEqual(attributes[.strikethroughStyle] as! NSNumber, // swiftlint:disable:this force_cast NSNumber(value: NSUnderlineStyle.single.rawValue)) - XCTAssertEqual(attributes[.foregroundColor] as! Color, .red) // swiftlint:disable:this force_cast + XCTAssertEqual(attributes[.foregroundColor] as! SFColor, .red) // swiftlint:disable:this force_cast #endif } @@ -76,16 +81,16 @@ final class NSAttributedStringExtensionsTests: XCTestCase { let string = NSAttributedString(string: "Colored") var out = string.colored(with: .red) var attributes = out.attributes(at: 0, effectiveRange: nil) - let filteredAttributes = attributes.filter { (key, value) -> Bool in - return (key == NSAttributedString.Key.foregroundColor && (value as? Color) == .red) + let filteredAttributes = attributes.filter { key, value -> Bool in + return key == NSAttributedString.Key.foregroundColor && (value as? SFColor) == .red } XCTAssertEqual(filteredAttributes.count, 1) out = out.colored(with: .blue) attributes = out.attributes(at: 0, effectiveRange: nil) - XCTAssertEqual(attributes[NSAttributedString.Key.foregroundColor] as? Color, .blue) - XCTAssertNotEqual(attributes[NSAttributedString.Key.foregroundColor] as? Color, .red) + XCTAssertEqual(attributes[NSAttributedString.Key.foregroundColor] as? SFColor, .blue) + XCTAssertNotEqual(attributes[NSAttributedString.Key.foregroundColor] as? SFColor, .red) #endif } @@ -231,7 +236,7 @@ final class NSAttributedStringExtensionsTests: XCTestCase { passed = false } } - } + } XCTAssert(passed) #endif @@ -245,18 +250,18 @@ final class NSAttributedStringExtensionsTests: XCTestCase { XCTAssertEqual(string.string, "Test Appending") var attributes = string.attributes(at: 0, effectiveRange: nil) - var filteredAttributes = attributes.filter { (key, value) -> Bool in + var filteredAttributes = attributes.filter { key, value -> Bool in var valid = false if key == NSAttributedString.Key.font, let value = value as? UIFont, - value == .italicSystemFont(ofSize: UIFont.systemFontSize) { + value == .italicSystemFont(ofSize: UIFont.systemFontSize) { valid = true } if key == NSAttributedString.Key.underlineStyle, let value = value as? NSUnderlineStyle.RawValue, - value == NSUnderlineStyle.single.rawValue { + value == NSUnderlineStyle.single.rawValue { valid = true } if key == NSAttributedString.Key.strikethroughStyle, let value = value as? NSUnderlineStyle.RawValue, - value == NSUnderlineStyle.single.rawValue { + value == NSUnderlineStyle.single.rawValue { valid = true } @@ -266,9 +271,9 @@ final class NSAttributedStringExtensionsTests: XCTestCase { XCTAssertEqual(filteredAttributes.count, 3) attributes = string.attributes(at: 5, effectiveRange: nil) - filteredAttributes = attributes.filter { (key, value) -> Bool in - return (key == NSAttributedString.Key - .font && (value as? UIFont) == .boldSystemFont(ofSize: UIFont.systemFontSize)) + filteredAttributes = attributes.filter { key, value -> Bool in + return key == NSAttributedString.Key + .font && (value as? UIFont) == .boldSystemFont(ofSize: UIFont.systemFontSize) } XCTAssertEqual(filteredAttributes.count, 1) @@ -287,7 +292,7 @@ final class NSAttributedStringExtensionsTests: XCTestCase { XCTAssertEqual(attributes.count, 4) - let filteredAttributes = attributes.filter { (key, value) -> Bool in + let filteredAttributes = attributes.filter { key, value -> Bool in switch key { case NSAttributedString.Key.underlineStyle: return (value as? NSUnderlineStyle.RawValue) == NSUnderlineStyle.single.rawValue @@ -319,6 +324,97 @@ final class NSAttributedStringExtensionsTests: XCTestCase { XCTAssertEqual(string1.string, "Test Appending") #endif } + + // MARK: - func joined(separator:) + + #if canImport(AppKit) || canImport(UIKit) + private let firstStringToJoin = "Hello" + private let secondStringToJoin = " " + private let thirdStringToJoin = "World" + + private var stringsToJoin: [NSAttributedString] { + let string1 = NSAttributedString( + string: firstStringToJoin, + attributes: [ + .strokeWidth: NSNumber(value: 1), + .kern: NSNumber(value: 2) + ]) + let string2 = NSAttributedString( + string: secondStringToJoin, + attributes: [ + .expansion: NSNumber(value: 3), + .obliqueness: NSNumber(value: 4) + ]) + let string3 = NSAttributedString(string: thirdStringToJoin, attributes: [:]) + return [string1, string2, string3] + } + + private func expectedAttrbiutedString( + with separator: String, + separatorAttrbiutes: [NSAttributedString.Key: Any]) -> NSAttributedString { + let expectation = NSMutableAttributedString( + string: firstStringToJoin + separator + secondStringToJoin + separator + thirdStringToJoin, + attributes: [:]) + + expectation.addAttributes([ + .strokeWidth: NSNumber(value: 1), + .kern: NSNumber(value: 2) + ], range: NSRange(location: 0, length: firstStringToJoin.count)) + + expectation.addAttributes( + separatorAttrbiutes, + range: NSRange(location: firstStringToJoin.count, length: separator.count)) + + expectation.addAttributes([ + .expansion: NSNumber(value: 3), + .obliqueness: NSNumber(value: 4) + ], range: NSRange(location: (firstStringToJoin + separator).count, length: secondStringToJoin.count)) + + expectation.addAttributes( + separatorAttrbiutes, + range: NSRange( + location: (firstStringToJoin + separator + secondStringToJoin).count, + length: separator.count)) + + return expectation + } + + func testJoinedWithEmptySeparator() { + XCTAssertEqual( + stringsToJoin.joined(separator: ""), + expectedAttrbiutedString(with: "", separatorAttrbiutes: [:])) + } + + func testJoinedWithEmptyAttributedSeparator() { + XCTAssertEqual( + stringsToJoin.joined(separator: NSAttributedString(string: "")), + expectedAttrbiutedString(with: "", separatorAttrbiutes: [:])) + } + + func testJoinedWithNonEmptySeparator() { + XCTAssertEqual( + stringsToJoin.joined(separator: " non empty "), + expectedAttrbiutedString(with: " non empty ", separatorAttrbiutes: [:])) + } + + func testJoinedWithNonEmptyAttributedSeparator() { + XCTAssertEqual( + stringsToJoin.joined(separator: NSAttributedString(string: " non empty ", attributes: [ + .expansion: NSNumber(value: 3), + .obliqueness: NSNumber(value: 4) + ])), + expectedAttrbiutedString(with: " non empty ", separatorAttrbiutes: [ + .expansion: NSNumber(value: 3), + .obliqueness: NSNumber(value: 4) + ])) + } + + func testEmptyArrayJoinedWithSeparator() { + XCTAssertEqual( + [].joined(separator: NSAttributedString(string: "Hello")), + NSAttributedString(string: "")) + } + #endif } #endif diff --git a/Tests/FoundationTests/NSPredicateExtensionsTests.swift b/Tests/FoundationTests/NSPredicateExtensionsTests.swift index 6f905033e..d6224840c 100644 --- a/Tests/FoundationTests/NSPredicateExtensionsTests.swift +++ b/Tests/FoundationTests/NSPredicateExtensionsTests.swift @@ -1,4 +1,4 @@ -// NSPredicateExtensionsTests.swift - Copyright 2020 SwifterSwift +// NSPredicateExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest diff --git a/Tests/FoundationTests/NSRegularExpressionExtensionsTests.swift b/Tests/FoundationTests/NSRegularExpressionExtensionsTests.swift index 1a014b455..6c3d71473 100644 --- a/Tests/FoundationTests/NSRegularExpressionExtensionsTests.swift +++ b/Tests/FoundationTests/NSRegularExpressionExtensionsTests.swift @@ -1,4 +1,4 @@ -// NSRegularExpressionExtensionsTests.swift - Copyright 2020 SwifterSwift +// NSRegularExpressionExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest @@ -18,10 +18,10 @@ final class NSRegularExpressionExtensionsTests: XCTestCase { regularExpression.enumerateMatches(in: string, options: [], range: string.startIndex..= max + XCTAssertEqual( + String(self.string[Range(result!.range, in: self.string)!]), + self.searchString) + count += 1 + stop = count >= max } XCTAssertEqual(count, max) } diff --git a/Tests/FoundationTests/NotificationCenterExtensionsTests.swift b/Tests/FoundationTests/NotificationCenterExtensionsTests.swift index da0b9e07d..1b07b2faf 100644 --- a/Tests/FoundationTests/NotificationCenterExtensionsTests.swift +++ b/Tests/FoundationTests/NotificationCenterExtensionsTests.swift @@ -1,4 +1,4 @@ -// NotificationCenterExtensionsTests.swift - Copyright 2020 SwifterSwift +// NotificationCenterExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest diff --git a/Tests/FoundationTests/URLExtensionsTests.swift b/Tests/FoundationTests/URLExtensionsTests.swift index 218ed1f9f..c1ac0b2a5 100644 --- a/Tests/FoundationTests/URLExtensionsTests.swift +++ b/Tests/FoundationTests/URLExtensionsTests.swift @@ -1,4 +1,4 @@ -// URLExtensionsTests.swift - Copyright 2020 SwifterSwift +// URLExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest @@ -7,14 +7,13 @@ import XCTest import Foundation final class URLExtensionsTests: XCTestCase { - var url = URL(string: "https://www.google.com")! - let params = ["q": "swifter swift"] - let queryUrl = URL(string: "https://www.google.com?q=swifter%20swift")! + let params = ["foo": "bar"] + let queryUrl = URL(string: "https://www.google.com?q=swifter%20swift&steve=jobs&empty")! + let queryUrlWithParams = URL(string: "https://www.google.com?q=swifter%20swift&steve=jobs&empty&foo=bar")! func testQueryParameters() { - let url = URL(string: "https://www.google.com?q=swifter%20swift&steve=jobs&empty")! - guard let parameters = url.queryParameters else { - XCTAssert(false) + guard let parameters = queryUrl.queryParameters else { + XCTFail("Failed to extract query parameters from \(queryUrl)") return } @@ -24,6 +23,18 @@ final class URLExtensionsTests: XCTestCase { XCTAssertNil(parameters["empty"]) } + func testAllQueryParameters() { + guard let parameters = queryUrl.allQueryParameters else { + XCTFail("Failed to extract query parameters from \(queryUrl)") + return + } + + XCTAssertEqual(parameters.count, 3) + XCTAssertEqual(parameters[0], URLQueryItem(name: "q", value: "swifter swift")) + XCTAssertEqual(parameters[1], URLQueryItem(name: "steve", value: "jobs")) + XCTAssertEqual(parameters[2], URLQueryItem(name: "empty", value: nil)) + } + func testOptionalStringInitializer() { XCTAssertNil(URL(string: nil, relativeTo: nil)) XCTAssertNil(URL(string: nil)) @@ -41,23 +52,24 @@ final class URLExtensionsTests: XCTestCase { } func testAppendingQueryParameters() { - XCTAssertEqual(url.appendingQueryParameters(params), queryUrl) + XCTAssertEqual(queryUrl.appendingQueryParameters(params), queryUrlWithParams) } func testAppendQueryParameters() { + var url = queryUrl url.appendQueryParameters(params) - XCTAssertEqual(url, queryUrl) + XCTAssertEqual(url, queryUrlWithParams) } func testValueForQueryKey() { let url = URL(string: "https://google.com?code=12345&empty")! let codeResult = url.queryValue(for: "code") - let emtpyResult = url.queryValue(for: "empty") + let emptyResult = url.queryValue(for: "empty") let otherResult = url.queryValue(for: "other") XCTAssertEqual(codeResult, "12345") - XCTAssertNil(emtpyResult) + XCTAssertNil(emptyResult) XCTAssertNil(otherResult) } @@ -65,17 +77,25 @@ final class URLExtensionsTests: XCTestCase { let url = URL(string: "https://domain.com/path/other/")! let result = url.deletingAllPathComponents() XCTAssertEqual(result.absoluteString, "https://domain.com/") + + let pathlessURL = URL(string: "https://domain.com")! + let pathlessResult = pathlessURL.deletingAllPathComponents() + XCTAssertEqual(pathlessResult.absoluteString, "https://domain.com") } func testDeleteAllPathComponents() { var url = URL(string: "https://domain.com/path/other/")! url.deleteAllPathComponents() XCTAssertEqual(url.absoluteString, "https://domain.com/") + + var pathlessURL = URL(string: "https://domain.com")! + pathlessURL.deleteAllPathComponents() + XCTAssertEqual(pathlessURL.absoluteString, "https://domain.com") } #if os(iOS) || os(tvOS) func testThumbnail() { - XCTAssertNil(url.thumbnail()) + XCTAssertNil(queryUrl.thumbnail()) let videoUrl = Bundle(for: URLExtensionsTests.self) .url(forResource: "big_buck_bunny_720p_1mb", withExtension: "mp4")! @@ -102,6 +122,12 @@ final class URLExtensionsTests: XCTestCase { XCTAssertEqual(url.droppedScheme()?.absoluteString, expected, "input url: \(input)") } } + + func testStringInitializer() throws { + let testURL = try XCTUnwrap(URL(string: "https://google.com")) + let extensionURL = URL(unsafeString: "https://google.com") + XCTAssertEqual(testURL, extensionURL) + } } #endif diff --git a/Tests/FoundationTests/URLRequestExtensionsTests.swift b/Tests/FoundationTests/URLRequestExtensionsTests.swift index 9d691422f..76abe42d1 100644 --- a/Tests/FoundationTests/URLRequestExtensionsTests.swift +++ b/Tests/FoundationTests/URLRequestExtensionsTests.swift @@ -1,4 +1,4 @@ -// URLRequestExtensionsTests.swift - Copyright 2020 SwifterSwift +// URLRequestExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest @@ -18,11 +18,13 @@ final class URLRequestExtensionsTests: XCTestCase { XCTAssertNotNil(request2) XCTAssertEqual(request1.url, request2!.url) - let invalidURLString = "invalid url" - XCTAssertNil(URLRequest(urlString: invalidURLString)) + if #unavailable(iOS 17.0) { + let invalidURLString = "invalid url" + XCTAssertNil(URLRequest(urlString: invalidURLString)) + } } - func testCUrlSring() { + func testCUrlString() { let insightNASAcURL = "curl https://api.nasa.gov/insight_weather/?api_key=mxbd8VDIy5CheCbrYgknXVH6X9ElpQaHMhne2YXP&feedtype=json&ver=1.0" let planetaryNASAcURL = @@ -62,6 +64,26 @@ final class URLRequestExtensionsTests: XCTestCase { let planetaryRequest = URLRequest(url: components!.url!) XCTAssertEqual(planetaryRequest.curlString, planetaryNASAcURL) } + + // MARK: - Methods + + func testMethod() throws { + let url = URL(string: "https://api.server.com/")! + let request = URLRequest(url: url) + .method("post") + XCTAssertEqual(request.url, url) + XCTAssertEqual(request.httpMethod, "POST") + } + + func testHeader() throws { + let url = URL(string: "https://api.server.com/")! + let request = URLRequest(url: url) + .header(name: "Content-Type", value: "application/json") + .header(name: "Number", value: "10") + XCTAssertNotNil(request.allHTTPHeaderFields) + XCTAssertEqual(request.allHTTPHeaderFields!["Content-Type"]!, "application/json") + XCTAssertEqual(Int(request.allHTTPHeaderFields!["Number"]!), 10) + } } #endif diff --git a/Tests/FoundationTests/URLSessionExtensionsTests.swift b/Tests/FoundationTests/URLSessionExtensionsTests.swift new file mode 100644 index 000000000..3423790c7 --- /dev/null +++ b/Tests/FoundationTests/URLSessionExtensionsTests.swift @@ -0,0 +1,43 @@ +// URLSessionExtensionsTests.swift - Copyright 2023 SwifterSwift + +@testable import SwifterSwift +import XCTest + +#if canImport(Foundation) +import Foundation + +#if canImport(FoundationNetworking) +import FoundationNetworking +#endif + +final class URLSessionExtensionsTests: XCTestCase { + func testDataSync() { + let gemfileContent = "source 'https://rubygems.org'\n\n" + + "gem 'xcpretty'\n" + + "gem 'danger'\n" + + "gem 'danger-swiftlint'\n" + + "gem 'danger-xcode_summary'\n" + + "gem 'xcpretty-json-formatter'\n\n" + + "gem 'fastlane'\n" + + "gem 'cocoapods'" + let url = URL(string: "https://raw.githubusercontent.com/SwifterSwift/SwifterSwift/6.0.0/Gemfile")! + var data: Data? + var response: URLResponse? + XCTAssertNoThrow((data, response) = try URLSession.shared.dataSync(with: URLRequest(url: url))) + XCTAssertNotNil(data) + XCTAssertNotNil(response) + let httpResponse = response as? HTTPURLResponse + let content = String(data: data!, encoding: .utf8) + XCTAssertEqual(content, gemfileContent) + XCTAssertNotNil(httpResponse) + XCTAssertEqual(httpResponse!.statusCode, 200) + XCTAssertEqual(httpResponse!.url, url) + } + + func testDataSyncError() { + let url = URL(string: "http://something.notexistingcountry/something")! + XCTAssertThrowsError(_ = try URLSession.shared.dataSync(with: URLRequest(url: url))) + } +} + +#endif diff --git a/Tests/FoundationTests/UserDefaultsExtensionsTests.swift b/Tests/FoundationTests/UserDefaultsExtensionsTests.swift index d4c588378..7e58bd831 100644 --- a/Tests/FoundationTests/UserDefaultsExtensionsTests.swift +++ b/Tests/FoundationTests/UserDefaultsExtensionsTests.swift @@ -1,4 +1,4 @@ -// UserDefaultsExtensionsTests.swift - Copyright 2020 SwifterSwift +// UserDefaultsExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest diff --git a/Tests/HealthKitTests/HKActivitySummaryExtensionsTests.swift b/Tests/HealthKitTests/HKActivitySummaryExtensionsTests.swift index 259594254..f8d5b8e2f 100644 --- a/Tests/HealthKitTests/HKActivitySummaryExtensionsTests.swift +++ b/Tests/HealthKitTests/HKActivitySummaryExtensionsTests.swift @@ -1,16 +1,21 @@ -// HKActivitySummaryExtensionsTests.swift - Copyright 2020 SwifterSwift +// HKActivitySummaryExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest - +#if !os(tvOS) #if canImport(HealthKit) import HealthKit +@available(macOS 13.0, *) class HKActivitySummaryExtensionsTests: XCTestCase { func testIsStandGoalMet() { let unit = HKUnit.count() let summary = HKActivitySummary() - summary.appleStandHoursGoal = HKQuantity(unit: unit, doubleValue: 12) + if #available(iOS 16.0, watchOS 9.0, *) { + summary.standHoursGoal = HKQuantity(unit: unit, doubleValue: 12) + } else { + summary.appleStandHoursGoal = HKQuantity(unit: unit, doubleValue: 12) + } summary.appleStandHours = HKQuantity(unit: unit, doubleValue: 6) XCTAssertFalse(summary.isStandGoalMet) @@ -25,7 +30,11 @@ class HKActivitySummaryExtensionsTests: XCTestCase { func testIsExerciseTimeGoalMet() { let unit = HKUnit.minute() let summary = HKActivitySummary() - summary.appleExerciseTimeGoal = HKQuantity(unit: unit, doubleValue: 30) + if #available(iOS 16.0, watchOS 9.0, *) { + summary.exerciseTimeGoal = HKQuantity(unit: unit, doubleValue: 30) + } else { + summary.appleExerciseTimeGoal = HKQuantity(unit: unit, doubleValue: 30) + } summary.appleExerciseTime = HKQuantity(unit: unit, doubleValue: 6) XCTAssertFalse(summary.isExerciseTimeGoalMet) @@ -54,3 +63,4 @@ class HKActivitySummaryExtensionsTests: XCTestCase { } #endif +#endif diff --git a/Tests/MapKitTests/MKMapViewTests.swift b/Tests/MapKitTests/MKMapViewExtensionsTests.swift similarity index 71% rename from Tests/MapKitTests/MKMapViewTests.swift rename to Tests/MapKitTests/MKMapViewExtensionsTests.swift index 16a238713..629878df5 100644 --- a/Tests/MapKitTests/MKMapViewTests.swift +++ b/Tests/MapKitTests/MKMapViewExtensionsTests.swift @@ -1,4 +1,4 @@ -// MKMapViewTests.swift - Copyright 2020 SwifterSwift +// MKMapViewExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest @@ -7,26 +7,22 @@ import XCTest import struct CoreLocation.CLLocationCoordinate2D import MapKit -#if !os(watchOS) -@available(tvOS 9.2, *) -final class MKMapViewTests: XCTestCase { - @available(iOS 11.0, tvOS 11.0, macOS 10.13, *) +final class MKMapViewExtensionsTests: XCTestCase { func testRegister() { let mapView = MKMapView() - mapView.register(annotationViewWithClass: MKPinAnnotationView.self) - let annotationView = mapView.dequeueReusableAnnotationView(withClass: MKPinAnnotationView.self) + mapView.register(annotationViewWithClass: annotationViewType) + let annotationView = mapView.dequeueReusableAnnotationView(withClass: annotationViewType) XCTAssertNotNil(annotationView) } - @available(iOS 11.0, tvOS 11.0, macOS 10.13, *) func testRegisterAndDequeue() { let mapView = MKMapView() let annotation = MKPlacemark(coordinate: CLLocationCoordinate2D(latitude: 0, longitude: 0)) - mapView.register(annotationViewWithClass: MKPinAnnotationView.self) + mapView.register(annotationViewWithClass: annotationViewType) let annotationViewWithAnnotation = mapView.dequeueReusableAnnotationView( - withClass: MKPinAnnotationView.self, + withClass: annotationViewType, for: annotation) XCTAssertNotNil(annotationViewWithAnnotation) } @@ -34,7 +30,7 @@ final class MKMapViewTests: XCTestCase { func testWithEmptyItemArray() { let mapView = MKMapView() let meter = 500.0 - let edgePadding = EdgeInsets(top: 50, left: 50, bottom: 50, right: 50) + let edgePadding = SFEdgeInsets(top: 50, left: 50, bottom: 50, right: 50) let previous = mapView.visibleMapRect mapView.zoom(to: [], meter: meter, edgePadding: edgePadding, animated: true) @@ -45,7 +41,7 @@ final class MKMapViewTests: XCTestCase { let mapView = MKMapView() let meter = 500.0 let oneItemArray = [CLLocationCoordinate2D(latitude: 36.9751, longitude: 38.4243)] - let edgePadding = EdgeInsets(top: 50, left: 50, bottom: 50, right: 50) + let edgePadding = SFEdgeInsets(top: 50, left: 50, bottom: 50, right: 50) mapView.zoom(to: oneItemArray, meter: meter, edgePadding: edgePadding, animated: true) let firstPoint = MKMapPoint(oneItemArray.first!) @@ -55,7 +51,7 @@ final class MKMapViewTests: XCTestCase { func testWithMultiItemArray() { let mapView = MKMapView() let meter = 500.0 - let edgePadding = EdgeInsets(top: 50, left: 50, bottom: 50, right: 50) + let edgePadding = SFEdgeInsets(top: 50, left: 50, bottom: 50, right: 50) let multiItemArray = [CLLocationCoordinate2D(latitude: 36.9751, longitude: 38.4243), CLLocationCoordinate2D(latitude: 37.06622, longitude: 37.38332), CLLocationCoordinate2D(latitude: 41.00527, longitude: 28.97696)] @@ -65,8 +61,14 @@ final class MKMapViewTests: XCTestCase { XCTAssert(mapView.visibleMapRect.contains(MKMapPoint(location))) } } -} -#endif + private var annotationViewType: MKAnnotationView.Type { + if #available(macOS 13, iOS 16, *) { + return MKMarkerAnnotationView.self + } else { + return MKPinAnnotationView.self + } + } +} #endif diff --git a/Tests/MapKitTests/MKPolylineTests.swift b/Tests/MapKitTests/MKMultiPointExtensionsTests.swift similarity index 69% rename from Tests/MapKitTests/MKPolylineTests.swift rename to Tests/MapKitTests/MKMultiPointExtensionsTests.swift index 6c1780683..5c2dc2ea9 100644 --- a/Tests/MapKitTests/MKPolylineTests.swift +++ b/Tests/MapKitTests/MKMultiPointExtensionsTests.swift @@ -1,4 +1,4 @@ -// MKPolylineTests.swift - Copyright 2020 SwifterSwift +// MKMultiPointExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest @@ -8,7 +8,7 @@ import MapKit import struct CoreLocation.CLLocationCoordinate2D -final class MKPolylineTests: XCTestCase { +final class MKMultiPointExtensionsTests: XCTestCase { let coordinates = [ (37.330514, -121.888863), (37.330832, -121.888337), @@ -17,24 +17,23 @@ final class MKPolylineTests: XCTestCase { (37.329767, -121.885813) ].map(CLLocationCoordinate2D.init) - func testInitWithCoordinates() { - var refCoordinates = coordinates - + func testCoordinatesForPolyLine() { let polyline = MKPolyline(coordinates: coordinates) - let polyline2 = MKPolyline(coordinates: &refCoordinates, count: refCoordinates.count) - for (coordinate1, coordinate2) in zip(polyline.coordinates, polyline2.coordinates) { + XCTAssertEqual(coordinates.count, polyline.coordinates.count) + + for (coordinate1, coordinate2) in zip(coordinates, polyline.coordinates) { XCTAssertEqual(coordinate1.latitude, coordinate2.latitude, accuracy: 0.000000001) XCTAssertEqual(coordinate1.longitude, coordinate2.longitude, accuracy: 0.000000001) } } - func testCoordinates() { - let polyline = MKPolyline(coordinates: coordinates) + func testCoordinatesForPolygon() { + let polygon = MKPolygon(coordinates: coordinates, count: coordinates.count) - XCTAssertEqual(coordinates.count, polyline.coordinates.count) + XCTAssertEqual(coordinates.count, polygon.coordinates.count) - for (coordinate1, coordinate2) in zip(coordinates, polyline.coordinates) { + for (coordinate1, coordinate2) in zip(coordinates, polygon.coordinates) { XCTAssertEqual(coordinate1.latitude, coordinate2.latitude, accuracy: 0.000000001) XCTAssertEqual(coordinate1.longitude, coordinate2.longitude, accuracy: 0.000000001) } diff --git a/Tests/MapKitTests/MKPolylineExtensionsTests.swift b/Tests/MapKitTests/MKPolylineExtensionsTests.swift new file mode 100644 index 000000000..9059f8471 --- /dev/null +++ b/Tests/MapKitTests/MKPolylineExtensionsTests.swift @@ -0,0 +1,33 @@ +// MKPolylineExtensionsTests.swift - Copyright 2023 SwifterSwift + +@testable import SwifterSwift +import XCTest + +#if canImport(MapKit) +import MapKit + +import struct CoreLocation.CLLocationCoordinate2D + +final class MKPolylineExtensionsTests: XCTestCase { + let coordinates = [ + (37.330514, -121.888863), + (37.330832, -121.888337), + (37.329599, -121.886859), + (37.330019, -121.885993), + (37.329767, -121.885813) + ].map(CLLocationCoordinate2D.init) + + func testInitWithCoordinates() { + var refCoordinates = coordinates + + let polyline = MKPolyline(coordinates: coordinates) + let polyline2 = MKPolyline(coordinates: &refCoordinates, count: refCoordinates.count) + + for (coordinate1, coordinate2) in zip(polyline.coordinates, polyline2.coordinates) { + XCTAssertEqual(coordinate1.latitude, coordinate2.latitude, accuracy: 0.000000001) + XCTAssertEqual(coordinate1.longitude, coordinate2.longitude, accuracy: 0.000000001) + } + } +} + +#endif diff --git a/Tests/ResourcesTests/TestImage.png b/Tests/ResourcesTests/Resources/TestImage.png similarity index 100% rename from Tests/ResourcesTests/TestImage.png rename to Tests/ResourcesTests/Resources/TestImage.png diff --git a/Tests/ResourcesTests/TestStoryboard.storyboard b/Tests/ResourcesTests/Resources/TestStoryboard.storyboard similarity index 100% rename from Tests/ResourcesTests/TestStoryboard.storyboard rename to Tests/ResourcesTests/Resources/TestStoryboard.storyboard diff --git a/Tests/ResourcesTests/UICollectionViewCell.xib b/Tests/ResourcesTests/Resources/UICollectionViewCell.xib similarity index 100% rename from Tests/ResourcesTests/UICollectionViewCell.xib rename to Tests/ResourcesTests/Resources/UICollectionViewCell.xib diff --git a/Tests/ResourcesTests/UIImageView.xib b/Tests/ResourcesTests/Resources/UIImageView.xib similarity index 100% rename from Tests/ResourcesTests/UIImageView.xib rename to Tests/ResourcesTests/Resources/UIImageView.xib diff --git a/Tests/ResourcesTests/UITableViewCell.xib b/Tests/ResourcesTests/Resources/UITableViewCell.xib similarity index 100% rename from Tests/ResourcesTests/UITableViewCell.xib rename to Tests/ResourcesTests/Resources/UITableViewCell.xib diff --git a/Tests/ResourcesTests/UITableViewHeaderFooterView.xib b/Tests/ResourcesTests/Resources/UITableViewHeaderFooterView.xib similarity index 100% rename from Tests/ResourcesTests/UITableViewHeaderFooterView.xib rename to Tests/ResourcesTests/Resources/UITableViewHeaderFooterView.xib diff --git a/Tests/ResourcesTests/big_buck_bunny_720p_1mb.mp4 b/Tests/ResourcesTests/Resources/big_buck_bunny_720p_1mb.mp4 similarity index 100% rename from Tests/ResourcesTests/big_buck_bunny_720p_1mb.mp4 rename to Tests/ResourcesTests/Resources/big_buck_bunny_720p_1mb.mp4 diff --git a/Tests/ResourcesTests/test.json b/Tests/ResourcesTests/Resources/test.json similarity index 100% rename from Tests/ResourcesTests/test.json rename to Tests/ResourcesTests/Resources/test.json diff --git a/Tests/ResourcesTests/TestStoryboard-tvOS.storyboard b/Tests/ResourcesTests/Resources/tvOS/TestStoryboard-tvOS.storyboard similarity index 100% rename from Tests/ResourcesTests/TestStoryboard-tvOS.storyboard rename to Tests/ResourcesTests/Resources/tvOS/TestStoryboard-tvOS.storyboard diff --git a/Tests/ResourcesTests/tvOS/UIImageView.xib b/Tests/ResourcesTests/Resources/tvOS/UIImageViewTvOS.xib similarity index 100% rename from Tests/ResourcesTests/tvOS/UIImageView.xib rename to Tests/ResourcesTests/Resources/tvOS/UIImageViewTvOS.xib diff --git a/Tests/ResourcesTests/MyViewController.swift b/Tests/ResourcesTests/Sources/MyViewController.swift similarity index 71% rename from Tests/ResourcesTests/MyViewController.swift rename to Tests/ResourcesTests/Sources/MyViewController.swift index f72f3783a..1148a173e 100644 --- a/Tests/ResourcesTests/MyViewController.swift +++ b/Tests/ResourcesTests/Sources/MyViewController.swift @@ -1,4 +1,4 @@ -// MyViewController.swift - Copyright 2020 SwifterSwift +// MyViewController.swift - Copyright 2023 SwifterSwift #if canImport(UIKit) && !os(watchOS) import UIKit diff --git a/Tests/SceneKitTests/SCNBoxExtensionsTests.swift b/Tests/SceneKitTests/SCNBoxExtensionsTests.swift index 169e326bf..33f97ab36 100644 --- a/Tests/SceneKitTests/SCNBoxExtensionsTests.swift +++ b/Tests/SceneKitTests/SCNBoxExtensionsTests.swift @@ -1,4 +1,4 @@ -// SCNBoxExtensionsTests.swift - Copyright 2020 SwifterSwift +// SCNBoxExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest @@ -19,9 +19,9 @@ final class SCNBoxExtensionsTests: XCTestCase { } func testInitWithColor() { - let color = Color.red + let color = SFColor.red let box = SCNBox(width: 1, height: 2, length: 3, chamferRadius: 0, color: color) - XCTAssertEqual(box.materials[0].diffuse.contents as? Color, color) + XCTAssertEqual(box.materials[0].diffuse.contents as? SFColor, color) } func testInitWithSideLength() { @@ -37,10 +37,10 @@ final class SCNBoxExtensionsTests: XCTestCase { } func testInitWithSideLengthAndColor() { - let color = Color.red + let color = SFColor.red let box = SCNBox(sideLength: 1, chamferRadius: 0, color: color) XCTAssertEqual(box.boundingSize, SCNVector3(1, 1, 1)) - XCTAssertEqual(box.materials[0].diffuse.contents as? Color, color) + XCTAssertEqual(box.materials[0].diffuse.contents as? SFColor, color) } } diff --git a/Tests/SceneKitTests/SCNCapsuleExtensionsTests.swift b/Tests/SceneKitTests/SCNCapsuleExtensionsTests.swift index f2b6ebc5b..8302e4c03 100644 --- a/Tests/SceneKitTests/SCNCapsuleExtensionsTests.swift +++ b/Tests/SceneKitTests/SCNCapsuleExtensionsTests.swift @@ -1,4 +1,4 @@ -// SCNCapsuleExtensionsTests.swift - Copyright 2020 SwifterSwift +// SCNCapsuleExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest @@ -19,9 +19,9 @@ final class SCNCapsuleExtensionsTests: XCTestCase { } func testInitWithColor() { - let color = Color.red + let color = SFColor.red let capsule = SCNCapsule(capRadius: 5, height: 20, color: color) - XCTAssertEqual(capsule.materials[0].diffuse.contents as? Color, color) + XCTAssertEqual(capsule.materials[0].diffuse.contents as? SFColor, color) } func testInitWithDiameterAndMaterial() { @@ -32,10 +32,10 @@ final class SCNCapsuleExtensionsTests: XCTestCase { } func testInitWithDiameterAndColor() { - let color = Color.red + let color = SFColor.red let capsule = SCNCapsule(capDiameter: 10, height: 20, color: color) XCTAssertEqual(capsule.boundingSize, SCNVector3(10, 20, 10)) - XCTAssertEqual(capsule.materials[0].diffuse.contents as? Color, color) + XCTAssertEqual(capsule.materials[0].diffuse.contents as? SFColor, color) } } diff --git a/Tests/SceneKitTests/SCNConeExtensionsTests.swift b/Tests/SceneKitTests/SCNConeExtensionsTests.swift index 9768ca58e..877661b1d 100644 --- a/Tests/SceneKitTests/SCNConeExtensionsTests.swift +++ b/Tests/SceneKitTests/SCNConeExtensionsTests.swift @@ -1,4 +1,4 @@ -// SCNConeExtensionsTests.swift - Copyright 2020 SwifterSwift +// SCNConeExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest @@ -19,9 +19,9 @@ final class SCNConeExtensionsTests: XCTestCase { } func testInitWithColor() { - let color = Color.red + let color = SFColor.red let cone = SCNCone(topRadius: 5, bottomRadius: 5, height: 20, color: color) - XCTAssertEqual(cone.materials[0].diffuse.contents as? Color, color) + XCTAssertEqual(cone.materials[0].diffuse.contents as? SFColor, color) } func testInitWithDiameterAndMaterial() { @@ -32,10 +32,10 @@ final class SCNConeExtensionsTests: XCTestCase { } func testInitWithDiameterAndColor() { - let color = Color.red + let color = SFColor.red let cone = SCNCone(topDiameter: 10, bottomDiameter: 10, height: 20, color: color) XCTAssertEqual(cone.boundingSize, SCNVector3(10, 20, 10)) - XCTAssertEqual(cone.materials[0].diffuse.contents as? Color, color) + XCTAssertEqual(cone.materials[0].diffuse.contents as? SFColor, color) } } diff --git a/Tests/SceneKitTests/SCNCylinderExtensionsTests.swift b/Tests/SceneKitTests/SCNCylinderExtensionsTests.swift index 0c9623986..7cb44760d 100644 --- a/Tests/SceneKitTests/SCNCylinderExtensionsTests.swift +++ b/Tests/SceneKitTests/SCNCylinderExtensionsTests.swift @@ -1,4 +1,4 @@ -// SCNCylinderExtensionsTests.swift - Copyright 2020 SwifterSwift +// SCNCylinderExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest @@ -19,9 +19,9 @@ final class SCNCylinderExtensionsTests: XCTestCase { } func testInitWithColor() { - let color = Color.red + let color = SFColor.red let cylinder = SCNCylinder(radius: 5, height: 20, color: color) - XCTAssertEqual(cylinder.materials[0].diffuse.contents as? Color, color) + XCTAssertEqual(cylinder.materials[0].diffuse.contents as? SFColor, color) } func testInitWithDiameterAndMaterial() { @@ -32,10 +32,10 @@ final class SCNCylinderExtensionsTests: XCTestCase { } func testInitWithDiameterAndColor() { - let color = Color.red + let color = SFColor.red let cylinder = SCNCylinder(diameter: 10, height: 20, color: color) XCTAssertEqual(cylinder.boundingSize, SCNVector3(10, 20, 10)) - XCTAssertEqual(cylinder.materials[0].diffuse.contents as? Color, color) + XCTAssertEqual(cylinder.materials[0].diffuse.contents as? SFColor, color) } } diff --git a/Tests/SceneKitTests/SCNGeometryExtensionsTests.swift b/Tests/SceneKitTests/SCNGeometryExtensionsTests.swift index b8b357663..7f05b7898 100644 --- a/Tests/SceneKitTests/SCNGeometryExtensionsTests.swift +++ b/Tests/SceneKitTests/SCNGeometryExtensionsTests.swift @@ -1,4 +1,4 @@ -// SCNGeometryExtensionsTests.swift - Copyright 2020 SwifterSwift +// SCNGeometryExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest diff --git a/Tests/SceneKitTests/SCNMaterialExtensionsTests.swift b/Tests/SceneKitTests/SCNMaterialExtensionsTests.swift index d14b4cb4d..92282b5fe 100644 --- a/Tests/SceneKitTests/SCNMaterialExtensionsTests.swift +++ b/Tests/SceneKitTests/SCNMaterialExtensionsTests.swift @@ -1,4 +1,4 @@ -// SCNMaterialExtensionsTests.swift - Copyright 2020 SwifterSwift +// SCNMaterialExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest @@ -8,9 +8,9 @@ import SceneKit final class SCNMaterialExtensionsTests: XCTestCase { func testInitWithColor() { - let color = Color.red + let color = SFColor.red let material = SCNMaterial(color: color) - XCTAssertEqual(material.diffuse.contents as? Color, color) + XCTAssertEqual(material.diffuse.contents as? SFColor, color) } } diff --git a/Tests/SceneKitTests/SCNPlaneExtensionsTests.swift b/Tests/SceneKitTests/SCNPlaneExtensionsTests.swift index 24881bbd2..dca9da3d6 100644 --- a/Tests/SceneKitTests/SCNPlaneExtensionsTests.swift +++ b/Tests/SceneKitTests/SCNPlaneExtensionsTests.swift @@ -1,4 +1,4 @@ -// SCNPlaneExtensionsTests.swift - Copyright 2020 SwifterSwift +// SCNPlaneExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest @@ -19,9 +19,9 @@ final class SCNPlaneExtensionsTests: XCTestCase { } func testInitWithColor() { - let color = Color.red + let color = SFColor.red let plane = SCNPlane(width: 10, height: 20, color: color) - XCTAssertEqual(plane.materials[0].diffuse.contents as? Color, color) + XCTAssertEqual(plane.materials[0].diffuse.contents as? SFColor, color) } func testInitWithWidthAndMaterial() { @@ -32,10 +32,10 @@ final class SCNPlaneExtensionsTests: XCTestCase { } func testInitWithWidthAndColor() { - let color = Color.red + let color = SFColor.red let plane = SCNPlane(width: 10, color: color) XCTAssertEqual(plane.boundingSize, SCNVector3(10, 10, 0)) - XCTAssertEqual(plane.materials[0].diffuse.contents as? Color, color) + XCTAssertEqual(plane.materials[0].diffuse.contents as? SFColor, color) } } diff --git a/Tests/SceneKitTests/SCNShapeExtensionsTests.swift b/Tests/SceneKitTests/SCNShapeExtensionsTests.swift index 97da1aaa4..cabcfe352 100644 --- a/Tests/SceneKitTests/SCNShapeExtensionsTests.swift +++ b/Tests/SceneKitTests/SCNShapeExtensionsTests.swift @@ -1,4 +1,4 @@ -// SCNShapeExtensionsTests.swift - Copyright 2020 SwifterSwift +// SCNShapeExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest @@ -23,7 +23,7 @@ final class SCNShapeExtensionsTests: XCTestCase { func testInitWithColor() { let color = UIColor.red let shape = SCNShape(path: UIBezierPath(rect: rect), extrusionDepth: 1, color: color) - XCTAssertEqual(shape.materials[0].diffuse.contents as? Color, color) + XCTAssertEqual(shape.materials[0].diffuse.contents as? SFColor, color) } #endif } diff --git a/Tests/SceneKitTests/SCNSphereExtensionsTests.swift b/Tests/SceneKitTests/SCNSphereExtensionsTests.swift index c47566daa..3a7abe0c2 100644 --- a/Tests/SceneKitTests/SCNSphereExtensionsTests.swift +++ b/Tests/SceneKitTests/SCNSphereExtensionsTests.swift @@ -1,4 +1,4 @@ -// SCNSphereExtensionsTests.swift - Copyright 2020 SwifterSwift +// SCNSphereExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest @@ -19,9 +19,9 @@ final class SCNSphereExtensionsTests: XCTestCase { } func testInitWithColor() { - let color = Color.red + let color = SFColor.red let sphere = SCNSphere(radius: 5, color: color) - XCTAssertEqual(sphere.materials[0].diffuse.contents as? Color, color) + XCTAssertEqual(sphere.materials[0].diffuse.contents as? SFColor, color) } func testInitWithDiameterAndMaterial() { @@ -32,10 +32,10 @@ final class SCNSphereExtensionsTests: XCTestCase { } func testInitWithDiameterAndColor() { - let color = Color.red + let color = SFColor.red let sphere = SCNSphere(diameter: 10, color: color) XCTAssertEqual(sphere.boundingSize, SCNVector3(10, 10, 10)) - XCTAssertEqual(sphere.materials[0].diffuse.contents as? Color, color) + XCTAssertEqual(sphere.materials[0].diffuse.contents as? SFColor, color) } } diff --git a/Tests/SceneKitTests/SCNVector3ExtensionsTests.swift b/Tests/SceneKitTests/SCNVector3ExtensionsTests.swift index 2b3f30d0d..a2d9d37c8 100644 --- a/Tests/SceneKitTests/SCNVector3ExtensionsTests.swift +++ b/Tests/SceneKitTests/SCNVector3ExtensionsTests.swift @@ -1,4 +1,4 @@ -// SCNVector3ExtensionsTests.swift - Copyright 2020 SwifterSwift +// SCNVector3ExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest @@ -19,17 +19,17 @@ final class SceneKitTests: XCTestCase { XCTAssertEqual(vector.length, 7) } - func testNormaized() { + func testNormalized() { let v3Norm = SCNVector3(3, -5, 0.125).normalized XCTAssertEqual(v3Norm.length, 1) - + let vector4 = SCNVector3(4, 4, 2) let v4Norm = vector4.normalized XCTAssertEqual(v4Norm.x, 4 / 6) XCTAssertEqual(v4Norm.x, v4Norm.y) XCTAssertEqual(v4Norm.z, 2 / 6) } - + func testAdd() { let result = vector1 + vector2 XCTAssertEqual(result, SCNVector3(30, -20, 20)) @@ -41,12 +41,12 @@ final class SceneKitTests: XCTestCase { XCTAssertEqual(vector, SCNVector3(30, -20, 20)) } - func testSubstract() { + func testSubtract() { let result = vector1 - vector2 XCTAssertEqual(result, SCNVector3(-10, -20, 40)) } - func testSubstractEqual() { + func testSubtractEqual() { var vector = vector1 vector -= vector2 XCTAssertEqual(vector, SCNVector3(-10, -20, 40)) @@ -67,7 +67,7 @@ final class SceneKitTests: XCTestCase { let result = 3 * vector1 XCTAssertEqual(result, SCNVector3(30, -60, 90)) } - + func testDivide() { let result = vector1 / 10 XCTAssertEqual(result, SCNVector3(1, -2, 3)) diff --git a/Tests/SharedTests/ColorExtensionsTests.swift b/Tests/SharedTests/ColorExtensionsTests.swift index a0abf7f70..1460ffad9 100644 --- a/Tests/SharedTests/ColorExtensionsTests.swift +++ b/Tests/SharedTests/ColorExtensionsTests.swift @@ -1,4 +1,4 @@ -// ColorExtensionsTests.swift - Copyright 2020 SwifterSwift +// ColorExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest @@ -14,72 +14,72 @@ final class ColorExtensionsTests: XCTestCase { // MARK: - Test properties func testCGFloatComponents() { - XCTAssertEqual(Color.red.cgFloatComponents.red, 1.0) - XCTAssertEqual(Color.red.cgFloatComponents.green, 0.0) - XCTAssertEqual(Color.red.cgFloatComponents.blue, 0.0) + XCTAssertEqual(SFColor.red.cgFloatComponents.red, 1.0) + XCTAssertEqual(SFColor.red.cgFloatComponents.green, 0.0) + XCTAssertEqual(SFColor.red.cgFloatComponents.blue, 0.0) - XCTAssertEqual(Color.green.cgFloatComponents.red, 0.0) - XCTAssertEqual(Color.green.cgFloatComponents.green, 1.0) - XCTAssertEqual(Color.green.cgFloatComponents.blue, 0.0) + XCTAssertEqual(SFColor.green.cgFloatComponents.red, 0.0) + XCTAssertEqual(SFColor.green.cgFloatComponents.green, 1.0) + XCTAssertEqual(SFColor.green.cgFloatComponents.blue, 0.0) - XCTAssertEqual(Color.blue.cgFloatComponents.red, 0.0) - XCTAssertEqual(Color.blue.cgFloatComponents.green, 0.0) - XCTAssertEqual(Color.blue.cgFloatComponents.blue, 1.0) + XCTAssertEqual(SFColor.blue.cgFloatComponents.red, 0.0) + XCTAssertEqual(SFColor.blue.cgFloatComponents.green, 0.0) + XCTAssertEqual(SFColor.blue.cgFloatComponents.blue, 1.0) - XCTAssertEqual(Color.black.cgFloatComponents.red, 0.0) - XCTAssertEqual(Color.black.cgFloatComponents.green, 0.0) - XCTAssertEqual(Color.black.cgFloatComponents.blue, 0.0) + XCTAssertEqual(SFColor.black.cgFloatComponents.red, 0.0) + XCTAssertEqual(SFColor.black.cgFloatComponents.green, 0.0) + XCTAssertEqual(SFColor.black.cgFloatComponents.blue, 0.0) - XCTAssertEqual(Color.white.cgFloatComponents.red, 1.0) - XCTAssertEqual(Color.white.cgFloatComponents.green, 1.0) - XCTAssertEqual(Color.white.cgFloatComponents.blue, 1.0) + XCTAssertEqual(SFColor.white.cgFloatComponents.red, 1.0) + XCTAssertEqual(SFColor.white.cgFloatComponents.green, 1.0) + XCTAssertEqual(SFColor.white.cgFloatComponents.blue, 1.0) - XCTAssertEqual(Int(Color(hex: 0x12FFFF)!.cgFloatComponents.red * 255.0), 0x12) + XCTAssertEqual(Int(SFColor(hex: 0x12FFFF)!.cgFloatComponents.red * 255.0), 0x12) } // MARK: - Test properties func testRgbComponents() { - XCTAssertEqual(Color.red.rgbComponents.red, 255) - XCTAssertEqual(Color.red.rgbComponents.green, 0) - XCTAssertEqual(Color.red.rgbComponents.blue, 0) + XCTAssertEqual(SFColor.red.rgbComponents.red, 255) + XCTAssertEqual(SFColor.red.rgbComponents.green, 0) + XCTAssertEqual(SFColor.red.rgbComponents.blue, 0) - XCTAssertEqual(Color.green.rgbComponents.red, 0) - XCTAssertEqual(Color.green.rgbComponents.green, 255) - XCTAssertEqual(Color.green.rgbComponents.blue, 0) + XCTAssertEqual(SFColor.green.rgbComponents.red, 0) + XCTAssertEqual(SFColor.green.rgbComponents.green, 255) + XCTAssertEqual(SFColor.green.rgbComponents.blue, 0) - XCTAssertEqual(Color.blue.rgbComponents.red, 0) - XCTAssertEqual(Color.blue.rgbComponents.green, 0) - XCTAssertEqual(Color.blue.rgbComponents.blue, 255) + XCTAssertEqual(SFColor.blue.rgbComponents.red, 0) + XCTAssertEqual(SFColor.blue.rgbComponents.green, 0) + XCTAssertEqual(SFColor.blue.rgbComponents.blue, 255) - XCTAssertEqual(Color.black.rgbComponents.red, 0) - XCTAssertEqual(Color.black.rgbComponents.green, 0) - XCTAssertEqual(Color.black.rgbComponents.blue, 0) + XCTAssertEqual(SFColor.black.rgbComponents.red, 0) + XCTAssertEqual(SFColor.black.rgbComponents.green, 0) + XCTAssertEqual(SFColor.black.rgbComponents.blue, 0) - XCTAssertEqual(Color.white.rgbComponents.red, 255) - XCTAssertEqual(Color.white.rgbComponents.green, 255) - XCTAssertEqual(Color.white.rgbComponents.blue, 255) + XCTAssertEqual(SFColor.white.rgbComponents.red, 255) + XCTAssertEqual(SFColor.white.rgbComponents.green, 255) + XCTAssertEqual(SFColor.white.rgbComponents.blue, 255) - XCTAssertEqual(Color(hex: 0x12FFFF)?.rgbComponents.red, 0x12) + XCTAssertEqual(SFColor(hex: 0x12FFFF)?.rgbComponents.red, 0x12) } func testAlpha() { - var color = Color.red + var color = SFColor.red XCTAssertEqual(color.alpha, 1.0) - color = Color.white.withAlphaComponent(0.5) + color = SFColor.white.withAlphaComponent(0.5) XCTAssertEqual(color.alpha, 0.5) - color = Color(red: 0, green: 0, blue: 0, transparency: 0.7)! + color = SFColor(red: 0, green: 0, blue: 0, transparency: 0.7)! XCTAssertEqual(color.alpha, 0.7) - color = Color(red: 0, green: 0, blue: 0, transparency: 1.1)! + color = SFColor(red: 0, green: 0, blue: 0, transparency: 1.1)! XCTAssertEqual(color.alpha, 1.0) } #if !os(watchOS) func testCoreImageColor() { - let color = Color.red + let color = SFColor.red let coreImageColor = color.coreImageColor XCTAssertNotNil(color.coreImageColor) XCTAssertEqual(color.coreImageColor!, coreImageColor) @@ -89,118 +89,118 @@ final class ColorExtensionsTests: XCTestCase { // MARK: - Test properties func testHsbaComponents() { - var color = Color(hex: 0x00FF00, transparency: 1.0)! + var color = SFColor(hex: 0x00FF00, transparency: 1.0)! XCTAssertEqual(color.hsbaComponents.hue, 120.0 / 360.0, accuracy: 0.001) XCTAssertEqual(color.hsbaComponents.saturation, 1.0) XCTAssertEqual(color.hsbaComponents.brightness, 1.0) - color = Color(hex: 0x0000FF, transparency: 1.0)! + color = SFColor(hex: 0x0000FF, transparency: 1.0)! XCTAssertEqual(color.hsbaComponents.hue, 240.0 / 360.0, accuracy: 0.001) XCTAssertEqual(color.hsbaComponents.saturation, 1.0) XCTAssertEqual(color.hsbaComponents.brightness, 1.0) - color = Color(hex: 0x000000, transparency: 1.0)! + color = SFColor(hex: 0x000000, transparency: 1.0)! XCTAssertEqual(color.hsbaComponents.hue, 0.0) XCTAssertEqual(color.hsbaComponents.saturation, 0.0) XCTAssertEqual(color.hsbaComponents.brightness, 0.0) - color = Color(hex: 0xFFFFFF, transparency: 1.0)! + color = SFColor(hex: 0xFFFFFF, transparency: 1.0)! XCTAssertEqual(color.hsbaComponents.hue, 0.0) XCTAssertEqual(color.hsbaComponents.saturation, 0.0) XCTAssertEqual(color.hsbaComponents.brightness, 1.0) - color = Color(hex: 0x123456, transparency: 1.0)! + color = SFColor(hex: 0x123456, transparency: 1.0)! XCTAssertEqual(color.hsbaComponents.hue, 210.0 / 360.0, accuracy: 0.001) XCTAssertEqual((color.hsbaComponents.saturation * 100).rounded(), 79) XCTAssertEqual((color.hsbaComponents.brightness * 100).rounded(), 34) - color = Color(hex: 0xFCA864, transparency: 1.0)! + color = SFColor(hex: 0xFCA864, transparency: 1.0)! XCTAssertEqual(color.hsbaComponents.hue, 27.0 / 360.0, accuracy: 0.001) XCTAssertEqual((color.hsbaComponents.saturation * 100).rounded(), 60) XCTAssertEqual((color.hsbaComponents.brightness * 100).rounded(), 99) - color = Color(hex: 0x1F2D3C, transparency: 1.0)! + color = SFColor(hex: 0x1F2D3C, transparency: 1.0)! XCTAssertEqual(color.hsbaComponents.hue, 211.0 / 360.0, accuracy: 0.001) XCTAssertEqual((color.hsbaComponents.saturation * 100).rounded(), 48) XCTAssertEqual((color.hsbaComponents.brightness * 100).rounded(), 24) } func testUInt() { - var color = Color(hex: 0xFF0000, transparency: 1.0) + var color = SFColor(hex: 0xFF0000, transparency: 1.0) XCTAssertEqual(color?.uInt, 0xFF0000) - color = Color(hex: 0x00FF00, transparency: 1.0) + color = SFColor(hex: 0x00FF00, transparency: 1.0) XCTAssertEqual(color?.uInt, 0x00FF00) - color = Color(hex: 0x0000FF, transparency: 1.0) + color = SFColor(hex: 0x0000FF, transparency: 1.0) XCTAssertEqual(color?.uInt, 0x0000FF) - color = Color(hex: 0x000000, transparency: 1.0) + color = SFColor(hex: 0x000000, transparency: 1.0) XCTAssertEqual(color?.uInt, 0x000000) - color = Color(hex: 0xFFFFFF, transparency: 1.0) + color = SFColor(hex: 0xFFFFFF, transparency: 1.0) XCTAssertEqual(color?.uInt, 0xFFFFFF) - color = Color(hex: 0x123456, transparency: 1.0) + color = SFColor(hex: 0x123456, transparency: 1.0) XCTAssertEqual(color?.uInt, 0x123456) - color = Color(hex: 0xFCA864, transparency: 1.0) + color = SFColor(hex: 0xFCA864, transparency: 1.0) XCTAssertEqual(color?.uInt, 0xFCA864) - color = Color(hex: 0xFCA864, transparency: 1.0) + color = SFColor(hex: 0xFCA864, transparency: 1.0) XCTAssertEqual(color?.uInt, 0xFCA864) - color = Color(hex: 0x1F2D3C, transparency: 1.0) + color = SFColor(hex: 0x1F2D3C, transparency: 1.0) XCTAssertEqual(color?.uInt, 0x1F2D3C) } func testHexString() { - var color = Color.red + var color = SFColor.red XCTAssertEqual(color.hexString, "#FF0000") - color = Color.blue + color = SFColor.blue XCTAssertEqual(color.hexString, "#0000FF") - color = Color(hex: 0xABCDEF)! + color = SFColor(hex: 0xABCDEF)! XCTAssertEqual(color.hexString, "#ABCDEF") - color = Color(hex: 0xABC)! + color = SFColor(hex: 0xABC)! XCTAssertEqual(color.hexString, "#000ABC") - color = Color.black + color = SFColor.black XCTAssertEqual(color.hexString, "#000000") } func testShortHexString() { - var color: Color? = Color.red + var color: SFColor? = SFColor.red XCTAssertEqual(color?.shortHexString, "#F00") - color = Color.blue + color = SFColor.blue XCTAssertEqual(color?.shortHexString, "#00F") - color = Color(hexString: "#0F120F") + color = SFColor(hexString: "#0F120F") XCTAssertNil(color?.shortHexString) - color = Color(hexString: "#8FFFF") + color = SFColor(hexString: "#8FFFF") XCTAssertNil(color?.shortHexString) } func testShortHexOrHexString() { - var color: Color? = Color.red + var color: SFColor? = SFColor.red XCTAssertEqual(color?.shortHexOrHexString, "#F00") - color = Color(hexString: "#8FFFFF") + color = SFColor(hexString: "#8FFFFF") XCTAssertEqual(color?.shortHexOrHexString, "#8FFFFF") - color = Color(hexString: "#F") + color = SFColor(hexString: "#F") XCTAssertEqual(color?.shortHexOrHexString, "#00000F") - color = Color(hexString: "#11") + color = SFColor(hexString: "#11") XCTAssertEqual(color?.shortHexOrHexString, "#001") } func testComplementary() { - var color = Color.black + var color = SFColor.black var red: CGFloat = 0 var green: CGFloat = 0 var blue: CGFloat = 0 @@ -209,13 +209,13 @@ final class ColorExtensionsTests: XCTestCase { XCTAssertEqual(green, 1) XCTAssertEqual(blue, 1) - color = Color.white + color = SFColor.white color.complementary?.getRed(&red, green: &green, blue: &blue, alpha: nil) XCTAssertEqual(red, 0) XCTAssertEqual(green, 0) XCTAssertEqual(blue, 0) - color = Color.red + color = SFColor.red color.complementary?.getRed(&red, green: &green, blue: &blue, alpha: nil) XCTAssertEqual(red, 0) XCTAssertEqual(green, 1) @@ -223,8 +223,8 @@ final class ColorExtensionsTests: XCTestCase { } func testRandom() { - let color1 = Color.random - let color2 = Color.random + let color1 = SFColor.random + let color2 = SFColor.random XCTAssertNotEqual(color1, color2) } @@ -232,24 +232,24 @@ final class ColorExtensionsTests: XCTestCase { // MARK: - Test methods func testBlend() { - var color1 = Color.white - var color2 = Color.black + var color1 = SFColor.white + var color2 = SFColor.black - var blendColor = Color.blend(color1, with: color2) + var blendColor = SFColor.blend(color1, with: color2) XCTAssertEqual(blendColor.rgbComponents.red, 0xFF / 2) XCTAssertEqual(blendColor.rgbComponents.green, 0xFF / 2) XCTAssertEqual(blendColor.rgbComponents.blue, 0xFF / 2) - color1 = Color(hex: 0x123456, transparency: 0.5)! - color2 = Color(hex: 0x665544, transparency: 0.7)! + color1 = SFColor(hex: 0x123456, transparency: 0.5)! + color2 = SFColor(hex: 0x665544, transparency: 0.7)! - blendColor = Color.blend(color1, with: color2) + blendColor = SFColor.blend(color1, with: color2) XCTAssertEqual(blendColor.rgbComponents.red, (0x12 + 0x66) / 2) XCTAssertEqual(blendColor.rgbComponents.green, (0x34 + 0x55) / 2) XCTAssertEqual(blendColor.rgbComponents.blue, (0x56 + 0x44) / 2) XCTAssertEqual(blendColor.alpha, (0.7 + 0.5) / 2) - blendColor = Color.blend(color1, intensity1: 0.7, with: color2, intensity2: 0.3) + blendColor = SFColor.blend(color1, intensity1: 0.7, with: color2, intensity2: 0.3) var output: Double = 0x12 * 0.7 + 0x66 * 0.3 XCTAssertEqual(blendColor.rgbComponents.red, Int(output)) output = 0x34 * 0.7 + 0x55 * 0.3 @@ -259,7 +259,7 @@ final class ColorExtensionsTests: XCTestCase { output = 0.5 * 0.7 + 0.7 * 0.3 XCTAssertEqual(blendColor.alpha, CGFloat(output)) - blendColor = Color.blend(color1, intensity1: 0.0, with: color2, intensity2: 0.3) + blendColor = SFColor.blend(color1, intensity1: 0.0, with: color2, intensity2: 0.3) output = (0x12 * 0.0 + 0x66 * 0.3) / 0.3 XCTAssertEqual(blendColor.rgbComponents.red, Int(output)) output = (0x34 * 0.0 + 0x55 * 0.3) / 0.3 @@ -269,12 +269,12 @@ final class ColorExtensionsTests: XCTestCase { output = (0.5 * 0.0 + 0.7 * 0.3 / 0.3) XCTAssertEqual(blendColor.alpha, CGFloat(output)) - blendColor = Color.blend(color1, intensity1: 1.0, with: color2, intensity2: 0.0) + blendColor = SFColor.blend(color1, intensity1: 1.0, with: color2, intensity2: 0.0) XCTAssertEqual(blendColor, color1) } func testLighten() { - let color = Color.blue + let color = SFColor.blue var red: CGFloat = 0, green: CGFloat = 0, blue: CGFloat = 0 color.getRed(&red, green: &green, blue: &blue, alpha: nil) @@ -288,7 +288,7 @@ final class ColorExtensionsTests: XCTestCase { } func testDarken() { - let color = Color.blue + let color = SFColor.blue var red: CGFloat = 0, green: CGFloat = 0, blue: CGFloat = 0 color.getRed(&red, green: &green, blue: &blue, alpha: nil) @@ -304,71 +304,104 @@ final class ColorExtensionsTests: XCTestCase { // MARK: - Test initializers func testInit() { - var color = Color(hex: 0xFFF) + var color = SFColor(hex: 0xFFF) XCTAssertEqual(color?.rgbComponents.red, 0) XCTAssertEqual(color?.rgbComponents.green, 0xF) XCTAssertEqual(color?.rgbComponents.blue, 0xFF) XCTAssertEqual(color?.alpha, 1.0) - color = Color(hex: 0xFFFFFFF) + color = SFColor(hex: 0xFFFFFFF) XCTAssertEqual(color?.rgbComponents.red, 0xFF) XCTAssertEqual(color?.rgbComponents.green, 0xFF) XCTAssertEqual(color?.rgbComponents.blue, 0xFF) XCTAssertEqual(color?.alpha, 1.0) - color = Color(hex: 0x123456, transparency: 1.0) + color = SFColor(hex: 0x123456, transparency: 1.0) XCTAssertEqual(color?.rgbComponents.red, 0x12) XCTAssertEqual(color?.rgbComponents.green, 0x34) XCTAssertEqual(color?.rgbComponents.blue, 0x56) XCTAssertEqual(color?.alpha, 1.0) - color = Color(hex: 0x999, transparency: 21.0) + color = SFColor(hex: 0x999, transparency: 21.0) XCTAssertEqual(color?.rgbComponents.red, 0) XCTAssertEqual(color?.rgbComponents.green, 0x09) XCTAssertEqual(color?.rgbComponents.blue, 0x99) XCTAssertEqual(color?.alpha, 1.0) - color = Color(hex: 0xAABBCC, transparency: 0.0) + color = SFColor(hex: 0xAABBCC, transparency: 0.0) XCTAssertEqual(color?.rgbComponents.red, 0xAA) XCTAssertEqual(color?.rgbComponents.green, 0xBB) XCTAssertEqual(color?.rgbComponents.blue, 0xCC) XCTAssertEqual(color?.alpha, 0.0) - color = Color(hex: 0x1, transparency: 0.5) + color = SFColor(hex: 0x1, transparency: 0.5) XCTAssertEqual(color?.rgbComponents.red, 0) XCTAssertEqual(color?.rgbComponents.green, 0) XCTAssertEqual(color?.rgbComponents.blue, 1) XCTAssertEqual(color?.alpha, 0.5) - let color1 = Color(hex: 0xFFF, transparency: -0.4) - let color2 = Color(hex: 0xFFF, transparency: 0) + let color1 = SFColor(hex: 0xFFF, transparency: -0.4) + let color2 = SFColor(hex: 0xFFF, transparency: 0) XCTAssertEqual(color1, color2) - let color3 = Color(hex: 0xFFF, transparency: 1.5) - let color4 = Color(hex: 0xFFF, transparency: 1) + let color3 = SFColor(hex: 0xFFF, transparency: 1.5) + let color4 = SFColor(hex: 0xFFF, transparency: 1) XCTAssertEqual(color3, color4) } func testFailableInit() { - var color = Color(hexString: "0xFFFFFF") + var color = SFColor(hexString: "0xFFFFFF") XCTAssertNotNil(color) - color = Color(hexString: "#FFFFFF") + color = SFColor(hexString: "0XFFAABB") XCTAssertNotNil(color) - color = Color(hexString: "FFFFFF") + color = SFColor(hexString: "#FFFFFF") XCTAssertNotNil(color) - color = Color(hexString: "#ABC") + color = SFColor(hexString: "FFFFFF") XCTAssertNotNil(color) - color = Color(hexString: "#GGG") + color = SFColor(hexString: "#ABC") + XCTAssertNotNil(color) + + color = SFColor(hexString: "#GGG") XCTAssertNil(color) - color = Color(hexString: "4#fff") + color = SFColor(hexString: "4#fff") XCTAssertNil(color) - color = Color(hexString: "FFFFFFF") + color = SFColor(hexString: "FFFFFFF") + XCTAssertNotNil(color) + } + + func testInitARGB() { + var color = SFColor(argbHexString: "0xFFFF") + XCTAssertNotNil(color) + XCTAssertEqual(color!.rgbComponents.red, 0xFF) + XCTAssertEqual(color!.rgbComponents.green, 0xFF) + XCTAssertEqual(color!.rgbComponents.blue, 0xFF) + XCTAssertEqual(color!.alpha, 1.0) + + color = SFColor(argbHexString: "#FFFFFFFFF") + XCTAssertNotNil(color) + XCTAssertEqual(color!.rgbComponents.red, 0xFF) + XCTAssertEqual(color!.rgbComponents.green, 0xFF) + XCTAssertEqual(color!.rgbComponents.blue, 0xFF) + XCTAssertEqual(color!.alpha, 1.0) + + color = SFColor(argbHexString: "7F123456") + XCTAssertNotNil(color) + XCTAssertEqual(color!.rgbComponents.red, 0x12) + XCTAssertEqual(color!.rgbComponents.green, 0x34) + XCTAssertEqual(color!.rgbComponents.blue, 0x56) + XCTAssertEqual(color!.alpha, 0.5, accuracy: 0.01) + + color = SFColor(argbHexString: "#9999") XCTAssertNotNil(color) + XCTAssertEqual(color!.rgbComponents.red, 0x99) + XCTAssertEqual(color!.rgbComponents.green, 0x99) + XCTAssertEqual(color!.rgbComponents.blue, 0x99) + XCTAssertEqual(color!.alpha, 0.6) } func testInitWithComponents() { @@ -381,8 +414,8 @@ final class ColorExtensionsTests: XCTestCase { var alpha1: CGFloat = 0 var alpha2: CGFloat = 0 - var color1 = Color(red: 255, green: 244, blue: 255, transparency: 2.0) - var color2 = Color(red: 1.0, green: 244.0 / 255.0, blue: 1.0, alpha: 1.0) + var color1 = SFColor(red: 255, green: 244, blue: 255, transparency: 2.0) + var color2 = SFColor(red: 1.0, green: 244.0 / 255.0, blue: 1.0, alpha: 1.0) color1?.getRed(&red1, green: &green1, blue: &blue1, alpha: &alpha1) color2.getRed(&red2, green: &green2, blue: &blue2, alpha: &alpha2) XCTAssertEqual(red1, red2) @@ -390,8 +423,8 @@ final class ColorExtensionsTests: XCTestCase { XCTAssertEqual(blue1, blue2) XCTAssertEqual(alpha1, alpha2) - color1 = Color(red: 25, green: 244, blue: 55, transparency: -1.0) - color2 = Color(red: 25.0 / 255.0, green: 244.0 / 255.0, blue: 55.0 / 255.0, alpha: 0.0) + color1 = SFColor(red: 25, green: 244, blue: 55, transparency: -1.0) + color2 = SFColor(red: 25.0 / 255.0, green: 244.0 / 255.0, blue: 55.0 / 255.0, alpha: 0.0) color1?.getRed(&red1, green: &green1, blue: &blue1, alpha: &alpha1) color2.getRed(&red2, green: &green2, blue: &blue2, alpha: &alpha2) XCTAssertEqual(red1, red2) @@ -399,8 +432,8 @@ final class ColorExtensionsTests: XCTestCase { XCTAssertEqual(blue1, blue2) XCTAssertEqual(alpha1, alpha2) - color1 = Color(red: 2, green: 4, blue: 5) - color2 = Color(red: 2.0 / 255.0, green: 4.0 / 255.0, blue: 5.0 / 255.0, alpha: 1.0) + color1 = SFColor(red: 2, green: 4, blue: 5) + color2 = SFColor(red: 2.0 / 255.0, green: 4.0 / 255.0, blue: 5.0 / 255.0, alpha: 1.0) color1?.getRed(&red1, green: &green1, blue: &blue1, alpha: &alpha1) color2.getRed(&red2, green: &green2, blue: &blue2, alpha: &alpha2) XCTAssertEqual(red1, red2) @@ -410,21 +443,21 @@ final class ColorExtensionsTests: XCTestCase { } func testFailableInitWithComponents() { - let color1 = Color(red: 258, green: 0, blue: 0) + let color1 = SFColor(red: 258, green: 0, blue: 0) XCTAssertNil(color1) - let color2 = Color(red: 0, green: 258, blue: 0) + let color2 = SFColor(red: 0, green: 258, blue: 0) XCTAssertNil(color2) - let color3 = Color(red: 0, green: 0, blue: 258) + let color3 = SFColor(red: 0, green: 0, blue: 258) XCTAssertNil(color3) - let color4 = Color(red: 258, green: 258, blue: 258) + let color4 = SFColor(red: 258, green: 258, blue: 258) XCTAssertNil(color4) } func testFailableInitWithComplementaryColor() { - var color = Color(complementaryFor: Color.black) + var color = SFColor(complementaryFor: SFColor.black) var red: CGFloat = 0 var green: CGFloat = 0 var blue: CGFloat = 0 @@ -434,7 +467,7 @@ final class ColorExtensionsTests: XCTestCase { XCTAssertEqual(green, 1) XCTAssertEqual(blue, 1) - color = Color(complementaryFor: Color.red) + color = SFColor(complementaryFor: SFColor.red) color?.getRed(&red, green: &green, blue: &blue, alpha: nil) XCTAssertEqual(red, 0) XCTAssertEqual(green, 1) diff --git a/Tests/SharedTests/EdgeInsetsExtensionsTests.swift b/Tests/SharedTests/EdgeInsetsExtensionsTests.swift index 9877ef074..9035ab7f9 100644 --- a/Tests/SharedTests/EdgeInsetsExtensionsTests.swift +++ b/Tests/SharedTests/EdgeInsetsExtensionsTests.swift @@ -1,4 +1,4 @@ -// EdgeInsetsExtensionsTests.swift - Copyright 2020 SwifterSwift +// EdgeInsetsExtensionsTests.swift - Copyright 2023 SwifterSwift #if os(iOS) || os(tvOS) || os(watchOS) || os(macOS) @@ -7,17 +7,17 @@ import XCTest final class EdgeInsetsExtensionsTests: XCTestCase { func testHorizontal() { - let inset = EdgeInsets(top: 30.0, left: 5.0, bottom: 5.0, right: 10.0) + let inset = SFEdgeInsets(top: 30.0, left: 5.0, bottom: 5.0, right: 10.0) XCTAssertEqual(inset.horizontal, 15.0) } func testVertical() { - let inset = EdgeInsets(top: 10.0, left: 10.0, bottom: 5.0, right: 10.0) + let inset = SFEdgeInsets(top: 10.0, left: 10.0, bottom: 5.0, right: 10.0) XCTAssertEqual(inset.vertical, 15.0) } func testInitInset() { - let inset = EdgeInsets(inset: 5.0) + let inset = SFEdgeInsets(inset: 5.0) XCTAssertEqual(inset.top, 5.0) XCTAssertEqual(inset.bottom, 5.0) XCTAssertEqual(inset.right, 5.0) @@ -25,7 +25,7 @@ final class EdgeInsetsExtensionsTests: XCTestCase { } func testInitVerticalHorizontal() { - let inset = EdgeInsets(horizontal: 20.0, vertical: 10.0) + let inset = SFEdgeInsets(horizontal: 20.0, vertical: 10.0) XCTAssertEqual(inset.top, 5.0) XCTAssertEqual(inset.bottom, 5.0) XCTAssertEqual(inset.right, 10.0) @@ -33,7 +33,7 @@ final class EdgeInsetsExtensionsTests: XCTestCase { } func testInsetByTop() { - let inset = EdgeInsets(top: 10.0, left: 10.0, bottom: 10.0, right: 10.0) + let inset = SFEdgeInsets(top: 10.0, left: 10.0, bottom: 10.0, right: 10.0) let insetByTop = inset.insetBy(top: 5.0) XCTAssertNotEqual(inset, insetByTop) XCTAssertEqual(insetByTop.top, 15.0) @@ -50,7 +50,7 @@ final class EdgeInsetsExtensionsTests: XCTestCase { } func testInsetByLeft() { - let inset = EdgeInsets(top: 10.0, left: 10.0, bottom: 10.0, right: 10.0) + let inset = SFEdgeInsets(top: 10.0, left: 10.0, bottom: 10.0, right: 10.0) let insetByLeft = inset.insetBy(left: 5.0) XCTAssertNotEqual(inset, insetByLeft) XCTAssertEqual(insetByLeft.top, 10.0) @@ -67,7 +67,7 @@ final class EdgeInsetsExtensionsTests: XCTestCase { } func testInsetByBottom() { - let inset = EdgeInsets(top: 10.0, left: 10.0, bottom: 10.0, right: 10.0) + let inset = SFEdgeInsets(top: 10.0, left: 10.0, bottom: 10.0, right: 10.0) let insetByBottom = inset.insetBy(bottom: 5.0) XCTAssertNotEqual(inset, insetByBottom) XCTAssertEqual(insetByBottom.top, 10.0) @@ -84,7 +84,7 @@ final class EdgeInsetsExtensionsTests: XCTestCase { } func testInsetByRight() { - let inset = EdgeInsets(top: 10.0, left: 10.0, bottom: 10.0, right: 10.0) + let inset = SFEdgeInsets(top: 10.0, left: 10.0, bottom: 10.0, right: 10.0) let insetByRight = inset.insetBy(right: 5.0) XCTAssertNotEqual(inset, insetByRight) XCTAssertEqual(insetByRight.top, 10.0) @@ -101,7 +101,7 @@ final class EdgeInsetsExtensionsTests: XCTestCase { } func testInsetByHorizontal() { - let inset = EdgeInsets(top: 10.0, left: 10.0, bottom: 10.0, right: 10.0) + let inset = SFEdgeInsets(top: 10.0, left: 10.0, bottom: 10.0, right: 10.0) let insetByHorizontal = inset.insetBy(horizontal: 5.0) XCTAssertNotEqual(inset, insetByHorizontal) XCTAssertEqual(insetByHorizontal.left, 12.5) @@ -118,7 +118,7 @@ final class EdgeInsetsExtensionsTests: XCTestCase { } func testInsetByVertical() { - let inset = EdgeInsets(top: 10.0, left: 10.0, bottom: 10.0, right: 10.0) + let inset = SFEdgeInsets(top: 10.0, left: 10.0, bottom: 10.0, right: 10.0) let insetByVertical = inset.insetBy(vertical: 5.0) XCTAssertNotEqual(inset, insetByVertical) XCTAssertEqual(insetByVertical.left, 10.0) @@ -135,7 +135,7 @@ final class EdgeInsetsExtensionsTests: XCTestCase { } func testInsetComposing() { - let inset = EdgeInsets(top: 10.0, left: 10.0, bottom: 10.0, right: 10.0) + let inset = SFEdgeInsets(top: 10.0, left: 10.0, bottom: 10.0, right: 10.0) let composedInset = inset.insetBy(bottom: 5.0).insetBy(horizontal: 5.0) XCTAssertNotEqual(inset, composedInset) XCTAssertEqual(composedInset.left, 12.5) @@ -152,32 +152,32 @@ final class EdgeInsetsExtensionsTests: XCTestCase { } func testAddition() { - XCTAssertEqual(EdgeInsets.zero + EdgeInsets.zero, EdgeInsets.zero) + XCTAssertEqual(SFEdgeInsets.zero + SFEdgeInsets.zero, SFEdgeInsets.zero) - let insets1 = EdgeInsets(top: 1, left: 2, bottom: 3, right: 4) - let insets2 = EdgeInsets(top: 5, left: 6, bottom: 7, right: 8) - let expected = EdgeInsets(top: 6, left: 8, bottom: 10, right: 12) + let insets1 = SFEdgeInsets(top: 1, left: 2, bottom: 3, right: 4) + let insets2 = SFEdgeInsets(top: 5, left: 6, bottom: 7, right: 8) + let expected = SFEdgeInsets(top: 6, left: 8, bottom: 10, right: 12) XCTAssertEqual(insets1 + insets2, expected) - let negativeInsets1 = EdgeInsets(top: -1, left: -2, bottom: -3, right: -4) - let negativeInsets2 = EdgeInsets(top: -5, left: -6, bottom: -7, right: -8) - let negativeExpected = EdgeInsets(top: -6, left: -8, bottom: -10, right: -12) + let negativeInsets1 = SFEdgeInsets(top: -1, left: -2, bottom: -3, right: -4) + let negativeInsets2 = SFEdgeInsets(top: -5, left: -6, bottom: -7, right: -8) + let negativeExpected = SFEdgeInsets(top: -6, left: -8, bottom: -10, right: -12) XCTAssertEqual(negativeInsets1 + negativeInsets2, negativeExpected) } func testInPlaceAddition() { - var zero = EdgeInsets.zero - zero += EdgeInsets.zero - XCTAssertEqual(zero, EdgeInsets.zero) + var zero = SFEdgeInsets.zero + zero += SFEdgeInsets.zero + XCTAssertEqual(zero, SFEdgeInsets.zero) - var insets = EdgeInsets(top: 1, left: 2, bottom: 3, right: 4) - insets += EdgeInsets(top: 5, left: 6, bottom: 7, right: 8) - let expected = EdgeInsets(top: 6, left: 8, bottom: 10, right: 12) + var insets = SFEdgeInsets(top: 1, left: 2, bottom: 3, right: 4) + insets += SFEdgeInsets(top: 5, left: 6, bottom: 7, right: 8) + let expected = SFEdgeInsets(top: 6, left: 8, bottom: 10, right: 12) XCTAssertEqual(insets, expected) - var negativeInsets = EdgeInsets(top: -1, left: -2, bottom: -3, right: -4) - negativeInsets += EdgeInsets(top: -5, left: -6, bottom: -7, right: -8) - let negativeExpected = EdgeInsets(top: -6, left: -8, bottom: -10, right: -12) + var negativeInsets = SFEdgeInsets(top: -1, left: -2, bottom: -3, right: -4) + negativeInsets += SFEdgeInsets(top: -5, left: -6, bottom: -7, right: -8) + let negativeExpected = SFEdgeInsets(top: -6, left: -8, bottom: -10, right: -12) XCTAssertEqual(negativeInsets, negativeExpected) } } diff --git a/Tests/SpriteKitTests/SKNodeExtensionTests.swift b/Tests/SpriteKitTests/SKNodeExtensionTests.swift index 7b5d22059..c5d4ac2d3 100644 --- a/Tests/SpriteKitTests/SKNodeExtensionTests.swift +++ b/Tests/SpriteKitTests/SKNodeExtensionTests.swift @@ -1,4 +1,4 @@ -// SKNodeExtensionTests.swift - Copyright 2020 SwifterSwift +// SKNodeExtensionTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest diff --git a/Tests/SpriteKitTests/SKSpriteNodeExtensionTests.swift b/Tests/SpriteKitTests/SKSpriteNodeExtensionTests.swift new file mode 100644 index 000000000..ce737a497 --- /dev/null +++ b/Tests/SpriteKitTests/SKSpriteNodeExtensionTests.swift @@ -0,0 +1,43 @@ +// SKSpriteNodeExtensionTests.swift - Copyright 2023 SwifterSwift + +@testable import SwifterSwift +import XCTest + +#if canImport(SpriteKit) && canImport(UIKit) +import SpriteKit + +final class SKSpriteNodeExtensionTests: XCTestCase { + func testAspectFill() { + let bundle = Bundle(for: SKSpriteNodeExtensionTests.self) + let scene = SKScene(size: CGSize(width: 750, height: 1334)) + let node = SKSpriteNode() + let image = UIImage(named: "TestImage", in: bundle, compatibleWith: nil)! + + node.size = CGSize(width: 300, height: 300) + node.texture = SKTexture(image: image) + scene.addChild(node) + + node.aspectFill(to: CGSize(width: 100, height: 100)) + + let accuracy = CGFloat(0.01) + XCTAssertEqual(node.size.width, 100, accuracy: accuracy) + XCTAssertEqual(node.size.height, 23.2, accuracy: accuracy) + } + + func testAspectFillWithEmptyImage() { + let scene = SKScene(size: CGSize(width: 750, height: 1334)) + let node = SKSpriteNode() + let image = UIImage(color: .clear, size: .zero) + + node.size = CGSize(width: 300, height: 300) + node.texture = SKTexture(image: image) + scene.addChild(node) + + let accuracy = CGFloat(0.01) + node.aspectFill(to: CGSize(width: 100, height: 100)) + XCTAssertEqual(node.size.width, 300, accuracy: accuracy) + XCTAssertEqual(node.size.height, 300, accuracy: accuracy) + } +} + +#endif diff --git a/Tests/StoreKitTests/SKProductTests.swift b/Tests/StoreKitTests/SKProductTests.swift index 80529fb13..1df973521 100644 --- a/Tests/StoreKitTests/SKProductTests.swift +++ b/Tests/StoreKitTests/SKProductTests.swift @@ -1,4 +1,4 @@ -// SKProductTests.swift - Copyright 2020 SwifterSwift +// SKProductTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest diff --git a/Tests/SwiftStdlibTests/ArrayExtensionsTests.swift b/Tests/SwiftStdlibTests/ArrayExtensionsTests.swift index 6d38672da..00abc09a9 100644 --- a/Tests/SwiftStdlibTests/ArrayExtensionsTests.swift +++ b/Tests/SwiftStdlibTests/ArrayExtensionsTests.swift @@ -1,4 +1,4 @@ -// ArrayExtensionsTests.swift - Copyright 2020 SwifterSwift +// ArrayExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift // @@ -11,6 +11,12 @@ import XCTest final class ArrayExtensionsTests: XCTestCase { + func testInitClosure() { + let squares = [0, 1, 4, 9, 16, 25, 36, 49, 64, 81, 100] + let array = [Int](count: 11) { $0 * $0 } + XCTAssertEqual(squares, array) + } + func testPrepend() { var arr = [2, 3, 4, 5] arr.prepend(1) @@ -45,7 +51,6 @@ final class ArrayExtensionsTests: XCTestCase { let candidate2 = [2, 5, 3, 6, 1, 4] XCTAssertEqual(candidate2.sorted(like: order1, keyPath: \.self), [1, 2, 3, 4, 5, 6]) - // swiftlint:disable:next nesting struct TestStruct { let prop: String } let order3 = ["1", "2", "3", "4", "5"] let candidate3 = [ @@ -115,4 +120,27 @@ final class ArrayExtensionsTests: XCTestCase { ] XCTAssertEqual(arrayWithoutDuplicatesNHashable, arrayWithoutDuplicatesNHashablePrepared) } + + func testAppendElement() { + var testArray = ["h", "e", "l", "l", "o"] + let optionalString: String? = "f" + testArray.appendIfNonNil(optionalString) + XCTAssertEqual(testArray, ["h", "e", "l", "l", "o", "f"]) + + let nilString: String? = nil + testArray.appendIfNonNil(nilString) + XCTAssertEqual(testArray, ["h", "e", "l", "l", "o", "f"]) + } + + func testAppendSequence() { + var testEmptyArray: [Double] = [] + let numbersToAppend: [Double]? = [69.0, 68.0, 67.0] + testEmptyArray.appendIfNonNil(contentsOf: numbersToAppend) + XCTAssertEqual(testEmptyArray, numbersToAppend) + + var testStringArray: [String] = ["h", "e", "l", "l", "o"] + let nilArray: [String]? = nil + testStringArray.appendIfNonNil(contentsOf: nilArray) + XCTAssertEqual(testStringArray, ["h", "e", "l", "l", "o"]) + } } diff --git a/Tests/SwiftStdlibTests/BidirectionalCollectionExtensionsTests.swift b/Tests/SwiftStdlibTests/BidirectionalCollectionExtensionsTests.swift index fa3af4027..cdcb6179b 100644 --- a/Tests/SwiftStdlibTests/BidirectionalCollectionExtensionsTests.swift +++ b/Tests/SwiftStdlibTests/BidirectionalCollectionExtensionsTests.swift @@ -1,4 +1,4 @@ -// BidirectionalCollectionExtensionsTests.swift - Copyright 2020 SwifterSwift +// BidirectionalCollectionExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest diff --git a/Tests/SwiftStdlibTests/BinaryFloatingPointExtensionsTests.swift b/Tests/SwiftStdlibTests/BinaryFloatingPointExtensionsTests.swift index 509cf74ae..70711ad5a 100644 --- a/Tests/SwiftStdlibTests/BinaryFloatingPointExtensionsTests.swift +++ b/Tests/SwiftStdlibTests/BinaryFloatingPointExtensionsTests.swift @@ -1,4 +1,4 @@ -// BinaryFloatingPointExtensionsTests.swift - Copyright 2020 SwifterSwift +// BinaryFloatingPointExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest diff --git a/Tests/SwiftStdlibTests/BinaryIntegerExtensionsTests.swift b/Tests/SwiftStdlibTests/BinaryIntegerExtensionsTests.swift new file mode 100644 index 000000000..d5c03b9bc --- /dev/null +++ b/Tests/SwiftStdlibTests/BinaryIntegerExtensionsTests.swift @@ -0,0 +1,34 @@ +// BinaryIntegerExtensionsTests.swift - Copyright 2023 SwifterSwift + +@testable import SwifterSwift +import XCTest + +final class BinaryIntegerExtensionsTests: XCTestCase { + func testBytes() { + let zero = Int32.zero.bytes + XCTAssertEqual(zero, Array(repeating: 0, count: 4)) + + let negativeOne = Int8(-1).bytes + XCTAssertEqual(negativeOne, [0xFF]) + + let threeHundred = Int16(300).bytes + XCTAssertEqual(threeHundred, [1, 0b0010_1100]) + + let uint64Max = UInt64.max.bytes + XCTAssertEqual(uint64Max, Array(repeating: 0xFF, count: 8)) + } + + func testInitBytes() { + let zero = Int8(bytes: [0]) + XCTAssertEqual(zero, 0) + + let negativeOne = Int16(bytes: [0b1111_1111, 0b1111_1111]) + XCTAssertEqual(negativeOne, -1) + + let fortyTwo = Int16(bytes: [0, 0b0010_1010]) + XCTAssertEqual(fortyTwo, 42) + + let uint64Max = UInt64(bytes: Array(repeating: 0xFF, count: 8)) + XCTAssertEqual(uint64Max, UInt64.max) + } +} diff --git a/Tests/SwiftStdlibTests/BoolExtensionsTests.swift b/Tests/SwiftStdlibTests/BoolExtensionsTests.swift index 7086f52af..206764f10 100644 --- a/Tests/SwiftStdlibTests/BoolExtensionsTests.swift +++ b/Tests/SwiftStdlibTests/BoolExtensionsTests.swift @@ -1,4 +1,4 @@ -// BoolExtensionsTests.swift - Copyright 2020 SwifterSwift +// BoolExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest diff --git a/Tests/SwiftStdlibTests/CharacterExtensionsTests.swift b/Tests/SwiftStdlibTests/CharacterExtensionsTests.swift index 6df3ae7cf..a48b16af0 100644 --- a/Tests/SwiftStdlibTests/CharacterExtensionsTests.swift +++ b/Tests/SwiftStdlibTests/CharacterExtensionsTests.swift @@ -1,4 +1,4 @@ -// CharacterExtensionsTests.swift - Copyright 2020 SwifterSwift +// CharacterExtensionsTests.swift - Copyright 2023 SwifterSwift // // CharacterExtensionsTests.swift diff --git a/Tests/SwiftStdlibTests/CollectionExtensionsTests.swift b/Tests/SwiftStdlibTests/CollectionExtensionsTests.swift index c87834a14..9938f5e3a 100644 --- a/Tests/SwiftStdlibTests/CollectionExtensionsTests.swift +++ b/Tests/SwiftStdlibTests/CollectionExtensionsTests.swift @@ -1,4 +1,4 @@ -// CollectionExtensionsTests.swift - Copyright 2020 SwifterSwift +// CollectionExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest @@ -8,7 +8,7 @@ final class CollectionExtensionsTests: XCTestCase { func testFullRange() { XCTAssertEqual(collection.fullRange, 0..<5) - XCTAssertEqual([].fullRange, 0..<0) + XCTAssertEqual([Int]().fullRange, 0..<0) } func testForEachInParallel() { @@ -47,7 +47,7 @@ final class CollectionExtensionsTests: XCTestCase { func testForEachSlice() { // A slice with value zero - var iterations: Int = 0 + var iterations = 0 var array: [String] = ["james", "irving", "jordan", "jonshon", "iverson", "shaq"] array.forEach(slice: 0) { _ in iterations += 1 @@ -141,4 +141,27 @@ final class CollectionExtensionsTests: XCTestCase { XCTAssertEqual([1, 2, 3, 4, 5].average(), 3) XCTAssertEqual([Int]().average(), 0) } + + func testAdjacentPairsWithOddNumberOfElementsInCollection() { + let pairs = Array([1, 2, 3].adjacentPairs()) + XCTAssertEqual(pairs.count, 3) + XCTAssertEqual(pairs[0].0, 1) + XCTAssertEqual(pairs[0].1, 2) + XCTAssertEqual(pairs[1].0, 1) + XCTAssertEqual(pairs[1].1, 3) + XCTAssertEqual(pairs[2].0, 2) + XCTAssertEqual(pairs[2].1, 3) + } + + func testAdjacentPairsWithEvenNumberOfElementsInCollection() { + let pairs = Array([1, 2].adjacentPairs()) + XCTAssertEqual(pairs.count, 1) + XCTAssertEqual(pairs[0].0, 1) + XCTAssertEqual(pairs[0].1, 2) + } + + func testAdjacentPairsWithEmptyCollection() { + let pairs = Array([Int]().adjacentPairs()) + XCTAssertEqual(pairs.count, 0) + } } diff --git a/Tests/SwiftStdlibTests/ComparableExtensionsTests.swift b/Tests/SwiftStdlibTests/ComparableExtensionsTests.swift index 6f19f7fad..1d8ca06e0 100644 --- a/Tests/SwiftStdlibTests/ComparableExtensionsTests.swift +++ b/Tests/SwiftStdlibTests/ComparableExtensionsTests.swift @@ -1,4 +1,4 @@ -// ComparableExtensionsTests.swift - Copyright 2020 SwifterSwift +// ComparableExtensionsTests.swift - Copyright 2023 SwifterSwift import XCTest diff --git a/Tests/SwiftStdlibTests/DecodableExtensionsTests.swift b/Tests/SwiftStdlibTests/DecodableExtensionsTests.swift index 225d7cb8d..dcfa53d3b 100644 --- a/Tests/SwiftStdlibTests/DecodableExtensionsTests.swift +++ b/Tests/SwiftStdlibTests/DecodableExtensionsTests.swift @@ -1,4 +1,4 @@ -// DecodableExtensionsTests.swift - Copyright 2020 SwifterSwift +// DecodableExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest @@ -14,13 +14,15 @@ private struct City: Decodable { final class DecodableExtensionsTests: XCTestCase { private var mockJsonData: Data { - return #"{"id": 1, "name": "Şanlıurfa", "url": "https://cdn.pixabay.com/photo/2017/09/27/20/55/sanliurfa-2793424_1280.jpg"}"# .data( - using: .utf8)! + return #"{"id": 1, "name": "Şanlıurfa", "url": "https://cdn.pixabay.com/photo/2017/09/27/20/55/sanliurfa-2793424_1280.jpg"}"# + .data( + using: .utf8)! } private var invalidMockJsonData: Data { - return #"{"id": "1", "name": "Şanlıurfa", "url": "https://cdn.pixabay.com/photo/2017/09/27/20/55/sanliurfa-2793424_1280.jpg"}"# .data( - using: .utf8)! + return #"{"id": "1", "name": "Şanlıurfa", "url": "https://cdn.pixabay.com/photo/2017/09/27/20/55/sanliurfa-2793424_1280.jpg"}"# + .data( + using: .utf8)! } func testDecodeModel() { diff --git a/Tests/SwiftStdlibTests/DefaultStringInterpolationExtensionsTests.swift b/Tests/SwiftStdlibTests/DefaultStringInterpolationExtensionsTests.swift new file mode 100644 index 000000000..fc750fddf --- /dev/null +++ b/Tests/SwiftStdlibTests/DefaultStringInterpolationExtensionsTests.swift @@ -0,0 +1,17 @@ +// DefaultStringInterpolationExtensionsTests.swift - Copyright 2023 SwifterSwift + +@testable import SwifterSwift +import XCTest + +// swiftlint:disable:next type_name +final class DefaultStringInterpolationExtensionsTests: XCTestCase { + func testStringInterpolationPlaceholder() { + var token: Int? + XCTAssertEqual("\(token, placeholder: "-")", "-") + XCTAssertEqual("\(token, placeholder: "*")", "*") + XCTAssertEqual("\(token, placeholder: "-", where: { $0 > 0 })", "-") + token = 0 + XCTAssertEqual("\(token, placeholder: "-")", "0") + XCTAssertEqual("\(token, placeholder: "-", where: { $0 > 0 })", "-") + } +} diff --git a/Tests/SwiftStdlibTests/DictionaryExtensionsTests.swift b/Tests/SwiftStdlibTests/DictionaryExtensionsTests.swift index 50ff585ca..2e66b94cf 100644 --- a/Tests/SwiftStdlibTests/DictionaryExtensionsTests.swift +++ b/Tests/SwiftStdlibTests/DictionaryExtensionsTests.swift @@ -1,4 +1,4 @@ -// DictionaryExtensionsTests.swift - Copyright 2020 SwifterSwift +// DictionaryExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest @@ -84,8 +84,8 @@ final class DictionaryExtensionsTests: XCTestCase { } func testOperatorPlus() { - let dict: [String: String] = ["key1": "value1"] - let dict2: [String: String] = ["key2": "value2"] + let dict = ["key1": "value1"] + let dict2 = ["key2": "value2"] let result = dict + dict2 XCTAssert(result.keys.contains("key1")) XCTAssert(result.keys.contains("key2")) @@ -100,8 +100,8 @@ final class DictionaryExtensionsTests: XCTestCase { } func testOperatorPlusEqual() { - var dict: [String: String] = ["key1": "value1"] - let dict2: [String: String] = ["key2": "value2"] + var dict = ["key1": "value1"] + let dict2 = ["key2": "value2"] dict += dict2 XCTAssert(dict.keys.contains("key1")) XCTAssert(dict.keys.contains("key2")) @@ -126,7 +126,6 @@ final class DictionaryExtensionsTests: XCTestCase { } func testCompactMapKeysAndValues() { - // swiftlint:disable:next nesting enum IntWord: String { case zero case one diff --git a/Tests/SwiftStdlibTests/DoubleExtensionsTests.swift b/Tests/SwiftStdlibTests/DoubleExtensionsTests.swift index 3541fbdaa..e77c1aaab 100644 --- a/Tests/SwiftStdlibTests/DoubleExtensionsTests.swift +++ b/Tests/SwiftStdlibTests/DoubleExtensionsTests.swift @@ -1,4 +1,4 @@ -// DoubleExtensionsTests.swift - Copyright 2020 SwifterSwift +// DoubleExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest diff --git a/Tests/SwiftStdlibTests/FloatExtensionsTests.swift b/Tests/SwiftStdlibTests/FloatExtensionsTests.swift index be4d54b09..4b74d339e 100644 --- a/Tests/SwiftStdlibTests/FloatExtensionsTests.swift +++ b/Tests/SwiftStdlibTests/FloatExtensionsTests.swift @@ -1,4 +1,4 @@ -// FloatExtensionsTests.swift - Copyright 2020 SwifterSwift +// FloatExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest diff --git a/Tests/SwiftStdlibTests/FloatingPointExtensionsTests.swift b/Tests/SwiftStdlibTests/FloatingPointExtensionsTests.swift index 0d1f2b265..82eae9e13 100644 --- a/Tests/SwiftStdlibTests/FloatingPointExtensionsTests.swift +++ b/Tests/SwiftStdlibTests/FloatingPointExtensionsTests.swift @@ -1,4 +1,4 @@ -// FloatingPointExtensionsTests.swift - Copyright 2020 SwifterSwift +// FloatingPointExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest @@ -52,12 +52,12 @@ final class FloatingPointExtensionsTests: XCTestCase { func testOperators() { XCTAssert((Float(5.0) ± Float(2.0)) == (Float(3.0), Float(7.0)) || (Float(5.0) ± Float(2.0)) == (Float(7.0), Float(3.0))) - XCTAssert((±Float(2.0)) == (Float(2.0), Float(-2.0)) || (±Float(2.0)) == (Float(-2.0), Float(2.0))) + XCTAssert((±Float(2.0)) == (Float(2.0), Float(-2.0)) || ±Float(2.0) == (Float(-2.0), Float(2.0))) XCTAssertEqual(√Float(25.0), Float(5.0)) XCTAssert((Double(5.0) ± Double(2.0)) == (Double(3.0), Double(7.0)) || (Double(5.0) ± Double(2.0)) == (Double(7.0), Double(3.0))) - XCTAssert((±Double(2.0)) == (Double(2.0), Double(-2.0)) || (±Double(2.0)) == (Double(-2.0), Double(2.0))) + XCTAssert((±Double(2.0)) == (Double(2.0), Double(-2.0)) || ±Double(2.0) == (Double(-2.0), Double(2.0))) XCTAssertEqual(√Double(25.0), Double(5.0)) } } diff --git a/Tests/SwiftStdlibTests/IntExtensionsTests.swift b/Tests/SwiftStdlibTests/IntExtensionsTests.swift index 829cfb828..7147e3d32 100644 --- a/Tests/SwiftStdlibTests/IntExtensionsTests.swift +++ b/Tests/SwiftStdlibTests/IntExtensionsTests.swift @@ -1,4 +1,4 @@ -// IntExtensionsTests.swift - Copyright 2020 SwifterSwift +// IntExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest @@ -103,7 +103,7 @@ final class IntExtensionsTests: XCTestCase { func testOperators() { XCTAssertEqual(5 ** 2, 25) XCTAssert((5 ± 2) == (3, 7) || (5 ± 2) == (7, 3)) - XCTAssert((±2) == (2, -2) || (±2) == (-2, 2)) + XCTAssert((±2) == (2, -2) || (-2, 2) == ±2) XCTAssertEqual(√25, 5.0) } } diff --git a/Tests/SwiftStdlibTests/KeyedDecodingContainerExtensionsTests.swift b/Tests/SwiftStdlibTests/KeyedDecodingContainerExtensionsTests.swift index 3c1d78211..d9c9ce28a 100644 --- a/Tests/SwiftStdlibTests/KeyedDecodingContainerExtensionsTests.swift +++ b/Tests/SwiftStdlibTests/KeyedDecodingContainerExtensionsTests.swift @@ -1,4 +1,4 @@ -// KeyedDecodingContainerExtensionsTests.swift - Copyright 2020 SwifterSwift +// KeyedDecodingContainerExtensionsTests.swift - Copyright 2023 SwifterSwift import XCTest @@ -13,7 +13,7 @@ private struct Video: Decodable { case isFullScreen } - init(from decoder: Decoder) throws { + init(from decoder: any Decoder) throws { let container = try decoder.container(keyedBy: CodingKeys.self) isPlaying = try container.decodeBoolAsIntOrString(forKey: .isPlaying) isFullScreen = try container.decodeBoolAsIntOrStringIfPresent(forKey: .isFullScreen) diff --git a/Tests/SwiftStdlibTests/MutableCollectionTests.swift b/Tests/SwiftStdlibTests/MutableCollectionTests.swift index e6e9d4d07..ae5712ce0 100644 --- a/Tests/SwiftStdlibTests/MutableCollectionTests.swift +++ b/Tests/SwiftStdlibTests/MutableCollectionTests.swift @@ -1,4 +1,4 @@ -// MutableCollectionTests.swift - Copyright 2020 SwifterSwift +// MutableCollectionTests.swift - Copyright 2023 SwifterSwift import XCTest diff --git a/Tests/SwiftStdlibTests/OptionalExtensionsTests.swift b/Tests/SwiftStdlibTests/OptionalExtensionsTests.swift index 0b266ed6d..951511708 100644 --- a/Tests/SwiftStdlibTests/OptionalExtensionsTests.swift +++ b/Tests/SwiftStdlibTests/OptionalExtensionsTests.swift @@ -1,4 +1,4 @@ -// OptionalExtensionsTests.swift - Copyright 2020 SwifterSwift +// OptionalExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest @@ -43,8 +43,8 @@ final class OptionalExtensionsTests: XCTestCase { let parameter1: String? = nil let parameter2: String? = "foo" - let key1: String = "key1" - let key2: String = "key2" + let key1 = "key1" + let key2 = "key2" var parameters = [String: String]() diff --git a/Tests/SwiftStdlibTests/RangeReplaceableCollectionTests.swift b/Tests/SwiftStdlibTests/RangeReplaceableCollectionTests.swift index e8ea483a4..458478321 100644 --- a/Tests/SwiftStdlibTests/RangeReplaceableCollectionTests.swift +++ b/Tests/SwiftStdlibTests/RangeReplaceableCollectionTests.swift @@ -1,4 +1,4 @@ -// RangeReplaceableCollectionTests.swift - Copyright 2020 SwifterSwift +// RangeReplaceableCollectionTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest diff --git a/Tests/SwiftStdlibTests/SequenceExtensionsTests.swift b/Tests/SwiftStdlibTests/SequenceExtensionsTests.swift index 45d1afd3c..13fc50396 100644 --- a/Tests/SwiftStdlibTests/SequenceExtensionsTests.swift +++ b/Tests/SwiftStdlibTests/SequenceExtensionsTests.swift @@ -1,4 +1,4 @@ -// SequenceExtensionsTests.swift - Copyright 2020 SwifterSwift +// SequenceExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest diff --git a/Tests/SwiftStdlibTests/SignedIntegerExtensionsTests.swift b/Tests/SwiftStdlibTests/SignedIntegerExtensionsTests.swift index 0aa4d4652..f6b5ff013 100644 --- a/Tests/SwiftStdlibTests/SignedIntegerExtensionsTests.swift +++ b/Tests/SwiftStdlibTests/SignedIntegerExtensionsTests.swift @@ -1,4 +1,4 @@ -// SignedIntegerExtensionsTests.swift - Copyright 2020 SwifterSwift +// SignedIntegerExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest diff --git a/Tests/SwiftStdlibTests/SignedNumericExtensionsTests.swift b/Tests/SwiftStdlibTests/SignedNumericExtensionsTests.swift index 12463ed56..2534a839a 100644 --- a/Tests/SwiftStdlibTests/SignedNumericExtensionsTests.swift +++ b/Tests/SwiftStdlibTests/SignedNumericExtensionsTests.swift @@ -1,4 +1,4 @@ -// SignedNumericExtensionsTests.swift - Copyright 2020 SwifterSwift +// SignedNumericExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest @@ -13,7 +13,7 @@ final class SignedNumericExtensionsTests: XCTestCase { } func testAsLocaleCurrency() { - let number1: Double = 3.2 + let number1 = 3.2 XCTAssertEqual(number1.asLocaleCurrency, "$3.20") let number2 = Double(10.23) diff --git a/Tests/SwiftStdlibTests/StringExtensionsTests.swift b/Tests/SwiftStdlibTests/StringExtensionsTests.swift index 52c9cf094..0d2b85626 100644 --- a/Tests/SwiftStdlibTests/StringExtensionsTests.swift +++ b/Tests/SwiftStdlibTests/StringExtensionsTests.swift @@ -1,4 +1,4 @@ -// StringExtensionsTests.swift - Copyright 2020 SwifterSwift +// StringExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest @@ -16,6 +16,8 @@ final class StringExtensionsTests: XCTestCase { func testBase64Decoded() { XCTAssertEqual("SGVsbG8gV29ybGQh".base64Decoded, helloWorld) XCTAssertEqual("http://example.com/xxx", "aHR0cDovL2V4YW1wbGUuY29tL3h4eA".base64Decoded) + XCTAssertEqual(helloWorld, "SGVsbG\n8gV29ybGQh".base64Decoded) + XCTAssertEqual(helloWorld, "SGVsbG8gV29ybGQh\n".base64Decoded) XCTAssertNil(helloWorld.base64Decoded) } @@ -221,7 +223,15 @@ final class StringExtensionsTests: XCTestCase { } func testUrl() { + #if os(Linux) XCTAssertNil("hello world".url) + #else + if #available(iOS 17.0, *) { + XCTAssertEqual("hello world".url, URL(string: "hello%20world")) + } else { + XCTAssertNil("hello world".url) + } + #endif let google = "https://www.google.com" XCTAssertEqual(google.url, URL(string: google)) @@ -310,6 +320,11 @@ final class StringExtensionsTests: XCTestCase { XCTAssertEqual(helloWorld.localized(comment: "comment"), NSLocalizedString(helloWorld, comment: "comment")) } + func testFormatLocalized() { + XCTAssertEqual("%d Swift %d Objective-C".formatLocalized(1, 2), "1 Swift 2 Objective-C") + XCTAssertEqual("%d Swift %d Objective-C".formatLocalized(comment: "comment", 1, 2), "1 Swift 2 Objective-C") + } + func testMostCommonCharacter() { let mostCommonCharacter = "This is a test, since e is appearing every where e should be the common character" .mostCommonCharacter @@ -335,6 +350,7 @@ final class StringExtensionsTests: XCTestCase { XCTAssertEqual("Swift is amazing".toSlug(), "swift-is-amazing") } + // swiftlint:disable:next function_body_length func testSubscript() { let str = "Hello world!" XCTAssertEqual(str[safe: 1], "e") @@ -358,6 +374,24 @@ final class StringExtensionsTests: XCTestCase { XCTAssertEqual(str[safe: 11...11], "!") XCTAssertNil(str[safe: 11...12]) + XCTAssertNil(str[safe: ...(-1)]) + XCTAssertEqual(str[safe: ...0], "H") + XCTAssertEqual(str[safe: ...4], "Hello") + XCTAssertEqual(str[safe: ...11], "Hello world!") + XCTAssertNil(str[safe: ...12]) + + XCTAssertNil(str[safe: ..<(-1)]) + XCTAssertEqual(str[safe: ..<0], "") + XCTAssertEqual(str[safe: ..<5], "Hello") + XCTAssertEqual(str[safe: ..<12], "Hello world!") + XCTAssertNil(str[safe: ..<13]) + + XCTAssertNil(str[safe: (-1)...]) + XCTAssertEqual(str[safe: 0...], "Hello world!") + XCTAssertEqual(str[safe: 6...], "world!") + XCTAssertEqual(str[safe: 11...], "!") + XCTAssertNil(str[safe: 12...]) + let oneCharStr = "a" XCTAssertEqual(oneCharStr[safe: 0..<0], "") XCTAssertEqual(oneCharStr[safe: 0..<1], "a") @@ -389,8 +423,9 @@ final class StringExtensionsTests: XCTestCase { let str = "Hello world!" #if os(iOS) str.copyToPasteboard() - let strFromPasteboard = UIPasteboard.general.string - XCTAssertEqual(strFromPasteboard, str) + // Fail because new os requires user accept paste. + // let strFromPasteboard = UIPasteboard.general.string + // XCTAssertEqual(strFromPasteboard, str) #elseif os(macOS) str.copyToPasteboard() @@ -576,33 +611,44 @@ final class StringExtensionsTests: XCTestCase { } func testRegexMatches() throws { - XCTAssertTrue("123".matches(regex: try NSRegularExpression(pattern: "\\d{3}"))) - XCTAssertFalse("dasda".matches(regex: try NSRegularExpression(pattern: "\\d{3}"))) - XCTAssertFalse("notanemail.com".matches(regex: try NSRegularExpression(pattern: emailPattern))) - XCTAssertTrue("email@mail.com".matches(regex: try NSRegularExpression(pattern: emailPattern))) + XCTAssertTrue(try "123".matches(regex: NSRegularExpression(pattern: "\\d{3}"))) + XCTAssertFalse(try "dasda".matches(regex: NSRegularExpression(pattern: "\\d{3}"))) + XCTAssertFalse(try "notanemail.com".matches(regex: NSRegularExpression(pattern: emailPattern))) + XCTAssertTrue(try "email@mail.com".matches(regex: NSRegularExpression(pattern: emailPattern))) } #if canImport(Foundation) func testPatternMatchOperator() { - XCTAssert("123" ~= "\\d{3}") - XCTAssertFalse("dasda" ~= "\\d{3}") - XCTAssertFalse("notanemail.com" ~= emailPattern) - XCTAssert("email@mail.com" ~= emailPattern) - XCTAssert("hat" ~= "[a-z]at") - XCTAssertFalse("" ~= "[a-z]at") - XCTAssert("" ~= "[a-z]*") - XCTAssertFalse("" ~= "[0-9]+") + XCTAssert("\\d{3}" ~= "123") + XCTAssertFalse("\\d{3}" ~= "dasda") + XCTAssertFalse(emailPattern ~= "notanemail.com") + XCTAssert(emailPattern ~= "email@mail.com") + XCTAssert("[a-z]at" ~= "hat") + XCTAssertFalse("[a-z]at" ~= "") + XCTAssert("[a-z]*" ~= "") + XCTAssertFalse("[0-9]+" ~= "") + + // https://github.com/SwifterSwift/SwifterSwift/issues/1109 + let codeString = "0" + switch codeString { + case "101": + XCTAssert(codeString == "101") + case "0": + XCTAssert(codeString == "0") + default: + XCTFail("Switch string value, not matching the correct result.") + } } #endif func testRegexMatchOperator() throws { let regex = try NSRegularExpression(pattern: "\\d{3}") - XCTAssert("123" ~= regex) - XCTAssertFalse("abc" ~= regex) + XCTAssert(regex ~= "123") + XCTAssertFalse(regex ~= "abc") } func testPadStart() { - var str: String = "str" + var str = "str" str.padStart(10) XCTAssertEqual(str, " str") @@ -637,7 +683,7 @@ final class StringExtensionsTests: XCTestCase { } func testPadEnd() { - var str: String = "str" + var str = "str" str.padEnd(10) XCTAssertEqual(str, "str ") @@ -703,18 +749,6 @@ final class StringExtensionsTests: XCTestCase { XCTAssertNil(String(base64: "hello")) } - func testInitRandomOfLength() { - let str1 = String(randomOfLength: 10) - XCTAssertEqual(str1.count, 10) - - let str2 = String(randomOfLength: 10) - XCTAssertEqual(str2.count, 10) - - XCTAssertNotEqual(str1, str2) - - XCTAssertEqual(String(randomOfLength: 0), "") - } - func testBold() { #if canImport(Foundation) && os(macOS) let boldString = "hello".bold @@ -797,7 +831,7 @@ final class StringExtensionsTests: XCTestCase { in: NSRange(location: 0, length: coloredString.length)) XCTAssertNotNil(attrs[NSAttributedString.Key.foregroundColor]) - guard let color = attrs[.foregroundColor] as? Color else { + guard let color = attrs[.foregroundColor] as? SFColor else { XCTFail("Unable to find color in testColored") return } @@ -904,7 +938,6 @@ final class StringExtensionsTests: XCTestCase { XCTAssertEqual(num.spelledOutString(locale: Locale(identifier: "en_US")), "twelve point three two") } - @available(macOS 10.11, *) func testIntOrdinal() { let num = 12 XCTAssertNotNil(num.ordinalString()) diff --git a/Tests/SwiftStdlibTests/StringProtocolExtensionsTests.swift b/Tests/SwiftStdlibTests/StringProtocolExtensionsTests.swift index aeb726e74..c86df69a8 100644 --- a/Tests/SwiftStdlibTests/StringProtocolExtensionsTests.swift +++ b/Tests/SwiftStdlibTests/StringProtocolExtensionsTests.swift @@ -1,4 +1,4 @@ -// StringProtocolExtensionsTests.swift - Copyright 2020 SwifterSwift +// StringProtocolExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest diff --git a/Tests/SwiftStdlibTests/TestHelpers.swift b/Tests/SwiftStdlibTests/TestHelpers.swift index 865568604..fef8ab986 100644 --- a/Tests/SwiftStdlibTests/TestHelpers.swift +++ b/Tests/SwiftStdlibTests/TestHelpers.swift @@ -1,4 +1,4 @@ -// TestHelpers.swift - Copyright 2020 SwifterSwift +// TestHelpers.swift - Copyright 2023 SwifterSwift enum Season: String { case summer diff --git a/Tests/UIKitTests/UIAlertControllerExtensionsTests.swift b/Tests/UIKitTests/UIAlertControllerExtensionsTests.swift index cf04a340b..75a2ab678 100644 --- a/Tests/UIKitTests/UIAlertControllerExtensionsTests.swift +++ b/Tests/UIKitTests/UIAlertControllerExtensionsTests.swift @@ -1,4 +1,4 @@ -// UIAlertControllerExtensionsTests.swift - Copyright 2020 SwifterSwift +// UIAlertControllerExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest diff --git a/Tests/UIKitTests/UIBarButtonExtensionsTests.swift b/Tests/UIKitTests/UIBarButtonExtensionsTests.swift index d9d56fe3a..37f6e2a1b 100644 --- a/Tests/UIKitTests/UIBarButtonExtensionsTests.swift +++ b/Tests/UIKitTests/UIBarButtonExtensionsTests.swift @@ -1,4 +1,4 @@ -// UIBarButtonExtensionsTests.swift - Copyright 2020 SwifterSwift +// UIBarButtonExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest diff --git a/Tests/UIKitTests/UIBezierPathExtensionsTests.swift b/Tests/UIKitTests/UIBezierPathExtensionsTests.swift index 21079654c..73e831ec5 100644 --- a/Tests/UIKitTests/UIBezierPathExtensionsTests.swift +++ b/Tests/UIKitTests/UIBezierPathExtensionsTests.swift @@ -1,4 +1,4 @@ -// UIBezierPathExtensionsTests.swift - Copyright 2020 SwifterSwift +// UIBezierPathExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest @@ -79,17 +79,15 @@ fileprivate extension UIBezierPath { // Only works for straight lines var points: [CGPoint] { var points = [CGPoint]() - if #available(iOS 11.0, *) { - cgPath.applyWithBlock { pointer in - let element = pointer.pointee - var point = CGPoint.zero - switch element.type { - case .moveToPoint: point = element.points[0] - case .addLineToPoint: point = element.points[0] - default: break - } - points.append(point) + cgPath.applyWithBlock { pointer in + let element = pointer.pointee + var point = CGPoint.zero + switch element.type { + case .moveToPoint: point = element.points[0] + case .addLineToPoint: point = element.points[0] + default: break } + points.append(point) } return points } diff --git a/Tests/UIKitTests/UIButtonExtensionsTests.swift b/Tests/UIKitTests/UIButtonExtensionsTests.swift index e036c33c1..fd409c0f4 100644 --- a/Tests/UIKitTests/UIButtonExtensionsTests.swift +++ b/Tests/UIKitTests/UIButtonExtensionsTests.swift @@ -1,4 +1,4 @@ -// UIButtonExtensionsTests.swift - Copyright 2020 SwifterSwift +// UIButtonExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest @@ -43,6 +43,15 @@ final class UIButtonExtensionsTests: XCTestCase { XCTAssertEqual(button.imageForSelected, newImage) } + func testImageForFocused() { + let button = UIButton() + XCTAssertEqual(button.imageForFocused, button.image(for: .focused)) + + let newImage = UIImage() + button.imageForFocused = newImage + XCTAssertEqual(button.imageForFocused, newImage) + } + func testTitleColorForDisabled() { let button = UIButton() XCTAssertEqual(button.titleColorForDisabled, button.titleColor(for: .disabled)) @@ -75,6 +84,14 @@ final class UIButtonExtensionsTests: XCTestCase { XCTAssertEqual(button.titleColorForSelected, .green) } + func testTitleColorForFocused() { + let button = UIButton() + XCTAssertEqual(button.titleColorForFocused, button.titleColor(for: .focused)) + + button.titleColorForFocused = .green + XCTAssertEqual(button.titleColorForFocused, .green) + } + func testTitleForDisabled() { let button = UIButton() XCTAssertEqual(button.titleForDisabled, button.title(for: .disabled)) @@ -111,6 +128,70 @@ final class UIButtonExtensionsTests: XCTestCase { XCTAssertEqual(button.titleForSelected, title) } + func testTitleForFocused() { + let button = UIButton() + XCTAssertEqual(button.titleForFocused, button.title(for: .focused)) + + let title = "Focused" + button.titleForFocused = title + XCTAssertEqual(button.titleForFocused, title) + } + + func testAttributedTitleForDisabled() { + let button = UIButton() + XCTAssertEqual(button.attributedTitleForDisabled, button.attributedTitle(for: .disabled)) + + let title = NSAttributedString( + string: "Disabled", + attributes: [.foregroundColor: UIColor.yellow, .backgroundColor: UIColor.green]) + button.attributedTitleForDisabled = title + XCTAssertEqual(button.attributedTitleForDisabled, title) + } + + func testAttributedTitleForHighlighted() { + let button = UIButton() + XCTAssertEqual(button.attributedTitleForHighlighted, button.attributedTitle(for: .highlighted)) + + let title = NSAttributedString( + string: "Highlighted", + attributes: [.foregroundColor: UIColor.yellow, .backgroundColor: UIColor.green]) + button.attributedTitleForHighlighted = title + XCTAssertEqual(button.attributedTitleForHighlighted, title) + } + + func testAttributedTitleForNormal() { + let button = UIButton() + XCTAssertEqual(button.attributedTitleForNormal, button.attributedTitle(for: .normal)) + + let title = NSAttributedString( + string: "Normal", + attributes: [.foregroundColor: UIColor.yellow, .backgroundColor: UIColor.green]) + button.attributedTitleForNormal = title + XCTAssertEqual(button.attributedTitleForNormal, title) + } + + func testAttributedTitleForSelected() { + let button = UIButton() + XCTAssertEqual(button.attributedTitleForSelected, button.attributedTitle(for: .selected)) + + let title = NSAttributedString( + string: "Selected", + attributes: [.foregroundColor: UIColor.yellow, .backgroundColor: UIColor.green]) + button.attributedTitleForSelected = title + XCTAssertEqual(button.attributedTitleForSelected, title) + } + + func testAttributedTitleForFocused() { + let button = UIButton() + XCTAssertEqual(button.attributedTitleForFocused, button.attributedTitle(for: .focused)) + + let title = NSAttributedString( + string: "Focused", + attributes: [.foregroundColor: UIColor.yellow, .backgroundColor: UIColor.green]) + button.attributedTitleForFocused = title + XCTAssertEqual(button.attributedTitleForFocused, title) + } + func testSetImageForAllStates() { let button = UIButton() let image = UIImage() @@ -120,6 +201,7 @@ final class UIButtonExtensionsTests: XCTestCase { XCTAssertEqual(button.imageForHighlighted, image) XCTAssertEqual(button.imageForNormal, image) XCTAssertEqual(button.imageForSelected, image) + XCTAssertEqual(button.imageForFocused, image) } func testSetTitleColorForAllStates() { @@ -131,6 +213,7 @@ final class UIButtonExtensionsTests: XCTestCase { XCTAssertEqual(button.titleColorForHighlighted, color) XCTAssertEqual(button.titleColorForNormal, color) XCTAssertEqual(button.titleColorForSelected, color) + XCTAssertEqual(button.titleColorForFocused, color) } func testSetTitleForAllStates() { @@ -142,6 +225,7 @@ final class UIButtonExtensionsTests: XCTestCase { XCTAssertEqual(button.titleForHighlighted, title) XCTAssertEqual(button.titleForNormal, title) XCTAssertEqual(button.titleForSelected, title) + XCTAssertEqual(button.titleForFocused, title) } func testCenterTextAndImage() { @@ -170,9 +254,35 @@ final class UIButtonExtensionsTests: XCTestCase { titleFrame = button.titleLabel!.frame XCTAssert(titleFrame.midY > imageFrame.midY) - XCTAssertEqual(titleFrame.midX, imageFrame.midX, accuracy: 1.0) + XCTAssertEqual(titleFrame.midX, imageFrame.midX, accuracy: 1.5) XCTAssertEqual(titleFrame.minY - spacing, imageFrame.maxY, accuracy: 1.0) } + + func testSetAttrbiutedTitleForAllStates() { + let button = UIButton() + let title = NSAttributedString( + string: "Title", + attributes: [.foregroundColor: UIColor.yellow, .backgroundColor: UIColor.green]) + button.setAttributedTitleForAllStates(title) + + XCTAssertEqual(button.attributedTitleForDisabled, title) + XCTAssertEqual(button.attributedTitleForHighlighted, title) + XCTAssertEqual(button.attributedTitleForNormal, title) + XCTAssertEqual(button.attributedTitleForSelected, title) + XCTAssertEqual(button.attributedTitleForFocused, title) + } + + func testSetBackgroundColorForState() { + let button = UIButton() + let color = UIColor.orange + + button.setBackgroundColor(color: color, forState: .highlighted) + + let highlightedBackgroundImage = button.backgroundImage(for: .highlighted) + let averageColor = highlightedBackgroundImage!.averageColor()! + + XCTAssertEqual(averageColor, color, accuracy: 0.01) + } } #endif diff --git a/Tests/UIKitTests/UICollectionViewExtensionsTests.swift b/Tests/UIKitTests/UICollectionViewExtensionsTests.swift index 1db761d8e..9a09cd297 100644 --- a/Tests/UIKitTests/UICollectionViewExtensionsTests.swift +++ b/Tests/UIKitTests/UICollectionViewExtensionsTests.swift @@ -1,4 +1,4 @@ -// UICollectionViewExtensionsTests.swift - Copyright 2020 SwifterSwift +// UICollectionViewExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest @@ -20,9 +20,7 @@ final class UICollectionViewExtensionsTests: XCTestCase { let collection = UICollectionView( frame: CGRect(x: 0, y: 0, width: 10, height: 15), collectionViewLayout: layout) - if #available(iOS 11, *) { - collection.insetsLayoutMarginsFromSafeArea = false - } + collection.insetsLayoutMarginsFromSafeArea = false collection.contentInset = .zero return collection }() diff --git a/Tests/UIKitTests/UIColorExtensionsTests.swift b/Tests/UIKitTests/UIColorExtensionsTests.swift index 4ea4c7011..8d0f2fa48 100644 --- a/Tests/UIKitTests/UIColorExtensionsTests.swift +++ b/Tests/UIKitTests/UIColorExtensionsTests.swift @@ -1,4 +1,4 @@ -// UIColorExtensionsTests.swift - Copyright 2020 SwifterSwift +// UIColorExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest diff --git a/Tests/UIKitTests/UIDatePickerExtensionsTests.swift b/Tests/UIKitTests/UIDatePickerExtensionsTests.swift deleted file mode 100644 index 0b90d6206..000000000 --- a/Tests/UIKitTests/UIDatePickerExtensionsTests.swift +++ /dev/null @@ -1,29 +0,0 @@ -// UIDatePickerExtensionsTests.swift - Copyright 2020 SwifterSwift - -@testable import SwifterSwift -import XCTest - -#if canImport(UIKit) && os(iOS) -import UIKit - -final class UIDatePickerExtensionsTests: XCTestCase { - #if !targetEnvironment(macCatalyst) - func testTextColor() { - let datePicker = UIDatePicker() - if let color = datePicker.textColor { - XCTAssertNotEqual(color, .red) - } - - datePicker.textColor = .red - XCTAssertEqual(datePicker.textColor, .red) - - datePicker.textColor = .green - XCTAssertEqual(datePicker.textColor, .green) - - datePicker.textColor = nil - XCTAssertNil(datePicker.textColor) - } - #endif -} - -#endif diff --git a/Tests/UIKitTests/UIFontExtensionsTests.swift b/Tests/UIKitTests/UIFontExtensionsTests.swift index 37a348c16..e48749594 100644 --- a/Tests/UIKitTests/UIFontExtensionsTests.swift +++ b/Tests/UIKitTests/UIFontExtensionsTests.swift @@ -1,4 +1,4 @@ -// UIFontExtensionsTests.swift - Copyright 2020 SwifterSwift +// UIFontExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest @@ -8,23 +8,23 @@ import UIKit final class UIFontExtension: XCTestCase { func testBold() { - let font = UIFont.preferredFont(forTextStyle: .body) + let font = UIFont.systemFont(ofSize: 10) let boldFont = font.bold XCTAssert(boldFont.fontDescriptor.symbolicTraits.contains(.traitBold)) } func testItalic() { - let font = UIFont.preferredFont(forTextStyle: .body) + let font = UIFont.systemFont(ofSize: 10) let italicFont = font.italic XCTAssert(italicFont.fontDescriptor.symbolicTraits.contains(.traitItalic)) } func testMonospacedDigitFont() { - let font = UIFont.preferredFont(forTextStyle: .body) + let font = UIFont.systemFont(ofSize: 10) let monoFont = font.monospaced let attributes = monoFont.fontDescriptor.fontAttributes - let fontKey: UIFontDescriptor.AttributeName = UIFontDescriptor.AttributeName.featureSettings + let fontKey = UIFontDescriptor.AttributeName.featureSettings guard let settings = attributes[fontKey] as? [[UIFontDescriptor.AttributeName: Int]] else { XCTFail("Unable to get settings from font") return diff --git a/Tests/UIKitTests/UIGestureRecognizerExtensionsTests.swift b/Tests/UIKitTests/UIGestureRecognizerExtensionsTests.swift index 6e4b13729..c9c6cd8d2 100644 --- a/Tests/UIKitTests/UIGestureRecognizerExtensionsTests.swift +++ b/Tests/UIKitTests/UIGestureRecognizerExtensionsTests.swift @@ -1,4 +1,4 @@ -// UIGestureRecognizerExtensionsTests.swift - Copyright 2020 SwifterSwift +// UIGestureRecognizerExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest diff --git a/Tests/UIKitTests/UIImageExtensionsTests.swift b/Tests/UIKitTests/UIImageExtensionsTests.swift index fd0f9e3c6..3b6303e91 100644 --- a/Tests/UIKitTests/UIImageExtensionsTests.swift +++ b/Tests/UIKitTests/UIImageExtensionsTests.swift @@ -1,4 +1,4 @@ -// UIImageExtensionsTests.swift - Copyright 2020 SwifterSwift +// UIImageExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest @@ -32,7 +32,11 @@ final class UIImageExtensionsTests: XCTestCase { func testBytesSize() { let bundle = Bundle(for: UIImageExtensionsTests.self) let image = UIImage(named: "TestImage", in: bundle, compatibleWith: nil)! - XCTAssertEqual(image.bytesSize, 68665) + if #available(iOS 17.0, *) { + XCTAssertEqual(image.bytesSize, 68717) + } else { + XCTAssertEqual(image.bytesSize, 68665) + } XCTAssertEqual(UIImage().bytesSize, 0) } @@ -120,7 +124,6 @@ final class UIImageExtensionsTests: XCTestCase { XCTAssertEqual(scaledImage!.size.width, 300, accuracy: 0.1) } - @available(tvOS 10.0, watchOS 3.0, *) func testRotatedByMeasurement() { let bundle = Bundle(for: UIImageExtensionsTests.self) let image = UIImage(named: "TestImage", in: bundle, compatibleWith: nil)! @@ -172,7 +175,11 @@ final class UIImageExtensionsTests: XCTestCase { let size = CGSize(width: 5, height: 5) XCTAssertEqual(image?.size, size) - XCTAssertEqual(image?.bytesSize, 787) + if #available(iOS 17.0, *) { + XCTAssertEqual(image?.bytesSize, 839) + } else { + XCTAssertEqual(image?.bytesSize, 787) + } let scale = CGFloat(5.0) let scaledSize = CGSize(width: size.width / scale, height: size.height / scale) @@ -231,16 +238,40 @@ final class UIImageExtensionsTests: XCTestCase { func testPNGBase64String() { let image = UIImage(color: .blue, size: CGSize(width: 1, height: 1)) + let base64String: String + if #available(iOS 17.0, *) { + base64String = + "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAIRlWElmTU0AKgAAAAgABQESAAMAAAABAAEAAAEaAAUAAAABAAAASgEbAAUAAAABAAAAUgEoAAMAAAABAAIAAIdpAAQAAAABAAAAWgAAAAAAAABIAAAAAQAAAEgAAAABAAOgAQADAAAAAQABAACgAgAEAAAAAQAAAAGgAwAEAAAAAQAAAAEAAAAAChjw/QAAAAlwSFlzAAALEwAACxMBAJqcGAAAAA1JREFUCB1jYGD4/x8AAwIB/6fhVKUAAAAASUVORK5CYII=" + } else { + base64String = + "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAAaADAAQAAAABAAAAAQAAAAD5Ip3+AAAADUlEQVQIHWNgYPj/HwADAgH/p+FUpQAAAABJRU5ErkJggg==" + } XCTAssertEqual( image.pngBase64String(), - "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAAaADAAQAAAABAAAAAQAAAAD5Ip3+AAAADUlEQVQIHWNgYPj/HwADAgH/p+FUpQAAAABJRU5ErkJggg==") + base64String) } func testJPEGBase64String() { let image = UIImage(color: .blue, size: CGSize(width: 1, height: 1)) + let base64String: String + if #available(iOS 17.0, *) { + base64String = + "/9j/4AAQSkZJRgABAQAASABIAAD/4QCMRXhpZgAATU0AKgAAAAgABQESAAMAAAABAAEAAAEaAAUAAAABAAAASgEbAAUAAAABAAAAUgEoAAMAAAABAAIAAIdpAAQAAAABAAAAWgAAAAAAAABIAAAAAQAAAEgAAAABAAOgAQADAAAAAQABAACgAgAEAAAAAQAAAAGgAwAEAAAAAQAAAAEAAAAA/+0AOFBob3Rvc2hvcCAzLjAAOEJJTQQEAAAAAAAAOEJJTQQlAAAAAAAQ1B2M2Y8AsgTpgAmY7PhCfv/AABEIAAEAAQMBEQACEQEDEQH/xAAfAAABBQEBAQEBAQAAAAAAAAAAAQIDBAUGBwgJCgv/xAC1EAACAQMDAgQDBQUEBAAAAX0BAgMABBEFEiExQQYTUWEHInEUMoGRoQgjQrHBFVLR8CQzYnKCCQoWFxgZGiUmJygpKjQ1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoOEhYaHiImKkpOUlZaXmJmaoqOkpaanqKmqsrO0tba3uLm6wsPExcbHyMnK0tPU1dbX2Nna4eLj5OXm5+jp6vHy8/T19vf4+fr/xAAfAQADAQEBAQEBAQEBAAAAAAAAAQIDBAUGBwgJCgv/xAC1EQACAQIEBAMEBwUEBAABAncAAQIDEQQFITEGEkFRB2FxEyIygQgUQpGhscEJIzNS8BVictEKFiQ04SXxFxgZGiYnKCkqNTY3ODk6Q0RFRkdISUpTVFVWV1hZWmNkZWZnaGlqc3R1dnd4eXqCg4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2dri4+Tl5ufo6ery8/T19vf4+fr/2wBDAAEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/2wBDAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQH/3QAEAAH/2gAMAwEAAhEDEQA/AP456/7+D+Vz/9k=" + } else { + base64String = + "/9j/4AAQSkZJRgABAQAASABIAAD/4QBYRXhpZgAATU0AKgAAAAgAAgESAAMAAAABAAEAAIdpAAQAAAABAAAAJgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAAaADAAQAAAABAAAAAQAAAAD/7QA4UGhvdG9zaG9wIDMuMAA4QklNBAQAAAAAAAA4QklNBCUAAAAAABDUHYzZjwCyBOmACZjs+EJ+/8AAEQgAAQABAwERAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/bAEMAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAf/bAEMBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAf/dAAQAAf/aAAwDAQACEQMRAD8A/jnr/v4P5XP/2Q==" + } XCTAssertEqual( image.jpegBase64String(compressionQuality: 1), - "/9j/4AAQSkZJRgABAQAASABIAAD/4QBYRXhpZgAATU0AKgAAAAgAAgESAAMAAAABAAEAAIdpAAQAAAABAAAAJgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAAaADAAQAAAABAAAAAQAAAAD/7QA4UGhvdG9zaG9wIDMuMAA4QklNBAQAAAAAAAA4QklNBCUAAAAAABDUHYzZjwCyBOmACZjs+EJ+/8AAEQgAAQABAwERAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/bAEMAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAf/bAEMBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAf/dAAQAAf/aAAwDAQACEQMRAD8A/jnr/v4P5XP/2Q==") + base64String) + } + + @available(iOS 13.0, tvOS 13.0, watchOS 6.0, *) + func testWithAlwaysOriginalTintColor() { + let image = UIImage(color: .blue, size: CGSize(width: 20, height: 20)) + XCTAssertEqual( + image.withAlwaysOriginalTintColor(.red), + image.withTintColor(.red, renderingMode: .alwaysOriginal)) } } diff --git a/Tests/UIKitTests/UIImageViewExtensionsTests.swift b/Tests/UIKitTests/UIImageViewExtensionsTests.swift index 508e8f859..e410c5ff1 100644 --- a/Tests/UIKitTests/UIImageViewExtensionsTests.swift +++ b/Tests/UIKitTests/UIImageViewExtensionsTests.swift @@ -1,4 +1,4 @@ -// UIImageViewExtensionsTests.swift - Copyright 2020 SwifterSwift +// UIImageViewExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest diff --git a/Tests/UIKitTests/UILabelExtensionsTests.swift b/Tests/UIKitTests/UILabelExtensionsTests.swift index caf087e22..eee7a1e9c 100644 --- a/Tests/UIKitTests/UILabelExtensionsTests.swift +++ b/Tests/UIKitTests/UILabelExtensionsTests.swift @@ -1,4 +1,4 @@ -// UILabelExtensionsTests.swift - Copyright 2020 SwifterSwift +// UILabelExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest diff --git a/Tests/UIKitTests/UILayoutPriorityExtensionsTests.swift b/Tests/UIKitTests/UILayoutPriorityExtensionsTests.swift index 4c7028873..a16c3c186 100644 --- a/Tests/UIKitTests/UILayoutPriorityExtensionsTests.swift +++ b/Tests/UIKitTests/UILayoutPriorityExtensionsTests.swift @@ -1,4 +1,4 @@ -// UILayoutPriorityExtensionsTests.swift - Copyright 2020 SwifterSwift +// UILayoutPriorityExtensionsTests.swift - Copyright 2023 SwifterSwift #if os(iOS) || os(tvOS) diff --git a/Tests/UIKitTests/UINavigationBarExtensionTests.swift b/Tests/UIKitTests/UINavigationBarExtensionTests.swift index 84c410313..c352e8862 100644 --- a/Tests/UIKitTests/UINavigationBarExtensionTests.swift +++ b/Tests/UIKitTests/UINavigationBarExtensionTests.swift @@ -1,4 +1,4 @@ -// UINavigationBarExtensionTests.swift - Copyright 2020 SwifterSwift +// UINavigationBarExtensionTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest @@ -11,41 +11,115 @@ final class UINavigationBarExtensionsTests: XCTestCase { let navigationBar = UINavigationBar() let helveticaFont = UIFont(name: "HelveticaNeue", size: 14)! navigationBar.setTitleFont(helveticaFont, color: .green) - let color = navigationBar.titleTextAttributes?[NSAttributedString.Key.foregroundColor] as? UIColor - XCTAssertEqual(color, .green) - let font = navigationBar.titleTextAttributes?[NSAttributedString.Key.font] as? UIFont - XCTAssertEqual(font, helveticaFont) - - navigationBar.setTitleFont(helveticaFont) - let defaultColor = navigationBar.titleTextAttributes?[NSAttributedString.Key.foregroundColor] as? UIColor - XCTAssertEqual(defaultColor, .black) + if #available(iOS 13.0, tvOS 13.0, *) { + let color = navigationBar.standardAppearance + .titleTextAttributes[NSAttributedString.Key.foregroundColor] as? UIColor + XCTAssertEqual(color, .green) + + let font = navigationBar.standardAppearance.titleTextAttributes[NSAttributedString.Key.font] as? UIFont + XCTAssertEqual(font, helveticaFont) + + navigationBar.setTitleFont(helveticaFont) + let defaultColor = navigationBar.standardAppearance + .titleTextAttributes[NSAttributedString.Key.foregroundColor] as? UIColor + XCTAssertEqual(defaultColor, .black) + } else { + let color = navigationBar.titleTextAttributes?[NSAttributedString.Key.foregroundColor] as? UIColor + XCTAssertEqual(color, .green) + let font = navigationBar.titleTextAttributes?[NSAttributedString.Key.font] as? UIFont + XCTAssertEqual(font, helveticaFont) + + navigationBar.setTitleFont(helveticaFont) + let defaultColor = navigationBar.titleTextAttributes?[NSAttributedString.Key.foregroundColor] as? UIColor + XCTAssertEqual(defaultColor, .black) + } } func testMakeTransparent() { let navigationBar = UINavigationBar() navigationBar.makeTransparent(withTint: .red) - XCTAssertNotNil(navigationBar.backgroundImage(for: .default)) - XCTAssertNotNil(navigationBar.shadowImage) - XCTAssert(navigationBar.isTranslucent) - XCTAssertEqual(navigationBar.tintColor, .red) - let color = navigationBar.titleTextAttributes?[NSAttributedString.Key.foregroundColor] as? UIColor - XCTAssertEqual(color, .red) - - navigationBar.makeTransparent() - let defaultColor = navigationBar.titleTextAttributes?[NSAttributedString.Key.foregroundColor] as? UIColor - XCTAssertEqual(defaultColor, .white) + let legacyTests = { + XCTAssertNotNil(navigationBar.backgroundImage(for: .default)) + XCTAssertNotNil(navigationBar.shadowImage) + XCTAssert(navigationBar.isTranslucent) + XCTAssertEqual(navigationBar.tintColor, .red) + let color = navigationBar.titleTextAttributes?[NSAttributedString.Key.foregroundColor] as? UIColor + XCTAssertEqual(color, .red) + + navigationBar.makeTransparent() + let defaultColor = navigationBar.titleTextAttributes?[NSAttributedString.Key.foregroundColor] as? UIColor + XCTAssertEqual(defaultColor, .white) + } + #if os(tvOS) + legacyTests() + #else + if #available(iOS 13.0, *) { + XCTAssertEqual(navigationBar.tintColor, .red) + + let standardAppearanceColor = navigationBar.standardAppearance + .titleTextAttributes[NSAttributedString.Key.foregroundColor] as? UIColor + let scrollEdgeAppearanceColor = navigationBar.scrollEdgeAppearance? + .titleTextAttributes[NSAttributedString.Key.foregroundColor] as? UIColor + let compactAppearanceColor = navigationBar.compactAppearance? + .titleTextAttributes[NSAttributedString.Key.foregroundColor] as? UIColor + XCTAssertEqual(standardAppearanceColor, .red) + XCTAssertEqual(scrollEdgeAppearanceColor, .red) + XCTAssertEqual(compactAppearanceColor, .red) + + navigationBar.makeTransparent() + let standardAppearanceDefaultColor = navigationBar.standardAppearance + .titleTextAttributes[NSAttributedString.Key.foregroundColor] as? UIColor + let scrollEdgeAppearanceDefaultColor = navigationBar.scrollEdgeAppearance? + .titleTextAttributes[NSAttributedString.Key.foregroundColor] as? UIColor + let compactAppearanceDefaultColor = navigationBar.compactAppearance? + .titleTextAttributes[NSAttributedString.Key.foregroundColor] as? UIColor + XCTAssertEqual(standardAppearanceDefaultColor, .white) + XCTAssertEqual(scrollEdgeAppearanceDefaultColor, .white) + XCTAssertEqual(compactAppearanceDefaultColor, .white) + } else { + legacyTests() + } + #endif } func testSetColors() { let navigationBar = UINavigationBar() navigationBar.setColors(background: .blue, text: .green) - XCTAssertFalse(navigationBar.isTranslucent) - XCTAssertEqual(navigationBar.backgroundColor, .blue) - XCTAssertEqual(navigationBar.barTintColor, .blue) - XCTAssertNotNil(navigationBar.backgroundImage(for: .default)) - XCTAssertEqual(navigationBar.tintColor, .green) - let color = navigationBar.titleTextAttributes?[NSAttributedString.Key.foregroundColor] as? UIColor - XCTAssertEqual(color, .green) + let legacyTests = { + XCTAssertFalse(navigationBar.isTranslucent) + XCTAssertEqual(navigationBar.backgroundColor, .blue) + XCTAssertEqual(navigationBar.barTintColor, .blue) + XCTAssertNotNil(navigationBar.backgroundImage(for: .default)) + XCTAssertEqual(navigationBar.tintColor, .green) + let color = navigationBar.titleTextAttributes?[NSAttributedString.Key.foregroundColor] as? UIColor + XCTAssertEqual(color, .green) + } + #if os(tvOS) + legacyTests() + #else + if #available(iOS 13.0, *) { + XCTAssertEqual(navigationBar.tintColor, .green) + + let standardAppearanceBackgroundColor = navigationBar.standardAppearance.backgroundColor + let scrollEdgeAppearanceBackgroundColor = navigationBar.scrollEdgeAppearance?.backgroundColor + let compactAppearanceBackgroundColor = navigationBar.compactAppearance?.backgroundColor + XCTAssertEqual(standardAppearanceBackgroundColor, .blue) + XCTAssertEqual(scrollEdgeAppearanceBackgroundColor, .blue) + XCTAssertEqual(compactAppearanceBackgroundColor, .blue) + + let standardAppearanceTextColor = navigationBar.standardAppearance + .titleTextAttributes[NSAttributedString.Key.foregroundColor] as? UIColor + let scrollEdgeAppearanceTextColor = navigationBar.scrollEdgeAppearance? + .titleTextAttributes[NSAttributedString.Key.foregroundColor] as? UIColor + let compactAppearanceTextColor = navigationBar.compactAppearance? + .titleTextAttributes[NSAttributedString.Key.foregroundColor] as? UIColor + XCTAssertEqual(standardAppearanceTextColor, .green) + XCTAssertEqual(scrollEdgeAppearanceTextColor, .green) + XCTAssertEqual(compactAppearanceTextColor, .green) + } else { + legacyTests() + } + #endif } } diff --git a/Tests/UIKitTests/UINavigationControllerExtensionsTests.swift b/Tests/UIKitTests/UINavigationControllerExtensionsTests.swift index 46748da93..30b10164c 100644 --- a/Tests/UIKitTests/UINavigationControllerExtensionsTests.swift +++ b/Tests/UIKitTests/UINavigationControllerExtensionsTests.swift @@ -1,4 +1,4 @@ -// UINavigationControllerExtensionsTests.swift - Copyright 2020 SwifterSwift +// UINavigationControllerExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest @@ -53,5 +53,18 @@ final class UINavigationControllerExtensionsTests: XCTestCase { XCTAssertNotNil(color) XCTAssertEqual(color!, .red) } + + #if !os(tvOS) + func testHideBottomBar() { + let rootVC = UIViewController() + let navigationController = UINavigationController(rootViewController: rootVC) + let vcToPush = UIViewController() + let tabVC = UITabBarController() + tabVC.viewControllers = [navigationController] + navigationController.pushViewController(vcToPush, hidesBottomBar: true, animated: false) + XCTAssert(vcToPush.hidesBottomBarWhenPushed) + XCTAssert(tabVC.tabBar.isHidden) + } + #endif } #endif diff --git a/Tests/UIKitTests/UINavigationItemExtensionsTests.swift b/Tests/UIKitTests/UINavigationItemExtensionsTests.swift index b4ed406dc..f7303ac0d 100644 --- a/Tests/UIKitTests/UINavigationItemExtensionsTests.swift +++ b/Tests/UIKitTests/UINavigationItemExtensionsTests.swift @@ -1,4 +1,4 @@ -// UINavigationItemExtensionsTests.swift - Copyright 2020 SwifterSwift +// UINavigationItemExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest diff --git a/Tests/UIKitTests/UIRefreshControlExntesionsTests.swift b/Tests/UIKitTests/UIRefreshControlExntesionsTests.swift deleted file mode 100644 index 2faebb5f2..000000000 --- a/Tests/UIKitTests/UIRefreshControlExntesionsTests.swift +++ /dev/null @@ -1,30 +0,0 @@ -// UIRefreshControlExntesionsTests.swift - Copyright 2020 SwifterSwift - -@testable import SwifterSwift -import XCTest - -#if canImport(UIKit) && os(iOS) -import UIKit - -final class UIRefreshControlExtensionTests: XCTestCase { - func testBeginRefreshAsRefreshControlSubview() { - let tableView = UITableView() - XCTAssertEqual(tableView.contentOffset, .zero) - let refreshControl = UIRefreshControl() - tableView.addSubview(refreshControl) - refreshControl.beginRefreshing(in: tableView, animated: true) - - XCTAssert(refreshControl.isRefreshing) - XCTAssertEqual(tableView.contentOffset.y, -refreshControl.frame.height) - - let anotherTableview = UITableView() - XCTAssertEqual(anotherTableview.contentOffset, .zero) - anotherTableview.refreshControl = UIRefreshControl() - anotherTableview.refreshControl?.beginRefreshing(in: anotherTableview, animated: true, sendAction: true) - - XCTAssert(anotherTableview.refreshControl!.isRefreshing) - XCTAssertEqual(anotherTableview.contentOffset.y, -anotherTableview.refreshControl!.frame.height) - } -} - -#endif diff --git a/Tests/UIKitTests/UIRefreshControlExtensionsTests.swift b/Tests/UIKitTests/UIRefreshControlExtensionsTests.swift new file mode 100644 index 000000000..24162a7a6 --- /dev/null +++ b/Tests/UIKitTests/UIRefreshControlExtensionsTests.swift @@ -0,0 +1,53 @@ +// UIRefreshControlExtensionsTests.swift - Copyright 2023 SwifterSwift + +@testable import SwifterSwift +import XCTest + +#if canImport(UIKit) && os(iOS) +import UIKit + +final class UIRefreshControlExtensionTests: XCTestCase { + func testBeginRefreshAsRefreshControlSubview() { + let window = UIWindow() + let tableView = UITableView() + window.addSubview(tableView) + let refreshControl = UIRefreshControl() + tableView.addSubview(refreshControl) + refreshControl.beginRefreshing(in: tableView, animated: false) + + XCTAssert(refreshControl.isRefreshing) + XCTAssertEqual(tableView.contentOffset.y, -refreshControl.frame.height) + + let anotherTableview = UITableView() + window.addSubview(anotherTableview) + anotherTableview.refreshControl = UIRefreshControl() + anotherTableview.refreshControl!.beginRefreshing(in: anotherTableview, animated: false, sendAction: true) + + XCTAssert(anotherTableview.refreshControl!.isRefreshing) + XCTAssertEqual(anotherTableview.contentOffset.y, -anotherTableview.refreshControl!.frame.height) + } + + func testBeginRefreshAsScrollViewSubview() { + let window = UIWindow() + let scrollView = UIScrollView() + window.addSubview(scrollView) + XCTAssertEqual(scrollView.contentOffset, .zero) + let refreshControl = UIRefreshControl() + scrollView.addSubview(refreshControl) + refreshControl.beginRefreshing(animated: false) + + XCTAssert(refreshControl.isRefreshing) + XCTAssertEqual(scrollView.contentOffset.y, -refreshControl.frame.height) + + let anotherScrollView = UIScrollView() + window.addSubview(anotherScrollView) + XCTAssertEqual(anotherScrollView.contentOffset, .zero) + anotherScrollView.refreshControl = UIRefreshControl() + anotherScrollView.refreshControl!.beginRefreshing(animated: false, sendAction: true) + + XCTAssert(anotherScrollView.refreshControl!.isRefreshing) + XCTAssertEqual(anotherScrollView.contentOffset.y, -anotherScrollView.refreshControl!.frame.height) + } +} + +#endif diff --git a/Tests/UIKitTests/UIScrollViewExtensionsTests.swift b/Tests/UIKitTests/UIScrollViewExtensionsTests.swift index b0297fdd4..a310f0e42 100644 --- a/Tests/UIKitTests/UIScrollViewExtensionsTests.swift +++ b/Tests/UIKitTests/UIScrollViewExtensionsTests.swift @@ -1,4 +1,4 @@ -// UIScrollViewExtensionsTests.swift - Copyright 2020 SwifterSwift +// UIScrollViewExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest diff --git a/Tests/UIKitTests/UISearchBarExtensionsTests.swift b/Tests/UIKitTests/UISearchBarExtensionsTests.swift index 22502ba28..eb949a0cb 100644 --- a/Tests/UIKitTests/UISearchBarExtensionsTests.swift +++ b/Tests/UIKitTests/UISearchBarExtensionsTests.swift @@ -1,4 +1,4 @@ -// UISearchBarExtensionsTests.swift - Copyright 2020 SwifterSwift +// UISearchBarExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest diff --git a/Tests/UIKitTests/UISegmentedControlExtensionsTests.swift b/Tests/UIKitTests/UISegmentedControlExtensionsTests.swift index 7e35a49a7..a3889cead 100644 --- a/Tests/UIKitTests/UISegmentedControlExtensionsTests.swift +++ b/Tests/UIKitTests/UISegmentedControlExtensionsTests.swift @@ -1,4 +1,4 @@ -// UISegmentedControlExtensionsTests.swift - Copyright 2020 SwifterSwift +// UISegmentedControlExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest diff --git a/Tests/UIKitTests/UISliderExtensionsTests.swift b/Tests/UIKitTests/UISliderExtensionsTests.swift index e60afc8d0..76cb42c70 100644 --- a/Tests/UIKitTests/UISliderExtensionsTests.swift +++ b/Tests/UIKitTests/UISliderExtensionsTests.swift @@ -1,4 +1,4 @@ -// UISliderExtensionsTests.swift - Copyright 2020 SwifterSwift +// UISliderExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest diff --git a/Tests/UIKitTests/UIStackViewExtensionsTests.swift b/Tests/UIKitTests/UIStackViewExtensionsTests.swift index a0e8136e1..5ee6def47 100644 --- a/Tests/UIKitTests/UIStackViewExtensionsTests.swift +++ b/Tests/UIKitTests/UIStackViewExtensionsTests.swift @@ -1,4 +1,4 @@ -// UIStackViewExtensionsTests.swift - Copyright 2020 SwifterSwift +// UIStackViewExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest @@ -58,6 +58,50 @@ final class UIStackViewExtensionsTest: XCTestCase { stack.removeArrangedSubviews() XCTAssert(stack.arrangedSubviews.isEmpty) } + + func testSwap() { + let view1 = UIView() + let view2 = UIView() + let view3 = UIView() + let view4 = UIView() + let view5 = UIView() + let view6 = UIView() + let stack = UIStackView(arrangedSubviews: [view1, view2, view3, view4, view5]) + stack.swap(view1, view3) + stack.swap(view2, view5) + XCTAssertEqual(stack.arrangedSubviews.firstIndex(of: view1), 2) + XCTAssertEqual(stack.arrangedSubviews.firstIndex(of: view2), 4) + XCTAssertEqual(stack.arrangedSubviews.firstIndex(of: view3), 0) + XCTAssertEqual(stack.arrangedSubviews.firstIndex(of: view4), 3) + XCTAssertEqual(stack.arrangedSubviews.firstIndex(of: view5), 1) + + stack.swap(view5, view6) + XCTAssertEqual(stack.arrangedSubviews.firstIndex(of: view5), 1) + XCTAssertNil(stack.arrangedSubviews.firstIndex(of: view6)) + + let swapExpectation = expectation(description: "views swapped") + stack.swap(view4, view3, animated: true, duration: 0.5) { _ in + swapExpectation.fulfill() + } + XCTAssertEqual(stack.arrangedSubviews.firstIndex(of: view3), 3) + XCTAssertEqual(stack.arrangedSubviews.firstIndex(of: view4), 0) + waitForExpectations(timeout: 1.0) + } + + func testBackgroundViewColor() { + let stack = UIStackView() + stack.backgroundViewColor = nil + XCTAssertNil(stack.backgroundViewColor) + + stack.backgroundViewColor = .red + XCTAssertNotNil(stack.backgroundViewColor) + + XCTAssertEqual(stack.backgroundViewColor!, UIColor.red) + + if #available(iOS 14, *) { + XCTAssertEqual(stack.backgroundColor!, UIColor.red) + } + } } #endif diff --git a/Tests/UIKitTests/UIStoryboardExtensionsTests.swift b/Tests/UIKitTests/UIStoryboardExtensionsTests.swift index af63ce27a..daaef6945 100644 --- a/Tests/UIKitTests/UIStoryboardExtensionsTests.swift +++ b/Tests/UIKitTests/UIStoryboardExtensionsTests.swift @@ -1,4 +1,4 @@ -// UIStoryboardExtensionsTests.swift - Copyright 2020 SwifterSwift +// UIStoryboardExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest diff --git a/Tests/UIKitTests/UISwitchExtensionsTests.swift b/Tests/UIKitTests/UISwitchExtensionsTests.swift index 1f550e932..0b3f5d280 100644 --- a/Tests/UIKitTests/UISwitchExtensionsTests.swift +++ b/Tests/UIKitTests/UISwitchExtensionsTests.swift @@ -1,4 +1,4 @@ -// UISwitchExtensionsTests.swift - Copyright 2020 SwifterSwift +// UISwitchExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest diff --git a/Tests/UIKitTests/UITabBarExtensionsTests.swift b/Tests/UIKitTests/UITabBarExtensionsTests.swift index efea2b1ed..2c08bf397 100644 --- a/Tests/UIKitTests/UITabBarExtensionsTests.swift +++ b/Tests/UIKitTests/UITabBarExtensionsTests.swift @@ -1,4 +1,4 @@ -// UITabBarExtensionsTests.swift - Copyright 2020 SwifterSwift +// UITabBarExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest diff --git a/Tests/UIKitTests/UITableViewExtensionsTests.swift b/Tests/UIKitTests/UITableViewExtensionsTests.swift index 129476b31..33994abeb 100644 --- a/Tests/UIKitTests/UITableViewExtensionsTests.swift +++ b/Tests/UIKitTests/UITableViewExtensionsTests.swift @@ -1,4 +1,4 @@ -// UITableViewExtensionsTests.swift - Copyright 2020 SwifterSwift +// UITableViewExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest diff --git a/Tests/UIKitTests/UITextFieldExtensionsTests.swift b/Tests/UIKitTests/UITextFieldExtensionsTests.swift index 4dcdd4ba2..84eb19459 100644 --- a/Tests/UIKitTests/UITextFieldExtensionsTests.swift +++ b/Tests/UIKitTests/UITextFieldExtensionsTests.swift @@ -1,4 +1,4 @@ -// UITextFieldExtensionsTests.swift - Copyright 2020 SwifterSwift +// UITextFieldExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest @@ -156,6 +156,29 @@ final class UITextFieldExtensionsTests: XCTestCase { textfield.addPaddingRightIcon(image, padding: 5) XCTAssertEqual(textfield.rightView?.frame.width, image.size.width + 5) } + + #if os(iOS) + func testAddToolbar() { + let textField = UITextField(frame: CGRect(x: 0, y: 0, width: 100, height: 44)) + let doneBarButtonItem = UIBarButtonItem(barButtonSystemItem: .done, target: nil, action: nil) + let addBarButtonItem = UIBarButtonItem(barButtonSystemItem: .add, target: nil, action: nil) + + textField.addToolbar(items: [doneBarButtonItem, addBarButtonItem]) + + guard let toolBar = textField.inputAccessoryView as? UIToolbar else { + XCTFail("Expecting toolbar added to textfield accessory view") + return + } + + guard let doneBarButton = toolBar.items?.first, + let addBarButton = toolBar.items?[1] else { + XCTFail("Expecting done and add bar button added to textfield accessory view") + return + } + XCTAssertEqual(doneBarButton, doneBarButtonItem) + XCTAssertEqual(addBarButton, addBarButtonItem) + } + #endif } #endif diff --git a/Tests/UIKitTests/UITextViewExtensionsTests.swift b/Tests/UIKitTests/UITextViewExtensionsTests.swift index 731285e7d..727c360e8 100644 --- a/Tests/UIKitTests/UITextViewExtensionsTests.swift +++ b/Tests/UIKitTests/UITextViewExtensionsTests.swift @@ -1,4 +1,4 @@ -// UITextViewExtensionsTests.swift - Copyright 2020 SwifterSwift +// UITextViewExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest @@ -9,10 +9,6 @@ import UIKit final class UITextViewExtensionsTests: XCTestCase { var textView = UITextView(frame: CGRect(x: 0, y: 0, width: 100, height: 100)) - override func setUp() { - super.setUp() - } - func testClear() { textView.text = "Hello" textView.clear() @@ -31,6 +27,7 @@ final class UITextViewExtensionsTests: XCTestCase { XCTAssertNotEqual(textView.contentOffset.y, 0.0) } + #if !targetEnvironment(macCatalyst) func testWrapToContent() { let text = "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." @@ -43,11 +40,9 @@ final class UITextViewExtensionsTests: XCTestCase { // determining the text size let constraintRect = CGSize(width: 100, height: CGFloat.greatestFiniteMagnitude) let boundingBox = text.boundingRect(with: constraintRect, - options: NSStringDrawingOptions.usesLineFragmentOrigin, + options: .usesLineFragmentOrigin, attributes: [.font: textView.font!], context: nil) - let textHeight = ceil(boundingBox.height) - let textSize = CGSize(width: 100, height: textHeight) // before setting wrap, content won't be equal to bounds XCTAssertNotEqual(textView.bounds.size, textView.contentSize) @@ -59,13 +54,12 @@ final class UITextViewExtensionsTests: XCTestCase { // This is important to set the frame after calling the wrapToContent, otherwise // boundingRect can give you fractional value, and method call `sizeToFit` inside the // wrapToContent would change to the fractional value instead of the ceil value. - textView.bounds = CGRect(origin: .zero, size: textSize) + textView.bounds.size = CGSize(width: 100, height: ceil(boundingBox.height)) - #if !targetEnvironment(macCatalyst) // after setting wrap, content size will be equal to bounds XCTAssertEqual(textView.bounds.size, textView.contentSize) - #endif } + #endif } #endif diff --git a/Tests/UIKitTests/UIViewControllerExtensionsTests.swift b/Tests/UIKitTests/UIViewControllerExtensionsTests.swift index 5fa636866..48c679c9e 100644 --- a/Tests/UIKitTests/UIViewControllerExtensionsTests.swift +++ b/Tests/UIKitTests/UIViewControllerExtensionsTests.swift @@ -1,4 +1,4 @@ -// UIViewControllerExtensionsTests.swift - Copyright 2020 SwifterSwift +// UIViewControllerExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest @@ -178,7 +178,6 @@ final class UIViewControllerExtensionsTests: XCTestCase { } func testPresentPopoverWithDelegate() { - // swiftlint:disable:next nesting class PopoverDelegate: NSObject, UIPopoverPresentationControllerDelegate { func adaptivePresentationStyle(for _: UIPresentationController) -> UIModalPresentationStyle { return .popover diff --git a/Tests/UIKitTests/UIViewExtensionsTests.swift b/Tests/UIKitTests/UIViewExtensionsTests.swift index ce2c792c7..b98211d9e 100644 --- a/Tests/UIKitTests/UIViewExtensionsTests.swift +++ b/Tests/UIKitTests/UIViewExtensionsTests.swift @@ -1,4 +1,4 @@ -// UIViewExtensionsTests.swift - Copyright 2020 SwifterSwift +// UIViewExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest @@ -11,22 +11,22 @@ final class UIViewExtensionsTests: XCTestCase { func testBorderColor() { let frame = CGRect(x: 0, y: 0, width: 100, height: 100) let view = UIView(frame: frame) - view.borderColor = nil - XCTAssertNil(view.borderColor) - view.borderColor = UIColor.red + view.layerBorderColor = nil + XCTAssertNil(view.layerBorderColor) + view.layerBorderColor = UIColor.red XCTAssertNotNil(view.layer.borderColor) - XCTAssertEqual(view.borderColor!, UIColor.red) + XCTAssertEqual(view.layerBorderColor!, UIColor.red) XCTAssertEqual(view.layer.borderColor!.uiColor, UIColor.red) } func testBorderWidth() { let frame = CGRect(x: 0, y: 0, width: 100, height: 100) let view = UIView(frame: frame) - view.borderWidth = 0 + view.layerBorderWidth = 0 XCTAssertEqual(view.layer.borderWidth, 0) - view.borderWidth = 5 - XCTAssertEqual(view.borderWidth, 5) + view.layerBorderWidth = 5 + XCTAssertEqual(view.layerBorderWidth, 5) } func testCornerRadius() { @@ -34,8 +34,8 @@ final class UIViewExtensionsTests: XCTestCase { let view = UIView(frame: frame) XCTAssertEqual(view.layer.cornerRadius, 0) - view.cornerRadius = 50 - XCTAssertEqual(view.cornerRadius, 50) + view.layerCornerRadius = 50 + XCTAssertEqual(view.layerCornerRadius, 50) } func testFirstResponder() { @@ -254,47 +254,43 @@ final class UIViewExtensionsTests: XCTestCase { func testRotateByAngle() { let view1 = UIView() - let transform1 = CGAffineTransform(rotationAngle: 2) - view1.rotate(byAngle: 2, ofType: .radians, animated: false, duration: 0, completion: nil) - XCTAssertEqual(view1.transform, transform1) + view1.rotate(byAngle: 1, ofType: .radians, animated: false, duration: 0, completion: nil) + XCTAssertEqual(view1.transform, CGAffineTransform(rotationAngle: 1), accuracy: 0.00001) + view1.rotate(byAngle: 1, ofType: .radians, animated: false, duration: 0, completion: nil) + XCTAssertEqual(view1.transform, CGAffineTransform(rotationAngle: 2), accuracy: 0.00001) let view2 = UIView() - let transform2 = CGAffineTransform(rotationAngle: .pi * 90.0 / 180.0) view2.rotate(byAngle: 90, ofType: .degrees, animated: false, duration: 0, completion: nil) - XCTAssertEqual(view2.transform, transform2) + XCTAssertEqual(view2.transform, CGAffineTransform(rotationAngle: .pi / 2.0)) let rotateExpectation = expectation(description: "view rotated") - let view3 = UIView() - let transform3 = CGAffineTransform(rotationAngle: 2) - view3.rotate(byAngle: 2, ofType: .radians, animated: true, duration: 0.5) { _ in rotateExpectation.fulfill() } - XCTAssertEqual(view3.transform, transform3) + XCTAssertEqual(view3.transform, CGAffineTransform(rotationAngle: 2)) waitForExpectations(timeout: 0.5) } func testRotateToAngle() { let view1 = UIView() - let transform1 = CGAffineTransform(rotationAngle: 2) - view1.rotate(toAngle: 2, ofType: .radians, animated: false, duration: 0, completion: nil) - XCTAssertEqual(view1.transform, transform1) + view1.rotate(toAngle: 1, ofType: .radians, animated: false, duration: 0, completion: nil) + XCTAssertEqual(view1.transform, CGAffineTransform(rotationAngle: 1)) + view1.rotate(toAngle: 0, ofType: .radians, animated: false, duration: 0, completion: nil) + XCTAssertEqual(view1.transform, CGAffineTransform(rotationAngle: 0), accuracy: 0.00001) let view2 = UIView() - let transform2 = CGAffineTransform(rotationAngle: .pi * 90.0 / 180.0) view2.rotate(toAngle: 90, ofType: .degrees, animated: false, duration: 0, completion: nil) - XCTAssertEqual(view2.transform, transform2) + XCTAssertEqual(view2.transform, CGAffineTransform(rotationAngle: .pi / 2.0)) + view2.rotate(toAngle: 30, ofType: .degrees, animated: false, duration: 0, completion: nil) + XCTAssertEqual(view2.transform, CGAffineTransform(rotationAngle: .pi / 6.0), accuracy: 0.00001) let rotateExpectation = expectation(description: "view rotated") - let view3 = UIView() - let transform3 = CGAffineTransform(rotationAngle: 2) - view3.rotate(toAngle: 2, ofType: .radians, animated: true, duration: 0.5) { _ in rotateExpectation.fulfill() } - XCTAssertEqual(view3.transform, transform3) + XCTAssertEqual(view3.transform, CGAffineTransform(rotationAngle: 2)) waitForExpectations(timeout: 0.5) } @@ -314,11 +310,18 @@ final class UIViewExtensionsTests: XCTestCase { XCTAssertEqual(view1.transform, view3.transform) } + #if os(tvOS) + func testLoadFromNib() { + let bundle = Bundle(for: UIViewExtensionsTests.self) + XCTAssertNotNil(UIView.loadFromNib(named: "UIImageViewTvOS", bundle: bundle)) + } + #else func testLoadFromNib() { let bundle = Bundle(for: UIViewExtensionsTests.self) XCTAssertNotNil(UIView.loadFromNib(named: "UIImageView", bundle: bundle)) XCTAssertNotNil(UIView.loadFromNib(withClass: UIImageView.self, bundle: bundle)) } + #endif func testRemoveSubviews() { let view = UIView() @@ -375,6 +378,121 @@ final class UIViewExtensionsTests: XCTestCase { XCTAssert(view.gestureRecognizers!.isEmpty) } + // swiftlint:disable:next function_body_length + func testAddGradient() { + // topToBottom + let view0 = UIView() + XCTAssertNil(view0.layer.sublayers) + view0.addGradient( + colors: [.red, .orange, .green, .blue], + locations: [0.0, 0.333, 0.667, 1.0], + direction: .topToBottom) + XCTAssertNotNil(view0.layer.sublayers) + if let sublayers = view0.layer.sublayers as? [CAGradientLayer] { + XCTAssertEqual(sublayers.count, 1) + XCTAssertTrue(sublayers[0].startPoint.x.isEqual(to: 0.5)) + XCTAssertTrue(sublayers[0].startPoint.y.isEqual(to: 0.0)) + XCTAssertTrue(sublayers[0].endPoint.x.isEqual(to: 0.5)) + XCTAssertTrue(sublayers[0].endPoint.y.isEqual(to: 1.0)) + XCTAssertEqual(sublayers[0].colors?.count, 4) + // swiftlint:disable force_cast + XCTAssertEqual(sublayers[0].colors?[0] as! CGColor, UIColor.red.cgColor) + XCTAssertEqual(sublayers[0].colors?[1] as! CGColor, UIColor.orange.cgColor) + XCTAssertEqual(sublayers[0].colors?[2] as! CGColor, UIColor.green.cgColor) + XCTAssertEqual(sublayers[0].colors?[3] as! CGColor, UIColor.blue.cgColor) + // swiftlint:enable force_cast + XCTAssertEqual(sublayers[0].locations?.count, 4) + XCTAssertNotNil(sublayers[0].locations?[0].isEqual(to: 0.0)) + XCTAssertNotNil(sublayers[0].locations?[1].isEqual(to: 0.333)) + XCTAssertNotNil(sublayers[0].locations?[2].isEqual(to: 0.667)) + XCTAssertNotNil(sublayers[0].locations?[3].isEqual(to: 1.0)) + } + + // bottomToTop + let view1 = UIView() + XCTAssertNil(view1.layer.sublayers) + view1.addGradient( + colors: [.red, .orange, .green, .blue], + locations: [0.0, 0.333, 0.667, 1.0], + direction: .bottomToTop) + XCTAssertNotNil(view1.layer.sublayers) + if let sublayers = view1.layer.sublayers as? [CAGradientLayer] { + XCTAssertEqual(sublayers.count, 1) + XCTAssertTrue(sublayers[0].startPoint.x.isEqual(to: 0.5)) + XCTAssertTrue(sublayers[0].startPoint.y.isEqual(to: 1.0)) + XCTAssertTrue(sublayers[0].endPoint.x.isEqual(to: 0.5)) + XCTAssertTrue(sublayers[0].endPoint.y.isEqual(to: 0.0)) + XCTAssertEqual(sublayers[0].colors?.count, 4) + // swiftlint:disable force_cast + XCTAssertEqual(sublayers[0].colors?[0] as! CGColor, UIColor.red.cgColor) + XCTAssertEqual(sublayers[0].colors?[1] as! CGColor, UIColor.orange.cgColor) + XCTAssertEqual(sublayers[0].colors?[2] as! CGColor, UIColor.green.cgColor) + XCTAssertEqual(sublayers[0].colors?[3] as! CGColor, UIColor.blue.cgColor) + // swiftlint:enable force_cast + XCTAssertEqual(sublayers[0].locations?.count, 4) + XCTAssertNotNil(sublayers[0].locations?[0].isEqual(to: 0.0)) + XCTAssertNotNil(sublayers[0].locations?[1].isEqual(to: 0.333)) + XCTAssertNotNil(sublayers[0].locations?[2].isEqual(to: 0.667)) + XCTAssertNotNil(sublayers[0].locations?[3].isEqual(to: 1.0)) + } + + // leftToRight + let view2 = UIView() + XCTAssertNil(view2.layer.sublayers) + view2.addGradient( + colors: [.red, .orange, .green, .blue], + locations: [0.0, 0.333, 0.667, 1.0], + direction: .leftToRight) + XCTAssertNotNil(view2.layer.sublayers) + if let sublayers = view2.layer.sublayers as? [CAGradientLayer] { + XCTAssertEqual(sublayers.count, 1) + XCTAssertTrue(sublayers[0].startPoint.x.isEqual(to: 0.0)) + XCTAssertTrue(sublayers[0].startPoint.y.isEqual(to: 0.5)) + XCTAssertTrue(sublayers[0].endPoint.x.isEqual(to: 1.0)) + XCTAssertTrue(sublayers[0].endPoint.y.isEqual(to: 0.5)) + XCTAssertEqual(sublayers[0].colors?.count, 4) + // swiftlint:disable force_cast + XCTAssertEqual(sublayers[0].colors?[0] as! CGColor, UIColor.red.cgColor) + XCTAssertEqual(sublayers[0].colors?[1] as! CGColor, UIColor.orange.cgColor) + XCTAssertEqual(sublayers[0].colors?[2] as! CGColor, UIColor.green.cgColor) + XCTAssertEqual(sublayers[0].colors?[3] as! CGColor, UIColor.blue.cgColor) + // swiftlint:enable force_cast + XCTAssertEqual(sublayers[0].locations?.count, 4) + XCTAssertNotNil(sublayers[0].locations?[0].isEqual(to: 0.0)) + XCTAssertNotNil(sublayers[0].locations?[1].isEqual(to: 0.333)) + XCTAssertNotNil(sublayers[0].locations?[2].isEqual(to: 0.667)) + XCTAssertNotNil(sublayers[0].locations?[3].isEqual(to: 1.0)) + } + + // rightToLeft + let view3 = UIView() + XCTAssertNil(view3.layer.sublayers) + view3.addGradient( + colors: [.red, .orange, .green, .blue], + locations: [0.0, 0.333, 0.667, 1.0], + direction: .rightToLeft) + XCTAssertNotNil(view3.layer.sublayers) + if let sublayers = view3.layer.sublayers as? [CAGradientLayer] { + XCTAssertEqual(sublayers.count, 1) + XCTAssertTrue(sublayers[0].startPoint.x.isEqual(to: 1.0)) + XCTAssertTrue(sublayers[0].startPoint.y.isEqual(to: 0.5)) + XCTAssertTrue(sublayers[0].endPoint.x.isEqual(to: 0.0)) + XCTAssertTrue(sublayers[0].endPoint.y.isEqual(to: 0.5)) + XCTAssertEqual(sublayers[0].colors?.count, 4) + // swiftlint:disable force_cast + XCTAssertEqual(sublayers[0].colors?[0] as! CGColor, UIColor.red.cgColor) + XCTAssertEqual(sublayers[0].colors?[1] as! CGColor, UIColor.orange.cgColor) + XCTAssertEqual(sublayers[0].colors?[2] as! CGColor, UIColor.green.cgColor) + XCTAssertEqual(sublayers[0].colors?[3] as! CGColor, UIColor.blue.cgColor) + // swiftlint:enable force_cast + XCTAssertEqual(sublayers[0].locations?.count, 4) + XCTAssertNotNil(sublayers[0].locations?[0].isEqual(to: 0.0)) + XCTAssertNotNil(sublayers[0].locations?[1].isEqual(to: 0.333)) + XCTAssertNotNil(sublayers[0].locations?[2].isEqual(to: 0.667)) + XCTAssertNotNil(sublayers[0].locations?[3].isEqual(to: 1.0)) + } + } + func testAnchor() { let view = UIView() let subview = UIView() @@ -468,6 +586,28 @@ final class UIViewExtensionsTests: XCTestCase { XCTAssertEqual(buttonSubview.ancestorView(withClass: UITableView.self), tableView) } + func testSubviewsOfType() { + // Test view with subviews with no subviews + XCTAssertEqual(UIView().subviews(ofType: UILabel.self), []) + + // Test view with subviews that have subviews + let parentView = UIView() + + let childView = UIView() + let childViewSubViews = [UILabel(), UIButton(), UITextView(), UILabel(), UIImageView()] + childView.addSubviews(childViewSubViews) + + let childView2 = UIView() + let childView2SubViews = [UISegmentedControl(), UILabel(), UITextView(), UIImageView()] + childView2.addSubviews(childView2SubViews) + + parentView.addSubviews([childView, childView2]) + + let expected = [childViewSubViews[0], childViewSubViews[3], childView2SubViews[1]] + XCTAssertEqual(parentView.subviews(ofType: UILabel.self), expected) + XCTAssertEqual(parentView.subviews(ofType: UITableViewCell.self), []) + } + func testFindConstraint() { let view = UIView() let container = UIView() diff --git a/Tests/UIKitTests/UIWindowExtensionsTests.swift b/Tests/UIKitTests/UIWindowExtensionsTests.swift index ea3dff2fe..eb3258437 100644 --- a/Tests/UIKitTests/UIWindowExtensionsTests.swift +++ b/Tests/UIKitTests/UIWindowExtensionsTests.swift @@ -1,4 +1,4 @@ -// UIWindowExtensionsTests.swift - Copyright 2020 SwifterSwift +// UIWindowExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest diff --git a/Tests/WebKitTests/WKWebViewExtensionsTests.swift b/Tests/WebKitTests/WKWebViewExtensionsTests.swift index 32f7be6d0..27d6d349f 100644 --- a/Tests/WebKitTests/WKWebViewExtensionsTests.swift +++ b/Tests/WebKitTests/WKWebViewExtensionsTests.swift @@ -1,4 +1,4 @@ -// WKWebViewExtensionsTests.swift - Copyright 2020 SwifterSwift +// WKWebViewExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest @@ -18,7 +18,7 @@ final class WKWebViewExtensionsTests: XCTestCase { func testLoadURL() { let successExpectation = WebViewSuccessExpectation(description: "Correct URL", webView: webView) - let url = URL(string: "https://example.com/")! + let url = URL(string: "https://www.apple.com/")! let navigation = webView.loadURL(url) XCTAssertNotNil(navigation) @@ -29,7 +29,7 @@ final class WKWebViewExtensionsTests: XCTestCase { func testLoadURLString() { let successExpectation = WebViewSuccessExpectation(description: "Correct URL string", webView: webView) - let urlString = "https://example.com/" + let urlString = "https://www.apple.com/" let navigation = webView.loadURLString(urlString) XCTAssertNotNil(navigation) @@ -38,17 +38,19 @@ final class WKWebViewExtensionsTests: XCTestCase { } func testLoadInvalidURLString() { - let invalidURLString = "invalid url" - let navigation = webView.loadURLString(invalidURLString) + if #unavailable(iOS 17.0) { + let invalidURLString = "invalid url" + let navigation = webView.loadURLString(invalidURLString) - XCTAssertNil(navigation) + XCTAssertNil(navigation) + } } func testLoadDeadURLString() { let failureExpectation = WebViewFailureExpectation(description: "Dead URL string", webView: webView) - let deadURLString = "https://deadurl.com" - let navigation = webView.loadURLString(deadURLString) + let deadURLString = "https://dead-url-573489574389.com" + let navigation = webView.loadURLString(deadURLString, timeout: 5.0) XCTAssertNotNil(navigation) @@ -73,11 +75,11 @@ class WebViewFailureExpectation: XCTestExpectation, WKNavigationDelegate { webView.navigationDelegate = self } - func webView(_: WKWebView, didFail _: WKNavigation!, withError _: Error) { + func webView(_: WKWebView, didFail _: WKNavigation!, withError _: any Error) { fulfill() } - func webView(_: WKWebView, didFailProvisionalNavigation _: WKNavigation!, withError _: Error) { + func webView(_: WKWebView, didFailProvisionalNavigation _: WKNavigation!, withError _: any Error) { fulfill() } } diff --git a/Tests/XCTest/XCTestExtensions.swift b/Tests/XCTest/XCTestExtensions.swift index a9d31a032..aae6818bd 100644 --- a/Tests/XCTest/XCTestExtensions.swift +++ b/Tests/XCTest/XCTestExtensions.swift @@ -1,4 +1,4 @@ -// XCTestExtensions.swift - Copyright 2020 SwifterSwift +// XCTestExtensions.swift - Copyright 2023 SwifterSwift #if canImport(XCTest) import XCTest @@ -6,13 +6,13 @@ import XCTest #if canImport(UIKit) import UIKit /// SwifterSwift: Color -public typealias Color = UIColor +public typealias SFColor = UIColor #endif #if canImport(AppKit) && !targetEnvironment(macCatalyst) import AppKit /// SwifterSwift: Color -public typealias Color = NSColor +public typealias SFColor = NSColor #endif #if canImport(AppKit) || canImport(UIKit) @@ -20,19 +20,21 @@ public typealias Color = NSColor /// - Parameters: /// - expression1: A `Color`. /// - expression2: A `Color`. -/// - accuracy: Describes the maximum difference between `expression1` and `expression2` for these values to be considered equal. +/// - accuracy: Describes the maximum difference between `expression1` and `expression2` for these values to be +/// considered equal. /// - message: An optional description of the failure. -/// - file: The file in which failure occurred. Defaults to the file name of the test case in which this function was called. +/// - file: The file in which failure occurred. Defaults to the file name of the test case in which this function was +/// called. /// - line: The line number on which failure occurred. Defaults to the line number on which this function was called. -public func XCTAssertEqual(_ expression1: @autoclosure () throws -> Color, - _ expression2: @autoclosure () throws -> Color, +public func XCTAssertEqual(_ expression1: @autoclosure () throws -> SFColor, + _ expression2: @autoclosure () throws -> SFColor, accuracy: CGFloat, _ message: @autoclosure () -> String = "", - file: StaticString = #file, + file: StaticString = #filePath, line: UInt = #line) { - var color1: Color! + var color1: SFColor! XCTAssertNoThrow(color1 = try expression1(), message(), file: file, line: line) - var color2: Color! + var color2: SFColor! XCTAssertNoThrow(color2 = try expression2(), message(), file: file, line: line) var red1: CGFloat = 0, green1: CGFloat = 0, blue1: CGFloat = 0, alpha1: CGFloat = 0 var red2: CGFloat = 0, green2: CGFloat = 0, blue2: CGFloat = 0, alpha2: CGFloat = 0 @@ -45,4 +47,34 @@ public func XCTAssertEqual(_ expression1: @autoclosure () throws -> Color, } #endif +#if canImport(CoreGraphics) +/// SwifterSwift: Asserts that the RGBA values of two `CGAffineTransform`s are equal within a certain accuracy. +/// - Parameters: +/// - expression1: A `CGAffineTransform`. +/// - expression2: A `CGAffineTransform`. +/// - accuracy: Describes the maximum difference between `expression1` and `expression2` for these values to be +/// considered equal. +/// - message: An optional description of the failure. +/// - file: The file in which failure occurred. Defaults to the file name of the test case in which this function was +/// called. +/// - line: The line number on which failure occurred. Defaults to the line number on which this function was called. +public func XCTAssertEqual(_ expression1: @autoclosure () throws -> CGAffineTransform, + _ expression2: @autoclosure () throws -> CGAffineTransform, + accuracy: CGFloat, + _ message: @autoclosure () -> String = "", + file: StaticString = #filePath, + line: UInt = #line) { + var transform1: CGAffineTransform! + XCTAssertNoThrow(transform1 = try expression1(), message(), file: file, line: line) + var transform2: CGAffineTransform! + XCTAssertNoThrow(transform2 = try expression2(), message(), file: file, line: line) + XCTAssertEqual(transform1.a, transform2.a, accuracy: accuracy, message(), file: file, line: line) + XCTAssertEqual(transform1.b, transform2.b, accuracy: accuracy, message(), file: file, line: line) + XCTAssertEqual(transform1.c, transform2.c, accuracy: accuracy, message(), file: file, line: line) + XCTAssertEqual(transform1.d, transform2.d, accuracy: accuracy, message(), file: file, line: line) + XCTAssertEqual(transform1.tx, transform2.tx, accuracy: accuracy, message(), file: file, line: line) + XCTAssertEqual(transform1.ty, transform2.ty, accuracy: accuracy, message(), file: file, line: line) +} +#endif + #endif diff --git a/Tests/XCTestTests/XCTestExtensionsTests.swift b/Tests/XCTestTests/XCTestExtensionsTests.swift index 4c273b148..c7aee1f0d 100644 --- a/Tests/XCTestTests/XCTestExtensionsTests.swift +++ b/Tests/XCTestTests/XCTestExtensionsTests.swift @@ -1,4 +1,4 @@ -// XCTestExtensionsTests.swift - Copyright 2020 SwifterSwift +// XCTestExtensionsTests.swift - Copyright 2023 SwifterSwift @testable import SwifterSwift import XCTest @@ -9,17 +9,17 @@ final class XCTestExtensionsTests: XCTestCase { XCTAssertEqual(.blue, .blue, accuracy: 0) let accuracy = CGFloat(0.1) - XCTAssertEqual(Color(red: 0, green: 0, blue: 0, alpha: 0), - Color(red: accuracy, green: 0, blue: 0, alpha: 0), + XCTAssertEqual(SFColor(red: 0, green: 0, blue: 0, alpha: 0), + SFColor(red: accuracy, green: 0, blue: 0, alpha: 0), accuracy: accuracy) - XCTAssertEqual(Color(red: 0, green: 0, blue: 0, alpha: 0), - Color(red: 0, green: accuracy, blue: 0, alpha: 0), + XCTAssertEqual(SFColor(red: 0, green: 0, blue: 0, alpha: 0), + SFColor(red: 0, green: accuracy, blue: 0, alpha: 0), accuracy: accuracy) - XCTAssertEqual(Color(red: 0, green: 0, blue: 0, alpha: 0), - Color(red: 0, green: 0, blue: accuracy, alpha: 0), + XCTAssertEqual(SFColor(red: 0, green: 0, blue: 0, alpha: 0), + SFColor(red: 0, green: 0, blue: accuracy, alpha: 0), accuracy: accuracy) - XCTAssertEqual(Color(red: 0, green: 0, blue: 0, alpha: 0), - Color(red: 0, green: 0, blue: 0, alpha: accuracy), + XCTAssertEqual(SFColor(red: 0, green: 0, blue: 0, alpha: 0), + SFColor(red: 0, green: 0, blue: 0, alpha: accuracy), accuracy: accuracy) } #endif