./.codecov.yml .editorconfig .swiftformat Assets/brand/brand.sketch Assets/logo.svg Brewfile Brewfile.lock.json Dangerfile Examples/Examples.playground/Pages/00-ToC.xcplaygroundpage/Contents.swift Examples/Examples.playground/Pages/01-SwiftStdlibExtensions.xcplaygroundpage/Contents.swift Examples/Examples.playground/Pages/02-FoundationExtensions.xcplaygroundpage/Contents.swift Examples/Examples.playground/Pages/03-UIKitExtensions.xcplaygroundpage/Contents.swift Examples/Examples.playground/Pages/04-CoreGraphicsExtensions.xcplaygroundpage/Contents.swift Examples/Examples.playground/Pages/05-TryYourself.xcplaygroundpage/Contents.swift Examples/Examples.playground/contents.xcplayground Gemfile Gemfile.lock LICENSE Package.swift Sources/Info.plist Sources/SwifterSwift.h Sources/SwifterSwift/AppKit/NSColorExtensions.swift Sources/SwifterSwift/AppKit/NSImageExtensions.swift Sources/SwifterSwift/AppKit/NSViewExtensions.swift Sources/SwifterSwift/CoreAnimation/CAGradientLayerExtensions.swift Sources/SwifterSwift/CoreAnimation/CATransform3DExtensions.swift Sources/SwifterSwift/CoreGraphics/CGAffineTransformExtensions.swift Sources/SwifterSwift/CoreGraphics/CGColorExtensions.swift Sources/SwifterSwift/CoreGraphics/CGFloatExtensions.swift Sources/SwifterSwift/CoreGraphics/CGPointExtensions.swift Sources/SwifterSwift/CoreGraphics/CGRectExtensions.swift Sources/SwifterSwift/CoreGraphics/CGSizeExtensions.swift Sources/SwifterSwift/CoreGraphics/CGVectorExtensions.swift Sources/SwifterSwift/CoreLocation/CLLocationArrayExtensions.swift Sources/SwifterSwift/CoreLocation/CLLocationExtensions.swift Sources/SwifterSwift/CoreLocation/CLVisitExtensions.swift Sources/SwifterSwift/Dispatch/DispatchQueueExtensions.swift Sources/SwifterSwift/Foundation/CalendarExtensions.swift Sources/SwifterSwift/Foundation/DataExtensions.swift Sources/SwifterSwift/Foundation/DateExtensions.swift Sources/SwifterSwift/Foundation/FileManagerExtensions.swift Sources/SwifterSwift/Foundation/LocaleExtensions.swift Sources/SwifterSwift/Foundation/NSAttributedStringExtensions.swift Sources/SwifterSwift/Foundation/NSPredicateExtensions.swift Sources/SwifterSwift/Foundation/NSRegularExpressionExtensions.swift Sources/SwifterSwift/Foundation/NotificationCenterExtensions.swift Sources/SwifterSwift/Foundation/URLExtensions.swift Sources/SwifterSwift/Foundation/URLRequestExtensions.swift Sources/SwifterSwift/Foundation/UserDefaultsExtensions.swift Sources/SwifterSwift/HealthKit/HKActivitySummaryExtensions.swift Sources/SwifterSwift/MapKit/MKMapViewExtensions.swift Sources/SwifterSwift/MapKit/MKPolylineExtensions.swift Sources/SwifterSwift/SceneKit/SCNBoxExtensions.swift Sources/SwifterSwift/SceneKit/SCNCapsuleExtensions.swift Sources/SwifterSwift/SceneKit/SCNConeExtensions.swift Sources/SwifterSwift/SceneKit/SCNCylinderExtensions.swift Sources/SwifterSwift/SceneKit/SCNGeometryExtensions.swift Sources/SwifterSwift/SceneKit/SCNMaterialExtensions.swift Sources/SwifterSwift/SceneKit/SCNPlaneExtensions.swift Sources/SwifterSwift/SceneKit/SCNShapeExtensions.swift Sources/SwifterSwift/SceneKit/SCNSphereExtensions.swift Sources/SwifterSwift/SceneKit/SCNVector3Extensions.swift Sources/SwifterSwift/Shared/ColorExtensions.swift Sources/SwifterSwift/Shared/EdgeInsetsExtensions.swift Sources/SwifterSwift/Shared/FontExtensions.swift Sources/SwifterSwift/SpriteKit/SKNodeExtensions.swift Sources/SwifterSwift/StoreKit/SKProductExtensions.swift Sources/SwifterSwift/SwiftStdlib/ArrayExtensions.swift Sources/SwifterSwift/SwiftStdlib/BidirectionalCollectionExtensions.swift Sources/SwifterSwift/SwiftStdlib/BinaryFloatingPointExtensions.swift Sources/SwifterSwift/SwiftStdlib/BoolExtensions.swift Sources/SwifterSwift/SwiftStdlib/CharacterExtensions.swift Sources/SwifterSwift/SwiftStdlib/CollectionExtensions.swift Sources/SwifterSwift/SwiftStdlib/ComparableExtensions.swift Sources/SwifterSwift/SwiftStdlib/DecodableExtensions.swift Sources/SwifterSwift/SwiftStdlib/Deprecated/StdlibDeprecated.swift Sources/SwifterSwift/SwiftStdlib/DictionaryExtensions.swift Sources/SwifterSwift/SwiftStdlib/DoubleExtensions.swift Sources/SwifterSwift/SwiftStdlib/FloatExtensions.swift Sources/SwifterSwift/SwiftStdlib/FloatingPointExtensions.swift Sources/SwifterSwift/SwiftStdlib/IntExtensions.swift Sources/SwifterSwift/SwiftStdlib/KeyedDecodingContainerExtensions.swift Sources/SwifterSwift/SwiftStdlib/MutableCollectionExtensions.swift Sources/SwifterSwift/SwiftStdlib/OptionalExtensions.swift Sources/SwifterSwift/SwiftStdlib/RangeReplaceableCollectionExtensions.swift Sources/SwifterSwift/SwiftStdlib/SequenceExtensions.swift Sources/SwifterSwift/SwiftStdlib/SignedIntegerExtensions.swift Sources/SwifterSwift/SwiftStdlib/SignedNumericExtensions.swift Sources/SwifterSwift/SwiftStdlib/StringExtensions.swift Sources/SwifterSwift/SwiftStdlib/StringProtocolExtensions.swift Sources/SwifterSwift/UIKit/UIActivityExtensions.swift Sources/SwifterSwift/UIKit/UIAlertControllerExtensions.swift Sources/SwifterSwift/UIKit/UIApplicationExtensions.swift Sources/SwifterSwift/UIKit/UIBarButtonItemExtensions.swift Sources/SwifterSwift/UIKit/UIBezierPathExtensions.swift Sources/SwifterSwift/UIKit/UIButtonExtensions.swift Sources/SwifterSwift/UIKit/UICollectionViewExtensions.swift Sources/SwifterSwift/UIKit/UIColorExtensions.swift Sources/SwifterSwift/UIKit/UIDatePickerExtensions.swift Sources/SwifterSwift/UIKit/UIFontExtensions.swift Sources/SwifterSwift/UIKit/UIGestureRecognizerExtensions.swift Sources/SwifterSwift/UIKit/UIImageExtensions.swift Sources/SwifterSwift/UIKit/UIImageViewExtensions.swift Sources/SwifterSwift/UIKit/UILabelExtensions.swift Sources/SwifterSwift/UIKit/UILayoutPriorityExtensions.swift Sources/SwifterSwift/UIKit/UINavigationBarExtensions.swift Sources/SwifterSwift/UIKit/UINavigationControllerExtensions.swift Sources/SwifterSwift/UIKit/UINavigationItemExtensions.swift Sources/SwifterSwift/UIKit/UIRefreshControlExtensions.swift Sources/SwifterSwift/UIKit/UIScrollViewExtensions.swift Sources/SwifterSwift/UIKit/UISearchBarExtensions.swift Sources/SwifterSwift/UIKit/UISegmentedControlExtensions.swift Sources/SwifterSwift/UIKit/UISliderExtensions.swift Sources/SwifterSwift/UIKit/UIStackViewExtensions.swift Sources/SwifterSwift/UIKit/UIStoryboardExtensions.swift Sources/SwifterSwift/UIKit/UISwitchExtensions.swift Sources/SwifterSwift/UIKit/UITabBarExtensions.swift Sources/SwifterSwift/UIKit/UITableViewExtensions.swift Sources/SwifterSwift/UIKit/UITextFieldExtensions.swift Sources/SwifterSwift/UIKit/UITextViewExtensions.swift Sources/SwifterSwift/UIKit/UIViewControllerExtensions.swift Sources/SwifterSwift/UIKit/UIViewExtensions.swift Sources/SwifterSwift/UIKit/UIWindowExtensions.swift Sources/SwifterSwift/WebKit/WKWebViewExtensions.swift SwifterSwift.podspec SwifterSwift.xcodeproj/project.pbxproj SwifterSwift.xcodeproj/project.xcworkspace/contents.xcworkspacedata SwifterSwift.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist SwifterSwift.xcodeproj/xcshareddata/xcschemes/SwifterSwift-iOS.xcscheme SwifterSwift.xcodeproj/xcshareddata/xcschemes/SwifterSwift-macOS.xcscheme SwifterSwift.xcodeproj/xcshareddata/xcschemes/SwifterSwift-tvOS.xcscheme SwifterSwift.xcodeproj/xcshareddata/xcschemes/SwifterSwift-watchOS.xcscheme SwifterSwift.xcworkspace/contents.xcworkspacedata SwifterSwift.xcworkspace/xcshareddata/IDETemplateMacros.plist SwifterSwift.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist SwifterSwift.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings Tests/AppKitTests/NSColorExtensionsTests.swift Tests/AppKitTests/NSImageExtensionsTests.swift Tests/AppKitTests/NSViewExtensionsTests.swift Tests/CoreAnimationTests/CAGradientLayerExtensionsTests.swift Tests/CoreAnimationTests/CATransform3DExtensionsTests.swift Tests/CoreGraphicsTests/CGAffineTransformExtensionsTests.swift Tests/CoreGraphicsTests/CGColorExtensionsTests.swift Tests/CoreGraphicsTests/CGFloatExtensionsTests.swift Tests/CoreGraphicsTests/CGPointExtensionsTests.swift Tests/CoreGraphicsTests/CGRectExtensionsTests.swift Tests/CoreGraphicsTests/CGSizeExtensionsTests.swift Tests/CoreGraphicsTests/CGVectorExtensionsTests.swift Tests/CoreLocationTests/CLLocationArrayExtensionsTests.swift Tests/CoreLocationTests/CLLocationExtensionsTests.swift Tests/CoreLocationTests/CLVisitExtensionsTests.swift Tests/DispatchTests/DispatchQueueExtensionsTests.swift Tests/FoundationTests/CalendarExtensionTest.swift Tests/FoundationTests/DataExtensionsTests.swift Tests/FoundationTests/DateExtensionsTests.swift Tests/FoundationTests/FileManagerExtensionsTests.swift Tests/FoundationTests/LocaleExtensionsTests.swift Tests/FoundationTests/NSAttributedStringExtensionsTests.swift Tests/FoundationTests/NSPredicateExtensionsTests.swift Tests/FoundationTests/NSRegularExpressionExtensionsTests.swift Tests/FoundationTests/NotificationCenterExtensionsTests.swift Tests/FoundationTests/URLExtensionsTests.swift Tests/FoundationTests/URLRequestExtensionsTests.swift Tests/FoundationTests/UserDefaultsExtensionsTests.swift Tests/HealthKitTests/HKActivitySummaryExtensionsTests.swift Tests/Info.plist Tests/MapKitTests/MKMapViewTests.swift Tests/MapKitTests/MKPolylineTests.swift Tests/ResourcesTests/MyViewController.swift Tests/ResourcesTests/TestStoryboard-tvOS.storyboard Tests/ResourcesTests/TestStoryboard.storyboard Tests/ResourcesTests/UICollectionViewCell.xib Tests/ResourcesTests/UIImageView.xib Tests/ResourcesTests/UITableViewCell.xib Tests/ResourcesTests/UITableViewHeaderFooterView.xib Tests/ResourcesTests/big_buck_bunny_720p_1mb.mp4 Tests/ResourcesTests/test.json Tests/ResourcesTests/tvOS/UIImageView.xib Tests/SceneKitTests/SCNBoxExtensionsTests.swift Tests/SceneKitTests/SCNCapsuleExtensionsTests.swift Tests/SceneKitTests/SCNConeExtensionsTests.swift Tests/SceneKitTests/SCNCylinderExtensionsTests.swift Tests/SceneKitTests/SCNGeometryExtensionsTests.swift Tests/SceneKitTests/SCNMaterialExtensionsTests.swift Tests/SceneKitTests/SCNPlaneExtensionsTests.swift Tests/SceneKitTests/SCNShapeExtensionsTests.swift Tests/SceneKitTests/SCNSphereExtensionsTests.swift Tests/SceneKitTests/SCNVector3ExtensionsTests.swift Tests/SharedTests/ColorExtensionsTests.swift Tests/SharedTests/EdgeInsetsExtensionsTests.swift Tests/SpriteKitTests/SKNodeExtensionTests.swift Tests/StoreKitTests/SKProductTests.swift Tests/SwiftStdlibTests/ArrayExtensionsTests.swift Tests/SwiftStdlibTests/BidirectionalCollectionExtensionsTests.swift Tests/SwiftStdlibTests/BinaryFloatingPointExtensionsTests.swift Tests/SwiftStdlibTests/BoolExtensionsTests.swift Tests/SwiftStdlibTests/CharacterExtensionsTests.swift Tests/SwiftStdlibTests/CollectionExtensionsTests.swift Tests/SwiftStdlibTests/ComparableExtensionsTests.swift Tests/SwiftStdlibTests/DecodableExtensionsTests.swift Tests/SwiftStdlibTests/DictionaryExtensionsTests.swift Tests/SwiftStdlibTests/DoubleExtensionsTests.swift Tests/SwiftStdlibTests/FloatExtensionsTests.swift Tests/SwiftStdlibTests/FloatingPointExtensionsTests.swift Tests/SwiftStdlibTests/IntExtensionsTests.swift Tests/SwiftStdlibTests/KeyedDecodingContainerExtensionsTests.swift Tests/SwiftStdlibTests/MutableCollectionTests.swift Tests/SwiftStdlibTests/OptionalExtensionsTests.swift Tests/SwiftStdlibTests/RangeReplaceableCollectionTests.swift Tests/SwiftStdlibTests/SequenceExtensionsTests.swift Tests/SwiftStdlibTests/SignedIntegerExtensionsTests.swift Tests/SwiftStdlibTests/SignedNumericExtensionsTests.swift Tests/SwiftStdlibTests/StringExtensionsTests.swift Tests/SwiftStdlibTests/StringProtocolExtensionsTests.swift Tests/SwiftStdlibTests/TestHelpers.swift Tests/UIKitTests/UIAlertControllerExtensionsTests.swift Tests/UIKitTests/UIBarButtonExtensionsTests.swift Tests/UIKitTests/UIBezierPathExtensionsTests.swift Tests/UIKitTests/UIButtonExtensionsTests.swift Tests/UIKitTests/UICollectionViewExtensionsTests.swift Tests/UIKitTests/UIColorExtensionsTests.swift Tests/UIKitTests/UIDatePickerExtensionsTests.swift Tests/UIKitTests/UIFontExtensionsTests.swift Tests/UIKitTests/UIGestureRecognizerExtensionsTests.swift Tests/UIKitTests/UIImageExtensionsTests.swift Tests/UIKitTests/UIImageViewExtensionsTests.swift Tests/UIKitTests/UILabelExtensionsTests.swift Tests/UIKitTests/UILayoutPriorityExtensionsTests.swift Tests/UIKitTests/UINavigationBarExtensionTests.swift Tests/UIKitTests/UINavigationControllerExtensionsTests.swift Tests/UIKitTests/UINavigationItemExtensionsTests.swift Tests/UIKitTests/UIRefreshControlExntesionsTests.swift Tests/UIKitTests/UIScrollViewExtensionsTests.swift Tests/UIKitTests/UISearchBarExtensionsTests.swift Tests/UIKitTests/UISegmentedControlExtensionsTests.swift Tests/UIKitTests/UISliderExtensionsTests.swift Tests/UIKitTests/UIStackViewExtensionsTests.swift Tests/UIKitTests/UIStoryboardExtensionsTests.swift Tests/UIKitTests/UISwitchExtensionsTests.swift Tests/UIKitTests/UITabBarExtensionsTests.swift Tests/UIKitTests/UITableViewExtensionsTests.swift Tests/UIKitTests/UITextFieldExtensionsTests.swift Tests/UIKitTests/UITextViewExtensionsTests.swift Tests/UIKitTests/UIViewControllerExtensionsTests.swift Tests/UIKitTests/UIViewExtensionsTests.swift Tests/UIKitTests/UIWindowExtensionsTests.swift Tests/WebKitTests/WKWebViewExtensionsTests.swift Tests/XCTest/XCTestExtensions.swift Tests/XCTestTests/XCTestExtensionsTests.swift fastlane/.env fastlane/Appfile fastlane/Fastfile scripts/pre-commit <<<<<< network # path=./SwifterSwift-tvOSTests.xctest.coverage.txt /Users/runner/work/SwifterSwift/SwifterSwift/Tests/CoreAnimationTests/CAGradientLayerExtensionsTests.swift: 1| |// CAGradientLayerExtensionsTests.swift - Copyright 2020 SwifterSwift 2| | 3| |@testable import SwifterSwift 4| |import XCTest 5| | 6| |#if !os(watchOS) && !os(Linux) 7| | 8| |final class CAGradientLayerExtensionsTests: XCTestCase { 9| 1| func testInitWithGradientAttributes() { 10| 1| let colors: [Color] = [.red, .blue, .orange, .yellow] 11| 1| let locations: [CGFloat]? = [0, 0.3, 0.6, 1] 12| 1| let startPoint = CGPoint(x: 0.0, y: 0.5) 13| 1| let endPoint = CGPoint(x: 1.0, y: 0.5) 14| 1| let gradientLayerType = CAGradientLayerType.axial 15| 1| 16| 1| let gradientLayer = CAGradientLayer( 17| 1| colors: colors, 18| 1| locations: locations, 19| 1| startPoint: startPoint, 20| 1| endPoint: endPoint, 21| 1| type: gradientLayerType) 22| 1| 23| 1| XCTAssertEqual(gradientLayer.colors?.count, colors.count) 24| 1| XCTAssertEqual(gradientLayer.locations as? [CGFloat], locations) 25| 1| XCTAssertEqual(gradientLayer.startPoint, startPoint) 26| 1| XCTAssertEqual(gradientLayer.endPoint, endPoint) 27| 1| XCTAssertEqual(gradientLayer.type, gradientLayerType) 28| 1| } 29| |} 30| | 31| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Tests/CoreAnimationTests/CATransform3DExtensionsTests.swift: 1| |// CATransform3DExtensionsTests.swift - Copyright 2020 SwifterSwift 2| | 3| |@testable import SwifterSwift 4| |import XCTest 5| | 6| |#if canImport(QuartzCore) 7| | 8| |#if canImport(CoreGraphics) 9| |import CoreGraphics 10| |#endif 11| | 12| |final class CATransform3DExtensionsTests: XCTestCase { 13| 18| let x = CGFloat(5) 14| 18| let y = CGFloat(10) 15| 18| let z = CGFloat(20) 16| 18| let angle = CGFloat.pi / 3 17| | 18| 14| var translation: CATransform3D { CATransform3DMakeTranslation(x, y, z) } 19| 14| var scale: CATransform3D { CATransform3DMakeScale(x, y, z) } 20| 14| var rotation: CATransform3D { CATransform3DMakeRotation(angle, x, y, z) } 21| | 22| 1| func testIdentity() { 23| 1| XCTAssertEqual(CATransform3D.identity, CATransform3DIdentity) 24| 1| XCTAssertNotEqual(translation, CATransform3D.identity) 25| 1| XCTAssertNotEqual(scale, CATransform3D.identity) 26| 1| XCTAssertNotEqual(rotation, CATransform3D.identity) 27| 1| } 28| | 29| 1| func testCodable() { 30| 1| let transform = translation.concatenating(scale).concatenating(rotation) 31| 1| 32| 1| let encoder = JSONEncoder() 33| 1| var data: Data? 34| 1| XCTAssertNoThrow(data = try encoder.encode(transform)) 35| 1| XCTAssertEqual(data?.isEmpty, false) 36| 1| 37| 1| let decoder = JSONDecoder() 38| 1| var decodedTransform: CATransform3D? 39| 1| XCTAssertNoThrow(decodedTransform = try decoder.decode(CATransform3D.self, from: data!)) 40| 1| 41| 1| let accuracy = CGFloat(0.000001) 42| 1| XCTAssertEqual(transform.m11, decodedTransform!.m11, accuracy: accuracy) 43| 1| XCTAssertEqual(transform.m12, decodedTransform!.m12, accuracy: accuracy) 44| 1| XCTAssertEqual(transform.m13, decodedTransform!.m13, accuracy: accuracy) 45| 1| XCTAssertEqual(transform.m14, decodedTransform!.m14, accuracy: accuracy) 46| 1| XCTAssertEqual(transform.m21, decodedTransform!.m21, accuracy: accuracy) 47| 1| XCTAssertEqual(transform.m22, decodedTransform!.m22, accuracy: accuracy) 48| 1| XCTAssertEqual(transform.m23, decodedTransform!.m23, accuracy: accuracy) 49| 1| XCTAssertEqual(transform.m24, decodedTransform!.m24, accuracy: accuracy) 50| 1| XCTAssertEqual(transform.m31, decodedTransform!.m31, accuracy: accuracy) 51| 1| XCTAssertEqual(transform.m32, decodedTransform!.m32, accuracy: accuracy) 52| 1| XCTAssertEqual(transform.m33, decodedTransform!.m33, accuracy: accuracy) 53| 1| XCTAssertEqual(transform.m34, decodedTransform!.m34, accuracy: accuracy) 54| 1| XCTAssertEqual(transform.m41, decodedTransform!.m41, accuracy: accuracy) 55| 1| XCTAssertEqual(transform.m42, decodedTransform!.m42, accuracy: accuracy) 56| 1| XCTAssertEqual(transform.m43, decodedTransform!.m43, accuracy: accuracy) 57| 1| XCTAssertEqual(transform.m44, decodedTransform!.m44, accuracy: accuracy) 58| 1| } 59| | 60| 1| func testInitTranslation() { 61| 1| XCTAssertEqual(CATransform3D(translationX: x, y: y, z: z), translation) 62| 1| } 63| | 64| 1| func testInitScale() { 65| 1| XCTAssertEqual(CATransform3D(scaleX: x, y: y, z: z), scale) 66| 1| } 67| | 68| 1| func testInitRotation() { 69| 1| XCTAssertEqual(CATransform3D(rotationAngle: angle, x: x, y: y, z: z), rotation) 70| 1| } 71| | 72| 1| func testIsIdentity() { 73| 1| XCTAssert(CATransform3DIdentity.isIdentity) 74| 1| XCTAssertFalse(translation.isIdentity) 75| 1| XCTAssertFalse(scale.isIdentity) 76| 1| XCTAssertFalse(rotation.isIdentity) 77| 1| } 78| | 79| 1| func testTranslated() { 80| 1| XCTAssertEqual(CATransform3DIdentity.translatedBy(x: x, y: y, z: z), translation) 81| 1| } 82| | 83| 1| func testScaled() { 84| 1| XCTAssertEqual(CATransform3DIdentity.scaledBy(x: x, y: y, z: z), scale) 85| 1| } 86| | 87| 1| func testRotated() { 88| 1| XCTAssertEqual(CATransform3DIdentity.rotated(by: angle, x: x, y: y, z: z), rotation) 89| 1| } 90| | 91| 1| func testInverted() { 92| 1| XCTAssertEqual(CATransform3DIdentity, CATransform3DIdentity.inverted()) 93| 1| XCTAssertEqual(translation.inverted(), CATransform3DInvert(translation)) 94| 1| XCTAssertEqual(scale.inverted(), CATransform3DInvert(scale)) 95| 1| XCTAssertEqual(rotation.inverted(), CATransform3DInvert(rotation)) 96| 1| } 97| | 98| 1| func testConcatenated() { 99| 1| XCTAssertEqual(CATransform3DIdentity.concatenating(translation), translation) 100| 1| XCTAssertEqual(CATransform3DIdentity.concatenating(scale), scale) 101| 1| XCTAssertEqual(CATransform3DIdentity.concatenating(rotation), rotation) 102| 1| } 103| | 104| 1| func testTranslate() { 105| 1| var transform = CATransform3DIdentity 106| 1| transform.translateBy(x: x, y: y, z: z) 107| 1| XCTAssertEqual(transform, translation) 108| 1| } 109| | 110| 1| func testScale() { 111| 1| var transform = CATransform3DIdentity 112| 1| transform.scaleBy(x: x, y: y, z: z) 113| 1| XCTAssertEqual(transform, scale) 114| 1| } 115| | 116| 1| func testRotate() { 117| 1| var transform = CATransform3DIdentity 118| 1| transform.rotate(by: angle, x: x, y: y, z: z) 119| 1| XCTAssertEqual(transform, rotation) 120| 1| } 121| | 122| 1| func testInvert() { 123| 1| var transform = CATransform3DIdentity 124| 1| transform.invert() 125| 1| XCTAssertEqual(transform, CATransform3DIdentity) 126| 1| 127| 1| transform = translation 128| 1| transform.invert() 129| 1| XCTAssertEqual(transform, CATransform3DInvert(translation)) 130| 1| 131| 1| transform = scale 132| 1| transform.invert() 133| 1| XCTAssertEqual(transform, CATransform3DInvert(scale)) 134| 1| 135| 1| transform = rotation 136| 1| transform.invert() 137| 1| XCTAssertEqual(transform, CATransform3DInvert(rotation)) 138| 1| } 139| | 140| 1| func testConcatenate() { 141| 1| var transform = CATransform3DIdentity 142| 1| transform.concatenate(translation) 143| 1| XCTAssertEqual(transform, translation) 144| 1| 145| 1| transform = CATransform3DIdentity 146| 1| transform.concatenate(scale) 147| 1| XCTAssertEqual(transform, scale) 148| 1| 149| 1| transform = CATransform3DIdentity 150| 1| transform.concatenate(rotation) 151| 1| XCTAssertEqual(transform, rotation) 152| 1| } 153| | 154| | #if canImport(CoreGraphics) 155| | 156| 1| func testIsAffine() { 157| 1| XCTAssert(CATransform3DIdentity.isAffine) 158| 1| XCTAssertFalse(CATransform3DMakeTranslation(0, 0, 1).isAffine) 159| 1| } 160| | 161| 1| func testAffineTransform() { 162| 1| XCTAssertEqual(CATransform3DIdentity.affineTransform(), CGAffineTransform.identity) 163| 1| XCTAssertEqual( 164| 1| CATransform3DMakeTranslation(x, y, 1).affineTransform(), 165| 1| CGAffineTransform(translationX: x, y: y)) 166| 1| } 167| | 168| | #endif 169| |} 170| | 171| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Tests/CoreGraphicsTests/CGAffineTransformExtensionsTests.swift: 1| |// CGAffineTransformExtensionsTests.swift - Copyright 2020 SwifterSwift 2| | 3| |@testable import SwifterSwift 4| |import XCTest 5| | 6| |#if canImport(CoreGraphics) 7| |import CoreGraphics 8| | 9| |#if canImport(QuartzCore) 10| |import QuartzCore 11| |#endif 12| | 13| |final class CGAffineTransformExtensionsTests: XCTestCase { 14| | #if canImport(QuartzCore) 15| 1| func testTransform3D() { 16| 1| XCTAssertEqual(CGAffineTransform.identity.transform3D(), CATransform3DIdentity) 17| 1| 18| 1| let x = CGFloat(5) 19| 1| let y = CGFloat(10) 20| 1| let angle = CGFloat.pi / 3 21| 1| 22| 1| XCTAssertEqual(CGAffineTransform(translationX: x, y: y).transform3D(), CATransform3DMakeTranslation(x, y, 0)) 23| 1| XCTAssertEqual(CGAffineTransform(scaleX: x, y: y).transform3D(), CATransform3DMakeScale(x, y, 1)) 24| 1| XCTAssertEqual(CGAffineTransform(rotationAngle: angle).transform3D(), CATransform3DMakeRotation(angle, 0, 0, 1)) 25| 1| } 26| | #endif 27| |} 28| | 29| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Tests/CoreGraphicsTests/CGColorExtensionsTests.swift: 1| |// CGColorExtensionsTests.swift - Copyright 2020 SwifterSwift 2| | 3| |@testable import SwifterSwift 4| |import XCTest 5| | 6| |#if canImport(CoreGraphics) 7| |import CoreGraphics 8| | 9| |#if os(macOS) 10| |import AppKit 11| |#else 12| |import UIKit 13| |#endif 14| | 15| |final class CGColorExtensionsTests: XCTestCase { 16| | #if !os(macOS) 17| 1| func testUIColor() { 18| 1| let red = UIColor.red 19| 1| let cgRed = red.cgColor 20| 1| XCTAssertEqual(cgRed.uiColor, red) 21| 1| } 22| | #endif 23| | 24| | #if os(macOS) 25| | func testNSColor() { 26| | let red = NSColor.red 27| | let cgRed = red.cgColor 28| | XCTAssertEqual(cgRed.nsColor, red) 29| | } 30| | #endif 31| |} 32| | 33| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Tests/CoreGraphicsTests/CGFloatExtensionsTests.swift: 1| |// CGFloatExtensionsTests.swift - Copyright 2020 SwifterSwift 2| | 3| |@testable import SwifterSwift 4| |import XCTest 5| | 6| |#if canImport(CoreGraphics) 7| |import CoreGraphics 8| | 9| |final class CGFloatExtensionsTests: XCTestCase { 10| 1| func testAbs() { 11| 1| XCTAssertEqual(CGFloat(-9.3).abs, CGFloat(9.3)) 12| 1| } 13| | 14| 1| func testCeil() { 15| 1| XCTAssertEqual(CGFloat(9.3).ceil, CGFloat(10.0)) 16| 1| } 17| | 18| 1| func testDegreesToRadians() { 19| 1| XCTAssertEqual(CGFloat(180).degreesToRadians, CGFloat.pi) 20| 1| } 21| | 22| 1| func testIsPositive() { 23| 1| XCTAssert(CGFloat(9.3).isPositive) 24| 1| XCTAssertFalse(CGFloat(0).isPositive) 25| 1| XCTAssertFalse(CGFloat(-9.2).isPositive) 26| 1| } 27| | 28| 1| func testIsNegative() { 29| 1| XCTAssert(CGFloat(-9.3).isNegative) 30| 1| XCTAssertFalse(CGFloat(0).isNegative) 31| 1| XCTAssertFalse(CGFloat(9.3).isNegative) 32| 1| } 33| | 34| 1| func testInt() { 35| 1| XCTAssertEqual(CGFloat(9.3).int, Int(9)) 36| 1| } 37| | 38| 1| func testDouble() { 39| 1| XCTAssertEqual(CGFloat(9.3).double, Double(9.3)) 40| 1| } 41| | 42| 1| func testFloat() { 43| 1| XCTAssertEqual(CGFloat(9.3).float, Float(9.3)) 44| 1| } 45| | 46| 1| func testFloor() { 47| 1| XCTAssertEqual(CGFloat(9.3).floor, CGFloat(9.0)) 48| 1| } 49| | 50| 1| func testRadiansToDegrees() { 51| 1| XCTAssertEqual(CGFloat.pi.radiansToDegrees, CGFloat(180)) 52| 1| } 53| |} 54| | 55| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Tests/CoreGraphicsTests/CGPointExtensionsTests.swift: 1| |// CGPointExtensionsTests.swift - Copyright 2020 SwifterSwift 2| | 3| |@testable import SwifterSwift 4| |import XCTest 5| | 6| |#if canImport(CoreGraphics) 7| |import CoreGraphics 8| | 9| |final class CGPointExtensionsTests: XCTestCase { 10| 8| let point1 = CGPoint(x: 10, y: 10) 11| 8| let point2 = CGPoint(x: 30, y: 30) 12| | 13| 1| func testDistanceFromPoint() { 14| 1| let distance = point1.distance(from: point2) 15| 1| XCTAssertEqual(distance, 28.28, accuracy: 0.01) 16| 1| } 17| | 18| 1| func testStaticDistance() { 19| 1| let distance = CGPoint.distance(from: point2, to: point1) 20| 1| XCTAssertEqual(distance, 28.28, accuracy: 0.01) 21| 1| } 22| | 23| 1| func testAdd() { 24| 1| let point = point1 + point2 25| 1| let result = CGPoint(x: 40, y: 40) 26| 1| XCTAssertEqual(point, result) 27| 1| } 28| | 29| 1| func testAddEqual() { 30| 1| var point = point1 31| 1| point += point2 32| 1| let result = CGPoint(x: 40, y: 40) 33| 1| XCTAssertEqual(point, result) 34| 1| } 35| | 36| 1| func testSubtract() { 37| 1| let point = point1 - point2 38| 1| let result = CGPoint(x: -20, y: -20) 39| 1| XCTAssertEqual(point, result) 40| 1| } 41| | 42| 1| func testSubtractEqual() { 43| 1| var point = point1 44| 1| point -= point2 45| 1| let result = CGPoint(x: -20, y: -20) 46| 1| XCTAssertEqual(point, result) 47| 1| } 48| | 49| 1| func testScalarMultiply() { 50| 1| let point = 5 * point1 51| 1| let result = CGPoint(x: 50, y: 50) 52| 1| XCTAssertEqual(point, result) 53| 1| 54| 1| let point2 = 5 * point1 55| 1| let result2 = CGPoint(x: 50, y: 50) 56| 1| XCTAssertEqual(point2, result2) 57| 1| } 58| | 59| 1| func testScalarMultiplyEqual() { 60| 1| var point = point1 61| 1| point *= 5 62| 1| let result = CGPoint(x: 50, y: 50) 63| 1| XCTAssertEqual(point, result) 64| 1| } 65| |} 66| | 67| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Tests/CoreGraphicsTests/CGRectExtensionsTests.swift: 1| |// CGRectExtensionsTests.swift - Copyright 2020 SwifterSwift 2| | 3| |@testable import SwifterSwift 4| |import XCTest 5| | 6| |#if canImport(CoreGraphics) 7| |import CoreGraphics 8| | 9| |final class CGRectExtensionsTests: XCTestCase { 10| 1| func testCenter() { 11| 1| let rect = CGRect(x: 10, y: 20, width: 30, height: 40) 12| 1| let center = rect.center 13| 1| XCTAssertEqual(center.x, 25) 14| 1| XCTAssertEqual(center.y, 40) 15| 1| } 16| | 17| 1| func testInitWithCenterAndSize() { 18| 1| let rect = CGRect(center: CGPoint(x: 10, y: 20), size: CGSize(width: 30, height: 40)) 19| 1| XCTAssertEqual(rect.midX, 10) 20| 1| XCTAssertEqual(rect.midY, 20) 21| 1| XCTAssertEqual(rect.width, 30) 22| 1| XCTAssertEqual(rect.height, 40) 23| 1| } 24| | 25| 1| func testResizingWithAnchor() { 26| 1| let rect = CGRect(x: 10, y: 20, width: 30, height: 40) 27| 1| var resizingRect: CGRect 28| 1| let newSize = CGSize(width: 40, height: 50) 29| 1| 30| 1| // By anchor center 31| 1| resizingRect = rect.resizing(to: newSize, anchor: CGPoint(x: 0.5, y: 0.5)) 32| 1| let rect2 = rect.resizing(to: newSize) 33| 1| XCTAssertEqual(resizingRect, rect2) 34| 1| XCTAssertEqual(rect.midX, resizingRect.midX) 35| 1| XCTAssertEqual(rect.midY, resizingRect.midY) 36| 1| XCTAssertNotEqual(rect.size, resizingRect.size) 37| 1| XCTAssertEqual(newSize, resizingRect.size) 38| 1| 39| 1| // By anchor top left 40| 1| resizingRect = rect.resizing(to: newSize, anchor: .zero) 41| 1| XCTAssertEqual(rect.origin, resizingRect.origin) 42| 1| XCTAssertNotEqual(rect.size, resizingRect.size) 43| 1| XCTAssertEqual(newSize, resizingRect.size) 44| 1| 45| 1| // By anchor top right 46| 1| resizingRect = rect.resizing(to: newSize, anchor: CGPoint(x: 1.0, y: 0.0)) 47| 1| XCTAssertEqual(rect.maxX, resizingRect.maxX) 48| 1| XCTAssertEqual(rect.minY, resizingRect.minY) 49| 1| XCTAssertNotEqual(rect.size, resizingRect.size) 50| 1| XCTAssertEqual(newSize, resizingRect.size) 51| 1| 52| 1| // By anchor bottom left 53| 1| resizingRect = rect.resizing(to: newSize, anchor: CGPoint(x: 0.0, y: 1.0)) 54| 1| XCTAssertEqual(rect.minX, resizingRect.minX) 55| 1| XCTAssertEqual(rect.maxY, resizingRect.maxY) 56| 1| XCTAssertNotEqual(rect.size, resizingRect.size) 57| 1| XCTAssertEqual(newSize, resizingRect.size) 58| 1| 59| 1| // By anchor bottom right 60| 1| resizingRect = rect.resizing(to: newSize, anchor: CGPoint(x: 1.0, y: 1.0)) 61| 1| XCTAssertEqual(rect.maxX, resizingRect.maxX) 62| 1| XCTAssertEqual(rect.maxY, resizingRect.maxY) 63| 1| XCTAssertNotEqual(rect.size, resizingRect.size) 64| 1| XCTAssertEqual(newSize, resizingRect.size) 65| 1| } 66| |} 67| | 68| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Tests/CoreGraphicsTests/CGSizeExtensionsTests.swift: 1| |// CGSizeExtensionsTests.swift - Copyright 2020 SwifterSwift 2| | 3| |@testable import SwifterSwift 4| |import XCTest 5| | 6| |#if canImport(CoreGraphics) 7| |import CoreGraphics 8| | 9| |final class CGSizeExtensionsTests: XCTestCase { 10| 1| func testAspectFit() { 11| 1| let rect = CGSize(width: 120, height: 80) 12| 1| let parentRect = CGSize(width: 100, height: 50) 13| 1| let newRect = rect.aspectFit(to: parentRect) 14| 1| XCTAssertEqual(newRect.width, 75) 15| 1| XCTAssertEqual(newRect.height, 50) 16| 1| } 17| | 18| 1| func testAspectFill() { 19| 1| let rect = CGSize(width: 20, height: 120) 20| 1| let parentRect = CGSize(width: 100, height: 60) 21| 1| let newRect = rect.aspectFill(to: parentRect) 22| 1| XCTAssertEqual(newRect.width, 100) 23| 1| XCTAssertEqual(newRect.height, 60) 24| 1| } 25| | 26| 1| func testAspectRatio() { 27| 1| let size1 = CGSize(width: 10, height: 0) 28| 1| XCTAssertEqual(size1.aspectRatio, 0) 29| 1| 30| 1| let size2 = CGSize(width: 20, height: 10) 31| 1| XCTAssertEqual(size2.aspectRatio, 2) 32| 1| } 33| | 34| 1| func testMaxDimension() { 35| 1| let size1 = CGSize(width: 10, height: 0) 36| 1| XCTAssertEqual(size1.maxDimension, 10) 37| 1| 38| 1| let size2 = CGSize(width: 20, height: 40) 39| 1| XCTAssertEqual(size2.maxDimension, 40) 40| 1| } 41| | 42| 1| func testMinDimension() { 43| 1| let size1 = CGSize(width: 10, height: 0) 44| 1| XCTAssertEqual(size1.minDimension, 0) 45| 1| 46| 1| let size2 = CGSize(width: 20, height: 40) 47| 1| XCTAssertEqual(size2.minDimension, 20) 48| 1| } 49| | 50| 1| func testAdd() { 51| 1| let sizeA = CGSize(width: 5, height: 10) 52| 1| let sizeB = CGSize(width: 3, height: 4) 53| 1| let result = sizeA + sizeB 54| 1| XCTAssertEqual(result.width, 8) 55| 1| XCTAssertEqual(result.height, 14) 56| 1| } 57| | 58| 1| func testAddTuple() { 59| 1| let size = CGSize(width: 5, height: 10) 60| 1| let result = size + (width: 4, height: 4) 61| 1| XCTAssertEqual(result.width, 9) 62| 1| XCTAssertEqual(result.height, 14) 63| 1| } 64| | 65| 1| func testAddEqual() { 66| 1| var sizeA = CGSize(width: 5, height: 10) 67| 1| let sizeB = CGSize(width: 3, height: 4) 68| 1| sizeA += sizeB 69| 1| XCTAssertEqual(sizeA.width, 8) 70| 1| XCTAssertEqual(sizeA.height, 14) 71| 1| } 72| | 73| 1| func testAddEqualTuple() { 74| 1| var size = CGSize(width: 5, height: 10) 75| 1| size += (3, 0) 76| 1| XCTAssertEqual(size.width, 8) 77| 1| XCTAssertEqual(size.height, 10) 78| 1| } 79| | 80| 1| func testSubtract() { 81| 1| let sizeA = CGSize(width: 5, height: 10) 82| 1| let sizeB = CGSize(width: 3, height: 4) 83| 1| let result = sizeA - sizeB 84| 1| XCTAssertEqual(result.width, 2) 85| 1| XCTAssertEqual(result.height, 6) 86| 1| } 87| | 88| 1| func testSubtractTuple() { 89| 1| let size = CGSize(width: 5, height: 10) 90| 1| let result = size - (2, 3) 91| 1| XCTAssertEqual(result.width, 3) 92| 1| XCTAssertEqual(result.height, 7) 93| 1| } 94| | 95| 1| func testSubtractEqual() { 96| 1| var sizeA = CGSize(width: 5, height: 10) 97| 1| let sizeB = CGSize(width: 3, height: 4) 98| 1| sizeA -= sizeB 99| 1| XCTAssertEqual(sizeA.width, 2) 100| 1| XCTAssertEqual(sizeA.height, 6) 101| 1| } 102| | 103| 1| func testSubtractEqualTuple() { 104| 1| var size = CGSize(width: 5, height: 10) 105| 1| size -= (1, 4) 106| 1| XCTAssertEqual(size.width, 4) 107| 1| XCTAssertEqual(size.height, 6) 108| 1| } 109| | 110| 1| func testMultiplyCGSize() { 111| 1| let sizeA = CGSize(width: 5, height: 10) 112| 1| let sizeB = CGSize(width: 3, height: 4) 113| 1| let result = sizeA * sizeB 114| 1| XCTAssertEqual(result.width, 15) 115| 1| XCTAssertEqual(result.height, 40) 116| 1| } 117| | 118| 1| func testMultiplyScalarRight() { 119| 1| let sizeA = CGSize(width: 5, height: 10) 120| 1| let result = sizeA * 4 121| 1| XCTAssertEqual(result.width, 20) 122| 1| XCTAssertEqual(result.height, 40) 123| 1| } 124| | 125| 1| func testMultiplyScalarLeft() { 126| 1| let sizeA = CGSize(width: 5, height: 10) 127| 1| let result = 5 * sizeA 128| 1| XCTAssertEqual(result.width, 25) 129| 1| XCTAssertEqual(result.height, 50) 130| 1| } 131| | 132| 1| func testMultiplyEqualCGSize() { 133| 1| var sizeA = CGSize(width: 5, height: 10) 134| 1| let sizeB = CGSize(width: 3, height: 4) 135| 1| sizeA *= sizeB 136| 1| XCTAssertEqual(sizeA.width, 15) 137| 1| XCTAssertEqual(sizeA.height, 40) 138| 1| } 139| | 140| 1| func testMultiplyEqualScalar() { 141| 1| var sizeA = CGSize(width: 5, height: 0) 142| 1| sizeA *= 4 143| 1| XCTAssertEqual(sizeA.width, 20) 144| 1| XCTAssertEqual(sizeA.height, 0) 145| 1| } 146| |} 147| | 148| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Tests/CoreGraphicsTests/CGVectorExtensionsTests.swift: 1| |// CGVectorExtensionsTests.swift - Copyright 2020 SwifterSwift 2| | 3| |@testable import SwifterSwift 4| |import XCTest 5| | 6| |#if canImport(CoreGraphics) 7| |import CoreGraphics 8| | 9| |final class CGVectorExtensionsTests: XCTestCase { 10| 1| func testAngle() { 11| 1| let vector1 = CGVector(dx: 1, dy: 1) // pi/4 12| 1| let vector2 = CGVector(dx: 1, dy: 0) // 0 13| 1| let vector3 = CGVector(dx: 0, dy: 1) // pi/2 14| 1| let vector4 = CGVector(dx: -1, dy: 0) // pi 15| 1| let vector5 = CGVector(dx: 0, dy: -1) // -pi/2 16| 1| let vector6 = CGVector(dx: -1, dy: -1) // -3pi/4 17| 1| let vector7 = CGVector(dx: 1, dy: -1) // -pi/4 18| 1| let vector8 = CGVector(dx: -1, dy: 1) // 3pi/4 19| 1| 20| 1| XCTAssertEqual(vector1.angle, .pi / 4) 21| 1| XCTAssertEqual(vector2.angle, 0) 22| 1| XCTAssertEqual(vector3.angle, .pi / 2) 23| 1| XCTAssertEqual(vector4.angle, .pi) 24| 1| XCTAssertEqual(vector5.angle, -(.pi / 2)) 25| 1| XCTAssertEqual(vector6.angle, -(3 * .pi / 4)) 26| 1| XCTAssertEqual(vector7.angle, -(.pi / 4)) 27| 1| XCTAssertEqual(vector8.angle, 3 * .pi / 4) 28| 1| } 29| | 30| 1| func testMagnitude() { 31| 1| let vector1 = CGVector(dx: 3, dy: 4) 32| 1| let vector2 = CGVector(dx: 1, dy: 1) 33| 1| let vector3 = CGVector(dx: 20, dy: 0) 34| 1| let vector4 = CGVector(dx: 5, dy: 12) 35| 1| let vector5 = CGVector(dx: 8, dy: 15) 36| 1| 37| 1| XCTAssertEqual(vector1.magnitude, 5) 38| 1| XCTAssertEqual(vector2.magnitude, sqrt(2)) 39| 1| XCTAssertEqual(vector3.magnitude, 20) 40| 1| XCTAssertEqual(vector4.magnitude, 13) 41| 1| XCTAssertEqual(vector5.magnitude, 17) 42| 1| } 43| | 44| 1| func testScalarMultiplication() { 45| 1| let vector = CGVector(dx: 3, dy: 4) 46| 1| 47| 1| XCTAssertEqual(2 * vector, vector * 2) 48| 1| XCTAssertEqual(0 * vector, .zero) 49| 1| XCTAssertEqual(1 * vector, vector) 50| 1| XCTAssertEqual(-1 * vector, CGVector(dx: -3, dy: -4)) 51| 1| XCTAssertEqual(3 * vector, CGVector(dx: 9, dy: 12)) 52| 1| 53| 1| var mutableVector = CGVector(dx: 3, dy: 4) 54| 1| 55| 1| XCTAssertEqual(vector, mutableVector) 56| 1| 57| 1| mutableVector *= 5 58| 1| 59| 1| XCTAssertEqual(mutableVector, CGVector(dx: 15, dy: 20)) 60| 1| XCTAssertEqual(mutableVector, vector * 5) 61| 1| } 62| | 63| 1| func testNegation() { 64| 1| let vector = CGVector(dx: 3, dy: -4) 65| 1| 66| 1| XCTAssertEqual(-vector, CGVector(dx: -3, dy: 4)) 67| 1| } 68| | 69| 1| func testInitWithAngleAndMagnitude() { 70| 1| let vector1 = CGVector(angle: .pi / 4, magnitude: sqrt(2)) 71| 1| let vector2 = CGVector(angle: .pi, magnitude: 1) 72| 1| let vector3 = CGVector(angle: .pi / 6, magnitude: 2) 73| 1| let vector4 = CGVector(angle: .pi / 3, magnitude: 2) 74| 1| 75| 1| let cgFloatPrecision: CGFloat = 0.000000000000001 76| 1| 77| 1| XCTAssertEqual(vector1.dx, 1, accuracy: cgFloatPrecision) 78| 1| XCTAssertEqual(vector1.dy, 1, accuracy: cgFloatPrecision) 79| 1| 80| 1| XCTAssertEqual(vector2.dx, -1, accuracy: cgFloatPrecision) 81| 1| XCTAssertEqual(vector2.dy, 0, accuracy: cgFloatPrecision) 82| 1| 83| 1| XCTAssertEqual(vector3.dx, sqrt(3), accuracy: cgFloatPrecision) 84| 1| XCTAssertEqual(vector3.dy, 1, accuracy: cgFloatPrecision) 85| 1| 86| 1| XCTAssertEqual(vector4.dx, 1, accuracy: cgFloatPrecision) 87| 1| XCTAssertEqual(vector4.dy, sqrt(3), accuracy: cgFloatPrecision) 88| 1| } 89| |} 90| | 91| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Tests/CoreLocationTests/CLLocationArrayExtensionsTests.swift: 1| |// CLLocationArrayExtensionsTests.swift - Copyright 2020 SwifterSwift 2| | 3| |@testable import SwifterSwift 4| |import XCTest 5| | 6| |#if canImport(CoreLocation) 7| |import CoreLocation 8| | 9| |final class CLLocationArrayExtensionsTests: XCTestCase { 10| | @available(tvOS 10.0, macOS 10.12, watchOS 3.0, *) 11| 1| func testDistance() { 12| 1| let locations1 = [CLLocation]() 13| 1| let locations2 = [ 14| 1| CLLocation(latitude: 41.0, longitude: -111.0) 15| 1| ] 16| 1| let locations3 = [ 17| 1| CLLocation(latitude: 41.0, longitude: -111.0), 18| 1| CLLocation(latitude: 41.0, longitude: -112.0), 19| 1| CLLocation(latitude: 41.0, longitude: -113.0) 20| 1| ] 21| 1| 22| 1| XCTAssertEqual(locations1.distance(unitLength: .kilometers).value, 0.0, accuracy: 0.01) 23| 1| XCTAssertEqual(locations2.distance(unitLength: .kilometers).value, 0.0, accuracy: 0.01) 24| 1| XCTAssertEqual(locations3.distance(unitLength: .kilometers).value, 168.27, accuracy: 0.01) 25| 1| } 26| |} 27| | 28| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Tests/CoreLocationTests/CLLocationExtensionsTests.swift: 1| |// CLLocationExtensionsTests.swift - Copyright 2020 SwifterSwift 2| | 3| |@testable import SwifterSwift 4| |import XCTest 5| | 6| |#if canImport(CoreLocation) 7| |import CoreLocation 8| | 9| |final class CLLocationExtensionsTests: XCTestCase { 10| 1| func testMidLocation() { 11| 1| let aLoc = CLLocation(latitude: -15.822877, longitude: -47.941839) 12| 1| let bLoc = CLLocation(latitude: -15.692030, longitude: -47.594397) 13| 1| let mid = CLLocation.midLocation(start: aLoc, end: bLoc) 14| 1| 15| 1| XCTAssertEqual(mid.coordinate.latitude, -15.7575223324019, accuracy: 0.0000000000001) 16| 1| XCTAssertEqual(mid.coordinate.longitude, -47.7680620274339, accuracy: 0.0000000000001) 17| 1| 18| 1| XCTAssertEqual(aLoc.midLocation(to: bLoc).coordinate.latitude, -15.7575223324019, accuracy: 0.0000000000001) 19| 1| XCTAssertEqual(aLoc.midLocation(to: bLoc).coordinate.longitude, -47.7680620274339, accuracy: 0.0000000000001) 20| 1| } 21| | 22| 1| func testBearing() { 23| 1| let aLoc = CLLocation(latitude: 38.6318909290283, longitude: -90.2828979492187) 24| 1| let bLoc = CLLocation(latitude: 38.5352759115441, longitude: -89.8448181152343) 25| 1| let bearing = aLoc.bearing(to: bLoc) 26| 1| 27| 1| XCTAssertEqual(bearing, 105.619, accuracy: 0.001) 28| 1| } 29| |} 30| | 31| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Tests/DispatchTests/DispatchQueueExtensionsTests.swift: 1| |// DispatchQueueExtensionsTests.swift - Copyright 2020 SwifterSwift 2| | 3| |@testable import SwifterSwift 4| |import XCTest 5| | 6| |#if canImport(Dispatch) 7| |import Dispatch 8| | 9| |final class DispatchQueueExtensionsTests: XCTestCase { 10| 1| func testIsMainQueue() { 11| 1| let expect = expectation(description: "isMainQueue") 12| 1| let group = DispatchGroup() 13| 1| 14| 1| DispatchQueue.main.async(group: group) { 15| 1| XCTAssert(DispatchQueue.isMainQueue) 16| 1| } 17| 1| DispatchQueue.global().async(group: group) { 18| 1| XCTAssertFalse(DispatchQueue.isMainQueue) 19| 1| } 20| 1| 21| 1| group.notify(queue: .main) { 22| 1| expect.fulfill() 23| 1| } 24| 1| 25| 1| waitForExpectations(timeout: 0.5) 26| 1| } 27| | 28| 1| func testIsCurrent() { 29| 1| let expect = expectation(description: "isCurrent") 30| 1| let group = DispatchGroup() 31| 1| let queue = DispatchQueue.global() 32| 1| 33| 1| queue.async(group: group) { 34| 1| XCTAssert(DispatchQueue.isCurrent(queue)) 35| 1| } 36| 1| DispatchQueue.main.async(group: group) { 37| 1| XCTAssert(DispatchQueue.isCurrent(DispatchQueue.main)) 38| 1| XCTAssertFalse(DispatchQueue.isCurrent(queue)) 39| 1| } 40| 1| 41| 1| group.notify(queue: .main) { 42| 1| expect.fulfill() 43| 1| } 44| 1| 45| 1| waitForExpectations(timeout: 0.5) 46| 1| } 47| | 48| 1| func testAsyncAfter() { 49| 1| let delay = TimeInterval(2) 50| 1| let codeShouldBeExecuted = expectation(description: "Executed") 51| 1| 52| 1| DispatchQueue.main.asyncAfter(delay: delay) { 53| 1| codeShouldBeExecuted.fulfill() 54| 1| } 55| 1| 56| 1| waitForExpectations(timeout: delay + 1) 57| 1| } 58| | 59| 1| func testDebounce() { 60| 1| var value = 0 61| 1| let done = expectation(description: "Execute block after delay") 62| 1| 63| 1| let delay = 0.02 64| 1| let debouncedIncrementor = DispatchQueue.main.debounce(delay: delay) { 65| 1| value += 1 66| 1| } 67| 1| 68| 10| for _ in 1...10 { 69| 10| debouncedIncrementor() 70| 10| } 71| 1| 72| 1| DispatchQueue.main.asyncAfter(deadline: .now() + delay) { 73| 1| done.fulfill() 74| 1| } 75| 1| 76| 1| XCTAssertEqual(value, 0, "Debounced function was executed right away") 77| 1| 78| 1| waitForExpectations(timeout: 2.5) { _ in 79| 1| XCTAssertEqual(value, 1, "Value was incremented more once") 80| 1| } 81| 1| } 82| |} 83| | 84| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Tests/FoundationTests/CalendarExtensionTest.swift: 1| |// CalendarExtensionTest.swift - Copyright 2020 SwifterSwift 2| | 3| |@testable import SwifterSwift 4| |import XCTest 5| | 6| |#if canImport(Foundation) 7| |import Foundation 8| | 9| |class CalendarExtensionTests: XCTestCase { 10| 1| func testNumberOfDaysInAMonth() { 11| 1| let calendar = Calendar(identifier: .gregorian) 12| 1| let longMonths = [1, 3, 5, 7, 8, 10, 12] 13| 1| let shortMonths = [4, 6, 9, 11] 14| 1| let febDateComponent = DateComponents(year: 2015, month: 2) 15| 1| let febDate = calendar.date(from: febDateComponent)! 16| 1| let leapYearDateComponent = DateComponents(year: 2020, month: 2) 17| 1| let leapYearDate = calendar.date(from: leapYearDateComponent)! 18| 7| let longMonthsDateComponents = longMonths.map { DateComponents(year: 2015, month: $0) } 19| 4| let shortMonthsDateComponents = shortMonths.map { DateComponents(year: 2015, month: $0) } 20| 7| let longMonthDates = longMonthsDateComponents.compactMap { calendar.date(from: $0) } 21| 4| let shortMonthDates = shortMonthsDateComponents.compactMap { calendar.date(from: $0) } 22| 7| longMonthDates.forEach { XCTAssertEqual(calendar.numberOfDaysInMonth(for: $0), 31) } 23| 4| shortMonthDates.forEach { XCTAssertEqual(calendar.numberOfDaysInMonth(for: $0), 30) } 24| 1| XCTAssertEqual(calendar.numberOfDaysInMonth(for: febDate), 28) 25| 1| XCTAssertEqual(calendar.numberOfDaysInMonth(for: leapYearDate), 29) 26| 1| } 27| |} 28| | 29| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Tests/FoundationTests/DataExtensionsTests.swift: 1| |// DataExtensionsTests.swift - Copyright 2020 SwifterSwift 2| | 3| |@testable import SwifterSwift 4| |import XCTest 5| | 6| |#if canImport(Foundation) 7| |import Foundation 8| | 9| |final class DataExtensionsTests: XCTestCase { 10| 1| func testString() { 11| 1| let dataFromString = "hello".data(using: .utf8) 12| 1| XCTAssertNotNil(dataFromString) 13| 1| XCTAssertNotNil(dataFromString?.string(encoding: .utf8)) 14| 1| XCTAssertEqual(dataFromString?.string(encoding: .utf8), "hello") 15| 1| } 16| | 17| 1| func testBytes() { 18| 1| let dataFromString = "hello".data(using: .utf8) 19| 1| let bytes = dataFromString?.bytes 20| 1| XCTAssertNotNil(bytes) 21| 1| XCTAssertEqual(bytes?.count, 5) 22| 1| } 23| | 24| 1| func testJsonObject() { 25| 1| let invalidData = "hello".data(using: .utf8) 26| 1| XCTAssertThrowsError(try invalidData?.jsonObject()) 27| 1| XCTAssertThrowsError(try invalidData?.jsonObject(options: [.allowFragments])) 28| 1| 29| 1| let stringData = "\"hello\"".data(using: .utf8) 30| 1| XCTAssertThrowsError(try stringData?.jsonObject()) 31| 1| XCTAssertEqual((try? stringData?.jsonObject(options: [.allowFragments])) as? String, "hello") 32| 1| 33| 1| let objectData = "{\"message\": \"hello\"}".data(using: .utf8) 34| 1| let object = (try? objectData?.jsonObject()) as? [String: String] 35| 1| XCTAssertNotNil(object) 36| 1| XCTAssertEqual(object?["message"], "hello") 37| 1| 38| 1| let arrayData = "[\"hello\"]".data(using: .utf8) 39| 1| let array = (try? arrayData?.jsonObject()) as? [String] 40| 1| XCTAssertNotNil(array) 41| 1| XCTAssertEqual(array?.first, "hello") 42| 1| } 43| |} 44| | 45| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Tests/FoundationTests/DateExtensionsTests.swift: 1| |// DateExtensionsTests.swift - Copyright 2020 SwifterSwift 2| | 3| |@testable import SwifterSwift 4| |import XCTest 5| | 6| |#if canImport(Foundation) 7| |import Foundation 8| | 9| |// swiftlint:disable:next type_body_length 10| |final class DateExtensionsTests: XCTestCase { 11| 58| override func setUp() { 12| 58| super.setUp() 13| 58| NSTimeZone.default = TimeZone(abbreviation: "UTC")! 14| 58| } 15| | 16| | // swiftlint:disable:next cyclomatic_complexity 17| 1| func testCalendar() { 18| 1| switch Calendar.current.identifier { 19| 1| case .buddhist: 20| 0| XCTAssertEqual(Date().calendar.identifier, Calendar(identifier: .buddhist).identifier) 21| 1| case .chinese: 22| 0| XCTAssertEqual(Date().calendar.identifier, Calendar(identifier: .chinese).identifier) 23| 1| case .coptic: 24| 0| XCTAssertEqual(Date().calendar.identifier, Calendar(identifier: .coptic).identifier) 25| 1| case .ethiopicAmeteAlem: 26| 0| XCTAssertEqual(Date().calendar.identifier, Calendar(identifier: .ethiopicAmeteAlem).identifier) 27| 1| case .ethiopicAmeteMihret: 28| 0| XCTAssertEqual(Date().calendar.identifier, Calendar(identifier: .ethiopicAmeteMihret).identifier) 29| 1| case .gregorian: 30| 1| XCTAssertEqual(Date().calendar.identifier, Calendar(identifier: .gregorian).identifier) 31| 1| case .hebrew: 32| 0| XCTAssertEqual(Date().calendar.identifier, Calendar(identifier: .hebrew).identifier) 33| 1| case .indian: 34| 0| XCTAssertEqual(Date().calendar.identifier, Calendar(identifier: .indian).identifier) 35| 1| case .islamic: 36| 0| XCTAssertEqual(Date().calendar.identifier, Calendar(identifier: .islamic).identifier) 37| 1| case .islamicCivil: 38| 0| XCTAssertEqual(Date().calendar.identifier, Calendar(identifier: .islamicCivil).identifier) 39| 1| case .islamicTabular: 40| 0| XCTAssertEqual(Date().calendar.identifier, Calendar(identifier: .islamicTabular).identifier) 41| 1| case .islamicUmmAlQura: 42| 0| XCTAssertEqual(Date().calendar.identifier, Calendar(identifier: .islamicUmmAlQura).identifier) 43| 1| case .iso8601: 44| 0| XCTAssertEqual(Date().calendar.identifier, Calendar(identifier: .iso8601).identifier) 45| 1| case .japanese: 46| 0| XCTAssertEqual(Date().calendar.identifier, Calendar(identifier: .japanese).identifier) 47| 1| case .persian: 48| 0| XCTAssertEqual(Date().calendar.identifier, Calendar(identifier: .persian).identifier) 49| 1| case .republicOfChina: 50| 0| XCTAssertEqual(Date().calendar.identifier, Calendar(identifier: .republicOfChina).identifier) 51| 1| @unknown default: 52| 0| break 53| 1| } 54| 1| } 55| | 56| 1| func testEra() { 57| 1| let date = Date(timeIntervalSince1970: 0) 58| 1| XCTAssertEqual(date.era, 1) 59| 1| } 60| | 61| 1| func testQuarter() { 62| 1| #if !os(Linux) 63| 1| let date1 = Date(timeIntervalSince1970: 0) 64| 1| XCTAssertEqual(date1.quarter, 1) 65| 1| 66| 1| let date2 = Calendar.current.date(byAdding: .month, value: 4, to: date1) 67| 1| XCTAssertEqual(date2?.quarter, 2) 68| 1| 69| 1| let date3 = Calendar.current.date(byAdding: .month, value: 8, to: date1) 70| 1| XCTAssertEqual(date3?.quarter, 3) 71| 1| 72| 1| let date4 = Calendar.current.date(byAdding: .month, value: 11, to: date1) 73| 1| XCTAssertEqual(date4?.quarter, 4) 74| 1| #endif 75| 1| } 76| | 77| 1| func testWeekOfYear() { 78| 1| let date = Date(timeIntervalSince1970: 0) 79| 1| XCTAssertEqual(date.weekOfYear, 1) 80| 1| 81| 1| let dateAfter7Days = Calendar.current.date(byAdding: .day, value: 7, to: date) 82| 1| XCTAssertEqual(dateAfter7Days?.weekOfYear, 2) 83| 1| 84| 1| let originalDate = Calendar.current.date(byAdding: .day, value: -7, to: dateAfter7Days!) 85| 1| XCTAssertEqual(originalDate?.weekOfYear, 1) 86| 1| } 87| | 88| 1| func testWeekOfMonth() { 89| 1| let date = Date(timeIntervalSince1970: 0) 90| 1| XCTAssertEqual(date.weekOfMonth, 1) 91| 1| 92| 1| let dateAfter7Days = Calendar.current.date(byAdding: .day, value: 7, to: date) 93| 1| XCTAssertEqual(dateAfter7Days?.weekOfMonth, 2) 94| 1| 95| 1| let originalDate = Calendar.current.date(byAdding: .day, value: -7, to: dateAfter7Days!) 96| 1| XCTAssertEqual(originalDate?.weekOfMonth, 1) 97| 1| } 98| | 99| 1| func testYear() { 100| 1| var date = Date(timeIntervalSince1970: 100_000.123450040) 101| 1| XCTAssertEqual(date.year, 1970) 102| 1| 103| 5| var isLowerComponentsValid: Bool { 104| 5| guard date.month == 1 else { return false } 105| 5| guard date.day == 2 else { return false } 106| 5| guard date.hour == 3 else { return false } 107| 5| guard date.minute == 46 else { return false } 108| 5| guard date.second == 40 else { return false } 109| 5| guard date.nanosecond == 123_450_040 else { return false } 110| 5| return true 111| 5| } 112| 1| 113| 1| date.year = 2000 114| 1| XCTAssertEqual(date.year, 2000) 115| 1| XCTAssert(isLowerComponentsValid) 116| 1| 117| 1| date.year = 2017 118| 1| XCTAssertEqual(date.year, 2017) 119| 1| XCTAssert(isLowerComponentsValid) 120| 1| 121| 1| date.year = 1988 122| 1| XCTAssertEqual(date.year, 1988) 123| 1| XCTAssert(isLowerComponentsValid) 124| 1| 125| 1| date.year = -100 126| 1| XCTAssertEqual(date.year, 1988) 127| 1| XCTAssert(isLowerComponentsValid) 128| 1| 129| 1| date.year = 0 130| 1| XCTAssertEqual(date.year, 1988) 131| 1| XCTAssert(isLowerComponentsValid) 132| 1| } 133| | 134| 1| func testMonth() { 135| 1| var date = Date(timeIntervalSince1970: 100_000.123450040) 136| 1| XCTAssertEqual(date.month, 1) 137| 1| 138| 5| var isLowerComponentsValid: Bool { 139| 5| guard date.day == 2 else { return false } 140| 5| guard date.hour == 3 else { return false } 141| 5| guard date.minute == 46 else { return false } 142| 5| guard date.second == 40 else { return false } 143| 5| guard date.nanosecond == 123_450_040 else { return false } 144| 5| return true 145| 5| } 146| 1| 147| 1| date.month = 2 148| 1| XCTAssert(isLowerComponentsValid) 149| 1| 150| 1| date.month = 14 151| 1| XCTAssertEqual(date.month, 2) 152| 1| XCTAssert(isLowerComponentsValid) 153| 1| 154| 1| date.month = 1 155| 1| XCTAssertEqual(date.month, 1) 156| 1| XCTAssert(isLowerComponentsValid) 157| 1| 158| 1| date.month = 0 159| 1| XCTAssertEqual(date.month, 1) 160| 1| XCTAssert(isLowerComponentsValid) 161| 1| 162| 1| date.month = -3 163| 1| XCTAssertEqual(date.month, 1) 164| 1| XCTAssert(isLowerComponentsValid) 165| 1| } 166| | 167| 1| func testDay() { 168| 1| var date = Date(timeIntervalSince1970: 100_000.123450040) 169| 1| XCTAssertEqual(date.day, 2) 170| 1| 171| 5| var isLowerComponentsValid: Bool { 172| 5| guard date.hour == 3 else { return false } 173| 5| guard date.minute == 46 else { return false } 174| 5| guard date.second == 40 else { return false } 175| 5| guard date.nanosecond == 123_450_040 else { return false } 176| 5| return true 177| 5| } 178| 1| 179| 1| date.day = 4 180| 1| XCTAssertEqual(date.day, 4) 181| 1| XCTAssert(isLowerComponentsValid) 182| 1| 183| 1| date.day = 1 184| 1| XCTAssertEqual(date.day, 1) 185| 1| XCTAssert(isLowerComponentsValid) 186| 1| 187| 1| date.day = 0 188| 1| XCTAssertEqual(date.day, 1) 189| 1| XCTAssert(isLowerComponentsValid) 190| 1| 191| 1| date.day = -3 192| 1| XCTAssertEqual(date.day, 1) 193| 1| XCTAssert(isLowerComponentsValid) 194| 1| 195| 1| date.day = 45 196| 1| XCTAssertEqual(date.day, 1) 197| 1| XCTAssert(isLowerComponentsValid) 198| 1| } 199| | 200| 1| func testWeekday() { 201| 1| let date = Date(timeIntervalSince1970: 100_000) 202| 1| XCTAssertEqual(date.weekday, 6) 203| 1| } 204| | 205| 1| func testHour() { 206| 1| var date = Date(timeIntervalSince1970: 100_000.123450040) 207| 1| XCTAssertEqual(date.hour, 3) 208| 1| 209| 4| var isLowerComponentsValid: Bool { 210| 4| guard date.minute == 46 else { return false } 211| 4| guard date.second == 40 else { return false } 212| 4| guard date.nanosecond == 123_450_040 else { return false } 213| 4| return true 214| 4| } 215| 1| 216| 1| date.hour = -3 217| 1| XCTAssertEqual(date.hour, 3) 218| 1| XCTAssert(isLowerComponentsValid) 219| 1| 220| 1| date.hour = 25 221| 1| XCTAssertEqual(date.hour, 3) 222| 1| XCTAssert(isLowerComponentsValid) 223| 1| 224| 1| date.hour = 4 225| 1| XCTAssertEqual(date.hour, 4) 226| 1| XCTAssert(isLowerComponentsValid) 227| 1| 228| 1| date.hour = 1 229| 1| XCTAssertEqual(date.hour, 1) 230| 1| XCTAssert(isLowerComponentsValid) 231| 1| } 232| | 233| 1| func testMinute() { 234| 1| var date = Date(timeIntervalSince1970: 100_000.123450040) 235| 1| XCTAssertEqual(date.minute, 46) 236| 1| 237| 4| var isLowerComponentsValid: Bool { 238| 4| guard date.second == 40 else { return false } 239| 4| guard date.nanosecond == 123_450_040 else { return false } 240| 4| return true 241| 4| } 242| 1| 243| 1| date.minute = -3 244| 1| XCTAssertEqual(date.minute, 46) 245| 1| XCTAssert(isLowerComponentsValid) 246| 1| 247| 1| date.minute = 71 248| 1| XCTAssertEqual(date.minute, 46) 249| 1| XCTAssert(isLowerComponentsValid) 250| 1| 251| 1| date.minute = 4 252| 1| XCTAssertEqual(date.minute, 4) 253| 1| XCTAssert(isLowerComponentsValid) 254| 1| 255| 1| date.minute = 1 256| 1| XCTAssertEqual(date.minute, 1) 257| 1| XCTAssert(isLowerComponentsValid) 258| 1| } 259| | 260| 1| func testSecond() { 261| 1| var date = Date(timeIntervalSince1970: 100_000.123450040) 262| 1| XCTAssertEqual(date.second, 40) 263| 1| 264| 4| var isLowerComponentsValid: Bool { 265| 4| guard date.nanosecond == 123_450_040 else { return false } 266| 4| return true 267| 4| } 268| 1| 269| 1| date.second = -3 270| 1| XCTAssertEqual(date.second, 40) 271| 1| XCTAssert(isLowerComponentsValid) 272| 1| 273| 1| date.second = 71 274| 1| XCTAssertEqual(date.second, 40) 275| 1| XCTAssert(isLowerComponentsValid) 276| 1| 277| 1| date.second = 12 278| 1| XCTAssertEqual(date.second, 12) 279| 1| XCTAssert(isLowerComponentsValid) 280| 1| 281| 1| date.second = 1 282| 1| XCTAssertEqual(date.second, 1) 283| 1| XCTAssert(isLowerComponentsValid) 284| 1| } 285| | 286| 1| func testNanosecond() { 287| 1| var date = Date(timeIntervalSince1970: 100_000.123450040) 288| 1| XCTAssertEqual(date.nanosecond, 123_450_040) 289| 1| 290| 1| date.nanosecond = -3 291| 1| XCTAssertEqual(date.nanosecond, 123_450_040) 292| 1| 293| 1| date.nanosecond = 10000 294| 1| XCTAssert(date.nanosecond >= 1000) 295| 1| XCTAssert(date.nanosecond <= 100_000) 296| 1| } 297| | 298| 1| func testMillisecond() { 299| 1| var date = Date(timeIntervalSince1970: 0) 300| 1| XCTAssertEqual(date.millisecond, 0) 301| 1| 302| 1| date.millisecond = -3 303| 1| XCTAssertEqual(date.millisecond, 0) 304| 1| 305| 1| date.millisecond = 10 306| 1| XCTAssert(date.millisecond >= 9) 307| 1| XCTAssert(date.millisecond <= 11) 308| 1| 309| 1| date.millisecond = 3 310| 1| XCTAssert(date.millisecond >= 2) 311| 1| XCTAssert(date.millisecond <= 4) 312| 1| } 313| | 314| 1| func testIsInFuture() { 315| 1| let oldDate = Date(timeIntervalSince1970: 512) // 1970-01-01T00:08:32.000Z 316| 1| let futureDate = Date(timeIntervalSinceNow: 512) 317| 1| 318| 1| XCTAssert(futureDate.isInFuture) 319| 1| XCTAssertFalse(oldDate.isInFuture) 320| 1| } 321| | 322| 1| func testIsInPast() { 323| 1| let oldDate = Date(timeIntervalSince1970: 512) // 1970-01-01T00:08:32.000Z 324| 1| let futureDate = Date(timeIntervalSinceNow: 512) 325| 1| 326| 1| XCTAssert(oldDate.isInPast) 327| 1| XCTAssertFalse(futureDate.isInPast) 328| 1| } 329| | 330| 1| func testIsInToday() { 331| 1| XCTAssert(Date().isInToday) 332| 1| let tomorrow = Date().adding(.day, value: 1) 333| 1| XCTAssertFalse(tomorrow.isInToday) 334| 1| let yesterday = Date().adding(.day, value: -1) 335| 1| XCTAssertFalse(yesterday.isInToday) 336| 1| } 337| | 338| 1| func testIsInYesterday() { 339| 1| XCTAssertFalse(Date().isInYesterday) 340| 1| let tomorrow = Date().adding(.day, value: 1) 341| 1| XCTAssertFalse(tomorrow.isInYesterday) 342| 1| let yesterday = Date().adding(.day, value: -1) 343| 1| XCTAssert(yesterday.isInYesterday) 344| 1| } 345| | 346| 1| func testIsInTomorrow() { 347| 1| XCTAssertFalse(Date().isInTomorrow) 348| 1| let tomorrow = Date().adding(.day, value: 1) 349| 1| XCTAssert(tomorrow.isInTomorrow) 350| 1| let yesterday = Date().adding(.day, value: -1) 351| 1| XCTAssertFalse(yesterday.isInTomorrow) 352| 1| } 353| | 354| 1| func testIsInWeekend() { 355| 1| let date = Date() 356| 1| XCTAssertEqual(date.isInWeekend, Calendar.current.isDateInWeekend(date)) 357| 1| } 358| | 359| 1| func testIsWorkday() { 360| 1| let date = Date() 361| 1| XCTAssertEqual(date.isWorkday, !Calendar.current.isDateInWeekend(date)) 362| 1| } 363| | 364| 1| func testIsInCurrentWeek() { 365| 1| let date = Date() 366| 1| XCTAssert(date.isInCurrentWeek) 367| 1| let dateOneYearFromNow = Calendar.current.date(byAdding: .year, value: 1, to: date) ?? Date() 368| 1| XCTAssertFalse(dateOneYearFromNow.isInCurrentWeek) 369| 1| } 370| | 371| 1| func testIsInCurrentMonth() { 372| 1| let date = Date() 373| 1| XCTAssert(date.isInCurrentMonth) 374| 1| let dateOneYearFromNow = date.adding(.year, value: 1) 375| 1| XCTAssertFalse(dateOneYearFromNow.isInCurrentMonth) 376| 1| } 377| | 378| 1| func testIsInCurrentYear() { 379| 1| let date = Date() 380| 1| XCTAssert(date.isInCurrentYear) 381| 1| let dateOneYearFromNow = date.adding(.year, value: 1) 382| 1| XCTAssertFalse(dateOneYearFromNow.isInCurrentYear) 383| 1| } 384| | 385| 1| func testIso8601String() { 386| 1| let date = Date(timeIntervalSince1970: 512) // 1970-01-01T00:08:32.000Z 387| 1| XCTAssertEqual(date.iso8601String, "1970-01-01T00:08:32.000Z") 388| 1| } 389| | 390| 1| func testNearestFiveMinutes() { 391| 1| let date = Date(timeIntervalSince1970: 0) 392| 1| XCTAssertEqual(date.nearestFiveMinutes, date) 393| 1| 394| 1| let date2 = date.adding(.minute, value: 4) // adding 4 minutes 395| 1| XCTAssertNotEqual(date2.nearestFiveMinutes, date2) 396| 1| XCTAssertEqual(date2.nearestFiveMinutes, date2.adding(.minute, value: 1)) 397| 1| 398| 1| let date3 = date.adding(.minute, value: 7) // adding 7 minutes 399| 1| XCTAssertEqual(date3.nearestFiveMinutes, date3.adding(.minute, value: -2)) 400| 1| 401| 1| let date4 = date.adding(.hour, value: 1).adding(.minute, value: 2) // adding 1 hour and 2 minutes 402| 1| XCTAssertEqual(date4.nearestFiveMinutes, date.adding(.hour, value: 1)) 403| 1| } 404| | 405| 1| func testNearestTenMinutes() { 406| 1| let date = Date(timeIntervalSince1970: 0) 407| 1| XCTAssertEqual(date.nearestTenMinutes, date) 408| 1| 409| 1| let date2 = date.adding(.minute, value: 4) // adding 4 minutes 410| 1| XCTAssertEqual(date2.nearestTenMinutes, date) 411| 1| 412| 1| let date3 = date.adding(.minute, value: 7) // adding 7 minutes 413| 1| XCTAssertEqual(date3.nearestTenMinutes, date.adding(.minute, value: 10)) 414| 1| 415| 1| let date4 = date.adding(.hour, value: 1).adding(.minute, value: 2) // adding 1 hour and 2 minutes 416| 1| XCTAssertEqual(date4.nearestTenMinutes, date.adding(.hour, value: 1)) 417| 1| } 418| | 419| 1| func testNearestQuarterHour() { 420| 1| let date = Date(timeIntervalSince1970: 0) 421| 1| XCTAssertEqual(date.nearestQuarterHour, date) 422| 1| 423| 1| let date2 = date.adding(.minute, value: 4) // adding 4 minutes 424| 1| XCTAssertEqual(date2.nearestQuarterHour, date) 425| 1| 426| 1| let date3 = date.adding(.minute, value: 12) // adding 12 minutes 427| 1| XCTAssertEqual(date3.nearestQuarterHour, date.adding(.minute, value: 15)) 428| 1| 429| 1| let date4 = date.adding(.hour, value: 1).adding(.minute, value: 2) // adding 1 hour and 2 minutes 430| 1| XCTAssertEqual(date4.nearestQuarterHour, date.adding(.hour, value: 1)) 431| 1| } 432| | 433| 1| func testNearestHalfHour() { 434| 1| let date = Date(timeIntervalSince1970: 0) 435| 1| XCTAssertEqual(date.nearestHalfHour, date) 436| 1| 437| 1| let date2 = date.adding(.minute, value: 4) // adding 4 minutes 438| 1| XCTAssertEqual(date2.nearestHalfHour, date) 439| 1| 440| 1| let date3 = date.adding(.minute, value: 19) // adding 19 minutes 441| 1| XCTAssertEqual(date3.nearestHalfHour, date.adding(.minute, value: 30)) 442| 1| 443| 1| let date4 = date.adding(.hour, value: 1).adding(.minute, value: 2) // adding 1 hour and 2 minutes 444| 1| XCTAssertEqual(date4.nearestHalfHour, date.adding(.hour, value: 1)) 445| 1| } 446| | 447| 1| func testNearestHour() { 448| 1| let date = Date(timeIntervalSince1970: 0) 449| 1| XCTAssertEqual(date.nearestHour, date) 450| 1| 451| 1| let date2 = date.adding(.minute, value: 4) // adding 4 minutes 452| 1| XCTAssertEqual(date2.nearestHour, date) 453| 1| 454| 1| let date3 = date.adding(.minute, value: 34) // adding 34 minutes 455| 1| XCTAssertEqual(date3.nearestHour, date.adding(.hour, value: 1)) 456| 1| } 457| | 458| 1| func testUnixTimestamp() { 459| 1| let date = Date() 460| 1| XCTAssertEqual(date.unixTimestamp, date.timeIntervalSince1970) 461| 1| 462| 1| let date2 = Date(timeIntervalSince1970: 100) 463| 1| XCTAssertEqual(date2.unixTimestamp, 100) 464| 1| } 465| | 466| 1| func testAdding() { 467| 1| let date = Date(timeIntervalSince1970: 3610) // Jan 1, 1970, 3:00:10 AM 468| 1| 469| 1| XCTAssertEqual(date.adding(.second, value: 0), date) 470| 1| let date1 = date.adding(.second, value: 10) 471| 1| XCTAssertEqual(date1.second, date.second + 10) 472| 1| XCTAssertEqual(date1.adding(.second, value: -10), date) 473| 1| 474| 1| XCTAssertEqual(date.adding(.minute, value: 0), date) 475| 1| let date2 = date.adding(.minute, value: 10) 476| 1| XCTAssertEqual(date2.minute, date.minute + 10) 477| 1| XCTAssertEqual(date2.adding(.minute, value: -10), date) 478| 1| 479| 1| XCTAssertEqual(date.adding(.hour, value: 0), date) 480| 1| let date3 = date.adding(.hour, value: 2) 481| 1| XCTAssertEqual(date3.hour, date.hour + 2) 482| 1| XCTAssertEqual(date3.adding(.hour, value: -2), date) 483| 1| 484| 1| XCTAssertEqual(date.adding(.day, value: 0), date) 485| 1| let date4 = date.adding(.day, value: 2) 486| 1| XCTAssertEqual(date4.day, date.day + 2) 487| 1| XCTAssertEqual(date4.adding(.day, value: -2), date) 488| 1| 489| 1| XCTAssertEqual(date.adding(.weekOfYear, value: 0), date) 490| 1| let date5 = date.adding(.weekOfYear, value: 1) 491| 1| XCTAssertEqual(date5.day, date.day + 7) 492| 1| XCTAssertEqual(date5.adding(.weekOfYear, value: -1), date) 493| 1| 494| 1| XCTAssertEqual(date.adding(.weekOfMonth, value: 0), date) 495| 1| let date6 = date.adding(.weekOfMonth, value: 1) 496| 1| XCTAssertEqual(date6.day, date.day + 7) 497| 1| XCTAssertEqual(date6.adding(.weekOfMonth, value: -1), date) 498| 1| 499| 1| XCTAssertEqual(date.adding(.month, value: 0), date) 500| 1| let date7 = date.adding(.month, value: 2) 501| 1| XCTAssertEqual(date7.month, date.month + 2) 502| 1| XCTAssertEqual(date7.adding(.month, value: -2), date) 503| 1| 504| 1| XCTAssertEqual(date.adding(.year, value: 0), date) 505| 1| let date8 = date.adding(.year, value: 4) 506| 1| XCTAssertEqual(date8.year, date.year + 4) 507| 1| XCTAssertEqual(date8.adding(.year, value: -4), date) 508| 1| } 509| | 510| | // swiftlint:disable:next function_body_length 511| 1| func testAdd() { 512| 1| var date = Date(timeIntervalSince1970: 0) 513| 1| 514| 1| date.second = 10 515| 1| date.add(.second, value: -1) 516| 1| XCTAssertEqual(date.second, 9) 517| 1| date.add(.second, value: 0) 518| 1| XCTAssertEqual(date.second, 9) 519| 1| 520| 1| date.add(.second, value: 1) 521| 1| XCTAssertEqual(date.second, 10) 522| 1| 523| 1| date.minute = 10 524| 1| date.add(.minute, value: -1) 525| 1| XCTAssertEqual(date.minute, 9) 526| 1| date.add(.minute, value: 0) 527| 1| XCTAssertEqual(date.minute, 9) 528| 1| 529| 1| date.add(.minute, value: 1) 530| 1| XCTAssertEqual(date.minute, 10) 531| 1| 532| 1| date.hour = 10 533| 1| date.add(.hour, value: -1) 534| 1| XCTAssertEqual(date.hour, 9) 535| 1| date.add(.hour, value: 0) 536| 1| XCTAssertEqual(date.hour, 9) 537| 1| 538| 1| date.add(.hour, value: 1) 539| 1| XCTAssertEqual(date.hour, 10) 540| 1| 541| 1| date.day = 10 542| 1| date.add(.day, value: -1) 543| 1| XCTAssertEqual(date.day, 9) 544| 1| date.add(.day, value: 0) 545| 1| XCTAssertEqual(date.day, 9) 546| 1| 547| 1| date.add(.day, value: 1) 548| 1| XCTAssertEqual(date.day, 10) 549| 1| 550| 1| date.month = 10 551| 1| date.add(.month, value: -1) 552| 1| XCTAssertEqual(date.month, 9) 553| 1| date.add(.month, value: 0) 554| 1| XCTAssertEqual(date.month, 9) 555| 1| 556| 1| date.add(.month, value: 1) 557| 1| XCTAssertEqual(date.month, 10) 558| 1| 559| 1| date = Date(timeIntervalSince1970: 1_514_764_800) 560| 1| 561| 1| date.add(.year, value: -1) 562| 1| XCTAssertEqual(date.year, 2017) 563| 1| date.add(.year, value: 0) 564| 1| XCTAssertEqual(date.year, 2017) 565| 1| 566| 1| date.add(.year, value: 1) 567| 1| XCTAssertEqual(date.year, 2018) 568| 1| } 569| | 570| 1| func testChanging() { 571| 1| let date = Date(timeIntervalSince1970: 0) 572| 1| 573| 1| XCTAssertNil(date.changing(.nanosecond, value: -10)) 574| 1| XCTAssertNotNil(date.changing(.nanosecond, value: 123_450_040)) 575| 1| XCTAssertEqual(date.changing(.nanosecond, value: 123_450_040)?.nanosecond, 123_450_040) 576| 1| 577| 1| XCTAssertNil(date.changing(.second, value: -10)) 578| 1| XCTAssertNil(date.changing(.second, value: 70)) 579| 1| XCTAssertNotNil(date.changing(.second, value: 20)) 580| 1| XCTAssertEqual(date.changing(.second, value: 20)?.second, 20) 581| 1| 582| 1| XCTAssertNil(date.changing(.minute, value: -10)) 583| 1| XCTAssertNil(date.changing(.minute, value: 70)) 584| 1| XCTAssertNotNil(date.changing(.minute, value: 20)) 585| 1| XCTAssertEqual(date.changing(.minute, value: 20)?.minute, 20) 586| 1| 587| 1| XCTAssertNil(date.changing(.hour, value: -2)) 588| 1| XCTAssertNil(date.changing(.hour, value: 25)) 589| 1| XCTAssertNotNil(date.changing(.hour, value: 6)) 590| 1| XCTAssertEqual(date.changing(.hour, value: 6)?.hour, 6) 591| 1| 592| 1| XCTAssertNil(date.changing(.day, value: -2)) 593| 1| XCTAssertNil(date.changing(.day, value: 35)) 594| 1| XCTAssertNotNil(date.changing(.day, value: 6)) 595| 1| XCTAssertEqual(date.changing(.day, value: 6)?.day, 6) 596| 1| 597| 1| XCTAssertNil(date.changing(.month, value: -2)) 598| 1| XCTAssertNil(date.changing(.month, value: 13)) 599| 1| XCTAssertNotNil(date.changing(.month, value: 6)) 600| 1| XCTAssertEqual(date.changing(.month, value: 6)?.month, 6) 601| 1| 602| 1| XCTAssertNil(date.changing(.year, value: -2)) 603| 1| XCTAssertNil(date.changing(.year, value: 0)) 604| 1| XCTAssertNotNil(date.changing(.year, value: 2015)) 605| 1| XCTAssertEqual(date.changing(.year, value: 2015)?.year, 2015) 606| 1| 607| 1| let date1 = Date() 608| 1| let date2 = date1.changing(.weekOfYear, value: 10) 609| 1| XCTAssertEqual(date2, Calendar.current.date(bySetting: .weekOfYear, value: 10, of: date1)) 610| 1| } 611| | 612| 1| func testBeginning() { 613| 1| #if !os(Linux) 614| 1| let date = Date() 615| 1| 616| 1| XCTAssertNotNil(date.beginning(of: .second)) 617| 1| XCTAssertEqual(date.beginning(of: .second)?.nanosecond, 0) 618| 1| 619| 1| XCTAssertNotNil(date.beginning(of: .minute)) 620| 1| XCTAssertEqual(date.beginning(of: .minute)?.second, 0) 621| 1| 622| 1| XCTAssertNotNil(date.beginning(of: .hour)) 623| 1| XCTAssertEqual(date.beginning(of: .hour)?.minute, 0) 624| 1| 625| 1| XCTAssertNotNil(date.beginning(of: .day)) 626| 1| XCTAssertEqual(date.beginning(of: .day)?.hour, 0) 627| 1| XCTAssertEqual(date.beginning(of: .day)?.isInToday, true) 628| 1| 629| 1| let comps = Calendar.current.dateComponents([.yearForWeekOfYear, .weekOfYear], from: date) 630| 1| let beginningOfWeek = Calendar.current.date(from: comps) 631| 1| XCTAssertNotNil(date.beginning(of: .weekOfMonth)) 632| 1| XCTAssertNotNil(beginningOfWeek) 633| 1| XCTAssertEqual(date.beginning(of: .weekOfMonth)?.day, beginningOfWeek?.day) 634| 1| 635| 1| let beginningOfMonth = Date(year: 2016, month: 8, day: 1, hour: 5) 636| 1| XCTAssertNotNil(date.beginning(of: .month)) 637| 1| XCTAssertNotNil(beginningOfMonth) 638| 1| XCTAssertEqual(date.beginning(of: .month)?.day, beginningOfMonth?.day) 639| 1| 640| 1| let beginningOfYear = Date(year: 2016, month: 1, day: 1, hour: 5) 641| 1| XCTAssertNotNil(date.beginning(of: .year)) 642| 1| XCTAssertNotNil(beginningOfYear) 643| 1| XCTAssertEqual(date.beginning(of: .year)?.day, beginningOfYear?.day) 644| 1| 645| 1| XCTAssertNil(date.beginning(of: .quarter)) 646| 1| #endif 647| 1| } 648| | 649| 1| func testEnd() { 650| 1| let date = Date(timeIntervalSince1970: 512) // January 1, 1970 at 2:08:32 AM GMT+2 651| 1| 652| 1| XCTAssertEqual(date.end(of: .second)?.second, 32) 653| 1| XCTAssertEqual(date.end(of: .hour)?.minute, 59) 654| 1| XCTAssertEqual(date.end(of: .minute)?.second, 59) 655| 1| 656| 1| XCTAssertEqual(date.end(of: .day)?.hour, 23) 657| 1| XCTAssertEqual(date.end(of: .day)?.minute, 59) 658| 1| XCTAssertEqual(date.end(of: .day)?.second, 59) 659| 1| 660| 1| #if !os(Linux) 661| 1| var endOfWeek = date.beginning(of: .weekOfYear) 662| 1| endOfWeek?.add(.day, value: 7) 663| 1| endOfWeek?.add(.second, value: -1) 664| 1| XCTAssertEqual(date.end(of: .weekOfYear), endOfWeek) 665| 1| #endif 666| 1| 667| 1| XCTAssertEqual(date.end(of: .month)?.day, 31) 668| 1| XCTAssertEqual(date.end(of: .month)?.hour, 23) 669| 1| XCTAssertEqual(date.end(of: .month)?.minute, 59) 670| 1| XCTAssertEqual(date.end(of: .month)?.second, 59) 671| 1| 672| 1| XCTAssertEqual(date.end(of: .year)?.month, 12) 673| 1| XCTAssertEqual(date.end(of: .year)?.day, 31) 674| 1| XCTAssertEqual(date.end(of: .year)?.hour, 23) 675| 1| XCTAssertEqual(date.end(of: .year)?.minute, 59) 676| 1| XCTAssertEqual(date.end(of: .year)?.second, 59) 677| 1| 678| 1| XCTAssertNil(date.end(of: .quarter)) 679| 1| } 680| | 681| 1| func testDateString() { 682| 1| let date = Date(timeIntervalSince1970: 512) 683| 1| let formatter = DateFormatter() 684| 1| formatter.timeStyle = .none 685| 1| 686| 1| formatter.dateStyle = .short 687| 1| XCTAssertEqual(date.dateString(ofStyle: .short), formatter.string(from: date)) 688| 1| 689| 1| formatter.dateStyle = .medium 690| 1| XCTAssertEqual(date.dateString(ofStyle: .medium), formatter.string(from: date)) 691| 1| 692| 1| formatter.dateStyle = .long 693| 1| XCTAssertEqual(date.dateString(ofStyle: .long), formatter.string(from: date)) 694| 1| 695| 1| formatter.dateStyle = .full 696| 1| XCTAssertEqual(date.dateString(ofStyle: .full), formatter.string(from: date)) 697| 1| 698| 1| formatter.dateStyle = .none 699| 1| 700| 1| formatter.dateFormat = "dd/MM/yyyy" 701| 1| XCTAssertEqual(date.string(withFormat: "dd/MM/yyyy"), formatter.string(from: date)) 702| 1| 703| 1| formatter.dateFormat = "HH:mm" 704| 1| XCTAssertEqual(date.string(withFormat: "HH:mm"), formatter.string(from: date)) 705| 1| 706| 1| formatter.dateFormat = "dd/MM/yyyy HH:mm" 707| 1| XCTAssertEqual(date.string(withFormat: "dd/MM/yyyy HH:mm"), formatter.string(from: date)) 708| 1| 709| 1| formatter.dateFormat = "iiiii" 710| 1| XCTAssertEqual(date.string(withFormat: "iiiii"), formatter.string(from: date)) 711| 1| } 712| | 713| 1| func testDateTimeString() { 714| 1| let date = Date(timeIntervalSince1970: 512) 715| 1| let formatter = DateFormatter() 716| 1| 717| 1| formatter.timeStyle = .short 718| 1| formatter.dateStyle = .short 719| 1| XCTAssertEqual(date.dateTimeString(ofStyle: .short), formatter.string(from: date)) 720| 1| 721| 1| formatter.timeStyle = .medium 722| 1| formatter.dateStyle = .medium 723| 1| XCTAssertEqual(date.dateTimeString(ofStyle: .medium), formatter.string(from: date)) 724| 1| 725| 1| formatter.timeStyle = .long 726| 1| formatter.dateStyle = .long 727| 1| XCTAssertEqual(date.dateTimeString(ofStyle: .long), formatter.string(from: date)) 728| 1| 729| 1| formatter.timeStyle = .full 730| 1| formatter.dateStyle = .full 731| 1| XCTAssertEqual(date.dateTimeString(ofStyle: .full), formatter.string(from: date)) 732| 1| } 733| | 734| 1| func testIsInCurrent() { 735| 1| let date = Date() 736| 1| let oldDate = Date(timeIntervalSince1970: 512) // 1970-01-01T00:08:32.000Z 737| 1| 738| 1| XCTAssert(date.isInCurrent(.second)) 739| 1| XCTAssertFalse(oldDate.isInCurrent(.second)) 740| 1| 741| 1| XCTAssert(date.isInCurrent(.minute)) 742| 1| XCTAssertFalse(oldDate.isInCurrent(.minute)) 743| 1| 744| 1| XCTAssert(date.isInCurrent(.hour)) 745| 1| XCTAssertFalse(oldDate.isInCurrent(.hour)) 746| 1| 747| 1| XCTAssert(date.isInCurrent(.day)) 748| 1| XCTAssertFalse(oldDate.isInCurrent(.day)) 749| 1| 750| 1| XCTAssert(date.isInCurrent(.weekOfMonth)) 751| 1| XCTAssertFalse(oldDate.isInCurrent(.weekOfMonth)) 752| 1| 753| 1| XCTAssert(date.isInCurrent(.month)) 754| 1| XCTAssertFalse(oldDate.isInCurrent(.month)) 755| 1| 756| 1| XCTAssert(date.isInCurrent(.year)) 757| 1| XCTAssertFalse(oldDate.isInCurrent(.year)) 758| 1| 759| 1| XCTAssert(date.isInCurrent(.era)) 760| 1| } 761| | 762| 1| func testTimeString() { 763| 1| let date = Date(timeIntervalSince1970: 512) 764| 1| let formatter = DateFormatter() 765| 1| formatter.dateStyle = .none 766| 1| 767| 1| formatter.timeStyle = .short 768| 1| XCTAssertEqual(date.timeString(ofStyle: .short), formatter.string(from: date)) 769| 1| 770| 1| formatter.timeStyle = .medium 771| 1| XCTAssertEqual(date.timeString(ofStyle: .medium), formatter.string(from: date)) 772| 1| 773| 1| formatter.timeStyle = .long 774| 1| XCTAssertEqual(date.timeString(ofStyle: .long), formatter.string(from: date)) 775| 1| 776| 1| formatter.timeStyle = .full 777| 1| XCTAssertEqual(date.timeString(ofStyle: .full), formatter.string(from: date)) 778| 1| } 779| | 780| 1| func testDayName() { 781| 1| let date = Date(timeIntervalSince1970: 1_486_121_165) 782| 1| XCTAssertEqual(date.dayName(ofStyle: .full), "Friday") 783| 1| XCTAssertEqual(date.dayName(ofStyle: .threeLetters), "Fri") 784| 1| XCTAssertEqual(date.dayName(ofStyle: .oneLetter), "F") 785| 1| } 786| | 787| 1| func testMonthName() { 788| 1| let date = Date(timeIntervalSince1970: 1_486_121_165) 789| 1| XCTAssertEqual(date.monthName(ofStyle: .full), "February") 790| 1| XCTAssertEqual(date.monthName(ofStyle: .threeLetters), "Feb") 791| 1| XCTAssertEqual(date.monthName(ofStyle: .oneLetter), "F") 792| 1| } 793| | 794| 1| func testSecondsSince() { 795| 1| let date1 = Date(timeIntervalSince1970: 100) 796| 1| let date2 = Date(timeIntervalSince1970: 180) 797| 1| XCTAssertEqual(date2.secondsSince(date1), 80) 798| 1| XCTAssertEqual(date1.secondsSince(date2), -80) 799| 1| } 800| | 801| 1| func testMinutesSince() { 802| 1| let date1 = Date(timeIntervalSince1970: 120) 803| 1| let date2 = Date(timeIntervalSince1970: 180) 804| 1| XCTAssertEqual(date2.minutesSince(date1), 1) 805| 1| XCTAssertEqual(date1.minutesSince(date2), -1) 806| 1| } 807| | 808| 1| func testHoursSince() { 809| 1| let date1 = Date(timeIntervalSince1970: 3600) 810| 1| let date2 = Date(timeIntervalSince1970: 7200) 811| 1| XCTAssertEqual(date2.hoursSince(date1), 1) 812| 1| XCTAssertEqual(date1.hoursSince(date2), -1) 813| 1| } 814| | 815| 1| func testDaysSince() { 816| 1| let date1 = Date(timeIntervalSince1970: 0) 817| 1| let date2 = Date(timeIntervalSince1970: 86400) 818| 1| XCTAssertEqual(date2.daysSince(date1), 1) 819| 1| XCTAssertEqual(date1.daysSince(date2), -1) 820| 1| } 821| | 822| 1| func testIsBetween() { 823| 1| let date1 = Date(timeIntervalSince1970: 0) 824| 1| let date2 = date1.addingTimeInterval(60) 825| 1| let date3 = date2.addingTimeInterval(60) 826| 1| 827| 1| XCTAssert(date2.isBetween(date1, date3)) 828| 1| XCTAssertFalse(date1.isBetween(date2, date3)) 829| 1| XCTAssert(date1.isBetween(date1, date2, includeBounds: true)) 830| 1| XCTAssertFalse(date1.isBetween(date1, date2)) 831| 1| } 832| | 833| 1| func testIsWithin() { 834| 1| let date1 = Date(timeIntervalSince1970: 60 * 60 * 24) // 1970-01-01T00:00:00.000Z 835| 1| let date2 = date1.addingTimeInterval(60 * 60) // 1970-01-01T00:01:00.000Z, one hour later than date1 836| 1| 837| 1| // The regular 838| 1| XCTAssertFalse(date1.isWithin(1, .second, of: date2)) 839| 1| XCTAssertFalse(date1.isWithin(1, .minute, of: date2)) 840| 1| XCTAssert(date1.isWithin(1, .hour, of: date2)) 841| 1| XCTAssert(date1.isWithin(1, .day, of: date2)) 842| 1| 843| 1| // The other way around 844| 1| XCTAssertFalse(date2.isWithin(1, .second, of: date1)) 845| 1| XCTAssertFalse(date2.isWithin(1, .minute, of: date1)) 846| 1| XCTAssert(date2.isWithin(1, .hour, of: date1)) 847| 1| XCTAssert(date2.isWithin(1, .day, of: date1)) 848| 1| 849| 1| // With itself 850| 1| XCTAssert(date1.isWithin(1, .second, of: date1)) 851| 1| XCTAssert(date1.isWithin(1, .minute, of: date1)) 852| 1| XCTAssert(date1.isWithin(1, .hour, of: date1)) 853| 1| XCTAssert(date1.isWithin(1, .day, of: date1)) 854| 1| 855| 1| // Invalid 856| 1| XCTAssertFalse(Date().isWithin(1, .calendar, of: Date())) 857| 1| } 858| | 859| 1| func testNewDateFromComponenets() { 860| 1| let date = Date( 861| 1| calendar: Date().calendar, 862| 1| timeZone: NSTimeZone.default, 863| 1| era: Date().era, 864| 1| year: Date().year, 865| 1| month: Date().month, 866| 1| day: Date().day, 867| 1| hour: Date().hour, 868| 1| minute: Date().minute, 869| 1| second: Date().second, 870| 1| nanosecond: Date().nanosecond) 871| 1| XCTAssertNotNil(date) 872| 1| let date1 = Date(timeIntervalSince1970: date!.timeIntervalSince1970) 873| 1| 874| 1| XCTAssertEqual(date?.timeIntervalSince1970, date1.timeIntervalSince1970) 875| 1| 876| 1| let date2 = Date( 877| 1| calendar: nil, 878| 1| timeZone: NSTimeZone.default, 879| 1| era: Date().era, 880| 1| year: nil, 881| 1| month: nil, 882| 1| day: Date().day, 883| 1| hour: Date().hour, 884| 1| minute: Date().minute, 885| 1| second: Date().second, 886| 1| nanosecond: Date().nanosecond) 887| 1| XCTAssertNil(date2) 888| 1| } 889| | 890| 1| func testNewDateFromIso8601String() { 891| 1| let date = Date(timeIntervalSince1970: 512) // 1970-01-01T00:08:32.000Z 892| 1| let dateFromIso8601 = Date(iso8601String: "1970-01-01T00:08:32.000Z") 893| 1| XCTAssertEqual(date, dateFromIso8601) 894| 1| XCTAssertNil(Date(iso8601String: "hello")) 895| 1| } 896| | 897| 1| func testNewDateFromUnixTimestamp() { 898| 1| let date = Date(timeIntervalSince1970: 512) // 1970-01-01T00:08:32.000Z 899| 1| let dateFromUnixTimestamp = Date(unixTimestamp: 512) 900| 1| XCTAssertEqual(date, dateFromUnixTimestamp) 901| 1| } 902| | 903| 1| func testNewDateFromIntegerLiteral() { 904| 1| let date = Date(integerLiteral: 20_171_225) 905| 1| 906| 1| XCTAssertNotNil(date) 907| 1| 908| 1| if let date = date { 909| 1| XCTAssertEqual(String(describing: date), "2017-12-25 00:00:00 +0000") 910| 1| } 911| 1| 912| 1| #if !targetEnvironment(macCatalyst) 913| 1| XCTAssertNil(Date(integerLiteral: 222)) 914| 1| #endif 915| 1| } 916| | 917| 1| func testRandomRange() { 918| 1| var sinceDate = Date(timeIntervalSinceReferenceDate: 0) 919| 1| var toDate = Date(timeIntervalSinceReferenceDate: 10000) 920| 1| XCTAssert(Date.random(in: sinceDate.. Bool in 80| 1| return (key == NSAttributedString.Key.foregroundColor && (value as? Color) == .red) 81| 1| } 82| 1| 83| 1| XCTAssertEqual(filteredAttributes.count, 1) 84| 1| 85| 1| out = out.colored(with: .blue) 86| 1| attributes = out.attributes(at: 0, effectiveRange: nil) 87| 1| XCTAssertEqual(attributes[NSAttributedString.Key.foregroundColor] as? Color, .blue) 88| 1| XCTAssertNotEqual(attributes[NSAttributedString.Key.foregroundColor] as? Color, .red) 89| 1| #endif 90| 1| } 91| | 92| 1| func testApplyingToRegex() { 93| 1| #if canImport(UIKit) && os(iOS) 94| 1| let email = "sTeVe.jObS@apple.com" 95| 1| let testString = NSAttributedString(string: "Your email is \(email)!").bolded 96| 1| let attributes: [NSAttributedString.Key: Any] = [.underlineStyle: NSUnderlineStyle.single.rawValue, 97| 1| .foregroundColor: UIColor.blue] 98| 1| let casePattern = "Steve\\.Jobs" 99| 1| 100| 1| // Case sensitive 101| 1| caseSensitiveRegexTest(testString, attributes: attributes, pattern: casePattern) 102| 1| // Common 103| 1| commonRegexTest(stringToTest: testString, attributes: attributes, email) 104| 1| #endif 105| 1| } 106| | 107| | #if canImport(UIKit) && os(iOS) 108| | private func caseSensitiveRegexTest( 109| | _ stringToTest: NSAttributedString, 110| | attributes: [NSAttributedString.Key: Any], 111| | pattern: String) { 112| | let stringRange = NSRange(0.. attrAtBeginning.count else { return } 172| | 173| | let emailFromRange = attrTestString.attributedSubstring(from: range).string 174| | // confirm that the string with the applied attributes is the email 175| | XCTAssertEqual(emailFromRange, email) 176| | 177| | // the range contains the email, check to make sure the attributes are there and correct 178| | for attr in attrs { 179| | if attr.key == .underlineStyle { 180| | XCTAssertEqual(attr.value as? NSUnderlineStyle.RawValue, NSUnderlineStyle.single.rawValue) 181| | passed = true 182| | } else if attr.key == .foregroundColor { 183| | XCTAssertEqual(attr.value as? UIColor, UIColor.blue) 184| | passed = true 185| | } else if attr.key == .font { 186| | XCTAssertEqual(attr.value as? UIFont, .boldSystemFont(ofSize: UIFont.systemFontSize)) 187| | } else { 188| | passed = false 189| | } 190| | } 191| | 192| | XCTAssert(passed) 193| | } 194| | } 195| | #endif 196| | 197| 1| func testApplyingToOccurrences() { 198| 1| #if canImport(UIKit) && os(iOS) 199| 1| let name = "Steve Wozniak" 200| 1| let greeting = "Hello, \(name)." 201| 1| let attrGreeting = NSAttributedString(string: greeting).italicized.applying( 202| 1| attributes: [.underlineStyle: NSUnderlineStyle.single.rawValue, 203| 1| .foregroundColor: UIColor.red], toOccurrencesOf: name) 204| 1| 205| 1| let attrAtBeginning = attrGreeting.attributes(at: 0, effectiveRange: nil) 206| 1| // assert that there is only one attribute at beginning from italics 207| 1| XCTAssertEqual(attrAtBeginning.count, 1) 208| 1| 209| 1| var passed = false 210| 1| // iterate through each range of attributes 211| 1| attrGreeting.enumerateAttributes( 212| 1| in: NSRange(0.. attrAtBeginning.count else { return } 216| 1| 217| 1| // confirm that the attributed string is the name 218| 1| let stringAtRange = attrGreeting.attributedSubstring(from: range).string 219| 1| XCTAssertEqual(stringAtRange, name) 220| 1| 221| 1| for attr in attrs { 222| 1| if attr.key == .underlineStyle { 223| 1| XCTAssertEqual(attr.value as? NSUnderlineStyle.RawValue, NSUnderlineStyle.single.rawValue) 224| 1| passed = true 225| 1| } else if attr.key == .foregroundColor { 226| 1| XCTAssertEqual(attr.value as? UIColor, UIColor.red) 227| 1| passed = true 228| 1| } else if attr.key == .font { 229| 1| XCTAssertEqual(attr.value as? UIFont, .italicSystemFont(ofSize: UIFont.systemFontSize)) 230| 1| } else { 231| 1| passed = false 232| 1| } 233| 1| } 234| 1| } 235| 1| 236| 1| XCTAssert(passed) 237| 1| #endif 238| 1| } 239| | 240| 1| func testAppending() { 241| 1| #if os(iOS) 242| 1| var string = NSAttributedString(string: "Test").italicized.underlined.struckthrough 243| 1| string += NSAttributedString(string: " Appending").bolded 244| 1| 245| 1| XCTAssertEqual(string.string, "Test Appending") 246| 1| 247| 1| var attributes = string.attributes(at: 0, effectiveRange: nil) 248| 1| var filteredAttributes = attributes.filter { (key, value) -> Bool in 249| 1| var valid = false 250| 1| if key == NSAttributedString.Key.font, let value = value as? UIFont, 251| 1| value == .italicSystemFont(ofSize: UIFont.systemFontSize) { 252| 1| valid = true 253| 1| } 254| 1| if key == NSAttributedString.Key.underlineStyle, let value = value as? NSUnderlineStyle.RawValue, 255| 1| value == NSUnderlineStyle.single.rawValue { 256| 1| valid = true 257| 1| } 258| 1| if key == NSAttributedString.Key.strikethroughStyle, let value = value as? NSUnderlineStyle.RawValue, 259| 1| value == NSUnderlineStyle.single.rawValue { 260| 1| valid = true 261| 1| } 262| 1| 263| 1| return valid 264| 1| } 265| 1| 266| 1| XCTAssertEqual(filteredAttributes.count, 3) 267| 1| 268| 1| attributes = string.attributes(at: 5, effectiveRange: nil) 269| 1| filteredAttributes = attributes.filter { (key, value) -> Bool in 270| 1| return (key == NSAttributedString.Key 271| 1| .font && (value as? UIFont) == .boldSystemFont(ofSize: UIFont.systemFontSize)) 272| 1| } 273| 1| 274| 1| XCTAssertEqual(filteredAttributes.count, 1) 275| 1| #endif 276| 1| } 277| | 278| 1| func testAttributes() { 279| 1| #if os(iOS) 280| 1| let emptyString = NSAttributedString(string: "").bolded.struckthrough.underlined.colored(with: UIColor.blue) 281| 1| let emptyStringAttributes = emptyString.attributes 282| 1| XCTAssert(emptyStringAttributes.isEmpty) 283| 1| 284| 1| let attrString = NSAttributedString(string: "Test String").bolded.struckthrough.underlined 285| 1| .colored(with: UIColor.blue) 286| 1| let attributes = attrString.attributes 287| 1| 288| 1| XCTAssertEqual(attributes.count, 4) 289| 1| 290| 1| let filteredAttributes = attributes.filter { (key, value) -> Bool in 291| 1| switch key { 292| 1| case NSAttributedString.Key.underlineStyle: 293| 1| return (value as? NSUnderlineStyle.RawValue) == NSUnderlineStyle.single.rawValue 294| 1| case NSAttributedString.Key.strikethroughStyle: 295| 1| return (value as? NSUnderlineStyle.RawValue) == NSUnderlineStyle.single.rawValue 296| 1| case NSAttributedString.Key.font: 297| 1| return (value as? UIFont) == .boldSystemFont(ofSize: UIFont.systemFontSize) 298| 1| case NSAttributedString.Key.foregroundColor: 299| 1| return (value as? UIColor) == .blue 300| 1| default: 301| 1| return false 302| 1| } 303| 1| } 304| 1| 305| 1| XCTAssertEqual(filteredAttributes.count, 4) 306| 1| #endif 307| 1| } 308| | 309| | // MARK: - Operators 310| | 311| 1| func testOperators() { 312| 1| #if os(iOS) 313| 1| var string1 = NSAttributedString(string: "Test").italicized.underlined.struckthrough 314| 1| let string2 = NSAttributedString(string: " Appending").bolded 315| 1| XCTAssertEqual((string1 + string2).string, "Test Appending") 316| 1| XCTAssertEqual((string1 + string2.string).string, "Test Appending") 317| 1| 318| 1| string1 += string2.string 319| 1| XCTAssertEqual(string1.string, "Test Appending") 320| 1| #endif 321| 1| } 322| |} 323| | 324| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Tests/FoundationTests/NSPredicateExtensionsTests.swift: 1| |// NSPredicateExtensionsTests.swift - Copyright 2020 SwifterSwift 2| | 3| |@testable import SwifterSwift 4| |import XCTest 5| | 6| |#if canImport(Foundation) 7| |import Foundation 8| | 9| |final class NSPredicateExtensionsTests: XCTestCase { 10| 1| func testNot() { 11| 1| let predicate = NSPredicate(value: true) 12| 1| let notPredicate = predicate.not 13| 1| XCTAssertEqual(notPredicate.compoundPredicateType, .not) 14| 1| 15| 1| #if os(Linux) 16| 1| XCTAssertEqual(notPredicate.subpredicates, [predicate]) 17| 1| #else 18| 1| if let subpredicates = notPredicate.subpredicates as? [NSPredicate] { 19| 1| XCTAssertEqual(subpredicates, [predicate]) 20| 1| } 21| 1| #endif 22| 1| } 23| | 24| 1| func testAndPredicate() { 25| 1| let predicate1 = NSPredicate(value: true) 26| 1| let predicate2 = NSPredicate(value: false) 27| 1| let andPredicate = predicate1.and(predicate2) 28| 1| XCTAssertEqual(andPredicate.compoundPredicateType, .and) 29| 1| 30| 1| #if os(Linux) 31| 1| XCTAssertEqual(andPredicate.subpredicates, [predicate1, predicate2]) 32| 1| #else 33| 1| if let subpredicates = andPredicate.subpredicates as? [NSPredicate] { 34| 1| XCTAssertEqual(subpredicates, [predicate1, predicate2]) 35| 1| } 36| 1| #endif 37| 1| } 38| | 39| 1| func testOrPredicate() { 40| 1| let predicate1 = NSPredicate(value: true) 41| 1| let predicate2 = NSPredicate(value: false) 42| 1| let orPredicate = predicate1.or(predicate2) 43| 1| XCTAssertEqual(orPredicate.compoundPredicateType, .or) 44| 1| 45| 1| #if os(Linux) 46| 1| XCTAssertEqual(orPredicate.subpredicates, [predicate1, predicate2]) 47| 1| #else 48| 1| if let subpredicates = orPredicate.subpredicates as? [NSPredicate] { 49| 1| XCTAssertEqual(subpredicates, [predicate1, predicate2]) 50| 1| } 51| 1| #endif 52| 1| } 53| | 54| 1| func testOperatorNot() { 55| 1| let predicate = NSPredicate(value: false) 56| 1| let notPredicate = !predicate 57| 1| XCTAssertEqual(notPredicate.compoundPredicateType, .not) 58| 1| 59| 1| #if os(Linux) 60| 1| XCTAssertEqual(notPredicate.subpredicates, [predicate]) 61| 1| #else 62| 1| if let subpredicates = notPredicate.subpredicates as? [NSPredicate] { 63| 1| XCTAssertEqual(subpredicates, [predicate]) 64| 1| } 65| 1| #endif 66| 1| } 67| | 68| 1| func testOperatorAndPredicate() { 69| 1| let predicate1 = NSPredicate(value: false) 70| 1| let predicate2 = NSPredicate(value: true) 71| 1| let andPredicate = predicate1 + predicate2 72| 1| XCTAssertEqual(andPredicate.compoundPredicateType, .and) 73| 1| 74| 1| #if os(Linux) 75| 1| XCTAssertEqual(andPredicate.subpredicates, [predicate1, predicate2]) 76| 1| #else 77| 1| if let subpredicates = andPredicate.subpredicates as? [NSPredicate] { 78| 1| XCTAssertEqual(subpredicates, [predicate1, predicate2]) 79| 1| } 80| 1| #endif 81| 1| } 82| | 83| 1| func testOperatorOrPredicate() { 84| 1| let predicate1 = NSPredicate(value: true) 85| 1| let predicate2 = NSPredicate(value: false) 86| 1| let orPredicate = predicate1 | predicate2 87| 1| XCTAssertEqual(orPredicate.compoundPredicateType, .or) 88| 1| 89| 1| #if os(Linux) 90| 1| XCTAssertEqual(orPredicate.subpredicates, [predicate1, predicate2]) 91| 1| #else 92| 1| if let subpredicates = orPredicate.subpredicates as? [NSPredicate] { 93| 1| XCTAssertEqual(subpredicates, [predicate1, predicate2]) 94| 1| } 95| 1| #endif 96| 1| } 97| | 98| 1| func testOperatorSubPredicate() { 99| 2| let predicate1 = NSPredicate(block: { value, _ in 100| 2| guard let number = value as? Int else { return false } 101| 2| return 1..<5 ~= number 102| 2| }) 103| 2| let predicate2 = NSPredicate(block: { value, _ in 104| 2| guard let number = value as? Int else { return false } 105| 2| return 3..<6 ~= number 106| 2| }) 107| 1| 108| 1| let subPredicate = predicate1 - predicate2 109| 1| XCTAssert(subPredicate.evaluate(with: 2)) 110| 1| XCTAssertFalse(subPredicate.evaluate(with: 4)) 111| 1| } 112| |} 113| | 114| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Tests/FoundationTests/NSRegularExpressionExtensionsTests.swift: 1| |// NSRegularExpressionExtensionsTests.swift - Copyright 2020 SwifterSwift 2| | 3| |@testable import SwifterSwift 4| |import XCTest 5| | 6| |#if canImport(Foundation) 7| |import Foundation 8| | 9| |final class NSRegularExpressionExtensionsTests: XCTestCase { 10| | private let string = "bar foo bar foo bar" 11| | private let searchString = "bar" 12| | private let expectedMatches = 3 13| | private lazy var regularExpression = 14| | try! NSRegularExpression(pattern: searchString) // swiftlint:disable:this force_try 15| | 16| 1| func testEnumerateMatches() { 17| 1| var count = 0 18| 1| regularExpression.enumerateMatches(in: string, 19| 1| options: [], 20| 3| range: string.startIndex..= max 38| 2| } 39| 1| XCTAssertEqual(count, max) 40| 1| } 41| | 42| 1| func testMatches() { 43| 1| let matches = regularExpression.matches(in: string, 44| 1| options: [], 45| 1| range: string.startIndex.. Bool { 85| 30| return lhs.x == rhs.x && lhs.y == rhs.y && lhs.z == rhs.z 86| 30| } 87| |} 88| | 89| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Tests/SharedTests/ColorExtensionsTests.swift: 1| |// ColorExtensionsTests.swift - Copyright 2020 SwifterSwift 2| | 3| |@testable import SwifterSwift 4| |import XCTest 5| | 6| |#if canImport(AppKit) || canImport(UIKit) 7| | 8| |#if !os(watchOS) 9| |import CoreImage 10| |#endif 11| | 12| |// swiftlint:disable:next type_body_length 13| |final class ColorExtensionsTests: XCTestCase { 14| | // MARK: - Test properties 15| | 16| 1| func testCGFloatComponents() { 17| 1| XCTAssertEqual(Color.red.cgFloatComponents.red, 1.0) 18| 1| XCTAssertEqual(Color.red.cgFloatComponents.green, 0.0) 19| 1| XCTAssertEqual(Color.red.cgFloatComponents.blue, 0.0) 20| 1| 21| 1| XCTAssertEqual(Color.green.cgFloatComponents.red, 0.0) 22| 1| XCTAssertEqual(Color.green.cgFloatComponents.green, 1.0) 23| 1| XCTAssertEqual(Color.green.cgFloatComponents.blue, 0.0) 24| 1| 25| 1| XCTAssertEqual(Color.blue.cgFloatComponents.red, 0.0) 26| 1| XCTAssertEqual(Color.blue.cgFloatComponents.green, 0.0) 27| 1| XCTAssertEqual(Color.blue.cgFloatComponents.blue, 1.0) 28| 1| 29| 1| XCTAssertEqual(Color.black.cgFloatComponents.red, 0.0) 30| 1| XCTAssertEqual(Color.black.cgFloatComponents.green, 0.0) 31| 1| XCTAssertEqual(Color.black.cgFloatComponents.blue, 0.0) 32| 1| 33| 1| XCTAssertEqual(Color.white.cgFloatComponents.red, 1.0) 34| 1| XCTAssertEqual(Color.white.cgFloatComponents.green, 1.0) 35| 1| XCTAssertEqual(Color.white.cgFloatComponents.blue, 1.0) 36| 1| 37| 1| XCTAssertEqual(Int(Color(hex: 0x12FFFF)!.cgFloatComponents.red * 255.0), 0x12) 38| 1| } 39| | 40| | // MARK: - Test properties 41| | 42| 1| func testRgbComponents() { 43| 1| XCTAssertEqual(Color.red.rgbComponents.red, 255) 44| 1| XCTAssertEqual(Color.red.rgbComponents.green, 0) 45| 1| XCTAssertEqual(Color.red.rgbComponents.blue, 0) 46| 1| 47| 1| XCTAssertEqual(Color.green.rgbComponents.red, 0) 48| 1| XCTAssertEqual(Color.green.rgbComponents.green, 255) 49| 1| XCTAssertEqual(Color.green.rgbComponents.blue, 0) 50| 1| 51| 1| XCTAssertEqual(Color.blue.rgbComponents.red, 0) 52| 1| XCTAssertEqual(Color.blue.rgbComponents.green, 0) 53| 1| XCTAssertEqual(Color.blue.rgbComponents.blue, 255) 54| 1| 55| 1| XCTAssertEqual(Color.black.rgbComponents.red, 0) 56| 1| XCTAssertEqual(Color.black.rgbComponents.green, 0) 57| 1| XCTAssertEqual(Color.black.rgbComponents.blue, 0) 58| 1| 59| 1| XCTAssertEqual(Color.white.rgbComponents.red, 255) 60| 1| XCTAssertEqual(Color.white.rgbComponents.green, 255) 61| 1| XCTAssertEqual(Color.white.rgbComponents.blue, 255) 62| 1| 63| 1| XCTAssertEqual(Color(hex: 0x12FFFF)?.rgbComponents.red, 0x12) 64| 1| } 65| | 66| 1| func testAlpha() { 67| 1| var color = Color.red 68| 1| XCTAssertEqual(color.alpha, 1.0) 69| 1| 70| 1| color = Color.white.withAlphaComponent(0.5) 71| 1| XCTAssertEqual(color.alpha, 0.5) 72| 1| 73| 1| color = Color(red: 0, green: 0, blue: 0, transparency: 0.7)! 74| 1| XCTAssertEqual(color.alpha, 0.7) 75| 1| 76| 1| color = Color(red: 0, green: 0, blue: 0, transparency: 1.1)! 77| 1| XCTAssertEqual(color.alpha, 1.0) 78| 1| } 79| | 80| | #if !os(watchOS) 81| 1| func testCoreImageColor() { 82| 1| let color = Color.red 83| 1| let coreImageColor = color.coreImageColor 84| 1| XCTAssertNotNil(color.coreImageColor) 85| 1| XCTAssertEqual(color.coreImageColor!, coreImageColor) 86| 1| } 87| | #endif 88| | 89| | // MARK: - Test properties 90| | 91| 1| func testHsbaComponents() { 92| 1| var color = Color(hex: 0x00FF00, transparency: 1.0)! 93| 1| XCTAssertEqual(color.hsbaComponents.hue, 120.0 / 360.0, accuracy: 0.001) 94| 1| XCTAssertEqual(color.hsbaComponents.saturation, 1.0) 95| 1| XCTAssertEqual(color.hsbaComponents.brightness, 1.0) 96| 1| 97| 1| color = Color(hex: 0x0000FF, transparency: 1.0)! 98| 1| XCTAssertEqual(color.hsbaComponents.hue, 240.0 / 360.0, accuracy: 0.001) 99| 1| XCTAssertEqual(color.hsbaComponents.saturation, 1.0) 100| 1| XCTAssertEqual(color.hsbaComponents.brightness, 1.0) 101| 1| 102| 1| color = Color(hex: 0x000000, transparency: 1.0)! 103| 1| XCTAssertEqual(color.hsbaComponents.hue, 0.0) 104| 1| XCTAssertEqual(color.hsbaComponents.saturation, 0.0) 105| 1| XCTAssertEqual(color.hsbaComponents.brightness, 0.0) 106| 1| 107| 1| color = Color(hex: 0xFFFFFF, transparency: 1.0)! 108| 1| XCTAssertEqual(color.hsbaComponents.hue, 0.0) 109| 1| XCTAssertEqual(color.hsbaComponents.saturation, 0.0) 110| 1| XCTAssertEqual(color.hsbaComponents.brightness, 1.0) 111| 1| 112| 1| color = Color(hex: 0x123456, transparency: 1.0)! 113| 1| XCTAssertEqual(color.hsbaComponents.hue, 210.0 / 360.0, accuracy: 0.001) 114| 1| XCTAssertEqual((color.hsbaComponents.saturation * 100).rounded(), 79) 115| 1| XCTAssertEqual((color.hsbaComponents.brightness * 100).rounded(), 34) 116| 1| 117| 1| color = Color(hex: 0xFCA864, transparency: 1.0)! 118| 1| XCTAssertEqual(color.hsbaComponents.hue, 27.0 / 360.0, accuracy: 0.001) 119| 1| XCTAssertEqual((color.hsbaComponents.saturation * 100).rounded(), 60) 120| 1| XCTAssertEqual((color.hsbaComponents.brightness * 100).rounded(), 99) 121| 1| 122| 1| color = Color(hex: 0x1F2D3C, transparency: 1.0)! 123| 1| XCTAssertEqual(color.hsbaComponents.hue, 211.0 / 360.0, accuracy: 0.001) 124| 1| XCTAssertEqual((color.hsbaComponents.saturation * 100).rounded(), 48) 125| 1| XCTAssertEqual((color.hsbaComponents.brightness * 100).rounded(), 24) 126| 1| } 127| | 128| 1| func testUInt() { 129| 1| var color = Color(hex: 0xFF0000, transparency: 1.0) 130| 1| XCTAssertEqual(color?.uInt, 0xFF0000) 131| 1| 132| 1| color = Color(hex: 0x00FF00, transparency: 1.0) 133| 1| XCTAssertEqual(color?.uInt, 0x00FF00) 134| 1| 135| 1| color = Color(hex: 0x0000FF, transparency: 1.0) 136| 1| XCTAssertEqual(color?.uInt, 0x0000FF) 137| 1| 138| 1| color = Color(hex: 0x000000, transparency: 1.0) 139| 1| XCTAssertEqual(color?.uInt, 0x000000) 140| 1| 141| 1| color = Color(hex: 0xFFFFFF, transparency: 1.0) 142| 1| XCTAssertEqual(color?.uInt, 0xFFFFFF) 143| 1| 144| 1| color = Color(hex: 0x123456, transparency: 1.0) 145| 1| XCTAssertEqual(color?.uInt, 0x123456) 146| 1| 147| 1| color = Color(hex: 0xFCA864, transparency: 1.0) 148| 1| XCTAssertEqual(color?.uInt, 0xFCA864) 149| 1| 150| 1| color = Color(hex: 0xFCA864, transparency: 1.0) 151| 1| XCTAssertEqual(color?.uInt, 0xFCA864) 152| 1| 153| 1| color = Color(hex: 0x1F2D3C, transparency: 1.0) 154| 1| XCTAssertEqual(color?.uInt, 0x1F2D3C) 155| 1| } 156| | 157| 1| func testHexString() { 158| 1| var color = Color.red 159| 1| XCTAssertEqual(color.hexString, "#FF0000") 160| 1| 161| 1| color = Color.blue 162| 1| XCTAssertEqual(color.hexString, "#0000FF") 163| 1| 164| 1| color = Color(hex: 0xABCDEF)! 165| 1| XCTAssertEqual(color.hexString, "#ABCDEF") 166| 1| 167| 1| color = Color(hex: 0xABC)! 168| 1| XCTAssertEqual(color.hexString, "#000ABC") 169| 1| 170| 1| color = Color.black 171| 1| XCTAssertEqual(color.hexString, "#000000") 172| 1| } 173| | 174| 1| func testShortHexString() { 175| 1| var color: Color? = Color.red 176| 1| XCTAssertEqual(color?.shortHexString, "#F00") 177| 1| 178| 1| color = Color.blue 179| 1| XCTAssertEqual(color?.shortHexString, "#00F") 180| 1| 181| 1| color = Color(hexString: "#0F120F") 182| 1| XCTAssertNil(color?.shortHexString) 183| 1| 184| 1| color = Color(hexString: "#8FFFF") 185| 1| XCTAssertNil(color?.shortHexString) 186| 1| } 187| | 188| 1| func testShortHexOrHexString() { 189| 1| var color: Color? = Color.red 190| 1| XCTAssertEqual(color?.shortHexOrHexString, "#F00") 191| 1| 192| 1| color = Color(hexString: "#8FFFFF") 193| 1| XCTAssertEqual(color?.shortHexOrHexString, "#8FFFFF") 194| 1| 195| 1| color = Color(hexString: "#F") 196| 1| XCTAssertEqual(color?.shortHexOrHexString, "#00000F") 197| 1| 198| 1| color = Color(hexString: "#11") 199| 1| XCTAssertEqual(color?.shortHexOrHexString, "#001") 200| 1| } 201| | 202| 1| func testComplementary() { 203| 1| var color = Color.black 204| 1| var red: CGFloat = 0 205| 1| var green: CGFloat = 0 206| 1| var blue: CGFloat = 0 207| 1| color.complementary?.getRed(&red, green: &green, blue: &blue, alpha: nil) 208| 1| XCTAssertEqual(red, 1) 209| 1| XCTAssertEqual(green, 1) 210| 1| XCTAssertEqual(blue, 1) 211| 1| 212| 1| color = Color.white 213| 1| color.complementary?.getRed(&red, green: &green, blue: &blue, alpha: nil) 214| 1| XCTAssertEqual(red, 0) 215| 1| XCTAssertEqual(green, 0) 216| 1| XCTAssertEqual(blue, 0) 217| 1| 218| 1| color = Color.red 219| 1| color.complementary?.getRed(&red, green: &green, blue: &blue, alpha: nil) 220| 1| XCTAssertEqual(red, 0) 221| 1| XCTAssertEqual(green, 1) 222| 1| XCTAssertEqual(blue, 1) 223| 1| } 224| | 225| 1| func testRandom() { 226| 1| let color1 = Color.random 227| 1| let color2 = Color.random 228| 1| 229| 1| XCTAssertNotEqual(color1, color2) 230| 1| } 231| | 232| | // MARK: - Test methods 233| | 234| 1| func testBlend() { 235| 1| var color1 = Color.white 236| 1| var color2 = Color.black 237| 1| 238| 1| var blendColor = Color.blend(color1, with: color2) 239| 1| XCTAssertEqual(blendColor.rgbComponents.red, 0xFF / 2) 240| 1| XCTAssertEqual(blendColor.rgbComponents.green, 0xFF / 2) 241| 1| XCTAssertEqual(blendColor.rgbComponents.blue, 0xFF / 2) 242| 1| 243| 1| color1 = Color(hex: 0x123456, transparency: 0.5)! 244| 1| color2 = Color(hex: 0x665544, transparency: 0.7)! 245| 1| 246| 1| blendColor = Color.blend(color1, with: color2) 247| 1| XCTAssertEqual(blendColor.rgbComponents.red, (0x12 + 0x66) / 2) 248| 1| XCTAssertEqual(blendColor.rgbComponents.green, (0x34 + 0x55) / 2) 249| 1| XCTAssertEqual(blendColor.rgbComponents.blue, (0x56 + 0x44) / 2) 250| 1| XCTAssertEqual(blendColor.alpha, (0.7 + 0.5) / 2) 251| 1| 252| 1| blendColor = Color.blend(color1, intensity1: 0.7, with: color2, intensity2: 0.3) 253| 1| var output: Double = 0x12 * 0.7 + 0x66 * 0.3 254| 1| XCTAssertEqual(blendColor.rgbComponents.red, Int(output)) 255| 1| output = 0x34 * 0.7 + 0x55 * 0.3 256| 1| XCTAssertEqual(blendColor.rgbComponents.green, Int(output)) 257| 1| output = 0x56 * 0.7 + 0x44 * 0.3 258| 1| XCTAssertEqual(blendColor.rgbComponents.blue, Int(output)) 259| 1| output = 0.5 * 0.7 + 0.7 * 0.3 260| 1| XCTAssertEqual(blendColor.alpha, CGFloat(output)) 261| 1| 262| 1| blendColor = Color.blend(color1, intensity1: 0.0, with: color2, intensity2: 0.3) 263| 1| output = (0x12 * 0.0 + 0x66 * 0.3) / 0.3 264| 1| XCTAssertEqual(blendColor.rgbComponents.red, Int(output)) 265| 1| output = (0x34 * 0.0 + 0x55 * 0.3) / 0.3 266| 1| XCTAssertEqual(blendColor.rgbComponents.green, Int(output)) 267| 1| output = (0x56 * 0.0 + 0x44 * 0.3) / 0.3 268| 1| XCTAssertEqual(blendColor.rgbComponents.blue, Int(output)) 269| 1| output = (0.5 * 0.0 + 0.7 * 0.3 / 0.3) 270| 1| XCTAssertEqual(blendColor.alpha, CGFloat(output)) 271| 1| 272| 1| blendColor = Color.blend(color1, intensity1: 1.0, with: color2, intensity2: 0.0) 273| 1| XCTAssertEqual(blendColor, color1) 274| 1| } 275| | 276| 1| func testLighten() { 277| 1| let color = Color.blue 278| 1| var red: CGFloat = 0, green: CGFloat = 0, blue: CGFloat = 0 279| 1| color.getRed(&red, green: &green, blue: &blue, alpha: nil) 280| 1| 281| 1| let lighterColor = color.lighten(by: 0.3) 282| 1| var lightR: CGFloat = 0, lightG: CGFloat = 0, lightB: CGFloat = 0 283| 1| lighterColor.getRed(&lightR, green: &lightG, blue: &lightB, alpha: nil) 284| 1| 285| 1| XCTAssertEqual(lightR, min(red + 0.3, 1.0)) 286| 1| XCTAssertEqual(lightG, min(green + 0.3, 1.0)) 287| 1| XCTAssertEqual(lightB, min(blue + 0.3, 1.0)) 288| 1| } 289| | 290| 1| func testDarken() { 291| 1| let color = Color.blue 292| 1| var red: CGFloat = 0, green: CGFloat = 0, blue: CGFloat = 0 293| 1| color.getRed(&red, green: &green, blue: &blue, alpha: nil) 294| 1| 295| 1| let darkerColor = color.darken(by: 0.3) 296| 1| var darkR: CGFloat = 0, darkG: CGFloat = 0, darkB: CGFloat = 0 297| 1| darkerColor.getRed(&darkR, green: &darkG, blue: &darkB, alpha: nil) 298| 1| 299| 1| XCTAssertEqual(darkR, max(red - 0.3, 0)) 300| 1| XCTAssertEqual(darkG, max(green - 0.3, 0)) 301| 1| XCTAssertEqual(darkB, max(blue - 0.3, 0)) 302| 1| } 303| | 304| | // MARK: - Test initializers 305| | 306| 1| func testInit() { 307| 1| var color = Color(hex: 0xFFF) 308| 1| XCTAssertEqual(color?.rgbComponents.red, 0) 309| 1| XCTAssertEqual(color?.rgbComponents.green, 0xF) 310| 1| XCTAssertEqual(color?.rgbComponents.blue, 0xFF) 311| 1| XCTAssertEqual(color?.alpha, 1.0) 312| 1| 313| 1| color = Color(hex: 0xFFFFFFF) 314| 1| XCTAssertEqual(color?.rgbComponents.red, 0xFF) 315| 1| XCTAssertEqual(color?.rgbComponents.green, 0xFF) 316| 1| XCTAssertEqual(color?.rgbComponents.blue, 0xFF) 317| 1| XCTAssertEqual(color?.alpha, 1.0) 318| 1| 319| 1| color = Color(hex: 0x123456, transparency: 1.0) 320| 1| XCTAssertEqual(color?.rgbComponents.red, 0x12) 321| 1| XCTAssertEqual(color?.rgbComponents.green, 0x34) 322| 1| XCTAssertEqual(color?.rgbComponents.blue, 0x56) 323| 1| XCTAssertEqual(color?.alpha, 1.0) 324| 1| 325| 1| color = Color(hex: 0x999, transparency: 21.0) 326| 1| XCTAssertEqual(color?.rgbComponents.red, 0) 327| 1| XCTAssertEqual(color?.rgbComponents.green, 0x09) 328| 1| XCTAssertEqual(color?.rgbComponents.blue, 0x99) 329| 1| XCTAssertEqual(color?.alpha, 1.0) 330| 1| 331| 1| color = Color(hex: 0xAABBCC, transparency: 0.0) 332| 1| XCTAssertEqual(color?.rgbComponents.red, 0xAA) 333| 1| XCTAssertEqual(color?.rgbComponents.green, 0xBB) 334| 1| XCTAssertEqual(color?.rgbComponents.blue, 0xCC) 335| 1| XCTAssertEqual(color?.alpha, 0.0) 336| 1| 337| 1| color = Color(hex: 0x1, transparency: 0.5) 338| 1| XCTAssertEqual(color?.rgbComponents.red, 0) 339| 1| XCTAssertEqual(color?.rgbComponents.green, 0) 340| 1| XCTAssertEqual(color?.rgbComponents.blue, 1) 341| 1| XCTAssertEqual(color?.alpha, 0.5) 342| 1| let color1 = Color(hex: 0xFFF, transparency: -0.4) 343| 1| let color2 = Color(hex: 0xFFF, transparency: 0) 344| 1| XCTAssertEqual(color1, color2) 345| 1| 346| 1| let color3 = Color(hex: 0xFFF, transparency: 1.5) 347| 1| let color4 = Color(hex: 0xFFF, transparency: 1) 348| 1| XCTAssertEqual(color3, color4) 349| 1| } 350| | 351| 1| func testFailableInit() { 352| 1| var color = Color(hexString: "0xFFFFFF") 353| 1| XCTAssertNotNil(color) 354| 1| 355| 1| color = Color(hexString: "#FFFFFF") 356| 1| XCTAssertNotNil(color) 357| 1| 358| 1| color = Color(hexString: "FFFFFF") 359| 1| XCTAssertNotNil(color) 360| 1| 361| 1| color = Color(hexString: "#ABC") 362| 1| XCTAssertNotNil(color) 363| 1| 364| 1| color = Color(hexString: "#GGG") 365| 1| XCTAssertNil(color) 366| 1| 367| 1| color = Color(hexString: "4#fff") 368| 1| XCTAssertNil(color) 369| 1| 370| 1| color = Color(hexString: "FFFFFFF") 371| 1| XCTAssertNotNil(color) 372| 1| } 373| | 374| 1| func testInitWithComponents() { 375| 1| var red1: CGFloat = 0 376| 1| var red2: CGFloat = 0 377| 1| var green1: CGFloat = 0 378| 1| var green2: CGFloat = 0 379| 1| var blue1: CGFloat = 0 380| 1| var blue2: CGFloat = 0 381| 1| var alpha1: CGFloat = 0 382| 1| var alpha2: CGFloat = 0 383| 1| 384| 1| var color1 = Color(red: 255, green: 244, blue: 255, transparency: 2.0) 385| 1| var color2 = Color(red: 1.0, green: 244.0 / 255.0, blue: 1.0, alpha: 1.0) 386| 1| color1?.getRed(&red1, green: &green1, blue: &blue1, alpha: &alpha1) 387| 1| color2.getRed(&red2, green: &green2, blue: &blue2, alpha: &alpha2) 388| 1| XCTAssertEqual(red1, red2) 389| 1| XCTAssertEqual(green1, green2) 390| 1| XCTAssertEqual(blue1, blue2) 391| 1| XCTAssertEqual(alpha1, alpha2) 392| 1| 393| 1| color1 = Color(red: 25, green: 244, blue: 55, transparency: -1.0) 394| 1| color2 = Color(red: 25.0 / 255.0, green: 244.0 / 255.0, blue: 55.0 / 255.0, alpha: 0.0) 395| 1| color1?.getRed(&red1, green: &green1, blue: &blue1, alpha: &alpha1) 396| 1| color2.getRed(&red2, green: &green2, blue: &blue2, alpha: &alpha2) 397| 1| XCTAssertEqual(red1, red2) 398| 1| XCTAssertEqual(green1, green2) 399| 1| XCTAssertEqual(blue1, blue2) 400| 1| XCTAssertEqual(alpha1, alpha2) 401| 1| 402| 1| color1 = Color(red: 2, green: 4, blue: 5) 403| 1| color2 = Color(red: 2.0 / 255.0, green: 4.0 / 255.0, blue: 5.0 / 255.0, alpha: 1.0) 404| 1| color1?.getRed(&red1, green: &green1, blue: &blue1, alpha: &alpha1) 405| 1| color2.getRed(&red2, green: &green2, blue: &blue2, alpha: &alpha2) 406| 1| XCTAssertEqual(red1, red2) 407| 1| XCTAssertEqual(green1, green2) 408| 1| XCTAssertEqual(blue1, blue2) 409| 1| XCTAssertEqual(alpha1, alpha2) 410| 1| } 411| | 412| 1| func testFailableInitWithComponents() { 413| 1| let color1 = Color(red: 258, green: 0, blue: 0) 414| 1| XCTAssertNil(color1) 415| 1| 416| 1| let color2 = Color(red: 0, green: 258, blue: 0) 417| 1| XCTAssertNil(color2) 418| 1| 419| 1| let color3 = Color(red: 0, green: 0, blue: 258) 420| 1| XCTAssertNil(color3) 421| 1| 422| 1| let color4 = Color(red: 258, green: 258, blue: 258) 423| 1| XCTAssertNil(color4) 424| 1| } 425| | 426| 1| func testFailableInitWithComplementaryColor() { 427| 1| var color = Color(complementaryFor: Color.black) 428| 1| var red: CGFloat = 0 429| 1| var green: CGFloat = 0 430| 1| var blue: CGFloat = 0 431| 1| 432| 1| color?.getRed(&red, green: &green, blue: &blue, alpha: nil) 433| 1| XCTAssertEqual(red, 1) 434| 1| XCTAssertEqual(green, 1) 435| 1| XCTAssertEqual(blue, 1) 436| 1| 437| 1| color = Color(complementaryFor: Color.red) 438| 1| color?.getRed(&red, green: &green, blue: &blue, alpha: nil) 439| 1| XCTAssertEqual(red, 0) 440| 1| XCTAssertEqual(green, 1) 441| 1| XCTAssertEqual(blue, 1) 442| 1| } 443| |} 444| | 445| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Tests/SharedTests/EdgeInsetsExtensionsTests.swift: 1| |// EdgeInsetsExtensionsTests.swift - Copyright 2020 SwifterSwift 2| | 3| |#if os(iOS) || os(tvOS) || os(watchOS) || os(macOS) 4| | 5| |@testable import SwifterSwift 6| |import XCTest 7| | 8| |final class EdgeInsetsExtensionsTests: XCTestCase { 9| 1| func testHorizontal() { 10| 1| let inset = EdgeInsets(top: 30.0, left: 5.0, bottom: 5.0, right: 10.0) 11| 1| XCTAssertEqual(inset.horizontal, 15.0) 12| 1| } 13| | 14| 1| func testVertical() { 15| 1| let inset = EdgeInsets(top: 10.0, left: 10.0, bottom: 5.0, right: 10.0) 16| 1| XCTAssertEqual(inset.vertical, 15.0) 17| 1| } 18| | 19| 1| func testInitInset() { 20| 1| let inset = EdgeInsets(inset: 5.0) 21| 1| XCTAssertEqual(inset.top, 5.0) 22| 1| XCTAssertEqual(inset.bottom, 5.0) 23| 1| XCTAssertEqual(inset.right, 5.0) 24| 1| XCTAssertEqual(inset.left, 5.0) 25| 1| } 26| | 27| 1| func testInitVerticalHorizontal() { 28| 1| let inset = EdgeInsets(horizontal: 20.0, vertical: 10.0) 29| 1| XCTAssertEqual(inset.top, 5.0) 30| 1| XCTAssertEqual(inset.bottom, 5.0) 31| 1| XCTAssertEqual(inset.right, 10.0) 32| 1| XCTAssertEqual(inset.left, 10.0) 33| 1| } 34| | 35| 1| func testInsetByTop() { 36| 1| let inset = EdgeInsets(top: 10.0, left: 10.0, bottom: 10.0, right: 10.0) 37| 1| let insetByTop = inset.insetBy(top: 5.0) 38| 1| XCTAssertNotEqual(inset, insetByTop) 39| 1| XCTAssertEqual(insetByTop.top, 15.0) 40| 1| XCTAssertEqual(insetByTop.left, 10.0) 41| 1| XCTAssertEqual(insetByTop.bottom, 10.0) 42| 1| XCTAssertEqual(insetByTop.right, 10.0) 43| 1| 44| 1| let negativeInsetByTop = inset.insetBy(top: -5.0) 45| 1| XCTAssertNotEqual(inset, negativeInsetByTop) 46| 1| XCTAssertEqual(negativeInsetByTop.top, 5.0) 47| 1| XCTAssertEqual(negativeInsetByTop.left, 10.0) 48| 1| XCTAssertEqual(negativeInsetByTop.bottom, 10.0) 49| 1| XCTAssertEqual(negativeInsetByTop.right, 10.0) 50| 1| } 51| | 52| 1| func testInsetByLeft() { 53| 1| let inset = EdgeInsets(top: 10.0, left: 10.0, bottom: 10.0, right: 10.0) 54| 1| let insetByLeft = inset.insetBy(left: 5.0) 55| 1| XCTAssertNotEqual(inset, insetByLeft) 56| 1| XCTAssertEqual(insetByLeft.top, 10.0) 57| 1| XCTAssertEqual(insetByLeft.left, 15.0) 58| 1| XCTAssertEqual(insetByLeft.bottom, 10.0) 59| 1| XCTAssertEqual(insetByLeft.right, 10.0) 60| 1| 61| 1| let negativeInsetByLeft = inset.insetBy(left: -5.0) 62| 1| XCTAssertNotEqual(inset, negativeInsetByLeft) 63| 1| XCTAssertEqual(negativeInsetByLeft.top, 10.0) 64| 1| XCTAssertEqual(negativeInsetByLeft.left, 5.0) 65| 1| XCTAssertEqual(negativeInsetByLeft.bottom, 10.0) 66| 1| XCTAssertEqual(negativeInsetByLeft.right, 10.0) 67| 1| } 68| | 69| 1| func testInsetByBottom() { 70| 1| let inset = EdgeInsets(top: 10.0, left: 10.0, bottom: 10.0, right: 10.0) 71| 1| let insetByBottom = inset.insetBy(bottom: 5.0) 72| 1| XCTAssertNotEqual(inset, insetByBottom) 73| 1| XCTAssertEqual(insetByBottom.top, 10.0) 74| 1| XCTAssertEqual(insetByBottom.left, 10.0) 75| 1| XCTAssertEqual(insetByBottom.bottom, 15.0) 76| 1| XCTAssertEqual(insetByBottom.right, 10.0) 77| 1| 78| 1| let negativeInsetByBottom = inset.insetBy(bottom: -5.0) 79| 1| XCTAssertNotEqual(inset, negativeInsetByBottom) 80| 1| XCTAssertEqual(negativeInsetByBottom.top, 10.0) 81| 1| XCTAssertEqual(negativeInsetByBottom.left, 10.0) 82| 1| XCTAssertEqual(negativeInsetByBottom.bottom, 5.0) 83| 1| XCTAssertEqual(negativeInsetByBottom.right, 10.0) 84| 1| } 85| | 86| 1| func testInsetByRight() { 87| 1| let inset = EdgeInsets(top: 10.0, left: 10.0, bottom: 10.0, right: 10.0) 88| 1| let insetByRight = inset.insetBy(right: 5.0) 89| 1| XCTAssertNotEqual(inset, insetByRight) 90| 1| XCTAssertEqual(insetByRight.top, 10.0) 91| 1| XCTAssertEqual(insetByRight.left, 10.0) 92| 1| XCTAssertEqual(insetByRight.bottom, 10.0) 93| 1| XCTAssertEqual(insetByRight.right, 15.0) 94| 1| 95| 1| let negativeInsetByRight = inset.insetBy(right: -5.0) 96| 1| XCTAssertNotEqual(inset, negativeInsetByRight) 97| 1| XCTAssertEqual(negativeInsetByRight.top, 10.0) 98| 1| XCTAssertEqual(negativeInsetByRight.left, 10.0) 99| 1| XCTAssertEqual(negativeInsetByRight.bottom, 10.0) 100| 1| XCTAssertEqual(negativeInsetByRight.right, 5.0) 101| 1| } 102| | 103| 1| func testInsetByHorizontal() { 104| 1| let inset = EdgeInsets(top: 10.0, left: 10.0, bottom: 10.0, right: 10.0) 105| 1| let insetByHorizontal = inset.insetBy(horizontal: 5.0) 106| 1| XCTAssertNotEqual(inset, insetByHorizontal) 107| 1| XCTAssertEqual(insetByHorizontal.left, 12.5) 108| 1| XCTAssertEqual(insetByHorizontal.right, 12.5) 109| 1| XCTAssertEqual(insetByHorizontal.top, 10.0) 110| 1| XCTAssertEqual(insetByHorizontal.bottom, 10.0) 111| 1| 112| 1| let negativeInsetByHorizontal = inset.insetBy(horizontal: -5.0) 113| 1| XCTAssertNotEqual(inset, negativeInsetByHorizontal) 114| 1| XCTAssertEqual(negativeInsetByHorizontal.left, 7.5) 115| 1| XCTAssertEqual(negativeInsetByHorizontal.right, 7.5) 116| 1| XCTAssertEqual(negativeInsetByHorizontal.top, 10.0) 117| 1| XCTAssertEqual(negativeInsetByHorizontal.bottom, 10.0) 118| 1| } 119| | 120| 1| func testInsetByVertical() { 121| 1| let inset = EdgeInsets(top: 10.0, left: 10.0, bottom: 10.0, right: 10.0) 122| 1| let insetByVertical = inset.insetBy(vertical: 5.0) 123| 1| XCTAssertNotEqual(inset, insetByVertical) 124| 1| XCTAssertEqual(insetByVertical.left, 10.0) 125| 1| XCTAssertEqual(insetByVertical.right, 10.0) 126| 1| XCTAssertEqual(insetByVertical.top, 12.5) 127| 1| XCTAssertEqual(insetByVertical.bottom, 12.5) 128| 1| 129| 1| let negativeInsetByVertical = inset.insetBy(vertical: -5.0) 130| 1| XCTAssertNotEqual(inset, negativeInsetByVertical) 131| 1| XCTAssertEqual(negativeInsetByVertical.left, 10.0) 132| 1| XCTAssertEqual(negativeInsetByVertical.right, 10.0) 133| 1| XCTAssertEqual(negativeInsetByVertical.top, 7.5) 134| 1| XCTAssertEqual(negativeInsetByVertical.bottom, 7.5) 135| 1| } 136| | 137| 1| func testInsetComposing() { 138| 1| let inset = EdgeInsets(top: 10.0, left: 10.0, bottom: 10.0, right: 10.0) 139| 1| let composedInset = inset.insetBy(bottom: 5.0).insetBy(horizontal: 5.0) 140| 1| XCTAssertNotEqual(inset, composedInset) 141| 1| XCTAssertEqual(composedInset.left, 12.5) 142| 1| XCTAssertEqual(composedInset.right, 12.5) 143| 1| XCTAssertEqual(composedInset.top, 10) 144| 1| XCTAssertEqual(composedInset.bottom, 15.0) 145| 1| 146| 1| let negativeComposedInset = inset.insetBy(bottom: -5.0).insetBy(horizontal: -5.0) 147| 1| XCTAssertNotEqual(inset, negativeComposedInset) 148| 1| XCTAssertEqual(negativeComposedInset.left, 7.5) 149| 1| XCTAssertEqual(negativeComposedInset.right, 7.5) 150| 1| XCTAssertEqual(negativeComposedInset.top, 10) 151| 1| XCTAssertEqual(negativeComposedInset.bottom, 5.0) 152| 1| } 153| | 154| 1| func testAddition() { 155| 1| XCTAssertEqual(EdgeInsets.zero + EdgeInsets.zero, EdgeInsets.zero) 156| 1| 157| 1| let insets1 = EdgeInsets(top: 1, left: 2, bottom: 3, right: 4) 158| 1| let insets2 = EdgeInsets(top: 5, left: 6, bottom: 7, right: 8) 159| 1| let expected = EdgeInsets(top: 6, left: 8, bottom: 10, right: 12) 160| 1| XCTAssertEqual(insets1 + insets2, expected) 161| 1| 162| 1| let negativeInsets1 = EdgeInsets(top: -1, left: -2, bottom: -3, right: -4) 163| 1| let negativeInsets2 = EdgeInsets(top: -5, left: -6, bottom: -7, right: -8) 164| 1| let negativeExpected = EdgeInsets(top: -6, left: -8, bottom: -10, right: -12) 165| 1| XCTAssertEqual(negativeInsets1 + negativeInsets2, negativeExpected) 166| 1| } 167| | 168| 1| func testInPlaceAddition() { 169| 1| var zero = EdgeInsets.zero 170| 1| zero += EdgeInsets.zero 171| 1| XCTAssertEqual(zero, EdgeInsets.zero) 172| 1| 173| 1| var insets = EdgeInsets(top: 1, left: 2, bottom: 3, right: 4) 174| 1| insets += EdgeInsets(top: 5, left: 6, bottom: 7, right: 8) 175| 1| let expected = EdgeInsets(top: 6, left: 8, bottom: 10, right: 12) 176| 1| XCTAssertEqual(insets, expected) 177| 1| 178| 1| var negativeInsets = EdgeInsets(top: -1, left: -2, bottom: -3, right: -4) 179| 1| negativeInsets += EdgeInsets(top: -5, left: -6, bottom: -7, right: -8) 180| 1| let negativeExpected = EdgeInsets(top: -6, left: -8, bottom: -10, right: -12) 181| 1| XCTAssertEqual(negativeInsets, negativeExpected) 182| 1| } 183| |} 184| | 185| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Tests/SpriteKitTests/SKNodeExtensionTests.swift: 1| |// SKNodeExtensionTests.swift - Copyright 2020 SwifterSwift 2| | 3| |@testable import SwifterSwift 4| |import XCTest 5| | 6| |#if canImport(SpriteKit) 7| |import SpriteKit 8| | 9| |final class SKNodeExtensionTests: XCTestCase { 10| 1| func testDescendants() { 11| 1| let scene = SKScene() 12| 1| let childOne = SKNode() 13| 1| scene.addChild(childOne) 14| 1| let childTwo = SKNode() 15| 1| childOne.addChild(childTwo) 16| 1| XCTAssertEqual(scene.descendants(), [childOne, childTwo]) 17| 1| XCTAssertEqual(childOne.descendants(), [childTwo]) 18| 1| XCTAssertEqual(childTwo.descendants(), []) 19| 1| } 20| | 21| 1| func testSKNodeCenter() { 22| 1| let scene = SKScene(size: CGSize(width: 100, height: 100)) 23| 1| let nodeSize = CGSize(width: 50, height: 50) 24| 1| let node = SKSpriteNode(color: .black, size: nodeSize) 25| 1| scene.addChild(node) 26| 1| 27| 1| node.center = CGPoint(x: scene.frame.midX, y: scene.frame.midY) 28| 1| XCTAssertEqual(node.center, CGPoint(x: 50, y: 50)) 29| 1| XCTAssertEqual(node.frame.size, nodeSize) 30| 1| XCTAssertEqual(node.frame.origin, CGPoint(x: 25, y: 25)) 31| 1| } 32| | 33| 1| func testTopLeft() { 34| 1| let scene = SKScene(size: CGSize(width: 100, height: 100)) 35| 1| let nodeSize = CGSize(width: 50, height: 50) 36| 1| let node = SKSpriteNode(color: .black, size: nodeSize) 37| 1| scene.addChild(node) 38| 1| 39| 1| node.topLeft = CGPoint(x: scene.frame.midX, y: scene.frame.midY) 40| 1| XCTAssertEqual(node.topLeft, CGPoint(x: 50, y: 50)) 41| 1| XCTAssertEqual(node.frame.size, nodeSize) 42| 1| XCTAssertEqual(node.frame.origin, CGPoint(x: 50, y: 0)) 43| 1| } 44| | 45| 1| func testTopRight() { 46| 1| let scene = SKScene(size: CGSize(width: 100, height: 100)) 47| 1| let nodeSize = CGSize(width: 50, height: 50) 48| 1| let node = SKSpriteNode(color: .black, size: nodeSize) 49| 1| scene.addChild(node) 50| 1| 51| 1| node.topRight = CGPoint(x: scene.frame.midX, y: scene.frame.midY) 52| 1| XCTAssertEqual(node.topRight, CGPoint(x: 50, y: 50)) 53| 1| XCTAssertEqual(node.frame.size, nodeSize) 54| 1| XCTAssertEqual(node.frame.origin, CGPoint(x: 0, y: 0)) 55| 1| } 56| | 57| 1| func testBottomLeft() { 58| 1| let scene = SKScene(size: CGSize(width: 100, height: 100)) 59| 1| let nodeSize = CGSize(width: 50, height: 50) 60| 1| let node = SKSpriteNode(color: .black, size: nodeSize) 61| 1| scene.addChild(node) 62| 1| 63| 1| node.bottomLeft = CGPoint(x: scene.frame.midX, y: scene.frame.midY) 64| 1| XCTAssertEqual(node.bottomLeft, CGPoint(x: 50, y: 50)) 65| 1| XCTAssertEqual(node.frame.size, nodeSize) 66| 1| XCTAssertEqual(node.frame.origin, CGPoint(x: 50, y: 50)) 67| 1| } 68| | 69| 1| func testBottomRight() { 70| 1| let scene = SKScene(size: CGSize(width: 100, height: 100)) 71| 1| let nodeSize = CGSize(width: 50, height: 50) 72| 1| let node = SKSpriteNode(color: .black, size: nodeSize) 73| 1| scene.addChild(node) 74| 1| 75| 1| node.bottomRight = CGPoint(x: scene.frame.midX, y: scene.frame.midY) 76| 1| XCTAssertEqual(node.bottomRight, CGPoint(x: 50, y: 50)) 77| 1| XCTAssertEqual(node.frame.size, nodeSize) 78| 1| XCTAssertEqual(node.frame.origin, CGPoint(x: 0, y: 50)) 79| 1| } 80| |} 81| | 82| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Tests/StoreKitTests/SKProductTests.swift: 1| |// SKProductTests.swift - Copyright 2020 SwifterSwift 2| | 3| |@testable import SwifterSwift 4| |import XCTest 5| | 6| |#if canImport(StoreKit) 7| |import StoreKit 8| | 9| |private extension SKProduct { 10| | /// Creates a mocked `SKProduct`. 11| | /// - Parameters: 12| | /// - identifier: Product identifier. 13| | /// - price: Product price. 14| | /// - priceLocale: Product price locale. 15| 4| convenience init(identifier: String, price: String, priceLocale: Locale) { 16| 4| self.init() 17| 4| setValue(identifier, forKey: "productIdentifier") 18| 4| setValue(NSDecimalNumber(string: price), forKey: "price") 19| 4| setValue(priceLocale, forKey: "priceLocale") 20| 4| } 21| |} 22| | 23| |final class SKProductTests: XCTestCase { 24| 1| func testLocalizedPrice() { 25| 1| let usMockedProduct = SKProduct( 26| 1| identifier: "com.swifterswift.usmockedproduct", 27| 1| price: "10.50", 28| 1| priceLocale: Locale(identifier: "en_US")) 29| 1| 30| 1| XCTAssertEqual(usMockedProduct.localizedPrice, "$10.50") 31| 1| 32| 1| let trMockedProduct = SKProduct( 33| 1| identifier: "com.swifterswift.trmockedproduct", 34| 1| price: "7.50", 35| 1| priceLocale: Locale(identifier: "tr_TR")) 36| 1| 37| 1| XCTAssertEqual(trMockedProduct.localizedPrice, "₺7,50") 38| 1| 39| 1| let noMockedProduct = SKProduct( 40| 1| identifier: "com.swifterswift.nomockedproduct", 41| 1| price: "100.00", 42| 1| priceLocale: Locale(identifier: "nb_NO")) 43| 1| 44| 1| XCTAssertEqual(noMockedProduct.localizedPrice, "kr 100,00") 45| 1| 46| 1| let frMockedProduct = SKProduct( 47| 1| identifier: "com.swifterswift.frmockedproduct", 48| 1| price: "10.00", 49| 1| priceLocale: Locale(identifier: "fr_FR")) 50| 1| 51| 1| XCTAssertEqual(frMockedProduct.localizedPrice, "10,00 €") 52| 1| } 53| |} 54| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Tests/SwiftStdlibTests/ArrayExtensionsTests.swift: 1| |// ArrayExtensionsTests.swift - Copyright 2020 SwifterSwift 2| | 3| |@testable import SwifterSwift 4| |// 5| |// ArrayExtensionsTests.swift 6| |// SwifterSwift 7| |// 8| |// Created by Omar Albeik on 8/26/16. 9| |// Copyright © 2016 SwifterSwift 10| |// 11| |import XCTest 12| | 13| |final class ArrayExtensionsTests: XCTestCase { 14| 1| func testPrepend() { 15| 1| var arr = [2, 3, 4, 5] 16| 1| arr.prepend(1) 17| 1| XCTAssertEqual(arr, [1, 2, 3, 4, 5]) 18| 1| } 19| | 20| 1| func testSafeSwap() { 21| 1| var array: [Int] = [1, 2, 3, 4, 5] 22| 1| array.safeSwap(from: 3, to: 0) 23| 1| XCTAssertEqual(array[3], 1) 24| 1| XCTAssertEqual(array[0], 4) 25| 1| 26| 1| var newArray = array 27| 1| newArray.safeSwap(from: 1, to: 1) 28| 1| XCTAssertEqual(newArray, array) 29| 1| 30| 1| newArray = array 31| 1| newArray.safeSwap(from: 1, to: 12) 32| 1| XCTAssertEqual(newArray, array) 33| 1| 34| 1| let emptyArray: [Int] = [] 35| 1| var swappedEmptyArray = emptyArray 36| 1| swappedEmptyArray.safeSwap(from: 1, to: 3) 37| 1| XCTAssertEqual(swappedEmptyArray, emptyArray) 38| 1| } 39| | 40| 1| func testSortedLike() { 41| 1| let order1 = [1, 2, 3, 4, 5] 42| 1| let candidate1 = [2, 5, 3, 1, 4] 43| 1| XCTAssertEqual(candidate1.sorted(like: order1, keyPath: \.self), order1) 44| 1| 45| 1| let candidate2 = [2, 5, 3, 6, 1, 4] 46| 1| XCTAssertEqual(candidate2.sorted(like: order1, keyPath: \.self), [1, 2, 3, 4, 5, 6]) 47| 1| 48| 1| // swiftlint:disable:next nesting 49| 1| struct TestStruct { let prop: String } 50| 1| let order3 = ["1", "2", "3", "4", "5"] 51| 1| let candidate3 = [ 52| 1| TestStruct(prop: "3"), 53| 1| TestStruct(prop: "2"), 54| 1| TestStruct(prop: "2"), 55| 1| TestStruct(prop: "1"), 56| 1| TestStruct(prop: "3") 57| 1| ] 58| 6| XCTAssertEqual(candidate3.sorted(like: order3, keyPath: \.prop).map(\.prop), ["1", "2", "2", "3", "3"]) ------------------ | $s22SwifterSwift_tvOSTests20ArrayExtensionsTestsC14testSortedLikeyyFSaySSGyKXEfu3_SSAcDyyF10TestStructL_Vcs7KeyPathCyAGSSGcfu4_: | 58| 1| XCTAssertEqual(candidate3.sorted(like: order3, keyPath: \.prop).map(\.prop), ["1", "2", "2", "3", "3"]) ------------------ | $s22SwifterSwift_tvOSTests20ArrayExtensionsTestsC14testSortedLikeyyFSaySSGyKXEfu3_SSAcDyyF10TestStructL_Vcs7KeyPathCyAGSSGcfu4_SSAGcfu5_: | 58| 5| XCTAssertEqual(candidate3.sorted(like: order3, keyPath: \.prop).map(\.prop), ["1", "2", "2", "3", "3"]) ------------------ 59| 1| } 60| | 61| 1| func testRemoveAll() { 62| 1| var arr = [0, 1, 2, 0, 3, 4, 5, 0, 0] 63| 1| arr.removeAll(0) 64| 1| XCTAssertEqual(arr, [1, 2, 3, 4, 5]) 65| 1| arr = [] 66| 1| arr.removeAll(0) 67| 1| XCTAssertEqual(arr, []) 68| 1| } 69| | 70| 1| func testRemoveAllItems() { 71| 1| var arr = [0, 1, 2, 2, 0, 3, 4, 5, 0, 0] 72| 1| arr.removeAll([0, 2]) 73| 1| XCTAssertEqual(arr, [1, 3, 4, 5]) 74| 1| arr.removeAll([]) 75| 1| XCTAssertEqual(arr, [1, 3, 4, 5]) 76| 1| arr = [] 77| 1| arr.removeAll([]) 78| 1| XCTAssertEqual(arr, []) 79| 1| } 80| | 81| 1| func testRemoveDuplicates() { 82| 1| var array = [1, 1, 2, 2, 3, 3, 3, 4, 5] 83| 1| array.removeDuplicates() 84| 1| XCTAssertEqual(array, [1, 2, 3, 4, 5]) 85| 1| } 86| | 87| 1| func testWithoutDuplicates() { 88| 1| XCTAssertEqual([1, 1, 2, 2, 3, 3, 3, 4, 5].withoutDuplicates(), [1, 2, 3, 4, 5]) 89| 1| XCTAssertEqual(["h", "e", "l", "l", "o"].withoutDuplicates(), ["h", "e", "l", "o"]) 90| 1| } 91| | 92| 1| func testWithoutDuplicatesUsingKeyPath() { 93| 1| let array = [ 94| 1| Person(name: "Wade", age: 20, location: Location(city: "London")), 95| 1| Person(name: "James", age: 32), 96| 1| Person(name: "James", age: 36), 97| 1| Person(name: "Rose", age: 29), 98| 1| Person(name: "James", age: 72, location: Location(city: "Moscow")), 99| 1| Person(name: "Rose", age: 56), 100| 1| Person(name: "Wade", age: 22, location: Location(city: "Prague")) 101| 1| ] 102| 1| let arrayWithoutDuplicatesHashable = array.withoutDuplicates(keyPath: \.name) 103| 1| let arrayWithoutDuplicatesHashablePrepared = [ 104| 1| Person(name: "Wade", age: 20, location: Location(city: "London")), 105| 1| Person(name: "James", age: 32), 106| 1| Person(name: "Rose", age: 29) 107| 1| ] 108| 1| XCTAssertEqual(arrayWithoutDuplicatesHashable, arrayWithoutDuplicatesHashablePrepared) 109| 1| let arrayWithoutDuplicatesNHashable = array.withoutDuplicates(keyPath: \.location) 110| 1| let arrayWithoutDuplicatesNHashablePrepared = [ 111| 1| Person(name: "Wade", age: 20, location: Location(city: "London")), 112| 1| Person(name: "James", age: 32), 113| 1| Person(name: "James", age: 72, location: Location(city: "Moscow")), 114| 1| Person(name: "Wade", age: 22, location: Location(city: "Prague")) 115| 1| ] 116| 1| XCTAssertEqual(arrayWithoutDuplicatesNHashable, arrayWithoutDuplicatesNHashablePrepared) 117| 1| } 118| |} /Users/runner/work/SwifterSwift/SwifterSwift/Tests/SwiftStdlibTests/BidirectionalCollectionExtensionsTests.swift: 1| |// BidirectionalCollectionExtensionsTests.swift - Copyright 2020 SwifterSwift 2| | 3| |@testable import SwifterSwift 4| |import XCTest 5| | 6| |final class BidirectionalCollectionExtensionsTests: XCTestCase { 7| 1| func testOffsetSubscript() { 8| 1| let arr = [1, 2, 3, 4, 5] 9| 1| XCTAssertEqual(arr[offset: 0], 1) 10| 1| XCTAssertEqual(arr[offset: 4], 5) 11| 1| XCTAssertEqual(arr[offset: -2], 4) 12| 1| } 13| | 14| 1| func testLastByKeyPath() { 15| 1| let array1 = [ 16| 1| Person(name: "John", age: 30, location: Location(city: "Boston")), 17| 1| Person(name: "Jan", age: 22, location: nil), 18| 1| Person(name: "Roman", age: 30, location: Location(city: "Moscow")) 19| 1| ] 20| 1| 21| 1| let last30Age = array1.last(where: \.age, equals: 30) 22| 1| 23| 1| XCTAssertEqual(last30Age, array1.last) 24| 1| 25| 1| let missingPerson = array1.last(where: \.name, equals: "Tom") 26| 1| 27| 1| XCTAssertNil(missingPerson) 28| 1| } 29| |} /Users/runner/work/SwifterSwift/SwifterSwift/Tests/SwiftStdlibTests/BinaryFloatingPointExtensionsTests.swift: 1| |// BinaryFloatingPointExtensionsTests.swift - Copyright 2020 SwifterSwift 2| | 3| |@testable import SwifterSwift 4| |import XCTest 5| | 6| |final class BinaryFloatingPointExtensionsTests: XCTestCase { 7| 1| func testRounded() { 8| 1| let double = 3.1415927 9| 1| XCTAssertEqual(double.rounded(numberOfDecimalPlaces: 3, rule: .up), 3.142) 10| 1| XCTAssertEqual(double.rounded(numberOfDecimalPlaces: 3, rule: .down), 3.141) 11| 1| XCTAssertEqual(double.rounded(numberOfDecimalPlaces: 2, rule: .awayFromZero), 3.15) 12| 1| 13| 1| let float: Float = 3.1415927 14| 1| XCTAssertEqual(float.rounded(numberOfDecimalPlaces: 4, rule: .towardZero), 3.1415) 15| 1| XCTAssertEqual(float.rounded(numberOfDecimalPlaces: -1, rule: .toNearestOrEven), 3) 16| 1| XCTAssertEqual(float.rounded(numberOfDecimalPlaces: 0, rule: .toNearestOrAwayFromZero), 3) 17| 1| } 18| |} /Users/runner/work/SwifterSwift/SwifterSwift/Tests/SwiftStdlibTests/BoolExtensionsTests.swift: 1| |// BoolExtensionsTests.swift - Copyright 2020 SwifterSwift 2| | 3| |@testable import SwifterSwift 4| |import XCTest 5| | 6| |final class BoolExtensionsTests: XCTestCase { 7| 1| func testInt() { 8| 1| XCTAssertEqual(true.int, 1) 9| 1| XCTAssertEqual(false.int, 0) 10| 1| } 11| | 12| 1| func testString() { 13| 1| XCTAssertEqual(true.string, "true") 14| 1| XCTAssertEqual(false.string, "false") 15| 1| } 16| |} /Users/runner/work/SwifterSwift/SwifterSwift/Tests/SwiftStdlibTests/CharacterExtensionsTests.swift: 1| |// CharacterExtensionsTests.swift - Copyright 2020 SwifterSwift 2| | 3| |// 4| |// CharacterExtensionsTests.swift 5| |// SwifterSwift 6| |// 7| |// Created by Omar Albeik on 8/27/16. 8| |// Copyright © 2016 SwifterSwift 9| |// 10| | 11| |@testable import SwifterSwift 12| |import XCTest 13| | 14| |final class CharacterExtensionsTests: XCTestCase { 15| 1| func testIsEmoji() { 16| 1| XCTAssert(Character("😂").isEmoji) 17| 1| XCTAssertFalse(Character("j").isEmoji) 18| 1| } 19| | 20| 1| func testInt() { 21| 1| XCTAssertNotNil(Character("1").int) 22| 1| XCTAssertEqual(Character("1").int, 1) 23| 1| XCTAssertNil(Character("s").int) 24| 1| } 25| | 26| 1| func testString() { 27| 1| XCTAssertEqual(Character("s").string, String("s")) 28| 1| } 29| | 30| 1| func testUppercased() { 31| 1| XCTAssertEqual(Character("s").uppercased, Character("S")) 32| 1| } 33| | 34| 1| func testLowercased() { 35| 1| XCTAssertEqual(Character("S").lowercased, Character("s")) 36| 1| } 37| | 38| 1| func testRandom() { 39| 1| var string1 = String() 40| 1| var string2 = String() 41| 10| for _ in 0..<10 { 42| 10| string1.append(Character.randomAlphanumeric()) 43| 10| string2.append(Character.randomAlphanumeric()) 44| 10| } 45| 1| XCTAssertNotEqual(string1, string2) 46| 1| } 47| | 48| 1| func testOperators() { 49| 1| let sLetter = Character("s") 50| 1| XCTAssertEqual(sLetter * 5, "sssss") 51| 1| XCTAssertEqual(5 * sLetter, "sssss") 52| 1| 53| 1| XCTAssertEqual(sLetter * 0, "") 54| 1| XCTAssertEqual(0 * sLetter, "") 55| 1| 56| 1| XCTAssertEqual(sLetter * -5, "") 57| 1| XCTAssertEqual(-5 * sLetter, "") 58| 1| } 59| |} /Users/runner/work/SwifterSwift/SwifterSwift/Tests/SwiftStdlibTests/CollectionExtensionsTests.swift: 1| |// CollectionExtensionsTests.swift - Copyright 2020 SwifterSwift 2| | 3| |@testable import SwifterSwift 4| |import XCTest 5| | 6| |final class CollectionExtensionsTests: XCTestCase { 7| 8| let collection = [1, 2, 3, 4, 5] 8| | 9| 1| func testFullRange() { 10| 1| XCTAssertEqual(collection.fullRange, 0..<5) 11| 1| XCTAssertEqual([].fullRange, 0..<0) 12| 1| } 13| | 14| 1| func testForEachInParallel() { 15| 1| let expectation = XCTestExpectation(description: "forEachInParallel") 16| 1| 17| 1| var count = 0 18| 1| let countQueue = DispatchQueue.global() 19| 5| collection.forEachInParallel { 20| 5| XCTAssert(collection.contains($0)) 21| 5| countQueue.async { 22| 5| count += 1 23| 5| if count == self.collection.count { 24| 1| expectation.fulfill() 25| 5| } 26| 5| } 27| 5| } 28| 1| if count != collection.count { 29| 0| wait(for: [expectation], timeout: 0.5) 30| 1| } 31| 1| } 32| | 33| 1| func testSafeSubscript() { 34| 1| XCTAssertNotNil(collection[safe: 2]) 35| 1| XCTAssertEqual(collection[safe: 2], 3) 36| 1| XCTAssertNil(collection[safe: 10]) 37| 1| } 38| | 39| 1| func testIndicesWhere() { 40| 1| let array: [Int] = [0, 1, 2, 3, 4, 5, 6, 7, 8, 9] 41| 10| let indices = array.indices { $0 % 2 == 0 } 42| 1| XCTAssertEqual(indices, [0, 2, 4, 6, 8]) 43| 1| let emptyArray: [Int] = [] 44| 1| let emptyIndices = emptyArray.indices { $0 % 2 == 0 } 45| 1| XCTAssertNil(emptyIndices) 46| 1| } 47| | 48| 1| func testForEachSlice() { 49| 1| // A slice with value zero 50| 1| var iterations: Int = 0 51| 1| var array: [String] = ["james", "irving", "jordan", "jonshon", "iverson", "shaq"] 52| 1| array.forEach(slice: 0) { _ in 53| 0| iterations += 1 54| 0| } 55| 1| XCTAssertEqual(iterations, 0) 56| 1| 57| 1| // A slice that divide the total evenly 58| 1| array = ["james", "irving", "jordan", "jonshon", "iverson", "shaq"] 59| 3| array.forEach(slice: 2) { sliceArray in 60| 3| switch iterations { 61| 3| case 0: XCTAssertEqual(sliceArray, ["james", "irving"]) 62| 3| case 1: XCTAssertEqual(sliceArray, ["jordan", "jonshon"]) 63| 3| case 2: XCTAssertEqual(sliceArray, ["iverson", "shaq"]) 64| 3| default: break 65| 3| } 66| 3| iterations += 1 67| 3| } 68| 1| XCTAssertEqual(iterations, 3) 69| 1| 70| 1| // A slice that does not divide the total evenly 71| 1| iterations = 0 72| 1| array = ["james", "irving", "jordan", "jonshon", "iverson", "shaq", "bird"] 73| 4| array.forEach(slice: 2) { sliceArray in 74| 4| switch iterations { 75| 4| case 0: XCTAssertEqual(sliceArray, ["james", "irving"]) 76| 4| case 1: XCTAssertEqual(sliceArray, ["jordan", "jonshon"]) 77| 4| case 2: XCTAssertEqual(sliceArray, ["iverson", "shaq"]) 78| 4| case 3: XCTAssertEqual(sliceArray, ["bird"]) 79| 4| default: break 80| 4| } 81| 4| iterations += 1 82| 4| } 83| 1| XCTAssertEqual(iterations, 4) 84| 1| 85| 1| // A slice greater than the array count 86| 1| iterations = 0 87| 1| array = ["james", "irving", "jordan", "jonshon"] 88| 1| array.forEach(slice: 6) { sliceArray in 89| 1| XCTAssertEqual(sliceArray, ["james", "irving", "jordan", "jonshon"]) 90| 1| iterations += 1 91| 1| } 92| 1| XCTAssertEqual(iterations, 1) 93| 1| 94| 1| iterations = 0 95| 1| 96| 1| // Empty array 97| 1| array = [] 98| 1| array.forEach(slice: 1) { _ in 99| 0| XCTFail("Should not find any slices") 100| 0| iterations += 1 101| 0| } 102| 1| XCTAssertEqual(iterations, 0) 103| 1| } 104| | 105| 1| func testGroupBySize() { 106| 1| // A slice with value zero 107| 1| var array: [String] = ["james", "irving", "jordan", "jonshon", "iverson", "shaq"] 108| 1| var slices = array.group(by: 0) 109| 1| XCTAssertNil(slices) 110| 1| 111| 1| // A slice that divide the total evenly 112| 1| array = ["james", "irving", "jordan", "jonshon", "iverson", "shaq"] 113| 1| slices = array.group(by: 2) 114| 1| XCTAssertNotNil(slices) 115| 1| XCTAssertEqual(slices?.count, 3) 116| 1| 117| 1| // A slice that does not divide the total evenly 118| 1| array = ["james", "irving", "jordan", "jonshon", "iverson", "shaq", "bird"] 119| 1| slices = array.group(by: 2) 120| 1| XCTAssertNotNil(slices) 121| 1| XCTAssertEqual(slices?.count, 4) 122| 1| 123| 1| // A slice greater than the array count 124| 1| array = ["james", "irving", "jordan", "jonshon"] 125| 1| slices = array.group(by: 6) 126| 1| XCTAssertNotNil(slices) 127| 1| XCTAssertEqual(slices?.count, 1) 128| 1| } 129| | 130| 1| func testIndices() { 131| 1| XCTAssertEqual([].indices(of: 5), []) 132| 1| XCTAssertEqual([1, 1, 2, 3, 4, 1, 2, 1].indices(of: 5), []) 133| 1| XCTAssertEqual([1, 1, 2, 3, 4, 1, 2, 1].indices(of: 1), [0, 1, 5, 7]) 134| 1| XCTAssertEqual(["a", "b", "c", "b", "4", "1", "2", "1"].indices(of: "b"), [1, 3]) 135| 1| } 136| | 137| 1| func testAverage() { 138| 1| XCTAssertEqual([1.2, 2.3, 3.4, 4.5, 5.6].average(), 3.4) 139| 1| XCTAssertEqual([Double]().average(), 0) 140| 1| 141| 1| XCTAssertEqual([1, 2, 3, 4, 5].average(), 3) 142| 1| XCTAssertEqual([Int]().average(), 0) 143| 1| } 144| |} /Users/runner/work/SwifterSwift/SwifterSwift/Tests/SwiftStdlibTests/ComparableExtensionsTests.swift: 1| |// ComparableExtensionsTests.swift - Copyright 2020 SwifterSwift 2| | 3| |import XCTest 4| | 5| |@testable import SwifterSwift 6| | 7| |final class ComparableExtensionsTests: XCTestCase { 8| 1| func testIsBetween() { 9| 1| XCTAssertFalse(1.isBetween(5...7), "number range") 10| 1| XCTAssert(7.isBetween(6...12), "number range") 11| 1| XCTAssert(0.32.isBetween(0.31...0.33), "float range") 12| 1| XCTAssert("c".isBetween("a"..."d"), "string range") 13| 1| 14| 1| let date = Date() 15| 1| XCTAssert(date.isBetween(date...date.addingTimeInterval(1000)), "date range") 16| 1| } 17| | 18| 1| func testClamped() { 19| 1| XCTAssertEqual(1.clamped(to: 3...8), 3) 20| 1| XCTAssertEqual(4.clamped(to: 3...7), 4) 21| 1| XCTAssertEqual("c".clamped(to: "e"..."g"), "e") 22| 1| XCTAssertEqual(0.32.clamped(to: 0.37...0.42), 0.37) 23| 1| } 24| |} /Users/runner/work/SwifterSwift/SwifterSwift/Tests/SwiftStdlibTests/DecodableExtensionsTests.swift: 1| |// DecodableExtensionsTests.swift - Copyright 2020 SwifterSwift 2| | 3| |@testable import SwifterSwift 4| |import XCTest 5| | 6| |// swiftlint:disable identifier_name 7| |private struct City: Decodable { 8| | var id: Int 9| | var name: String 10| | var url: URL 11| |} 12| | 13| |// swiftlint:enable identifier_name 14| | 15| |final class DecodableExtensionsTests: XCTestCase { 16| 1| private var mockJsonData: Data { 17| 1| return #"{"id": 1, "name": "Şanlıurfa", "url": "https://cdn.pixabay.com/photo/2017/09/27/20/55/sanliurfa-2793424_1280.jpg"}"# .data( 18| 1| using: .utf8)! 19| 1| } 20| | 21| 1| private var invalidMockJsonData: Data { 22| 1| return #"{"id": "1", "name": "Şanlıurfa", "url": "https://cdn.pixabay.com/photo/2017/09/27/20/55/sanliurfa-2793424_1280.jpg"}"# .data( 23| 1| using: .utf8)! 24| 1| } 25| | 26| 1| func testDecodeModel() { 27| 1| guard let city = City(from: mockJsonData) else { 28| 0| XCTAssert(false, "Could not parse model") 29| 0| return 30| 1| } 31| 1| 32| 1| XCTAssertEqual(city.id, 1) 33| 1| XCTAssertEqual(city.name, "Şanlıurfa") 34| 1| XCTAssertEqual( 35| 1| city.url, 36| 1| URL(string: "https://cdn.pixabay.com/photo/2017/09/27/20/55/sanliurfa-2793424_1280.jpg")) 37| 1| } 38| | 39| 1| func testDecodeModelInvalidData() { 40| 1| XCTAssertNil(City(from: invalidMockJsonData)) 41| 1| } 42| |} /Users/runner/work/SwifterSwift/SwifterSwift/Tests/SwiftStdlibTests/DictionaryExtensionsTests.swift: 1| |// DictionaryExtensionsTests.swift - Copyright 2020 SwifterSwift 2| | 3| |@testable import SwifterSwift 4| |import XCTest 5| | 6| |final class DictionaryExtensionsTests: XCTestCase { 7| 16| var testDict: [String: Any] = ["testKey": "testValue", "testArrayKey": [1, 2, 3, 4, 5]] 8| | 9| 1| func testHasKey() { 10| 1| XCTAssert(testDict.has(key: "testKey")) 11| 1| XCTAssertFalse(testDict.has(key: "anotherKey")) 12| 1| } 13| | 14| 1| func testRemoveAll() { 15| 1| var dict: [String: String] = ["key1": "value1", "key2": "value2", "key3": "value3"] 16| 1| dict.removeAll(keys: ["key1", "key2"]) 17| 1| XCTAssert(dict.keys.contains("key3")) 18| 1| XCTAssertFalse(dict.keys.contains("key1")) 19| 1| XCTAssertFalse(dict.keys.contains("key2")) 20| 1| } 21| | 22| 1| func testRemoveElementForRandomKey() { 23| 1| var emptyDict = [String: String]() 24| 1| XCTAssertNil(emptyDict.removeValueForRandomKey()) 25| 1| 26| 1| var dict: [String: String] = ["key1": "value1", "key2": "value2", "key3": "value3"] 27| 1| let elements = dict.count 28| 1| let removedElement = dict.removeValueForRandomKey() 29| 1| XCTAssertEqual(elements - 1, dict.count) 30| 2| XCTAssertFalse(dict.contains(where: { $0.value == removedElement })) 31| 1| } 32| | 33| 1| func testJsonData() { 34| 1| let dict = ["key": "value"] 35| 1| 36| 1| let jsonString = "{\"key\":\"value\"}" 37| 1| let jsonData = jsonString.data(using: .utf8) 38| 1| 39| 1| let prettyJsonString = "{\n \"key\" : \"value\"\n}" 40| 1| let prettyJsonData = prettyJsonString.data(using: .utf8) 41| 1| 42| 1| XCTAssertEqual(dict.jsonData(), jsonData) 43| 1| XCTAssertEqual(dict.jsonData(prettify: true), prettyJsonData) 44| 1| 45| 1| XCTAssertNil(["key": NSObject()].jsonData()) 46| 1| XCTAssertNil([1: 2].jsonData()) 47| 1| } 48| | 49| 1| func testJsonString() { 50| 1| XCTAssertNotNil(testDict.jsonString()) 51| 1| XCTAssertEqual(testDict.jsonString()?.contains("\"testArrayKey\":[1,2,3,4,5]"), true) 52| 1| XCTAssertEqual(testDict.jsonString()?.contains("\"testKey\":\"testValue\""), true) 53| 1| 54| 1| XCTAssertEqual( 55| 1| testDict.jsonString(prettify: true)?.contains("[\n 1,\n 2,\n 3,\n 4,\n 5\n ]"), 56| 1| true) 57| 1| 58| 1| XCTAssertNil(["key": NSObject()].jsonString()) 59| 1| XCTAssertNil([1: 2].jsonString()) 60| 1| } 61| | 62| 1| func testKeysForValue() { 63| 1| let dict = ["key1": "value1", "key2": "value1", "key3": "value2"] 64| 1| let result = dict.keys(forValue: "value1") 65| 1| XCTAssert(result.contains("key1")) 66| 1| XCTAssert(result.contains("key2")) 67| 1| XCTAssertFalse(result.contains("key3")) 68| 1| } 69| | 70| 1| func testLowercaseAllKeys() { 71| 1| var dict = ["tEstKeY": "value"] 72| 1| dict.lowercaseAllKeys() 73| 1| XCTAssertEqual(dict, ["testkey": "value"]) 74| 1| } 75| | 76| 1| func testSubscriptKeypath() { 77| 1| var json = ["key": ["key1": ["key2": "value"]]] 78| 1| 79| 1| XCTAssertNil(json[path: []] as? String) 80| 1| XCTAssertEqual(json[path: ["key", "key1"]] as? [String: String], ["key2": "value"]) 81| 1| XCTAssertEqual(json[path: ["key", "key1", "key2"]] as? String, "value") 82| 1| json[path: ["key", "key1", "key2"]] = "newValue" 83| 1| XCTAssertEqual(json[path: ["key", "key1", "key2"]] as? String, "newValue") 84| 1| } 85| | 86| 1| func testOperatorPlus() { 87| 1| let dict: [String: String] = ["key1": "value1"] 88| 1| let dict2: [String: String] = ["key2": "value2"] 89| 1| let result = dict + dict2 90| 1| XCTAssert(result.keys.contains("key1")) 91| 1| XCTAssert(result.keys.contains("key2")) 92| 1| } 93| | 94| 1| func testOperatorMinus() { 95| 1| let dict: [String: String] = ["key1": "value1", "key2": "value2", "key3": "value3"] 96| 1| let result = dict - ["key1", "key2"] 97| 1| XCTAssert(result.keys.contains("key3")) 98| 1| XCTAssertFalse(result.keys.contains("key1")) 99| 1| XCTAssertFalse(result.keys.contains("key2")) 100| 1| } 101| | 102| 1| func testOperatorPlusEqual() { 103| 1| var dict: [String: String] = ["key1": "value1"] 104| 1| let dict2: [String: String] = ["key2": "value2"] 105| 1| dict += dict2 106| 1| XCTAssert(dict.keys.contains("key1")) 107| 1| XCTAssert(dict.keys.contains("key2")) 108| 1| } 109| | 110| 1| func testOperatorRemoveKeys() { 111| 1| var dict: [String: String] = ["key1": "value1", "key2": "value2", "key3": "value3"] 112| 1| dict -= ["key1", "key2"] 113| 1| XCTAssert(dict.keys.contains("key3")) 114| 1| XCTAssertFalse(dict.keys.contains("key1")) 115| 1| XCTAssertFalse(dict.keys.contains("key2")) 116| 1| } 117| | 118| 1| func testMapKeysAndValues() { 119| 1| let intToString = [0: "0", 1: "1", 2: "2", 3: "3", 4: "4", 5: "5", 6: "6", 7: "7", 8: "8", 9: "9"] 120| 1| 121| 10| let stringToInt: [String: Int] = intToString.mapKeysAndValues { key, value in 122| 10| return (String(describing: key), Int(value)!) 123| 10| } 124| 1| 125| 1| XCTAssertEqual(stringToInt, ["0": 0, "1": 1, "2": 2, "3": 3, "4": 4, "5": 5, "6": 6, "7": 7, "8": 8, "9": 9]) 126| 1| } 127| | 128| 1| func testCompactMapKeysAndValues() { 129| 1| // swiftlint:disable:next nesting 130| 1| enum IntWord: String { 131| 1| case zero 132| 1| case one 133| 1| case two 134| 1| } 135| 1| 136| 1| let strings = [ 137| 1| 0: "zero", 138| 1| 1: "one", 139| 1| 2: "two", 140| 1| 3: "three" 141| 1| ] 142| 4| let words: [String: IntWord] = strings.compactMapKeysAndValues { key, value in 143| 4| guard let word = IntWord(rawValue: value) else { return nil } 144| 3| return (String(describing: key), word) 145| 4| } 146| 1| 147| 1| XCTAssertEqual(words, ["0": .zero, "1": .one, "2": .two]) 148| 1| } 149| | 150| 1| func testInitGroupedByKeyPath() { 151| 1| let array1 = ["James", "Wade", "Bryant", "John"] 152| 1| let array2 = ["James", "Wade", "Bryant", "John", "", ""] 153| 1| let array3: [String] = [] 154| 1| 155| 1| XCTAssertEqual( 156| 1| Dictionary(grouping: array1, by: \String.count), 157| 1| [6: ["Bryant"], 5: ["James"], 4: ["Wade", "John"]]) 158| 1| XCTAssertEqual( 159| 1| Dictionary(grouping: array1, by: \String.first), 160| 1| [Optional("B"): ["Bryant"], Optional("J"): ["James", "John"], Optional("W"): ["Wade"]]) 161| 1| XCTAssertEqual( 162| 1| Dictionary(grouping: array2, by: \String.count), 163| 1| [6: ["Bryant"], 5: ["James"], 4: ["Wade", "John"], 0: ["", ""]]) 164| 1| XCTAssertEqual(Dictionary(grouping: array3, by: \String.count), [:]) 165| 1| } 166| | 167| 1| func testGetByKeys() { 168| 1| let dict = ["James": 100, 169| 1| "Wade": 200, 170| 1| "Bryant": 500, 171| 1| "John": 600, 172| 1| "Jack": 1000] 173| 1| let picked = dict.pick(keys: ["James", "Wade", "Jack"]) 174| 1| let empty1 = dict.pick(keys: ["Pippen", "Rodman"]) 175| 1| XCTAssertEqual(picked, ["James": 100, "Wade": 200, "Jack": 1000]) 176| 1| XCTAssertTrue(empty1.isEmpty) 177| 1| 178| 1| let optionalValuesDict = ["James": 100, 179| 1| "Wade": nil, 180| 1| "Bryant": 500, 181| 1| "John": nil, 182| 1| "Jack": 1000] 183| 1| 184| 1| let pickedWithOptionals = optionalValuesDict.pick(keys: ["James", "Bryant", "John"]) 185| 1| XCTAssertEqual(pickedWithOptionals, ["James": Optional(100), "Bryant": Optional(500), "John": nil]) 186| 1| 187| 1| let emptyDict = [String: Int]() 188| 1| let empty3 = emptyDict.pick(keys: ["James", "Bryant", "John"]) 189| 1| XCTAssertTrue(empty3.isEmpty) 190| 1| } 191| |} /Users/runner/work/SwifterSwift/SwifterSwift/Tests/SwiftStdlibTests/DoubleExtensionsTests.swift: 1| |// DoubleExtensionsTests.swift - Copyright 2020 SwifterSwift 2| | 3| |@testable import SwifterSwift 4| |import XCTest 5| | 6| |final class DoubleExtensionsTests: XCTestCase { 7| 1| func testInt() { 8| 1| XCTAssertEqual(Double(-1).int, -1) 9| 1| XCTAssertEqual(Double(2).int, 2) 10| 1| XCTAssertEqual(Double(4.3).int, 4) 11| 1| } 12| | 13| 1| func testFloat() { 14| 1| XCTAssertEqual(Double(-1).float, Float(-1)) 15| 1| XCTAssertEqual(Double(2).float, Float(2)) 16| 1| XCTAssertEqual(Double(4.3).float, Float(4.3)) 17| 1| } 18| | 19| 1| func testCGFloat() { 20| 1| #if canImport(CoreGraphics) 21| 1| XCTAssertEqual(Double(4.3).cgFloat, CGFloat(4.3)) 22| 1| #endif 23| 1| } 24| | 25| 1| func testOperators() { 26| 1| XCTAssertEqual(Double(5.0) ** Double(2.0), Double(25.0)) 27| 1| } 28| |} /Users/runner/work/SwifterSwift/SwifterSwift/Tests/SwiftStdlibTests/FloatExtensionsTests.swift: 1| |// FloatExtensionsTests.swift - Copyright 2020 SwifterSwift 2| | 3| |@testable import SwifterSwift 4| |import XCTest 5| | 6| |final class FloatExtensionsTests: XCTestCase { 7| 1| func testInt() { 8| 1| XCTAssertEqual(Float(-1).int, -1) 9| 1| XCTAssertEqual(Float(2).int, 2) 10| 1| XCTAssertEqual(Float(4.3).int, 4) 11| 1| } 12| | 13| 1| func testDouble() { 14| 1| XCTAssertEqual(Float(-1).double, Double(-1)) 15| 1| XCTAssertEqual(Float(2).double, Double(2)) 16| 1| XCTAssertEqual(Float(4.3).double, Double(4.3), accuracy: 0.00001) 17| 1| } 18| | 19| 1| func testCGFloat() { 20| 1| #if canImport(CoreGraphics) 21| 1| XCTAssertEqual(Float(4.3).cgFloat, CGFloat(4.3), accuracy: 0.00001) 22| 1| #endif 23| 1| } 24| | 25| 1| func testOperators() { 26| 1| XCTAssertEqual(Float(5.0) ** Float(2.0), Float(25.0)) 27| 1| } 28| |} /Users/runner/work/SwifterSwift/SwifterSwift/Tests/SwiftStdlibTests/FloatingPointExtensionsTests.swift: 1| |// FloatingPointExtensionsTests.swift - Copyright 2020 SwifterSwift 2| | 3| |@testable import SwifterSwift 4| |import XCTest 5| | 6| |final class FloatingPointExtensionsTests: XCTestCase { 7| 1| func testAbs() { 8| 1| XCTAssertEqual(Float(-9.3).abs, Float(9.3)) 9| 1| XCTAssertEqual(Double(-9.3).abs, Double(9.3)) 10| 1| } 11| | 12| 1| func testIsPositive() { 13| 1| XCTAssert(Float(1).isPositive) 14| 1| XCTAssertFalse(Float(0).isPositive) 15| 1| XCTAssertFalse(Float(-1).isPositive) 16| 1| 17| 1| XCTAssert(Double(1).isPositive) 18| 1| XCTAssertFalse(Double(0).isPositive) 19| 1| XCTAssertFalse(Double(-1).isPositive) 20| 1| } 21| | 22| 1| func testIsNegative() { 23| 1| XCTAssert(Float(-1).isNegative) 24| 1| XCTAssertFalse(Float(0).isNegative) 25| 1| XCTAssertFalse(Float(1).isNegative) 26| 1| 27| 1| XCTAssert(Double(-1).isNegative) 28| 1| XCTAssertFalse(Double(0).isNegative) 29| 1| XCTAssertFalse(Double(1).isNegative) 30| 1| } 31| | 32| 1| func testCeil() { 33| 1| XCTAssertEqual(Float(9.3).ceil, Float(10.0)) 34| 1| XCTAssertEqual(Double(9.3).ceil, Double(10.0)) 35| 1| } 36| | 37| 1| func testDegreesToRadians() { 38| 1| XCTAssertEqual(Float(180).degreesToRadians, Float.pi) 39| 1| XCTAssertEqual(Double(180).degreesToRadians, Double.pi) 40| 1| } 41| | 42| 1| func testFloor() { 43| 1| XCTAssertEqual(Float(9.3).floor, Float(9.0)) 44| 1| XCTAssertEqual(Double(9.3).floor, Double(9.0)) 45| 1| } 46| | 47| 1| func testRadiansToDegrees() { 48| 1| XCTAssertEqual(Float.pi.radiansToDegrees, Float(180)) 49| 1| XCTAssertEqual(Double.pi.radiansToDegrees, Double(180)) 50| 1| } 51| | 52| 1| func testOperators() { 53| 1| XCTAssert((Float(5.0) ± Float(2.0)) == (Float(3.0), Float(7.0)) || (Float(5.0) ± Float(2.0)) == 54| 1| (Float(7.0), Float(3.0))) 55| 1| XCTAssert((±Float(2.0)) == (Float(2.0), Float(-2.0)) || (±Float(2.0)) == (Float(-2.0), Float(2.0))) 56| 1| XCTAssertEqual(√Float(25.0), Float(5.0)) 57| 1| 58| 1| XCTAssert((Double(5.0) ± Double(2.0)) == (Double(3.0), Double(7.0)) || (Double(5.0) ± Double(2.0)) == 59| 1| (Double(7.0), Double(3.0))) 60| 1| XCTAssert((±Double(2.0)) == (Double(2.0), Double(-2.0)) || (±Double(2.0)) == (Double(-2.0), Double(2.0))) 61| 1| XCTAssertEqual(√Double(25.0), Double(5.0)) 62| 1| } 63| |} /Users/runner/work/SwifterSwift/SwifterSwift/Tests/SwiftStdlibTests/IntExtensionsTests.swift: 1| |// IntExtensionsTests.swift - Copyright 2020 SwifterSwift 2| | 3| |@testable import SwifterSwift 4| |import XCTest 5| | 6| |final class IntExtensionsTests: XCTestCase { 7| 1| func testCountableRange() { 8| 1| XCTAssertEqual(10.countableRange, 0..<10) 9| 1| } 10| | 11| 1| func testDegreesToRadians() { 12| 1| XCTAssertEqual(180.degreesToRadians, Double.pi) 13| 1| } 14| | 15| 1| func testRadiansToDegrees() { 16| 1| XCTAssertEqual(Int(3.radiansToDegrees), 171) 17| 1| } 18| | 19| 1| func testUInt() { 20| 1| XCTAssertEqual(Int(10).uInt, UInt(10)) 21| 1| } 22| | 23| 1| func testDouble() { 24| 1| XCTAssertEqual((-1).double, Double(-1)) 25| 1| XCTAssertEqual(2.double, Double(2)) 26| 1| } 27| | 28| 1| func testFloat() { 29| 1| XCTAssertEqual((-1).float, Float(-1)) 30| 1| XCTAssertEqual(2.float, Float(2)) 31| 1| } 32| | 33| 1| func testCGFloat() { 34| 1| #if canImport(CoreGraphics) 35| 1| XCTAssertEqual(1.cgFloat, CGFloat(1)) 36| 1| #endif 37| 1| } 38| | 39| 1| func testKFormatted() { 40| 1| XCTAssertEqual(10.kFormatted, "0k") 41| 1| XCTAssertEqual((-10).kFormatted, "0k") 42| 1| 43| 1| XCTAssertEqual(0.kFormatted, "0k") 44| 1| 45| 1| XCTAssertEqual(1000.kFormatted, "1k") 46| 1| XCTAssertEqual((-1000).kFormatted, "-1k") 47| 1| 48| 1| XCTAssertEqual(100_000.kFormatted, "100k") 49| 1| XCTAssertEqual((-100_000).kFormatted, "-100k") 50| 1| 51| 1| XCTAssertEqual(1_000_000.kFormatted, "10kk") 52| 1| } 53| | 54| 1| func testDigits() { 55| 1| let number = -123 56| 1| XCTAssertEqual(number.digits, [1, 2, 3]) 57| 1| XCTAssertEqual(123.digits, [1, 2, 3]) 58| 1| XCTAssertEqual(0.digits, [0]) 59| 1| } 60| | 61| 1| func testDigitsCount() { 62| 1| let number = -123 63| 1| XCTAssertEqual(number.digitsCount, 3) 64| 1| XCTAssertEqual(180.digitsCount, 3) 65| 1| XCTAssertEqual(0.digitsCount, 1) 66| 1| XCTAssertEqual(1.digitsCount, 1) 67| 1| } 68| | 69| 1| func testIsPrime() { 70| 1| // Prime number 71| 1| XCTAssert(2.isPrime()) 72| 1| XCTAssert(3.isPrime()) 73| 1| XCTAssert(7.isPrime()) 74| 1| XCTAssert(19.isPrime()) 75| 1| XCTAssert(577.isPrime()) 76| 1| XCTAssert(1999.isPrime()) 77| 1| 78| 1| // Composite number 79| 1| XCTAssertFalse(4.isPrime()) 80| 1| XCTAssertFalse(21.isPrime()) 81| 1| XCTAssertFalse(81.isPrime()) 82| 1| XCTAssertFalse(121.isPrime()) 83| 1| XCTAssertFalse(9409.isPrime()) 84| 1| 85| 1| // Others 86| 1| XCTAssertFalse((-1).isPrime()) 87| 1| XCTAssertFalse(0.isPrime()) 88| 1| XCTAssertFalse(1.isPrime()) 89| 1| } 90| | 91| 1| func testRomanNumeral() { 92| 1| XCTAssertEqual(10.romanNumeral(), "X") 93| 1| XCTAssertEqual(2784.romanNumeral(), "MMDCCLXXXIV") 94| 1| XCTAssertNil((-1).romanNumeral()) 95| 1| } 96| | 97| 1| func testRoundToNearest() { 98| 1| XCTAssertEqual(12.roundToNearest(5), 10) 99| 1| XCTAssertEqual(63.roundToNearest(25), 75) 100| 1| XCTAssertEqual(42.roundToNearest(0), 42) 101| 1| } 102| | 103| 1| func testOperators() { 104| 1| XCTAssertEqual(5 ** 2, 25) 105| 1| XCTAssert((5 ± 2) == (3, 7) || (5 ± 2) == (7, 3)) 106| 1| XCTAssert((±2) == (2, -2) || (±2) == (-2, 2)) 107| 1| XCTAssertEqual(√25, 5.0) 108| 1| } 109| |} /Users/runner/work/SwifterSwift/SwifterSwift/Tests/SwiftStdlibTests/KeyedDecodingContainerExtensionsTests.swift: 1| |// KeyedDecodingContainerExtensionsTests.swift - Copyright 2020 SwifterSwift 2| | 3| |import XCTest 4| | 5| |@testable import SwifterSwift 6| | 7| |private struct Video: Decodable { 8| | let isPlaying: Bool 9| | let isFullScreen: Bool? 10| | 11| | private enum CodingKeys: CodingKey { 12| | case isPlaying 13| | case isFullScreen 14| | } 15| | 16| 5| init(from decoder: Decoder) throws { 17| 5| let container = try decoder.container(keyedBy: CodingKeys.self) 18| 5| isPlaying = try container.decodeBoolAsIntOrString(forKey: .isPlaying) 19| 5| isFullScreen = try container.decodeBoolAsIntOrStringIfPresent(forKey: .isFullScreen) 20| 5| } 21| |} 22| | 23| |final class KeyedDecodingContainerTests: XCTestCase { 24| 1| func testDecodeBoolAsIntOrStringDataAsIntSuccessful() { 25| 1| let isPlayingAndIsFullScreenAsInt = #"{"isPlaying": 1, "isFullScreen": 0}"# 26| 1| let data = mockJsonData(from: isPlayingAndIsFullScreenAsInt) 27| 1| let video = try! JSONDecoder().decode(Video.self, from: data) // swiftlint:disable:this force_try 28| 1| XCTAssert(video.isPlaying) 29| 1| XCTAssertEqual(video.isFullScreen, false) 30| 1| } 31| | 32| 1| func testDecodeBoolAsIntOrStringDataAsStringSuccessful() { 33| 1| let isPlayingAndIsFullScreenAsString = #"{"isPlaying": "true", "isFullScreen": "false"}"# 34| 1| let data = mockJsonData(from: isPlayingAndIsFullScreenAsString) 35| 1| let video = try! JSONDecoder().decode(Video.self, from: data) // swiftlint:disable:this force_try 36| 1| XCTAssert(video.isPlaying) 37| 1| XCTAssertEqual(video.isFullScreen, false) 38| 1| } 39| | 40| 1| func testDecodeBoolAsIntOrStringDataAsBoolSuccessful() { 41| 1| let isPlayingAndIsFullScreenAsBool = #"{"isPlaying": true, "isFullScreen": false}"# 42| 1| let data = mockJsonData(from: isPlayingAndIsFullScreenAsBool) 43| 1| let video = try! JSONDecoder().decode(Video.self, from: data) // swiftlint:disable:this force_try 44| 1| XCTAssert(video.isPlaying) 45| 1| XCTAssertEqual(video.isFullScreen, false) 46| 1| } 47| | 48| 1| func testDecodeBoolAsIntOrStringIfPresentSuccessful() { 49| 1| let isPlayingAsIntAndIsFullScreenNotPresent = #"{"isPlaying": 0}"# 50| 1| let data = mockJsonData(from: isPlayingAsIntAndIsFullScreenNotPresent) 51| 1| let video = try! JSONDecoder().decode(Video.self, from: data) // swiftlint:disable:this force_try 52| 1| XCTAssertFalse(video.isPlaying) 53| 1| XCTAssertNil(video.isFullScreen) 54| 1| } 55| | 56| 1| func testDecodeBoolAsIntOrStringThrowsError() { 57| 1| let isPlayingAndIsFullScreenTypeMismatch = #"{"isPlaying": [1], "isFullScreen": ["true"]}"# 58| 1| let data = mockJsonData(from: isPlayingAndIsFullScreenTypeMismatch) 59| 1| XCTAssertThrowsError(try JSONDecoder().decode(Video.self, from: data)) 60| 1| } 61| | 62| 5| private func mockJsonData(from json: String) -> Data { 63| 5| return Data(json.utf8) 64| 5| } 65| |} /Users/runner/work/SwifterSwift/SwifterSwift/Tests/SwiftStdlibTests/MutableCollectionTests.swift: 1| |// MutableCollectionTests.swift - Copyright 2020 SwifterSwift 2| | 3| |import XCTest 4| | 5| |@testable import SwifterSwift 6| | 7| |final class MutableCollectionTests: XCTestCase { 8| 1| func testKeyPathSort() { 9| 1| var array = ["James", "Wade", "Bryant"] 10| 1| array.sort(by: \String.count, with: <) 11| 1| XCTAssertEqual(array, ["Wade", "James", "Bryant"]) 12| 1| array.sort(by: \String.count, with: >) 13| 1| XCTAssertEqual(array, ["Bryant", "James", "Wade"]) 14| 1| 15| 1| // Comparable version 16| 1| array.sort(by: \String.count) 17| 1| XCTAssertEqual(array, ["Wade", "James", "Bryant"]) 18| 1| 19| 1| // Testing optional keyPath 20| 4| let optionalCompare = { (char1: Character?, char2: Character?) -> Bool in 21| 4| guard let char1 = char1, let char2 = char2 else { return false } 22| 3| return char1 < char2 23| 4| } 24| 1| 25| 1| var array2 = ["James", "Wade", "Bryant", ""] 26| 1| array2.sort(by: \String.first, with: optionalCompare) 27| 1| XCTAssertEqual(array2, ["Bryant", "James", "Wade", ""]) 28| 1| } 29| | 30| 1| func testSortByTwoKeyPaths() { 31| 1| var people = [ 32| 1| SimplePerson(forename: "Tom", surname: "James", age: 32), 33| 1| SimplePerson(forename: "Angeline", surname: "Wade", age: 57), 34| 1| SimplePerson(forename: "Max", surname: "James", age: 34) 35| 1| ] 36| 1| people.sort(by: \.surname, and: \.age) 37| 1| let expectedResult = [ 38| 1| SimplePerson(forename: "Tom", surname: "James", age: 32), 39| 1| SimplePerson(forename: "Max", surname: "James", age: 34), 40| 1| SimplePerson(forename: "Angeline", surname: "Wade", age: 57) 41| 1| ] 42| 1| XCTAssertEqual(people, expectedResult) 43| 1| } 44| | 45| 1| func testSortByThreeKeyPaths() { 46| 1| var people = [ 47| 1| SimplePerson(forename: "Tom", surname: "James", age: 32), 48| 1| SimplePerson(forename: "Angeline", surname: "Wade", age: 57), 49| 1| SimplePerson(forename: "Max", surname: "James", age: 34), 50| 1| SimplePerson(forename: "Angeline", surname: "Wade", age: 82) 51| 1| ] 52| 1| people.sort(by: \.surname, and: \.forename, and: \.age) 53| 1| let expectedResult = [ 54| 1| SimplePerson(forename: "Max", surname: "James", age: 34), 55| 1| SimplePerson(forename: "Tom", surname: "James", age: 32), 56| 1| SimplePerson(forename: "Angeline", surname: "Wade", age: 57), 57| 1| SimplePerson(forename: "Angeline", surname: "Wade", age: 82) 58| 1| ] 59| 1| XCTAssertEqual(people, expectedResult) 60| 1| } 61| | 62| 1| func testAssignToAll() { 63| 1| var collection: [TestStruct] = [1, 2, 3, 4, 5] 64| 1| collection.assignToAll(value: 0, by: \.testField) 65| 1| let expectedCollection: [TestStruct] = [0, 0, 0, 0, 0] 66| 1| XCTAssertEqual(collection, expectedCollection) 67| 1| 68| 1| // check with an empty collection 69| 1| var initialEmptyCollection: [TestStruct] = [] 70| 1| initialEmptyCollection.assignToAll(value: 5, by: \.testField) 71| 1| let expectedEmptyCollection: [TestStruct] = [] 72| 1| XCTAssertEqual(initialEmptyCollection, expectedEmptyCollection) 73| 1| } 74| |} /Users/runner/work/SwifterSwift/SwifterSwift/Tests/SwiftStdlibTests/OptionalExtensionsTests.swift: 1| |// OptionalExtensionsTests.swift - Copyright 2020 SwifterSwift 2| | 3| |@testable import SwifterSwift 4| |import XCTest 5| | 6| |private enum OptionalTestError: Error { 7| | case optionalIsNil 8| |} 9| | 10| |final class OptionalExtensionsTests: XCTestCase { 11| 1| func testUnwrappedOrDefault() { 12| 1| var str: String? 13| 1| XCTAssertEqual(str.unwrapped(or: "swift"), "swift") 14| 1| 15| 1| str = "swifterswift" 16| 1| XCTAssertEqual(str.unwrapped(or: "swift"), "swifterswift") 17| 1| } 18| | 19| 1| func testUnwrappedOrError() { 20| 1| let null: String? = nil 21| 1| try XCTAssertThrowsError(null.unwrapped(or: OptionalTestError.optionalIsNil)) 22| 1| 23| 1| let some: String? = "I exist" 24| 1| try XCTAssertNoThrow(some.unwrapped(or: OptionalTestError.optionalIsNil)) 25| 1| } 26| | 27| 1| func testRunBlock() { 28| 1| var str: String? 29| 1| var didRun = false 30| 1| str.run { _ in 31| 0| didRun = true 32| 0| } 33| 1| XCTAssertFalse(didRun) 34| 1| str = "swift" 35| 1| str.run { item in 36| 1| didRun = true 37| 1| XCTAssert(didRun) 38| 1| XCTAssertEqual(item, "swift") 39| 1| } 40| 1| } 41| | 42| 1| func testOptionalAssignment() { 43| 1| let parameter1: String? = nil 44| 1| let parameter2: String? = "foo" 45| 1| 46| 1| let key1: String = "key1" 47| 1| let key2: String = "key2" 48| 1| 49| 1| var parameters = [String: String]() 50| 1| 51| 1| parameters[key1] ??= parameter1 52| 1| parameters[key2] ??= parameter2 53| 1| 54| 1| XCTAssertNil(parameters[key1]) 55| 1| XCTAssertEqual(parameters[key1], parameter1) 56| 1| XCTAssertEqual(parameters[key2], parameter2) 57| 1| } 58| | 59| 1| func testConditionalAssignment() { 60| 1| var text1: String? 61| 1| text1 ?= "newText1" 62| 1| XCTAssertEqual(text1, "newText1") 63| 1| 64| 1| var text2: String? = "text2" 65| 1| text2 ?= "newText2" 66| 1| XCTAssertEqual(text2, "text2") 67| 1| 68| 1| var text3: String? 69| 1| text3 ?= nil 70| 1| XCTAssertNil(text3) 71| 1| 72| 1| var text4: String? = "text4" 73| 1| text4 ?= nil 74| 1| XCTAssertEqual(text4, "text4") 75| 1| } 76| | 77| 1| func testIsNilOrEmpty() { 78| 1| let nilArray: [String]? = nil 79| 1| XCTAssert(nilArray.isNilOrEmpty) 80| 1| 81| 1| let emptyArray: [String]? = [] 82| 1| XCTAssert(emptyArray.isNilOrEmpty) 83| 1| 84| 1| let intArray: [Int]? = [1] 85| 1| XCTAssertFalse(intArray.isNilOrEmpty) 86| 1| 87| 1| let optionalArray: [Int]? = [1] 88| 1| XCTAssertFalse(optionalArray.isNilOrEmpty) 89| 1| 90| 1| let nilString: String? = nil 91| 1| XCTAssert(nilString.isNilOrEmpty) 92| 1| 93| 1| let emptyString: String? = "" 94| 1| XCTAssert(emptyString.isNilOrEmpty) 95| 1| 96| 1| let string: String? = "hello World!" 97| 1| XCTAssertFalse(string.isNilOrEmpty) 98| 1| } 99| | 100| 1| func testNonEmpty() { 101| 1| let nilCollection: [Int]? = nil 102| 1| XCTAssertNil(nilCollection.nonEmpty) 103| 1| 104| 1| let emptyCollection: [Int]? = [] 105| 1| XCTAssertNil(emptyCollection.nonEmpty) 106| 1| 107| 1| let collection: [Int]? = [1, 2, 3] 108| 1| XCTAssertNotNil(collection.nonEmpty) 109| 1| XCTAssertEqual(collection.nonEmpty!, [1, 2, 3]) 110| 1| } 111| | 112| 1| func testEqualsRawValue() { 113| 1| let summerString = "summer" 114| 1| let summerStringOptional: String? = "summer" 115| 1| let nilString: String? = nil 116| 1| 117| 1| let summer = Season.summer 118| 1| XCTAssert(summer == summerString) 119| 1| XCTAssert(summerString == summer) 120| 1| XCTAssert(summer == summerStringOptional) 121| 1| XCTAssert(summerStringOptional == summer) 122| 1| XCTAssertFalse(summer == nilString) 123| 1| XCTAssertFalse(nilString == summer) 124| 1| 125| 1| let summerOptional: Season? = Season.summer 126| 1| XCTAssert(summerOptional == summerString) 127| 1| XCTAssert(summerString == summerOptional) 128| 1| XCTAssert(summerOptional == summerStringOptional) 129| 1| XCTAssert(summerStringOptional == summerOptional) 130| 1| XCTAssertFalse(summerOptional == nilString) 131| 1| XCTAssertFalse(nilString == summerOptional) 132| 1| 133| 1| let nilSummer: Season? = nil 134| 1| XCTAssertFalse(nilSummer == summerString) 135| 1| XCTAssertFalse(summerString == nilSummer) 136| 1| XCTAssertFalse(nilSummer == summerStringOptional) 137| 1| XCTAssertFalse(summerStringOptional == nilSummer) 138| 1| XCTAssert(nilSummer == nilString) 139| 1| XCTAssert(nilString == nilSummer) 140| 1| } 141| | 142| 1| func testNotEqualsRawValue() { 143| 1| let summerString = "summer" 144| 1| let summerStringOptional: String? = "summer" 145| 1| let nilString: String? = nil 146| 1| 147| 1| let summer = Season.summer 148| 1| XCTAssertFalse(summer != summerString) 149| 1| XCTAssertFalse(summerString != summer) 150| 1| XCTAssertFalse(summer != summerStringOptional) 151| 1| XCTAssertFalse(summerStringOptional != summer) 152| 1| XCTAssert(summer != nilString) 153| 1| XCTAssert(nilString != summer) 154| 1| 155| 1| let summerOptional: Season? = Season.summer 156| 1| XCTAssertFalse(summerOptional != summerString) 157| 1| XCTAssertFalse(summerString != summerOptional) 158| 1| XCTAssertFalse(summerOptional != summerStringOptional) 159| 1| XCTAssertFalse(summerStringOptional != summerOptional) 160| 1| XCTAssert(summerOptional != nilString) 161| 1| XCTAssert(nilString != summerOptional) 162| 1| 163| 1| let nilSummer: Season? = nil 164| 1| XCTAssert(nilSummer != summerString) 165| 1| XCTAssert(summerString != nilSummer) 166| 1| XCTAssert(nilSummer != summerStringOptional) 167| 1| XCTAssert(summerStringOptional != nilSummer) 168| 1| XCTAssertFalse(nilSummer != nilString) 169| 1| XCTAssertFalse(nilString != nilSummer) 170| 1| } 171| |} /Users/runner/work/SwifterSwift/SwifterSwift/Tests/SwiftStdlibTests/RangeReplaceableCollectionTests.swift: 1| |// RangeReplaceableCollectionTests.swift - Copyright 2020 SwifterSwift 2| | 3| |@testable import SwifterSwift 4| |import XCTest 5| | 6| |final class RangeReplaceableCollectionTests: XCTestCase { 7| 1| func testInitExpressionOfSize() { 8| 1| var array = [1, 2, 3] 9| 3| let newArray = [Int](expression: array.removeLast(), count: array.count) 10| 1| XCTAssertEqual(newArray, [3, 2, 1]) 11| 1| XCTAssert(array.isEmpty) 12| 1| let empty = [Int](expression: 1, count: 0) 13| 1| XCTAssert(empty.isEmpty) 14| 1| } 15| | 16| 1| func testRotated() { 17| 1| let array: [Int] = [1, 2, 3, 4] 18| 1| XCTAssertEqual(array.rotated(by: 0), [1, 2, 3, 4]) 19| 1| XCTAssertEqual(array.rotated(by: 5), [4, 1, 2, 3]) 20| 1| XCTAssertEqual(array.rotated(by: 4), [1, 2, 3, 4]) 21| 1| XCTAssertEqual(array.rotated(by: 1), [4, 1, 2, 3]) 22| 1| XCTAssertEqual(array.rotated(by: 3), [2, 3, 4, 1]) 23| 1| XCTAssertEqual(array.rotated(by: -1), [2, 3, 4, 1]) 24| 1| XCTAssertEqual(array.rotated(by: -3), [4, 1, 2, 3]) 25| 1| XCTAssertEqual(array.rotated(by: -5), [2, 3, 4, 1]) 26| 1| } 27| | 28| 1| func testRotate() { 29| 1| var array: [Int] = [1, 2, 3, 4] 30| 1| array.rotate(by: 0) 31| 1| XCTAssertEqual(array, [1, 2, 3, 4]) 32| 1| array.rotate(by: 2) 33| 1| XCTAssertEqual(array, [3, 4, 1, 2]) 34| 1| array.rotate(by: -1) 35| 1| XCTAssertEqual(array, [4, 1, 2, 3]) 36| 1| } 37| | 38| 1| func testRemoveWhere() { 39| 1| var array = [0, 1, 2, 0, 3, 4, 5, 0, 0] 40| 2| array.removeFirst { $0 == 1 } 41| 1| XCTAssertEqual(array, [0, 2, 0, 3, 4, 5, 0, 0]) 42| 1| array = [] 43| 1| XCTAssertNil(array.removeFirst { $0 == 10 }) 44| 1| array = [2, 2, 1, 2, 3] 45| 1| let removedElement = array.removeFirst { $0 == 2 } 46| 1| XCTAssertEqual(array, [2, 1, 2, 3]) 47| 1| XCTAssertEqual(removedElement, 2) 48| 1| 49| 1| XCTAssertThrowsError(try array.removeFirst(where: { _ in throw NSError(domain: "", code: -1, userInfo: nil) })) 50| 1| } 51| | 52| 1| func testRemoveRandomElement() { 53| 1| var emptyArray = [Int]() 54| 1| XCTAssertNil(emptyArray.removeRandomElement()) 55| 1| 56| 1| var array = [1, 2, 3] 57| 1| let elements = array.count 58| 1| let removedElement = array.removeRandomElement()! 59| 1| XCTAssertEqual(elements - 1, array.count) 60| 1| XCTAssertFalse(array.contains(removedElement)) 61| 1| } 62| | 63| 1| func testKeepWhile() { 64| 1| var input = [2, 4, 6, 7, 8, 9, 10] 65| 4| input.keep(while: { $0 % 2 == 0 }) 66| 1| XCTAssertEqual(input, [2, 4, 6]) 67| 1| 68| 1| input = [7, 7, 8, 10] 69| 1| input.keep(while: { $0 % 2 == 0 }) 70| 1| XCTAssertEqual(input, [Int]()) 71| 1| } 72| | 73| 1| func testTakeWhile() { 74| 1| var input = [2, 4, 6, 7, 8, 9, 10] 75| 4| var output = input.take(while: { $0 % 2 == 0 }) 76| 1| XCTAssertEqual(output, [2, 4, 6]) 77| 1| 78| 1| input = [7, 7, 8, 10] 79| 1| output = input.take(while: { $0 % 2 == 0 }) 80| 1| XCTAssertEqual(output, [Int]()) 81| 1| 82| 1| XCTAssertEqual([].take(while: { $0 % 2 == 0 }), []) 83| 1| } 84| | 85| 1| func testSkipWhile() { 86| 1| var input = [2, 4, 6, 7, 8, 9, 10] 87| 4| var output = input.skip(while: { $0 % 2 == 0 }) 88| 1| XCTAssertEqual(output, [7, 8, 9, 10]) 89| 1| 90| 1| input = [7, 7, 8, 10] 91| 1| output = input.skip(while: { $0 % 2 == 0 }) 92| 1| XCTAssertEqual(output, [7, 7, 8, 10]) 93| 1| 94| 1| XCTAssertEqual([].skip(while: { $0 % 2 == 0 }), []) 95| 1| } 96| | 97| 1| func testRemoveDuplicatesUsingKeyPathHashable() { 98| 1| var input = [Person(name: "Wade", age: 20, location: Location(city: "London")), 99| 1| Person(name: "James", age: 32), 100| 1| Person(name: "James", age: 36), 101| 1| Person(name: "Rose", age: 29), 102| 1| Person(name: "James", age: 72, location: Location(city: "Moscow")), 103| 1| Person(name: "Rose", age: 56), 104| 1| Person(name: "Wade", age: 22, location: Location(city: "Prague"))] 105| 1| 106| 1| let expectedResult = [Person(name: "Wade", age: 20, location: Location(city: "London")), 107| 1| Person(name: "James", age: 32), 108| 1| Person(name: "Rose", age: 29)] 109| 1| 110| 1| input.removeDuplicates(keyPath: \.name) 111| 1| XCTAssertEqual(input, expectedResult) 112| 1| } 113| | 114| 1| func testRemoveDuplicatesUsingKeyPathEquatable() { 115| 1| var input = [Person(name: "Wade", age: 20, location: Location(city: "London")), 116| 1| Person(name: "James", age: 32), 117| 1| Person(name: "James", age: 36), 118| 1| Person(name: "Rose", age: 29), 119| 1| Person(name: "James", age: 72, location: Location(city: "Moscow")), 120| 1| Person(name: "Rose", age: 56), 121| 1| Person(name: "Wade", age: 22, location: Location(city: "Prague"))] 122| 1| 123| 1| let expectedResult = [Person(name: "Wade", age: 20, location: Location(city: "London")), 124| 1| Person(name: "James", age: 32), 125| 1| Person(name: "James", age: 72, location: Location(city: "Moscow")), 126| 1| Person(name: "Wade", age: 22, location: Location(city: "Prague"))] 127| 1| 128| 1| input.removeDuplicates(keyPath: \.location) 129| 1| XCTAssertEqual(input, expectedResult) 130| 1| } 131| | 132| 1| func testIntSubscripts() { 133| 1| var string = "Hello world!" 134| 1| XCTAssertEqual(string[0], "H") 135| 1| XCTAssertEqual(string[11], "!") 136| 1| 137| 1| XCTAssertEqual(string[0..<5], "Hello") 138| 1| XCTAssertEqual(string[6..<12], "world!") 139| 1| 140| 1| XCTAssertEqual(string[0...4], "Hello") 141| 1| XCTAssertEqual(string[6...11], "world!") 142| 1| 143| 1| XCTAssertEqual(string[0...], "Hello world!") 144| 1| 145| 1| XCTAssertEqual(string[...11], "Hello world!") 146| 1| 147| 1| XCTAssertEqual(string[..<12], "Hello world!") 148| 1| 149| 1| string[0] = "h" 150| 1| XCTAssertEqual(string, "hello world!") 151| 1| string[11] = "?" 152| 1| XCTAssertEqual(string, "hello world?") 153| 1| 154| 1| string[0..<5] = "Goodbye" 155| 1| XCTAssertEqual(string, "Goodbye world?") 156| 1| string[8..<14] = "planet!" 157| 1| XCTAssertEqual(string, "Goodbye planet!") 158| 1| 159| 1| string[0...6] = "Hello" 160| 1| XCTAssertEqual(string, "Hello planet!") 161| 1| string[6...12] = "world?" 162| 1| XCTAssertEqual(string, "Hello world?") 163| 1| 164| 1| string[5...] = "!" 165| 1| XCTAssertEqual(string, "Hello!") 166| 1| 167| 1| string[..<6] = "Hello Ferris" 168| 1| XCTAssertEqual(string, "Hello Ferris") 169| 1| 170| 1| string[...4] = "Save" 171| 1| XCTAssertEqual(string, "Save Ferris") 172| 1| } 173| |} /Users/runner/work/SwifterSwift/SwifterSwift/Tests/SwiftStdlibTests/SequenceExtensionsTests.swift: 1| |// SequenceExtensionsTests.swift - Copyright 2020 SwifterSwift 2| | 3| |@testable import SwifterSwift 4| |import XCTest 5| | 6| |private enum SequenceTestError: Error { 7| | case closureThrows 8| |} 9| | 10| |struct TestValue: Equatable, ExpressibleByIntegerLiteral { 11| | let value: Int 12| | 13| 22| init(integerLiteral value: Int) { self.value = value } 14| |} 15| | 16| |final class SequenceExtensionsTests: XCTestCase { 17| 1| func testAllMatch() { 18| 1| let collection = [2, 4, 6, 8, 10, 12] 19| 6| XCTAssert(collection.all { $0 % 2 == 0 }) 20| 1| } 21| | 22| 1| func testAnyMatch() { 23| 1| let collection = [3, 5, 8, 9, 11, 13] 24| 3| XCTAssert(collection.any { $0 % 2 == 0 }) 25| 1| } 26| | 27| 1| func testNoneMatch() { 28| 1| let collection = [3, 5, 7, 9, 11, 13] 29| 6| XCTAssert(collection.none { $0 % 2 == 0 }) 30| 1| } 31| | 32| 1| func testRejectWhere() { 33| 1| let input = [1, 2, 3, 4, 5] 34| 5| let output = input.reject { $0 % 2 == 0 } 35| 1| XCTAssertEqual(output, [1, 3, 5]) 36| 1| } 37| | 38| 1| func testCountWhere() { 39| 1| let array = [1, 1, 1, 1, 4, 4, 1, 1, 1] 40| 9| let count = array.count { $0 % 2 == 0 } 41| 1| XCTAssertEqual(count, 2) 42| 1| } 43| | 44| 1| func testForEachReversed() { 45| 1| let input = [1, 2, 3, 4, 5] 46| 1| var output: [Int] = [] 47| 5| input.forEachReversed { output.append($0) } 48| 1| XCTAssertEqual(output.first, 5) 49| 1| } 50| | 51| 1| func testForEachWhere() { 52| 1| let input = [1, 2, 2, 2, 1, 4, 1] 53| 1| var output: [Int] = [] 54| 7| input.forEach(where: { $0 % 2 == 0 }, body: { output.append($0 * 2) }) 55| 1| XCTAssertEqual(output, [4, 4, 4, 8]) 56| 1| } 57| | 58| 1| func testAccumulate() { 59| 1| let input = [1, 2, 3] 60| 1| let result = input.accumulate(initial: 0, next: +) 61| 1| XCTAssertEqual([1, 3, 6], result) 62| 1| } 63| | 64| 1| func testFilteredMap() { 65| 1| let input = [1, 2, 3, 4, 5] 66| 5| let result = input.filtered({ $0 % 2 == 0 }, map: { $0.string }) 67| 1| XCTAssertEqual(result.count, 2) 68| 1| XCTAssertEqual(["2", "4"], result) 69| 1| } 70| | 71| 1| func testSingle() { 72| 1| XCTAssertNil([].single(where: { _ in true })) 73| 1| XCTAssertEqual([4].single(where: { _ in true }), 4) 74| 2| XCTAssertNil([2, 4].single(where: { _ in true })) 75| 3| XCTAssertEqual([1, 4, 7].single(where: { $0 % 2 == 0 }), 4) 76| 2| XCTAssertNil([2, 2, 4, 7].single(where: { $0 % 2 == 0 })) 77| 1| XCTAssertThrowsError(try [2].single(where: { _ in throw SequenceTestError.closureThrows })) 78| 1| } 79| | 80| 1| func testWithoutDuplicates() { 81| 5| XCTAssertEqual([1, 2, 1, 3, 2].withoutDuplicates { $0 }, [1, 2, 3]) 82| 5| XCTAssertEqual([[1, 4], [2, 2], [1, 3], [3, 2], [2, 1]].withoutDuplicates { $0[0] }, [[1, 4], [2, 2], [3, 2]]) 83| 1| } 84| | 85| 1| func testDivided() { 86| 1| let input = [0, 1, 2, 3, 4, 5] 87| 6| let (even, odd) = input.divided { $0 % 2 == 0 } 88| 1| XCTAssertEqual(even, [0, 2, 4]) 89| 1| XCTAssertEqual(odd, [1, 3, 5]) 90| 1| 91| 1| // Parameter names + indexes 92| 6| let tuple = input.divided { $0 % 2 == 0 } 93| 1| XCTAssertEqual(tuple.matching, [0, 2, 4]) 94| 1| XCTAssertEqual(tuple.0, [0, 2, 4]) 95| 1| XCTAssertEqual(tuple.nonMatching, [1, 3, 5]) 96| 1| XCTAssertEqual(tuple.1, [1, 3, 5]) 97| 1| } 98| | 99| 1| func testContainsEquatable() { 100| 1| XCTAssert([TestValue]().contains([])) 101| 1| XCTAssertFalse([TestValue]().contains([1, 2])) 102| 1| XCTAssert(([1, 2, 3] as [TestValue]).contains([1, 2])) 103| 1| XCTAssert(([1, 2, 3] as [TestValue]).contains([2, 3])) 104| 1| XCTAssert(([1, 2, 3] as [TestValue]).contains([1, 3])) 105| 1| XCTAssertFalse(([1, 2, 3] as [TestValue]).contains([4, 5])) 106| 1| } 107| | 108| 1| func testContainsHashable() { 109| 1| XCTAssert([Int]().contains([])) 110| 1| XCTAssertFalse([Int]().contains([1, 2])) 111| 1| XCTAssert([1, 2, 3].contains([1, 2])) 112| 1| XCTAssert([1, 2, 3].contains([2, 3])) 113| 1| XCTAssert([1, 2, 3].contains([1, 3])) 114| 1| XCTAssertFalse([1, 2, 3].contains([4, 5])) 115| 1| } 116| | 117| 1| func testContainsDuplicates() { 118| 1| XCTAssertFalse([String]().containsDuplicates()) 119| 1| XCTAssert(["a", "b", "b", "c"].containsDuplicates()) 120| 1| XCTAssertFalse(["a", "b", "c", "d"].containsDuplicates()) 121| 1| } 122| | 123| 1| func testDuplicates() { 124| 1| XCTAssertEqual([1, 1, 2, 2, 3, 3, 3, 4, 5].duplicates().sorted(), [1, 2, 3]) 125| 1| XCTAssertEqual(["h", "e", "l", "l", "o"].duplicates().sorted(), ["l"]) 126| 1| } 127| | 128| 1| func testSum() { 129| 1| XCTAssertEqual([1, 2, 3, 4, 5].sum(), 15) 130| 1| XCTAssertEqual([1.2, 2.3, 3.4, 4.5, 5.6].sum(), 17) 131| 1| } 132| | 133| 1| func testKeyPathSum() { 134| 1| XCTAssertEqual(["James", "Wade", "Bryant"].sum(for: \.count), 15) 135| 1| XCTAssertEqual(["a", "b", "c", "d"].sum(for: \.count), 4) 136| 1| } 137| | 138| 1| func testKeyPathSorted() { 139| 1| let array = ["James", "Wade", "Bryant"] 140| 1| XCTAssertEqual(array.sorted(by: \String.count, with: <), ["Wade", "James", "Bryant"]) 141| 1| XCTAssertEqual(array.sorted(by: \String.count, with: >), ["Bryant", "James", "Wade"]) 142| 1| 143| 1| // Comparable version 144| 1| XCTAssertEqual(array.sorted(by: \String.count), ["Wade", "James", "Bryant"]) 145| 1| 146| 1| // Testing optional keyPath 147| 4| let optionalCompare = { (char1: Character?, char2: Character?) -> Bool in 148| 4| guard let char1 = char1, let char2 = char2 else { return false } 149| 3| return char1 < char2 150| 4| } 151| 1| 152| 1| let array2 = ["James", "Wade", "Bryant", ""] 153| 1| XCTAssertEqual(array2.sorted(by: \String.first, with: optionalCompare), ["Bryant", "James", "Wade", ""]) 154| 1| } 155| | 156| 1| func testSortedByTwoKeyPaths() { 157| 1| let people = [ 158| 1| SimplePerson(forename: "Tom", surname: "James", age: 32), 159| 1| SimplePerson(forename: "Angeline", surname: "Wade", age: 57), 160| 1| SimplePerson(forename: "Max", surname: "James", age: 34) 161| 1| ] 162| 1| let expectedResult = [ 163| 1| SimplePerson(forename: "Tom", surname: "James", age: 32), 164| 1| SimplePerson(forename: "Max", surname: "James", age: 34), 165| 1| SimplePerson(forename: "Angeline", surname: "Wade", age: 57) 166| 1| ] 167| 1| XCTAssertEqual(people.sorted(by: \.surname, and: \.age), expectedResult) 168| 1| } 169| | 170| 1| func testSortedByThreeKeyPaths() { 171| 1| let people = [ 172| 1| SimplePerson(forename: "Tom", surname: "James", age: 32), 173| 1| SimplePerson(forename: "Angeline", surname: "Wade", age: 57), 174| 1| SimplePerson(forename: "Max", surname: "James", age: 34), 175| 1| SimplePerson(forename: "Angeline", surname: "Wade", age: 82) 176| 1| ] 177| 1| let expectedResult = [ 178| 1| SimplePerson(forename: "Max", surname: "James", age: 34), 179| 1| SimplePerson(forename: "Tom", surname: "James", age: 32), 180| 1| SimplePerson(forename: "Angeline", surname: "Wade", age: 57), 181| 1| SimplePerson(forename: "Angeline", surname: "Wade", age: 82) 182| 1| ] 183| 1| XCTAssertEqual(people.sorted(by: \.surname, and: \.forename, and: \.age), expectedResult) 184| 1| } 185| | 186| 1| func testFirstByKeyPath() { 187| 1| let array1 = [ 188| 1| Person(name: "John", age: 30, location: Location(city: "Boston")), 189| 1| Person(name: "Jan", age: 22, location: nil), 190| 1| Person(name: "Roman", age: 30, location: Location(city: "Moscow")) 191| 1| ] 192| 1| 193| 1| let first30Age = array1.first(where: \.age, equals: 30) 194| 1| 195| 1| XCTAssertEqual(first30Age, array1.first) 196| 1| 197| 1| let missingPerson = array1.first(where: \.name, equals: "Tom") 198| 1| 199| 1| XCTAssertNil(missingPerson) 200| 1| } 201| |} /Users/runner/work/SwifterSwift/SwifterSwift/Tests/SwiftStdlibTests/SignedIntegerExtensionsTests.swift: 1| |// SignedIntegerExtensionsTests.swift - Copyright 2020 SwifterSwift 2| | 3| |@testable import SwifterSwift 4| |import XCTest 5| | 6| |final class SignedIntegerExtensionsTests: XCTestCase { 7| 1| func testAbs() { 8| 1| XCTAssertEqual((-9).abs, 9) 9| 1| } 10| | 11| 1| func testIsPositive() { 12| 1| XCTAssert(1.isPositive) 13| 1| XCTAssertFalse(0.isPositive) 14| 1| XCTAssertFalse((-1).isPositive) 15| 1| } 16| | 17| 1| func testIsNegative() { 18| 1| XCTAssert((-1).isNegative) 19| 1| XCTAssertFalse(0.isNegative) 20| 1| XCTAssertFalse(1.isNegative) 21| 1| } 22| | 23| 1| func testIsEven() { 24| 1| XCTAssert(2.isEven) 25| 1| XCTAssertFalse(3.isEven) 26| 1| } 27| | 28| 1| func testIsOdd() { 29| 1| XCTAssert(3.isOdd) 30| 1| XCTAssertFalse(2.isOdd) 31| 1| } 32| | 33| 1| func testTimeString() { 34| 1| XCTAssertEqual((-1).timeString, "0 sec") 35| 1| XCTAssertEqual(45.timeString, "45 sec") 36| 1| XCTAssertEqual(120.timeString, "2 min") 37| 1| XCTAssertEqual(3600.timeString, "1h") 38| 1| XCTAssertEqual(3660.timeString, "1h 1m") 39| 1| } 40| | 41| 1| func testGcd() { 42| 1| XCTAssertEqual(8.gcd(of: 20), 4) 43| 1| } 44| | 45| 1| func testLcm() { 46| 1| XCTAssertEqual(4.lcm(of: 3), 12) 47| 1| } 48| | 49| 1| func testString() { 50| 1| XCTAssertEqual(2.string, "2") 51| 1| } 52| |} /Users/runner/work/SwifterSwift/SwifterSwift/Tests/SwiftStdlibTests/SignedNumericExtensionsTests.swift: 1| |// SignedNumericExtensionsTests.swift - Copyright 2020 SwifterSwift 2| | 3| |@testable import SwifterSwift 4| |import XCTest 5| | 6| |final class SignedNumericExtensionsTests: XCTestCase { 7| 1| func testString() { 8| 1| let number1: Double = -1.2 9| 1| XCTAssertEqual(number1.string, "-1.2") 10| 1| 11| 1| let number2: Float = 2.3 12| 1| XCTAssertEqual(number2.string, "2.3") 13| 1| } 14| | 15| 1| func testAsLocaleCurrency() { 16| 1| let number1: Double = 3.2 17| 1| XCTAssertEqual(number1.asLocaleCurrency, "$3.20") 18| 1| 19| 1| let number2 = Double(10.23) 20| 1| if let symbol = Locale.current.currencySymbol { 21| 1| XCTAssertNotNil(number2.asLocaleCurrency!) 22| 1| XCTAssert(number2.asLocaleCurrency!.contains(symbol)) 23| 1| } 24| 1| XCTAssertNotNil(number2.asLocaleCurrency!) 25| 1| XCTAssert(number2.asLocaleCurrency!.contains("\(number2)")) 26| 1| 27| 1| let number3 = 10 28| 1| if let symbol = Locale.current.currencySymbol { 29| 1| XCTAssertNotNil(number3.asLocaleCurrency) 30| 1| XCTAssert(number3.asLocaleCurrency!.contains(symbol)) 31| 1| } 32| 1| XCTAssertNotNil(number3.asLocaleCurrency) 33| 1| XCTAssert(number3.asLocaleCurrency!.contains("\(number3)")) 34| 1| } 35| |} /Users/runner/work/SwifterSwift/SwifterSwift/Tests/SwiftStdlibTests/StringExtensionsTests.swift: 1| |// StringExtensionsTests.swift - Copyright 2020 SwifterSwift 2| | 3| |@testable import SwifterSwift 4| |import XCTest 5| | 6| |// swiftlint:disable:next type_body_length 7| |final class StringExtensionsTests: XCTestCase { 8| | let helloWorld = "Hello World!" 9| | let flower = "💐" // for testing multi-byte characters 10| | 11| 97| override func setUp() { 12| 97| super.setUp() 13| 97| NSTimeZone.default = NSTimeZone.system 14| 97| } 15| | 16| 1| func testBase64Decoded() { 17| 1| XCTAssertEqual("SGVsbG8gV29ybGQh".base64Decoded, helloWorld) 18| 1| XCTAssertEqual("http://example.com/xxx", "aHR0cDovL2V4YW1wbGUuY29tL3h4eA".base64Decoded) 19| 1| XCTAssertNil(helloWorld.base64Decoded) 20| 1| } 21| | 22| 1| func testBase64Encoded() { 23| 1| XCTAssertEqual(helloWorld.base64Encoded, "SGVsbG8gV29ybGQh") 24| 1| } 25| | 26| 1| func testCharactersArray() { 27| 1| let str = "Swift" 28| 1| let chars = [Character("S"), Character("w"), Character("i"), Character("f"), Character("t")] 29| 1| XCTAssertEqual(str.charactersArray, chars) 30| 1| } 31| | 32| 1| func testCamelCased() { 33| 1| XCTAssertEqual("Hello test".camelCased, "helloTest") 34| 1| XCTAssertEqual("Hellotest".camelCased, "hellotest") 35| 1| } 36| | 37| 1| func testContainEmoji() { 38| 1| XCTAssert("Hello 😂".containEmoji) 39| 1| XCTAssertFalse("Hello ;)".containEmoji) 40| 1| } 41| | 42| 1| func testFirstCharacter() { 43| 1| XCTAssertNil("".firstCharacterAsString) 44| 1| XCTAssertNotNil("Hello".firstCharacterAsString) 45| 1| XCTAssertEqual("Hello".firstCharacterAsString, "H") 46| 1| } 47| | 48| 1| func testHasLetters() { 49| 1| XCTAssert("hsj 1 wq3".hasLetters) 50| 1| XCTAssertFalse("123".hasLetters) 51| 1| XCTAssert("Hello test".hasLetters) 52| 1| } 53| | 54| 1| func testHasNumbers() { 55| 1| XCTAssert("hsj 1 wq3".hasNumbers) 56| 1| XCTAssert("123".hasNumbers) 57| 1| XCTAssertFalse("Hello test".hasNumbers) 58| 1| } 59| | 60| 1| func testIsAlphabetic() { 61| 1| XCTAssert("abc".isAlphabetic) 62| 1| XCTAssertFalse("123abc".isAlphabetic) 63| 1| XCTAssertFalse("123".isAlphabetic) 64| 1| } 65| | 66| 1| func testIsAlphaNumeric() { 67| 1| XCTAssert("123abc".isAlphaNumeric) 68| 1| XCTAssertFalse("123".isAlphaNumeric) 69| 1| XCTAssertFalse("abc".isAlphaNumeric) 70| 1| } 71| | 72| 1| func testIsPalindrome() { 73| 1| XCTAssert("abcdcba".isPalindrome) 74| 1| XCTAssert("Mom".isPalindrome) 75| 1| XCTAssert("A man a plan a canal, Panama!".isPalindrome) 76| 1| XCTAssertFalse("Mama".isPalindrome) 77| 1| XCTAssertFalse("".isPalindrome) 78| 1| } 79| | 80| 1| func testisValidEmail() { 81| 1| // https://blogs.msdn.microsoft.com/testing123/2009/02/06/email-address-test-cases/ 82| 1| 83| 1| XCTAssert("email@domain.com".isValidEmail) 84| 1| XCTAssert("firstname.lastname@domain.com".isValidEmail) 85| 1| XCTAssert("email@subdomain.domain.com".isValidEmail) 86| 1| XCTAssert("firstname+lastname@domain.com".isValidEmail) 87| 1| XCTAssert("email@123.123.123.123".isValidEmail) 88| 1| XCTAssert("email@[123.123.123.123]".isValidEmail) 89| 1| XCTAssert("\"email\"@domain.com".isValidEmail) 90| 1| XCTAssert("1234567890@domain.com".isValidEmail) 91| 1| XCTAssert("email@domain-one.com".isValidEmail) 92| 1| XCTAssert("_______@domain.com".isValidEmail) 93| 1| XCTAssert("email@domain.name".isValidEmail) 94| 1| XCTAssert("email@domain.co.jp".isValidEmail) 95| 1| XCTAssert("firstname-lastname@domain.com".isValidEmail) 96| 1| 97| 1| XCTAssertFalse("".isValidEmail) 98| 1| XCTAssertFalse("plainaddress".isValidEmail) 99| 1| XCTAssertFalse("#@%^%#$@#$@#.com".isValidEmail) 100| 1| XCTAssertFalse("@domain.com".isValidEmail) 101| 1| XCTAssertFalse("Joe Smith ".isValidEmail) 102| 1| XCTAssertFalse("email.domain.com".isValidEmail) 103| 1| XCTAssertFalse("email@domain@domain.com".isValidEmail) 104| 1| XCTAssertFalse(".email@domain.com".isValidEmail) 105| 1| XCTAssertFalse("email.@domain.com".isValidEmail) 106| 1| XCTAssertFalse("email..email@domain.com".isValidEmail) 107| 1| XCTAssertFalse("email@domain.com (Joe Smith)".isValidEmail) 108| 1| XCTAssertFalse("email@domain".isValidEmail) 109| 1| XCTAssertFalse("email@-domain.com".isValidEmail) 110| 1| XCTAssertFalse(" email@domain.com".isValidEmail) 111| 1| XCTAssertFalse("email@domain.com ".isValidEmail) 112| 1| XCTAssertFalse("\nemail@domain.com".isValidEmail) 113| 1| XCTAssertFalse("nemail@domain.com \n\n".isValidEmail) 114| 1| } 115| | 116| 1| func testIsValidUrl() { 117| 1| XCTAssert("https://google.com".isValidUrl) 118| 1| XCTAssert("http://google.com".isValidUrl) 119| 1| XCTAssert("ftp://google.com".isValidUrl) 120| 1| } 121| | 122| 1| func testIsValidSchemedUrl() { 123| 1| XCTAssertFalse("Hello world!".isValidSchemedUrl) 124| 1| XCTAssert("https://google.com".isValidSchemedUrl) 125| 1| XCTAssert("ftp://google.com".isValidSchemedUrl) 126| 1| XCTAssertFalse("google.com".isValidSchemedUrl) 127| 1| } 128| | 129| 1| func testIsValidHttpsUrl() { 130| 1| XCTAssertFalse("Hello world!".isValidHttpsUrl) 131| 1| XCTAssert("https://google.com".isValidHttpsUrl) 132| 1| XCTAssertFalse("http://google.com".isValidHttpsUrl) 133| 1| XCTAssertFalse("google.com".isValidHttpsUrl) 134| 1| } 135| | 136| 1| func testIsValidHttpUrl() { 137| 1| XCTAssertFalse("Hello world!".isValidHttpUrl) 138| 1| XCTAssert("http://google.com".isValidHttpUrl) 139| 1| XCTAssertFalse("google.com".isValidHttpUrl) 140| 1| } 141| | 142| 1| func testIsValidFileURL() { 143| 1| XCTAssertFalse("Hello world!".isValidFileUrl) 144| 1| XCTAssert("file://var/folder/file.txt".isValidFileUrl) 145| 1| XCTAssertFalse("google.com".isValidFileUrl) 146| 1| } 147| | 148| 1| func testIsNumeric() { 149| 1| XCTAssert("123".isNumeric) 150| 1| XCTAssert("123.4".isNumeric) 151| 1| XCTAssert("1.25e2".isNumeric) 152| 1| XCTAssert("1.25e-2".isNumeric) 153| 1| XCTAssert("000123.456".isNumeric) 154| 1| XCTAssertFalse("123abc".isNumeric) 155| 1| XCTAssertFalse("abc".isNumeric) 156| 1| XCTAssertFalse("123.@.".isNumeric) 157| 1| } 158| | 159| 1| func testIsDigits() { 160| 1| XCTAssert("123".isDigits) 161| 1| XCTAssert("987654321".isDigits) 162| 1| XCTAssertFalse("123.4".isDigits) 163| 1| XCTAssertFalse("1.25e2".isDigits) 164| 1| XCTAssertFalse("123abc".isDigits) 165| 1| } 166| | 167| 1| func testLastCharacter() { 168| 1| XCTAssertNotNil("Hello".lastCharacterAsString) 169| 1| XCTAssertEqual("Hello".lastCharacterAsString, "o") 170| 1| XCTAssertNil("".lastCharacterAsString) 171| 1| } 172| | 173| 1| func testLatinized() { 174| 1| XCTAssertEqual("Hëllô Teśt".latinized, "Hello Test") 175| 1| } 176| | 177| 1| func testBool() { 178| 1| XCTAssertNotNil("1".bool) 179| 1| XCTAssert("1".bool!) 180| 1| 181| 1| XCTAssertNotNil("false".bool) 182| 1| XCTAssertFalse("false".bool!) 183| 1| XCTAssertNil("8s".bool) 184| 1| } 185| | 186| 1| func testDate() { 187| 1| let dateFromStr = "2015-06-01".date 188| 1| XCTAssertNotNil(dateFromStr) 189| 1| XCTAssertEqual(dateFromStr?.year, 2015) 190| 1| XCTAssertEqual(dateFromStr?.month, 6) 191| 1| XCTAssertEqual(dateFromStr?.day, 1) 192| 1| } 193| | 194| 1| func testDateTime() { 195| 1| let dateFromStr = "2015-06-01 14:23:09".dateTime 196| 1| XCTAssertNotNil(dateFromStr) 197| 1| XCTAssertEqual(dateFromStr?.year, 2015) 198| 1| XCTAssertEqual(dateFromStr?.month, 6) 199| 1| XCTAssertEqual(dateFromStr?.day, 1) 200| 1| XCTAssertEqual(dateFromStr?.hour, 14) 201| 1| XCTAssertEqual(dateFromStr?.minute, 23) 202| 1| XCTAssertEqual(dateFromStr?.second, 9) 203| 1| } 204| | 205| 1| func testInt() { 206| 1| XCTAssertNotNil("8".int) 207| 1| XCTAssertEqual("8".int, 8) 208| 1| 209| 1| XCTAssertNil("8s".int) 210| 1| } 211| | 212| 1| func testLoremIpsum() { 213| 1| // https://www.lipsum.com/ 214| 1| let completeLoremIpsum = """ 215| 1| 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. 216| 1| """ 217| 1| 218| 1| XCTAssertEqual(String.loremIpsum(), completeLoremIpsum) 219| 1| XCTAssertEqual(String.loremIpsum(ofLength: 0), "") 220| 1| XCTAssertEqual(String.loremIpsum(ofLength: 26), "Lorem ipsum dolor sit amet") 221| 1| } 222| | 223| 1| func testUrl() { 224| 1| XCTAssertNil("hello world".url) 225| 1| 226| 1| let google = "https://www.google.com" 227| 1| XCTAssertEqual(google.url, URL(string: google)) 228| 1| } 229| | 230| 1| func testTrimmed() { 231| 1| XCTAssertEqual("\n Hello \n ".trimmed, "Hello") 232| 1| } 233| | 234| 1| func testUrlDecoded() { 235| 1| XCTAssertEqual("it's%20easy%20to%20encode%20strings".urlDecoded, "it's easy to encode strings") 236| 1| XCTAssertEqual("%%".urlDecoded, "%%") 237| 1| } 238| | 239| 1| func testUrlEncoded() { 240| 1| XCTAssertEqual("it's easy to encode strings".urlEncoded, "it's%20easy%20to%20encode%20strings") 241| 1| } 242| | 243| 1| func testRegexEscaped() { 244| 1| XCTAssertEqual("hello ^$ there".regexEscaped, "hello \\^\\$ there") 245| 1| } 246| | 247| 1| func testWithoutSpacesAndNewLines() { 248| 1| XCTAssertEqual("Hello \n Test".withoutSpacesAndNewLines, "HelloTest") 249| 1| } 250| | 251| 1| func testIsWhiteSpaces() { 252| 1| var str = "test string" 253| 1| XCTAssertEqual(str.isWhitespace, false) 254| 1| 255| 1| str = " " 256| 1| XCTAssertEqual(str.isWhitespace, true) 257| 1| 258| 1| str = " \n \n " 259| 1| XCTAssertEqual(str.isWhitespace, true) 260| 1| } 261| | 262| 1| func testFloat() { 263| 1| XCTAssertNotNil("8".float()) 264| 1| XCTAssertEqual("8".float(), 8) 265| 1| 266| 1| XCTAssertNotNil("8.23".float(locale: Locale(identifier: "en_US_POSIX"))) 267| 1| XCTAssertEqual("8.23".float(locale: Locale(identifier: "en_US_POSIX")), Float(8.23)) 268| 1| 269| 1| #if os(Linux) 270| 1| XCTAssertEqual("8s".float(), 8) 271| 1| #else 272| 1| XCTAssertNil("8s".float()) 273| 1| #endif 274| 1| } 275| | 276| 1| func testDouble() { 277| 1| XCTAssertNotNil("8".double()) 278| 1| XCTAssertEqual("8".double(), 8) 279| 1| 280| 1| XCTAssertNotNil("8.23".double(locale: Locale(identifier: "en_US_POSIX"))) 281| 1| XCTAssertEqual("8.23".double(locale: Locale(identifier: "en_US_POSIX")), 8.23) 282| 1| 283| 1| #if os(Linux) 284| 1| XCTAssertEqual("8s".double(), 8) 285| 1| #else 286| 1| XCTAssertNil("8s".double()) 287| 1| #endif 288| 1| } 289| | 290| 1| func testCgFloat() { 291| 1| #if !os(Linux) 292| 1| XCTAssertNotNil("8".cgFloat()) 293| 1| XCTAssertEqual("8".cgFloat(), 8) 294| 1| 295| 1| XCTAssertNotNil("8.23".cgFloat(locale: Locale(identifier: "en_US_POSIX"))) 296| 1| XCTAssertEqual("8.23".cgFloat(locale: Locale(identifier: "en_US_POSIX")), CGFloat(8.23)) 297| 1| 298| 1| XCTAssertNil("8s".cgFloat()) 299| 1| #endif 300| 1| } 301| | 302| 1| func testLines() { 303| 1| #if !os(Linux) 304| 1| XCTAssertEqual("Hello\ntest".lines(), ["Hello", "test"]) 305| 1| #endif 306| 1| } 307| | 308| 1| func testLocalized() { 309| 1| XCTAssertEqual(helloWorld.localized(), NSLocalizedString(helloWorld, comment: "")) 310| 1| XCTAssertEqual(helloWorld.localized(comment: "comment"), NSLocalizedString(helloWorld, comment: "comment")) 311| 1| } 312| | 313| 1| func testMostCommonCharacter() { 314| 1| let mostCommonCharacter = "This is a test, since e is appearing every where e should be the common character" 315| 1| .mostCommonCharacter 316| 1| XCTAssertEqual(mostCommonCharacter(), "e") 317| 1| XCTAssertNil("".mostCommonCharacter()) 318| 1| } 319| | 320| 1| func testUnicodeArray() { 321| 1| XCTAssertEqual("Hello".unicodeArray(), [72, 101, 108, 108, 111]) 322| 1| } 323| | 324| 1| func testWords() { 325| 1| XCTAssertEqual("Swift is amazing".words(), ["Swift", "is", "amazing"]) 326| 1| } 327| | 328| 1| func testWordsCount() { 329| 1| XCTAssertEqual("Swift is amazing".wordCount(), 3) 330| 1| } 331| | 332| 1| func testToSlug() { 333| 1| let str = " A nice & hög _ Str " 334| 1| XCTAssertEqual(str.toSlug(), "a-nice-&-hog-str") 335| 1| XCTAssertEqual("Swift is amazing".toSlug(), "swift-is-amazing") 336| 1| } 337| | 338| 1| func testSubscript() { 339| 1| let str = "Hello world!" 340| 1| XCTAssertEqual(str[safe: 1], "e") 341| 1| XCTAssertNil(str[safe: 18]) 342| 1| 343| 1| XCTAssertNil(str[safe: -5..<5]) 344| 1| XCTAssertNil(str[safe: -5...5]) 345| 1| 346| 1| XCTAssertEqual(str[safe: 0..<0], "") 347| 1| XCTAssertEqual(str[safe: 0..<4], "Hell") 348| 1| XCTAssertEqual(str[safe: 1..<5], "ello") 349| 1| XCTAssertEqual(str[safe: 7..<7], "") 350| 1| XCTAssertNil(str[safe: 10..<18]) 351| 1| XCTAssertEqual(str[safe: 11..<12], "!") 352| 1| 353| 1| XCTAssertEqual(str[safe: 0...0], "H") 354| 1| XCTAssertEqual(str[safe: 0...4], "Hello") 355| 1| XCTAssertEqual(str[safe: 1...5], "ello ") 356| 1| XCTAssertEqual(str[safe: 7...7], "o") 357| 1| XCTAssertNil(str[safe: 10...18]) 358| 1| XCTAssertEqual(str[safe: 11...11], "!") 359| 1| XCTAssertNil(str[safe: 11...12]) 360| 1| 361| 1| let oneCharStr = "a" 362| 1| XCTAssertEqual(oneCharStr[safe: 0..<0], "") 363| 1| XCTAssertEqual(oneCharStr[safe: 0..<1], "a") 364| 1| XCTAssertNil(oneCharStr[safe: 0..<2]) 365| 1| XCTAssertEqual(oneCharStr[safe: 1..<1], "") 366| 1| XCTAssertNil(oneCharStr[safe: 1..<2]) 367| 1| 368| 1| XCTAssertEqual(oneCharStr[safe: 0...0], "a") 369| 1| XCTAssertNil(oneCharStr[safe: 0...1]) 370| 1| XCTAssertNil(oneCharStr[safe: 0...2]) 371| 1| XCTAssertNil(oneCharStr[safe: 1...1]) 372| 1| XCTAssertNil(oneCharStr[safe: 1...2]) 373| 1| 374| 1| // Empty string 375| 1| XCTAssertEqual(""[safe: 0..<0], "") 376| 1| XCTAssertNil(""[safe: 0..<1]) 377| 1| XCTAssertNil(""[safe: 1..<1]) 378| 1| XCTAssertNil(""[safe: 1..<2]) 379| 1| XCTAssertNil(""[safe: 2..<3]) 380| 1| 381| 1| XCTAssertNil(""[safe: 0...0]) 382| 1| XCTAssertNil(""[safe: 0...1]) 383| 1| XCTAssertNil(""[safe: 1..<1]) 384| 1| XCTAssertNil(""[safe: 1...2]) 385| 1| XCTAssertNil(""[safe: 2...3]) 386| 1| } 387| | 388| 1| func testCopyToPasteboard() { 389| 1| let str = "Hello world!" 390| 1| #if os(iOS) 391| 1| str.copyToPasteboard() 392| 1| let strFromPasteboard = UIPasteboard.general.string 393| 1| XCTAssertEqual(strFromPasteboard, str) 394| 1| 395| 1| #elseif os(macOS) 396| 1| str.copyToPasteboard() 397| 1| let strFromPasteboard = NSPasteboard.general.string(forType: .string) 398| 1| XCTAssertEqual(strFromPasteboard, str) 399| 1| #endif 400| 1| } 401| | 402| 1| func testCamelize() { 403| 1| var str = "Hello test" 404| 1| str.camelize() 405| 1| XCTAssertEqual(str, "helloTest") 406| 1| 407| 1| str = "helloWorld" 408| 1| str.camelize() 409| 1| XCTAssertEqual(str, "helloworld") 410| 1| } 411| | 412| 1| func testFirstCharacterUppercased() { 413| 1| var str = "hello test" 414| 1| str.firstCharacterUppercased() 415| 1| XCTAssertEqual(str, "Hello test") 416| 1| 417| 1| str = "helloworld" 418| 1| str.firstCharacterUppercased() 419| 1| XCTAssertEqual(str, "Helloworld") 420| 1| 421| 1| str = "" 422| 1| str.firstCharacterUppercased() 423| 1| XCTAssertEqual(str, "") 424| 1| } 425| | 426| 1| func testHasUniqueCharacters() { 427| 1| XCTAssert("swift".hasUniqueCharacters()) 428| 1| XCTAssertFalse("language".hasUniqueCharacters()) 429| 1| XCTAssertFalse("".hasUniqueCharacters()) 430| 1| } 431| | 432| 1| func testContain() { 433| 1| XCTAssert("Hello Tests".contains("Hello", caseSensitive: true)) 434| 1| XCTAssert("Hello Tests".contains("hello", caseSensitive: false)) 435| 1| } 436| | 437| 1| func testCount() { 438| 1| XCTAssertEqual("Hello This Tests".count(of: "T"), 2) 439| 1| XCTAssertEqual("Hello This Tests".count(of: "t"), 1) 440| 1| XCTAssertEqual("Hello This Tests".count(of: "T", caseSensitive: false), 3) 441| 1| XCTAssertEqual("Hello This Tests".count(of: "t", caseSensitive: false), 3) 442| 1| } 443| | 444| 1| func testEnd() { 445| 1| XCTAssert("Hello Test".ends(with: "test", caseSensitive: false)) 446| 1| XCTAssert("Hello Tests".ends(with: "sts")) 447| 1| } 448| | 449| 1| func testLatinize() { 450| 1| var str = "Hëllô Teśt" 451| 1| str.latinize() 452| 1| XCTAssertEqual(str, "Hello Test") 453| 1| } 454| | 455| 1| func testRandom() { 456| 1| let str1 = String.random(ofLength: 10) 457| 1| XCTAssertEqual(str1.count, 10) 458| 1| 459| 1| let str2 = String.random(ofLength: 10) 460| 1| XCTAssertEqual(str2.count, 10) 461| 1| 462| 1| XCTAssertNotEqual(str1, str2) 463| 1| 464| 1| XCTAssertEqual(String.random(ofLength: 0), "") 465| 1| } 466| | 467| 1| func testReverse() { 468| 1| var str = "Hello" 469| 1| str.reverse() 470| 1| XCTAssertEqual(str, "olleH") 471| 1| } 472| | 473| 1| func testSlice() { 474| 1| XCTAssertEqual("12345678".slicing(from: 2, length: 3), "345") 475| 1| XCTAssertEqual("12345678".slicing(from: 2, length: 0), "") 476| 1| XCTAssertNil("12345678".slicing(from: 12, length: 0)) 477| 1| XCTAssertEqual("12345678".slicing(from: 2, length: 100), "345678") 478| 1| 479| 1| var str = "12345678" 480| 1| str.slice(from: 2, length: 3) 481| 1| XCTAssertEqual(str, "345") 482| 1| 483| 1| str = "12345678" 484| 1| str.slice(from: 2, length: 0) 485| 1| print(str) 486| 1| XCTAssertEqual(str, "") 487| 1| 488| 1| str = "12345678" 489| 1| str.slice(from: 12, length: 0) 490| 1| XCTAssertEqual(str, "12345678") 491| 1| 492| 1| str = "12345678" 493| 1| str.slice(from: 2, length: 100) 494| 1| XCTAssertEqual(str, "345678") 495| 1| 496| 1| str = "12345678" 497| 1| str.slice(from: 2, to: 5) 498| 1| XCTAssertEqual(str, "345") 499| 1| 500| 1| str = "12345678" 501| 1| str.slice(from: 2, to: 1) 502| 1| XCTAssertEqual(str, "12345678") 503| 1| 504| 1| str = "12345678" 505| 1| str.slice(at: 2) 506| 1| XCTAssertEqual(str, "345678") 507| 1| 508| 1| str = "12345678" 509| 1| str.slice(at: 20) 510| 1| XCTAssertEqual(str, "12345678") 511| 1| } 512| | 513| 1| func testStart() { 514| 1| XCTAssert("Hello Test".starts(with: "hello", caseSensitive: false)) 515| 1| XCTAssert("Hello Tests".starts(with: "He")) 516| 1| } 517| | 518| 1| func testDateWithFormat() { 519| 1| let dateString = "2012-12-08 17:00:00.0" 520| 1| let date = dateString.date(withFormat: "yyyy-dd-MM HH:mm:ss.S") 521| 1| XCTAssertNotNil(date) 522| 1| XCTAssertNil(dateString.date(withFormat: "Hello world!")) 523| 1| } 524| | 525| 1| func testTrim() { 526| 1| var str = "\n Hello \n " 527| 1| str.trim() 528| 1| XCTAssertEqual(str, "Hello") 529| 1| } 530| | 531| 1| func testTruncate() { 532| 1| var str = "This is a very long sentence" 533| 1| str.truncate(toLength: 14) 534| 1| XCTAssertEqual(str, "This is a very...") 535| 1| 536| 1| str = "This is a very long sentence" 537| 1| str.truncate(toLength: 14, trailing: nil) 538| 1| XCTAssertEqual(str, "This is a very") 539| 1| 540| 1| str = "This is a short sentence" 541| 1| str.truncate(toLength: 100) 542| 1| XCTAssertEqual(str, "This is a short sentence") 543| 1| 544| 1| str.truncate(toLength: -1) 545| 1| XCTAssertEqual(str, "This is a short sentence") 546| 1| } 547| | 548| 1| func testTruncated() { 549| 1| XCTAssertEqual("".truncated(toLength: 5, trailing: nil), "") 550| 1| XCTAssertEqual("This is a short sentence".truncated(toLength: -1, trailing: nil), "This is a short sentence") 551| 1| XCTAssertEqual("This is a very long sentence".truncated(toLength: 14), "This is a very...") 552| 1| 553| 1| XCTAssertEqual("This is a very long sentence".truncated(toLength: 14, trailing: nil), "This is a very") 554| 1| XCTAssertEqual("This is a short sentence".truncated(toLength: 100), "This is a short sentence") 555| 1| } 556| | 557| 1| func testUrlDecode() { 558| 1| var url = "it's%20easy%20to%20encode%20strings" 559| 1| url.urlDecode() 560| 1| XCTAssertEqual(url, "it's easy to encode strings") 561| 1| } 562| | 563| 1| func testUrlEncode() { 564| 1| var url = "it's easy to encode strings" 565| 1| url.urlEncode() 566| 1| XCTAssertEqual(url, "it's%20easy%20to%20encode%20strings") 567| 1| } 568| | 569| | let emailPattern = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}" 570| | 571| 1| func testPatternMatches() { 572| 1| XCTAssertTrue("123".matches(pattern: "\\d{3}")) 573| 1| XCTAssertFalse("dasda".matches(pattern: "\\d{3}")) 574| 1| XCTAssertFalse("notanemail.com".matches(pattern: emailPattern)) 575| 1| XCTAssertTrue("email@mail.com".matches(pattern: emailPattern)) 576| 1| } 577| | 578| 1| func testRegexMatches() throws { 579| 1| XCTAssertTrue("123".matches(regex: try NSRegularExpression(pattern: "\\d{3}"))) 580| 1| XCTAssertFalse("dasda".matches(regex: try NSRegularExpression(pattern: "\\d{3}"))) 581| 1| XCTAssertFalse("notanemail.com".matches(regex: try NSRegularExpression(pattern: emailPattern))) 582| 1| XCTAssertTrue("email@mail.com".matches(regex: try NSRegularExpression(pattern: emailPattern))) 583| 1| } 584| | 585| | #if canImport(Foundation) 586| 1| func testPatternMatchOperator() { 587| 1| XCTAssert("123" ~= "\\d{3}") 588| 1| XCTAssertFalse("dasda" ~= "\\d{3}") 589| 1| XCTAssertFalse("notanemail.com" ~= emailPattern) 590| 1| XCTAssert("email@mail.com" ~= emailPattern) 591| 1| XCTAssert("hat" ~= "[a-z]at") 592| 1| XCTAssertFalse("" ~= "[a-z]at") 593| 1| XCTAssert("" ~= "[a-z]*") 594| 1| XCTAssertFalse("" ~= "[0-9]+") 595| 1| } 596| | #endif 597| | 598| 1| func testRegexMatchOperator() throws { 599| 1| let regex = try NSRegularExpression(pattern: "\\d{3}") 600| 1| XCTAssert("123" ~= regex) 601| 1| XCTAssertFalse("abc" ~= regex) 602| 1| } 603| | 604| 1| func testPadStart() { 605| 1| var str: String = "str" 606| 1| str.padStart(10) 607| 1| XCTAssertEqual(str, " str") 608| 1| 609| 1| str = "str" 610| 1| str.padStart(10, with: "br") 611| 1| XCTAssertEqual(str, "brbrbrbstr") 612| 1| 613| 1| str = "str" 614| 1| str.padStart(5, with: "brazil") 615| 1| XCTAssertEqual(str, "brstr") 616| 1| 617| 1| str = "str" 618| 1| str.padStart(6, with: "a") 619| 1| XCTAssertEqual(str, "aaastr") 620| 1| 621| 1| str = "str" 622| 1| str.padStart(6, with: "abc") 623| 1| XCTAssertEqual(str, "abcstr") 624| 1| 625| 1| str = "str" 626| 1| str.padStart(2) 627| 1| XCTAssertEqual(str, "str") 628| 1| } 629| | 630| 1| func testPaddingStart() { 631| 1| XCTAssertEqual("str".paddingStart(10), " str") 632| 1| XCTAssertEqual("str".paddingStart(10, with: "br"), "brbrbrbstr") 633| 1| XCTAssertEqual("str".paddingStart(5, with: "brazil"), "brstr") 634| 1| XCTAssertEqual("str".paddingStart(6, with: "a"), "aaastr") 635| 1| XCTAssertEqual("str".paddingStart(6, with: "abc"), "abcstr") 636| 1| XCTAssertEqual("str".paddingStart(2), "str") 637| 1| } 638| | 639| 1| func testPadEnd() { 640| 1| var str: String = "str" 641| 1| str.padEnd(10) 642| 1| XCTAssertEqual(str, "str ") 643| 1| 644| 1| str = "str" 645| 1| str.padEnd(10, with: "br") 646| 1| XCTAssertEqual(str, "strbrbrbrb") 647| 1| 648| 1| str = "str" 649| 1| str.padEnd(5, with: "brazil") 650| 1| XCTAssertEqual(str, "strbr") 651| 1| 652| 1| str = "str" 653| 1| str.padEnd(6, with: "a") 654| 1| XCTAssertEqual(str, "straaa") 655| 1| 656| 1| str = "str" 657| 1| str.padEnd(6, with: "abc") 658| 1| XCTAssertEqual(str, "strabc") 659| 1| 660| 1| str = "str" 661| 1| str.padEnd(2) 662| 1| XCTAssertEqual(str, "str") 663| 1| } 664| | 665| 1| func testPaddingEnd() { 666| 1| XCTAssertEqual("str".paddingEnd(10), "str ") 667| 1| XCTAssertEqual("str".paddingEnd(10, with: "br"), "strbrbrbrb") 668| 1| XCTAssertEqual("str".paddingEnd(5, with: "brazil"), "strbr") 669| 1| XCTAssertEqual("str".paddingEnd(6, with: "a"), "straaa") 670| 1| XCTAssertEqual("str".paddingEnd(6, with: "abc"), "strabc") 671| 1| XCTAssertEqual("str".paddingEnd(2), "str") 672| 1| } 673| | 674| 1| func testIsSpelledCorrectly() { 675| 1| #if os(iOS) || os(tvOS) 676| 1| let strCorrect = "Hello, World!" 677| 1| 678| 1| XCTAssert(strCorrect.isSpelledCorrectly) 679| 1| 680| 1| let strNonCorrect = "Helol, Wrold!" 681| 1| XCTAssertFalse(strNonCorrect.isSpelledCorrectly) 682| 1| #endif 683| 1| } 684| | 685| 1| func testRemovingPrefix() { 686| 1| let inputStr = "Hello, World!" 687| 1| XCTAssertEqual(inputStr.removingPrefix("Hello, "), "World!") 688| 1| } 689| | 690| 1| func testRemovingSuffix() { 691| 1| let inputStr = "Hello, World!" 692| 1| XCTAssertEqual(inputStr.removingSuffix(", World!"), "Hello") 693| 1| } 694| | 695| 1| func testWithPrefix() { 696| 1| XCTAssertEqual("www.apple.com".withPrefix("https://"), "https://www.apple.com") 697| 1| XCTAssertEqual("https://www.apple.com".withPrefix("https://"), "https://www.apple.com") 698| 1| } 699| | 700| 1| func testInitFromBase64() { 701| 1| XCTAssertNotNil(String(base64: "SGVsbG8gV29ybGQh")) 702| 1| XCTAssertEqual(String(base64: "SGVsbG8gV29ybGQh"), "Hello World!") 703| 1| XCTAssertNil(String(base64: "hello")) 704| 1| } 705| | 706| 1| func testInitRandomOfLength() { 707| 1| let str1 = String(randomOfLength: 10) 708| 1| XCTAssertEqual(str1.count, 10) 709| 1| 710| 1| let str2 = String(randomOfLength: 10) 711| 1| XCTAssertEqual(str2.count, 10) 712| 1| 713| 1| XCTAssertNotEqual(str1, str2) 714| 1| 715| 1| XCTAssertEqual(String(randomOfLength: 0), "") 716| 1| } 717| | 718| 1| func testBold() { 719| 1| #if canImport(Foundation) && os(macOS) 720| 1| let boldString = "hello".bold 721| 1| let attrs = boldString.attributes( 722| 1| at: 0, 723| 1| longestEffectiveRange: nil, 724| 1| in: NSRange(location: 0, length: boldString.length)) 725| 1| XCTAssertNotNil(attrs[NSAttributedString.Key.font]) 726| 1| 727| 1| #if os(macOS) 728| 1| guard let font = attrs[.font] as? NSFont else { 729| 1| XCTFail("Unable to find font in testBold") 730| 1| return 731| 1| } 732| 1| XCTAssertEqual(font, NSFont.boldSystemFont(ofSize: NSFont.systemFontSize)) 733| 1| #elseif os(iOS) 734| 1| guard let font = attrs[NSAttributedString.Key.font] as? UIFont else { 735| 1| XCTFail("Unable to find font in testBold") 736| 1| return 737| 1| } 738| 1| XCTAssertEqual(font, UIFont.boldSystemFont(ofSize: UIFont.systemFontSize)) 739| 1| #endif 740| 1| #endif 741| 1| } 742| | 743| 1| func testUnderline() { 744| 1| #if !os(Linux) 745| 1| let underlinedString = "hello".underline 746| 1| let attrs = underlinedString.attributes( 747| 1| at: 0, 748| 1| longestEffectiveRange: nil, 749| 1| in: NSRange(location: 0, length: underlinedString.length)) 750| 1| XCTAssertNotNil(attrs[NSAttributedString.Key.underlineStyle]) 751| 1| guard let style = attrs[NSAttributedString.Key.underlineStyle] as? Int else { 752| 0| XCTFail("Unable to find style in testUnderline") 753| 0| return 754| 1| } 755| 1| XCTAssertEqual(style, NSUnderlineStyle.single.rawValue) 756| 1| #endif 757| 1| } 758| | 759| 1| func testStrikethrough() { 760| 1| #if !os(Linux) 761| 1| let strikedthroughString = "hello".strikethrough 762| 1| let attrs = strikedthroughString.attributes( 763| 1| at: 0, 764| 1| longestEffectiveRange: nil, 765| 1| in: NSRange(location: 0, length: strikedthroughString.length)) 766| 1| XCTAssertNotNil(attrs[NSAttributedString.Key.strikethroughStyle]) 767| 1| guard let style = attrs[NSAttributedString.Key.strikethroughStyle] as? NSNumber else { 768| 0| XCTFail("Unable to find style in testStrikethrough") 769| 0| return 770| 1| } 771| 1| XCTAssertEqual(style, NSNumber(value: NSUnderlineStyle.single.rawValue as Int)) 772| 1| #endif 773| 1| } 774| | 775| 1| func testItalic() { 776| 1| #if os(iOS) 777| 1| let italicString = "hello".italic 778| 1| let attrs = italicString.attributes( 779| 1| at: 0, 780| 1| longestEffectiveRange: nil, 781| 1| in: NSRange(location: 0, length: italicString.length)) 782| 1| XCTAssertNotNil(attrs[NSAttributedString.Key.font]) 783| 1| guard let font = attrs[NSAttributedString.Key.font] as? UIFont else { 784| 1| XCTFail("Unable to find font in testItalic") 785| 1| return 786| 1| } 787| 1| XCTAssertEqual(font, UIFont.italicSystemFont(ofSize: UIFont.systemFontSize)) 788| 1| #endif 789| 1| } 790| | 791| 1| func testColored() { 792| 1| #if canImport(AppKit) || canImport(UIKit) 793| 1| let coloredString = "hello".colored(with: .orange) 794| 1| let attrs = coloredString.attributes( 795| 1| at: 0, 796| 1| longestEffectiveRange: nil, 797| 1| in: NSRange(location: 0, length: coloredString.length)) 798| 1| XCTAssertNotNil(attrs[NSAttributedString.Key.foregroundColor]) 799| 1| 800| 1| guard let color = attrs[.foregroundColor] as? Color else { 801| 0| XCTFail("Unable to find color in testColored") 802| 0| return 803| 1| } 804| 1| XCTAssertEqual(color, .orange) 805| 1| #endif 806| 1| } 807| | 808| 1| func testNSString() { 809| 1| XCTAssertEqual("Hello".nsString, NSString(string: "Hello")) 810| 1| } 811| | 812| 1| func testFullNSRange() { 813| 1| XCTAssertEqual("".fullNSRange, NSRange(location: 0, length: 0)) 814| 1| XCTAssertEqual(helloWorld.fullNSRange, NSRange(location: 0, length: 12)) 815| 1| XCTAssertEqual(flower.fullNSRange, NSRange(location: 0, length: 2)) 816| 1| } 817| | 818| 1| func testLastPathComponent() { 819| 1| let string = "hello" 820| 1| let nsString = NSString(string: "hello") 821| 1| XCTAssertEqual(string.lastPathComponent, nsString.lastPathComponent) 822| 1| } 823| | 824| 1| func testLastPathExtension() { 825| 1| let string = "hello" 826| 1| let nsString = NSString(string: "hello") 827| 1| XCTAssertEqual(string.pathExtension, nsString.pathExtension) 828| 1| } 829| | 830| 1| func testLastDeletingLastPathComponent() { 831| 1| let string = "hello" 832| 1| let nsString = NSString(string: "hello") 833| 1| XCTAssertEqual(string.deletingLastPathComponent, nsString.deletingLastPathComponent) 834| 1| } 835| | 836| 1| func testLastDeletingPathExtension() { 837| 1| let string = "hello" 838| 1| let nsString = NSString(string: "hello") 839| 1| XCTAssertEqual(string.deletingPathExtension, nsString.deletingPathExtension) 840| 1| } 841| | 842| 1| func testLastPathComponents() { 843| 1| let string = "hello" 844| 1| let nsString = NSString(string: "hello") 845| 1| XCTAssertEqual(string.pathComponents, nsString.pathComponents) 846| 1| } 847| | 848| 1| func testRange() { 849| 1| let fullRange = helloWorld.range(from: NSRange(location: 0, length: 12)) 850| 1| XCTAssertEqual(String(helloWorld[fullRange]), helloWorld) 851| 1| 852| 1| let range = helloWorld.range(from: NSRange(location: 6, length: 6)) 853| 1| XCTAssertEqual(helloWorld[range], "World!") 854| 1| 855| 1| let emptyRange = helloWorld.range(from: NSRange(location: 0, length: 0)) 856| 1| XCTAssertEqual(helloWorld[emptyRange], "") 857| 1| 858| 1| let flowerRange = flower.range(from: NSRange(location: 0, length: 2)) 859| 1| XCTAssertEqual(String(flower[flowerRange]), flower) 860| 1| } 861| | 862| 1| func testNSRange() { 863| 1| let startIndex = helloWorld.startIndex 864| 1| let endIndex = helloWorld.endIndex 865| 1| XCTAssertEqual(helloWorld.nsRange(from: startIndex.. imageFrame.midX) 164| 1| XCTAssertEqual(titleFrame.midY, imageFrame.midY, accuracy: 1.0) 165| 1| XCTAssertEqual(titleFrame.minX - spacing, imageFrame.maxX, accuracy: 1.0) 166| 1| 167| 1| // Image above text 168| 1| button.centerTextAndImage(imageAboveText: true, spacing: spacing) 169| 1| imageFrame = button.imageView!.frame 170| 1| titleFrame = button.titleLabel!.frame 171| 1| 172| 1| XCTAssert(titleFrame.midY > imageFrame.midY) 173| 1| XCTAssertEqual(titleFrame.midX, imageFrame.midX, accuracy: 1.0) 174| 1| XCTAssertEqual(titleFrame.minY - spacing, imageFrame.maxY, accuracy: 1.0) 175| 1| } 176| |} 177| | 178| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Tests/UIKitTests/UICollectionViewExtensionsTests.swift: 1| |// UICollectionViewExtensionsTests.swift - Copyright 2020 SwifterSwift 2| | 3| |@testable import SwifterSwift 4| |import XCTest 5| | 6| |#if canImport(UIKit) && !os(watchOS) 7| |import UIKit 8| | 9| |private final class TestCell: UICollectionViewCell {} 10| | 11| |final class UICollectionViewExtensionsTests: XCTestCase { 12| 7| let collectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewLayout()) 13| 7| let emptyCollectionView = UICollectionView(frame: .zero, collectionViewLayout: UICollectionViewLayout()) 14| 7| let flowLayoutCollectionView: UICollectionView = { 15| 7| let layout = UICollectionViewFlowLayout() 16| 7| layout.scrollDirection = .vertical 17| 7| layout.itemSize = CGSize(width: 10, height: 10) 18| 7| layout.minimumInteritemSpacing = 0 19| 7| layout.sectionInset = .zero 20| 7| let collection = UICollectionView( 21| 7| frame: CGRect(x: 0, y: 0, width: 10, height: 15), 22| 7| collectionViewLayout: layout) 23| 7| if #available(iOS 11, *) { 24| 7| collection.insetsLayoutMarginsFromSafeArea = false 25| 7| } 26| 7| collection.contentInset = .zero 27| 7| return collection 28| 7| }() ------------------ | $s22SwifterSwift_tvOSTests31UICollectionViewExtensionsTestsC020flowLayoutCollectionF0So0eF0Cvpfi: | 14| 7| let flowLayoutCollectionView: UICollectionView = { | 15| 7| let layout = UICollectionViewFlowLayout() | 16| 7| layout.scrollDirection = .vertical | 17| 7| layout.itemSize = CGSize(width: 10, height: 10) | 18| 7| layout.minimumInteritemSpacing = 0 | 19| 7| layout.sectionInset = .zero | 20| 7| let collection = UICollectionView( | 21| 7| frame: CGRect(x: 0, y: 0, width: 10, height: 15), | 22| 7| collectionViewLayout: layout) | 23| 7| if #available(iOS 11, *) { | 24| 7| collection.insetsLayoutMarginsFromSafeArea = false | 25| 7| } | 26| 7| collection.contentInset = .zero | 27| 7| return collection | 28| 7| }() ------------------ | $s22SwifterSwift_tvOSTests31UICollectionViewExtensionsTestsC020flowLayoutCollectionF0So0eF0CvpfiAFyXEfU_: | 14| 7| let flowLayoutCollectionView: UICollectionView = { | 15| 7| let layout = UICollectionViewFlowLayout() | 16| 7| layout.scrollDirection = .vertical | 17| 7| layout.itemSize = CGSize(width: 10, height: 10) | 18| 7| layout.minimumInteritemSpacing = 0 | 19| 7| layout.sectionInset = .zero | 20| 7| let collection = UICollectionView( | 21| 7| frame: CGRect(x: 0, y: 0, width: 10, height: 15), | 22| 7| collectionViewLayout: layout) | 23| 7| if #available(iOS 11, *) { | 24| 7| collection.insetsLayoutMarginsFromSafeArea = false | 25| 7| } | 26| 7| collection.contentInset = .zero | 27| 7| return collection | 28| 7| }() ------------------ 29| | 30| 7| override func setUp() { 31| 7| super.setUp() 32| 7| 33| 7| collectionView.dataSource = self 34| 7| collectionView.reloadData() 35| 7| 36| 7| emptyCollectionView.dataSource = self 37| 7| emptyCollectionView.reloadData() 38| 7| 39| 7| flowLayoutCollectionView.dataSource = self 40| 7| flowLayoutCollectionView.reloadData() 41| 7| } 42| | 43| 1| func testIndexPathForLastRow() { 44| 1| XCTAssertEqual(collectionView.indexPathForLastItem, IndexPath(item: 0, section: 1)) 45| 1| } 46| | 47| 1| func testLastSection() { 48| 1| XCTAssertEqual(collectionView.lastSection, 1) 49| 1| XCTAssertEqual(emptyCollectionView.lastSection, 0) 50| 1| } 51| | 52| 1| func testNumberOfRows() { 53| 1| XCTAssertEqual(collectionView.numberOfItems(), 5) 54| 1| XCTAssertEqual(emptyCollectionView.numberOfItems(), 0) 55| 1| } 56| | 57| 1| func testIndexPathForLastRowInSection() { 58| 1| XCTAssertNil(collectionView.indexPathForLastItem(inSection: -1)) 59| 1| XCTAssertNil(collectionView.indexPathForLastItem(inSection: 2)) 60| 1| XCTAssertEqual(collectionView.indexPathForLastItem(inSection: 0), IndexPath(item: 4, section: 0)) 61| 1| } 62| | 63| 1| func testReloadData() { 64| 1| var completionCalled = false 65| 1| collectionView.reloadData { 66| 1| completionCalled = true 67| 1| XCTAssert(completionCalled) 68| 1| } 69| 1| } 70| | 71| | #if os(iOS) 72| | func testRegisterCellWithClass() { 73| | let indexPath = IndexPath(row: 0, section: 0) 74| | collectionView.register(cellWithClass: TestCell.self) 75| | let cell = collectionView.dequeueReusableCell(withClass: TestCell.self, for: indexPath) 76| | XCTAssertNotNil(cell) 77| | } 78| | #endif 79| | 80| | #if os(iOS) 81| | func testRegisterCellWithNibUsingClass() { 82| | let indexPath = IndexPath(row: 0, section: 0) 83| | collectionView.register(nibWithCellClass: UICollectionViewCell.self, at: UICollectionViewExtensionsTests.self) 84| | let cell = collectionView.dequeueReusableCell(withClass: UICollectionViewCell.self, for: indexPath) 85| | XCTAssertNotNil(cell) 86| | } 87| | #endif 88| | 89| 1| func testSafeScrollToIndexPath() { 90| 1| let validIndexPathTop = IndexPath(row: 0, section: 0) 91| 1| 92| 1| flowLayoutCollectionView.contentOffset = CGPoint(x: 0, y: 30) 93| 1| XCTAssertNotEqual(flowLayoutCollectionView.contentOffset, .zero) 94| 1| 95| 1| flowLayoutCollectionView.safeScrollToItem(at: validIndexPathTop, at: .top, animated: false) 96| 1| XCTAssertEqual(flowLayoutCollectionView.contentOffset, .zero) 97| 1| 98| 1| let validIndexPathBottom = IndexPath(row: 4, section: 0) 99| 1| 100| 1| let bottomOffset = CGPoint( 101| 1| x: 0, 102| 1| y: flowLayoutCollectionView.collectionViewLayout.collectionViewContentSize.height - flowLayoutCollectionView 103| 1| .bounds.size.height) 104| 1| 105| 1| flowLayoutCollectionView.contentOffset = CGPoint(x: 0, y: 30) 106| 1| XCTAssertNotEqual(flowLayoutCollectionView.contentOffset, bottomOffset) 107| 1| 108| 1| flowLayoutCollectionView.safeScrollToItem(at: validIndexPathBottom, at: .bottom, animated: false) 109| 1| 110| 1| XCTAssertEqual(bottomOffset.y, flowLayoutCollectionView.contentOffset.y) 111| 1| 112| 1| let invalidIndexPath = IndexPath(row: 213, section: 21) 113| 1| flowLayoutCollectionView.contentOffset = .zero 114| 1| 115| 1| flowLayoutCollectionView.safeScrollToItem(at: invalidIndexPath, at: .bottom, animated: false) 116| 1| XCTAssertEqual(flowLayoutCollectionView.contentOffset, .zero) 117| 1| 118| 1| let negativeIndexPath = IndexPath(item: -1, section: 0) 119| 1| 120| 1| flowLayoutCollectionView.safeScrollToItem(at: negativeIndexPath, at: .bottom, animated: false) 121| 1| XCTAssertEqual(flowLayoutCollectionView.contentOffset, .zero) 122| 1| } 123| | 124| 1| func testIsValidIndexPath() { 125| 1| let zeroIndexPath = IndexPath(item: 0, section: 0) 126| 1| let invalidIndexPath = IndexPath(item: 0, section: 3) 127| 1| let validIndexPath = IndexPath(item: 4, section: 0) 128| 1| let negativeIndexPath = IndexPath(item: -1, section: 0) 129| 1| 130| 1| XCTAssertFalse(emptyCollectionView.isValidIndexPath(zeroIndexPath)) 131| 1| 132| 1| XCTAssertFalse(collectionView.isValidIndexPath(negativeIndexPath)) 133| 1| XCTAssert(collectionView.isValidIndexPath(zeroIndexPath)) 134| 1| XCTAssert(collectionView.isValidIndexPath(validIndexPath)) 135| 1| XCTAssertFalse(collectionView.isValidIndexPath(invalidIndexPath)) 136| 1| } 137| |} 138| | 139| |extension UICollectionViewExtensionsTests: UICollectionViewDataSource, UICollectionViewDelegateFlowLayout { 140| 9| func numberOfSections(in collectionView: UICollectionView) -> Int { 141| 9| return (collectionView == self.collectionView || collectionView == flowLayoutCollectionView) ? 2 : 0 142| 9| } 143| | 144| 12| func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 145| 12| return (collectionView == self.collectionView || collectionView == flowLayoutCollectionView) ? 146| 12| (section == 0 ? 5 : 0) : 0 147| 12| } 148| | 149| 0| func collectionView(_: UICollectionView, cellForItemAt _: IndexPath) -> UICollectionViewCell { 150| 0| return UICollectionViewCell() 151| 0| } 152| |} 153| | 154| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Tests/UIKitTests/UIColorExtensionsTests.swift: 1| |// UIColorExtensionsTests.swift - Copyright 2020 SwifterSwift 2| | 3| |@testable import SwifterSwift 4| |import XCTest 5| | 6| |#if canImport(UIKit) 7| |import UIKit 8| | 9| |final class UIColorExtensionsTests: XCTestCase { 10| | #if !os(watchOS) 11| 1| func testInitLightDark() { 12| 1| let lightModeColor = UIColor.red 13| 1| let darkModeColor = UIColor.blue 14| 1| let color = UIColor(light: lightModeColor, dark: darkModeColor) 15| 1| 16| 1| if #available(iOS 13.0, tvOS 13.0, *) { 17| 1| XCTAssertEqual(color.resolvedColor(with: UITraitCollection(userInterfaceStyle: .light)), lightModeColor) 18| 1| XCTAssertEqual(color.resolvedColor(with: UITraitCollection(userInterfaceStyle: .dark)), darkModeColor) 19| 1| } else { 20| 0| XCTAssertEqual(color, lightModeColor) 21| 1| } 22| 1| } 23| | #endif 24| |} 25| | 26| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Tests/UIKitTests/UIFontExtensionsTests.swift: 1| |// UIFontExtensionsTests.swift - Copyright 2020 SwifterSwift 2| | 3| |@testable import SwifterSwift 4| |import XCTest 5| | 6| |#if canImport(UIKit) 7| |import UIKit 8| | 9| |final class UIFontExtension: XCTestCase { 10| 1| func testBold() { 11| 1| let font = UIFont.preferredFont(forTextStyle: .body) 12| 1| let boldFont = font.bold 13| 1| XCTAssert(boldFont.fontDescriptor.symbolicTraits.contains(.traitBold)) 14| 1| } 15| | 16| 1| func testItalic() { 17| 1| let font = UIFont.preferredFont(forTextStyle: .body) 18| 1| let italicFont = font.italic 19| 1| XCTAssert(italicFont.fontDescriptor.symbolicTraits.contains(.traitItalic)) 20| 1| } 21| | 22| 1| func testMonospacedDigitFont() { 23| 1| let font = UIFont.preferredFont(forTextStyle: .body) 24| 1| let monoFont = font.monospaced 25| 1| 26| 1| let attributes = monoFont.fontDescriptor.fontAttributes 27| 1| let fontKey: UIFontDescriptor.AttributeName = UIFontDescriptor.AttributeName.featureSettings 28| 1| guard let settings = attributes[fontKey] as? [[UIFontDescriptor.AttributeName: Int]] else { 29| 0| XCTFail("Unable to get settings from font") 30| 0| return 31| 1| } 32| 1| 33| 1| guard let selector = settings.first?[UIFontDescriptor.AttributeName(rawValue: "CTFeatureSelectorIdentifier")] else { 34| 0| XCTFail("Unable to get selector from font") 35| 0| return 36| 1| } 37| 1| 38| 1| guard let space = settings.first?[UIFontDescriptor.AttributeName(rawValue: "CTFeatureTypeIdentifier")] else { 39| 0| XCTFail("Unable to get space from font") 40| 0| return 41| 1| } 42| 1| 43| 1| XCTAssertEqual(selector, kMonospacedNumbersSelector) 44| 1| XCTAssertEqual(space, kNumberSpacingType) 45| 1| XCTAssertEqual(font.fontName, monoFont.fontName) 46| 1| XCTAssertEqual(font.familyName, monoFont.familyName) 47| 1| XCTAssertEqual(font.lineHeight, monoFont.lineHeight) 48| 1| } 49| |} 50| | 51| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Tests/UIKitTests/UIImageExtensionsTests.swift: 1| |// UIImageExtensionsTests.swift - Copyright 2020 SwifterSwift 2| | 3| |@testable import SwifterSwift 4| |import XCTest 5| | 6| |#if canImport(UIKit) 7| |import UIKit 8| | 9| |final class UIImageExtensionsTests: XCTestCase { 10| 1| func testAverageColor() { 11| 1| let size = CGSize(width: 10, height: 5) 12| 1| 13| 1| // simple fill test 14| 1| XCTAssertEqual(UIColor.blue, UIImage(color: .blue, size: size).averageColor()!, accuracy: 0.01) 15| 1| XCTAssertEqual(UIColor.orange, UIImage(color: .orange, size: size).averageColor()!, accuracy: 0.01) 16| 1| 17| 1| // more interesting - red + green = yellow 18| 1| let renderer = UIGraphicsImageRenderer(size: size) 19| 1| let yellow = renderer.image { 20| 1| var rect = CGRect(x: 0, y: 0, width: size.width / 2, height: size.height) 21| 2| for color in [UIColor.red, UIColor.green] { 22| 2| $0.cgContext.beginPath() 23| 2| $0.cgContext.setFillColor(color.cgColor) 24| 2| $0.cgContext.addRect(rect) 25| 2| $0.cgContext.fillPath() 26| 2| rect.origin.x += rect.size.width 27| 2| } 28| 1| } 29| 1| XCTAssertEqual(UIColor(red: 0.5, green: 0.5, blue: 0, alpha: 1), yellow.averageColor()!, accuracy: 0.01) 30| 1| } 31| | 32| 1| func testBytesSize() { 33| 1| let bundle = Bundle(for: UIImageExtensionsTests.self) 34| 1| let image = UIImage(named: "TestImage", in: bundle, compatibleWith: nil)! 35| 1| XCTAssertEqual(image.bytesSize, 68665) 36| 1| XCTAssertEqual(UIImage().bytesSize, 0) 37| 1| } 38| | 39| 1| func testKilobytesSize() { 40| 1| let bundle = Bundle(for: UIImageExtensionsTests.self) 41| 1| let image = UIImage(named: "TestImage", in: bundle, compatibleWith: nil)! 42| 1| XCTAssertEqual(image.kilobytesSize, 67) 43| 1| } 44| | 45| 1| func testOriginal() { 46| 1| let image = UIImage(color: .blue, size: CGSize(width: 20, height: 20)) 47| 1| XCTAssertEqual(image.original, image.withRenderingMode(.alwaysOriginal)) 48| 1| } 49| | 50| 1| func testTemplate() { 51| 1| let image = UIImage(color: .blue, size: CGSize(width: 20, height: 20)) 52| 1| XCTAssertEqual(image.template, image.withRenderingMode(.alwaysTemplate)) 53| 1| } 54| | 55| 1| func testCompressed() { 56| 1| let bundle = Bundle(for: UIImageExtensionsTests.self) 57| 1| let image = UIImage(named: "TestImage", in: bundle, compatibleWith: nil)! 58| 1| let originalSize = image.kilobytesSize 59| 1| let compressedImage = image.compressed(quality: 0.2) 60| 1| XCTAssertNotNil(compressedImage) 61| 1| XCTAssertLessThan(compressedImage!.kilobytesSize, originalSize) 62| 1| XCTAssertNil(UIImage().compressed()) 63| 1| } 64| | 65| 1| func testCompressedData() { 66| 1| let bundle = Bundle(for: UIImageExtensionsTests.self) 67| 1| let image = UIImage(named: "TestImage", in: bundle, compatibleWith: nil)! 68| 1| let originalSize = image.bytesSize 69| 1| let compressedImageData = image.compressedData(quality: 0.2) 70| 1| XCTAssertNotNil(compressedImageData) 71| 1| XCTAssertLessThan(compressedImageData!.count, originalSize) 72| 1| XCTAssertNil(UIImage().compressedData()) 73| 1| } 74| | 75| 1| func testCropped() { 76| 1| let image = UIImage(color: .black, size: CGSize(width: 20, height: 20)) 77| 1| var cropped = image.cropped(to: CGRect(x: 0, y: 0, width: 40, height: 40)) 78| 1| XCTAssertEqual(image, cropped) 79| 1| cropped = image.cropped(to: CGRect(x: 0, y: 0, width: 10, height: 10)) 80| 1| let small = UIImage(color: .black, size: CGSize(width: 10, height: 10)) 81| 1| XCTAssertEqual(cropped.bytesSize, small.bytesSize) 82| 1| 83| 1| let equalHeight = image.cropped(to: CGRect(x: 0, y: 0, width: 18, height: 20)) 84| 1| XCTAssertNotEqual(image, equalHeight) 85| 1| 86| 1| let equalWidth = image.cropped(to: CGRect(x: 0, y: 0, width: 20, height: 18)) 87| 1| XCTAssertNotEqual(image, equalWidth) 88| 1| 89| 1| guard let cgImage = image.cgImage else { 90| 0| XCTFail("Get cgImage from image failed") 91| 0| return 92| 1| } 93| 1| 94| 1| let imageWithScale = UIImage(cgImage: cgImage, scale: 2.0, orientation: .up) 95| 1| cropped = imageWithScale.cropped(to: CGRect(x: 0, y: 0, width: 15, height: 15)) 96| 1| XCTAssertEqual(imageWithScale, cropped) 97| 1| 98| 1| cropped = imageWithScale.cropped(to: CGRect(x: 0, y: 0, width: 5, height: 6)) 99| 1| XCTAssertEqual(imageWithScale.scale, cropped.scale) 100| 1| 101| 1| XCTAssertEqual(10, cropped.size.width * cropped.scale) 102| 1| XCTAssertEqual(12, cropped.size.height * cropped.scale) 103| 1| } 104| | 105| 1| func testScaledToHeight() { 106| 1| let bundle = Bundle(for: UIImageExtensionsTests.self) 107| 1| let image = UIImage(named: "TestImage", in: bundle, compatibleWith: nil)! 108| 1| 109| 1| let scaledImage = image.scaled(toHeight: 300) 110| 1| XCTAssertNotNil(scaledImage) 111| 1| XCTAssertEqual(scaledImage!.size.height, 300, accuracy: 0.1) 112| 1| } 113| | 114| 1| func testScaledToWidth() { 115| 1| let bundle = Bundle(for: UIImageExtensionsTests.self) 116| 1| let image = UIImage(named: "TestImage", in: bundle, compatibleWith: nil)! 117| 1| 118| 1| let scaledImage = image.scaled(toWidth: 300) 119| 1| XCTAssertNotNil(scaledImage) 120| 1| XCTAssertEqual(scaledImage!.size.width, 300, accuracy: 0.1) 121| 1| } 122| | 123| | @available(tvOS 10.0, watchOS 3.0, *) 124| 1| func testRotatedByMeasurement() { 125| 1| let bundle = Bundle(for: UIImageExtensionsTests.self) 126| 1| let image = UIImage(named: "TestImage", in: bundle, compatibleWith: nil)! 127| 1| 128| 1| let halfRotatedImage = image.rotated(by: Measurement(value: 90, unit: .degrees)) 129| 1| XCTAssertNotNil(halfRotatedImage) 130| 1| XCTAssertEqual(halfRotatedImage!.size, CGSize(width: image.size.height, height: image.size.width)) 131| 1| 132| 1| let rotatedImage = image.rotated(by: Measurement(value: 180, unit: .degrees)) 133| 1| XCTAssertNotNil(rotatedImage) 134| 1| XCTAssertEqual(rotatedImage!.size, image.size) 135| 1| XCTAssertNotEqual(image.jpegData(compressionQuality: 1), rotatedImage!.jpegData(compressionQuality: 1)) 136| 1| } 137| | 138| 1| func testRotatedByRadians() { 139| 1| let bundle = Bundle(for: UIImageExtensionsTests.self) 140| 1| let image = UIImage(named: "TestImage", in: bundle, compatibleWith: nil)! 141| 1| 142| 1| let halfRotatedImage = image.rotated(by: .pi / 2) 143| 1| XCTAssertNotNil(halfRotatedImage) 144| 1| XCTAssertEqual(halfRotatedImage!.size, CGSize(width: image.size.height, height: image.size.width)) 145| 1| 146| 1| let rotatedImage = image.rotated(by: .pi) 147| 1| XCTAssertNotNil(rotatedImage) 148| 1| XCTAssertEqual(rotatedImage!.size, image.size) 149| 1| XCTAssertNotEqual(image.jpegData(compressionQuality: 1), rotatedImage!.jpegData(compressionQuality: 1)) 150| 1| } 151| | 152| 1| func testFilled() { 153| 1| let image = UIImage(color: .black, size: CGSize(width: 20, height: 20)) 154| 1| let image2 = UIImage(color: .yellow, size: CGSize(width: 20, height: 20)) 155| 1| XCTAssertEqual(image.filled(withColor: .yellow).bytesSize, image2.bytesSize) 156| 1| 157| 1| var emptyImage = UIImage() 158| 1| var filledImage = emptyImage.filled(withColor: .red) 159| 1| XCTAssertEqual(emptyImage, filledImage) 160| 1| 161| 1| emptyImage = UIImage(color: .yellow, size: CGSize.zero) 162| 1| filledImage = emptyImage.filled(withColor: .red) 163| 1| XCTAssertEqual(emptyImage, filledImage) 164| 1| } 165| | 166| 1| func testBase64() { 167| 1| let base64String = 168| 1| "iVBORw0KGgoAAAANSUhEUgAAAAUAAAAFCAYAAACNbyblAAAAE0lEQVR42mP8v5JhEwMaYKSBIADNAwvIr8dhZAAAAABJRU5ErkJggg==" 169| 1| let image = UIImage(base64String: base64String) 170| 1| XCTAssertNotNil(image) 171| 1| 172| 1| let size = CGSize(width: 5, height: 5) 173| 1| XCTAssertEqual(image?.size, size) 174| 1| 175| 1| XCTAssertEqual(image?.bytesSize, 787) 176| 1| 177| 1| let scale = CGFloat(5.0) 178| 1| let scaledSize = CGSize(width: size.width / scale, height: size.height / scale) 179| 1| 180| 1| let scaledImage = UIImage(base64String: base64String, scale: scale) 181| 1| XCTAssertEqual(scaledImage?.size, scaledSize) 182| 1| } 183| | 184| 1| func testURL() { 185| 1| let bundle = Bundle(for: UIImageExtensionsTests.self) 186| 1| guard let swifterSwiftLogo = bundle.url(forResource: "TestImage", withExtension: "png") else { XCTAssert(false, "Swifter Swift Test Image not available, or url is no longer valid."); return } 187| 1| let image = try? UIImage(url: swifterSwiftLogo) 188| 1| XCTAssertNotNil(image) 189| 1| 190| 1| let size = CGSize(width: 1000, height: 232) 191| 1| XCTAssertEqual(image?.size, size) 192| 1| 193| 1| let scale: CGFloat = 5.0 194| 1| let scaledSize = CGSize(width: size.width / scale, height: size.height / scale) 195| 1| 196| 1| let scaledImage = try? UIImage(url: swifterSwiftLogo, scale: scale) 197| 1| XCTAssertNotNil(scaledImage) 198| 1| XCTAssertEqual(scaledImage?.size, scaledSize) 199| 1| 200| 1| guard let throwingURL = URL(string: "SwifterSwift://fakeurl/image1") else { 201| 0| XCTAssert(false, "Fake URL cannot be made") 202| 0| return 203| 1| } 204| 1| 205| 1| XCTAssertThrowsError(try UIImage(url: throwingURL)) 206| 1| } 207| | 208| 1| func testTinted() { 209| 1| let baseImage = UIImage(color: .white, size: CGSize(width: 20, height: 20)) 210| 1| let tintedImage = baseImage.tint(.black, blendMode: .overlay) 211| 1| let testImage = UIImage(color: .black, size: CGSize(width: 20, height: 20)) 212| 1| XCTAssertEqual(testImage.bytesSize, tintedImage.bytesSize) 213| 1| } 214| | 215| 1| func testWithBackgroundColor() { 216| 1| let size = CGSize(width: 1, height: 1) 217| 1| let clearImage = UIImage(color: .clear, size: size) 218| 1| let imageWithBackgroundColor = clearImage.withBackgroundColor(.black) 219| 1| XCTAssertNotNil(imageWithBackgroundColor) 220| 1| let blackImage = UIImage(color: .black, size: size) 221| 1| XCTAssertEqual(imageWithBackgroundColor.pngData(), blackImage.pngData()) 222| 1| } 223| | 224| 1| func testWithCornerRadius() { 225| 1| let image = UIImage(color: .black, size: CGSize(width: 20, height: 20)) 226| 1| XCTAssertNotNil(image.withRoundedCorners()) 227| 1| XCTAssertNotNil(image.withRoundedCorners(radius: 5)) 228| 1| XCTAssertNotNil(image.withRoundedCorners(radius: -10)) 229| 1| XCTAssertNotNil(image.withRoundedCorners(radius: 30)) 230| 1| } 231| | 232| 1| func testPNGBase64String() { 233| 1| let image = UIImage(color: .blue, size: CGSize(width: 1, height: 1)) 234| 1| XCTAssertEqual( 235| 1| image.pngBase64String(), 236| 1| "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAAAXNSR0IArs4c6QAAAERlWElmTU0AKgAAAAgAAYdpAAQAAAABAAAAGgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAAaADAAQAAAABAAAAAQAAAAD5Ip3+AAAADUlEQVQIHWNgYPj/HwADAgH/p+FUpQAAAABJRU5ErkJggg==") 237| 1| } 238| | 239| 1| func testJPEGBase64String() { 240| 1| let image = UIImage(color: .blue, size: CGSize(width: 1, height: 1)) 241| 1| XCTAssertEqual( 242| 1| image.jpegBase64String(compressionQuality: 1), 243| 1| "/9j/4AAQSkZJRgABAQAASABIAAD/4QBYRXhpZgAATU0AKgAAAAgAAgESAAMAAAABAAEAAIdpAAQAAAABAAAAJgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAAaADAAQAAAABAAAAAQAAAAD/7QA4UGhvdG9zaG9wIDMuMAA4QklNBAQAAAAAAAA4QklNBCUAAAAAABDUHYzZjwCyBOmACZjs+EJ+/8AAEQgAAQABAwERAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/bAEMAAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAf/bAEMBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAQEBAf/dAAQAAf/aAAwDAQACEQMRAD8A/jnr/v4P5XP/2Q==") 244| 1| } 245| |} 246| | 247| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Tests/UIKitTests/UIImageViewExtensionsTests.swift: 1| |// UIImageViewExtensionsTests.swift - Copyright 2020 SwifterSwift 2| | 3| |@testable import SwifterSwift 4| |import XCTest 5| | 6| |#if canImport(UIKit) && !os(watchOS) 7| |import UIKit 8| | 9| |final class UIImageViewExtensionsTests: XCTestCase { 10| 1| func testDownload() { 11| 1| // Success 12| 1| let imageView = UIImageView() 13| 1| let url = URL(string: "https://developer.apple.com/swift/images/swift-og.png")! 14| 1| let placeHolder = UIImage() 15| 1| let downloadExpectation = expectation(description: "Download success") 16| 1| imageView.download(from: url, contentMode: .scaleAspectFill, placeholder: placeHolder) { image in 17| 1| XCTAssertEqual(imageView.image, image) 18| 1| downloadExpectation.fulfill() 19| 1| } 20| 1| XCTAssertEqual(imageView.image, placeHolder) 21| 1| XCTAssertEqual(imageView.contentMode, .scaleAspectFill) 22| 1| 23| 1| // Failure 24| 1| let failImageView = UIImageView() 25| 1| let failingURL = URL(string: "https://developer.apple.com/")! 26| 1| let failExpectation = expectation(description: "Download failure") 27| 1| failImageView.image = nil 28| 1| failImageView.download(from: failingURL, contentMode: .center, placeholder: nil) { image in 29| 1| XCTAssertNil(image) 30| 1| DispatchQueue.main.async { 31| 1| XCTAssertNil(failImageView.image) 32| 1| } 33| 1| failExpectation.fulfill() 34| 1| } 35| 1| XCTAssertEqual(failImageView.contentMode, .center) 36| 1| XCTAssertNil(failImageView.image) 37| 1| waitForExpectations(timeout: 15) 38| 1| } 39| | 40| 1| func testBlur() { 41| 1| let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 50, height: 100)) 42| 1| imageView.blur(withStyle: .dark) 43| 1| 44| 1| let blurView = imageView.subviews.first as? UIVisualEffectView 45| 1| XCTAssertNotNil(blurView) 46| 1| XCTAssertNotNil(blurView?.effect) 47| 1| XCTAssertEqual(blurView?.frame, imageView.bounds) 48| 1| XCTAssertEqual(blurView?.autoresizingMask, [.flexibleWidth, .flexibleHeight]) 49| 1| XCTAssert(imageView.clipsToBounds) 50| 1| } 51| | 52| 1| func testBlurred() { 53| 1| let imageView = UIImageView() 54| 1| let blurredImageView = imageView.blurred(withStyle: .extraLight) 55| 1| XCTAssertEqual(blurredImageView, imageView) 56| 1| } 57| |} 58| | 59| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Tests/UIKitTests/UILabelExtensionsTests.swift: 1| |// UILabelExtensionsTests.swift - Copyright 2020 SwifterSwift 2| | 3| |@testable import SwifterSwift 4| |import XCTest 5| | 6| |#if canImport(UIKit) && !os(watchOS) 7| |import UIKit 8| | 9| |final class UILabelExtensionsTests: XCTestCase { 10| 1| func testInitWithText() { 11| 1| let label = UILabel(text: "Hello world") 12| 1| XCTAssertEqual(label.text, "Hello world") 13| 1| } 14| | 15| 1| func testInitWithTextAndStyle() { 16| 1| let style = UIFont.TextStyle.headline 17| 1| let text = "Hello world" 18| 1| let label = UILabel(text: text, style: style) 19| 1| 20| 1| let preferredFont = UIFont.preferredFont(forTextStyle: style) 21| 1| XCTAssertEqual(label.text, text) 22| 1| XCTAssertEqual(label.font, preferredFont) 23| 1| } 24| | 25| 1| func testrequiredHeight() { 26| 1| let frame = CGRect(x: 0, y: 0, width: 100, height: 100) 27| 1| let label = UILabel(frame: frame) 28| 1| label.text = "Hello world" 29| 1| 30| 1| #if os(iOS) 31| 1| XCTAssert(label.requiredHeight >= 20) 32| 1| XCTAssert(label.requiredHeight < 25) 33| 1| #else 34| 1| XCTAssert(label.requiredHeight > 0) 35| 1| XCTAssert(label.requiredHeight < 100) 36| 1| #endif 37| 1| } 38| |} 39| | 40| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Tests/UIKitTests/UILayoutPriorityExtensionsTests.swift: 1| |// UILayoutPriorityExtensionsTests.swift - Copyright 2020 SwifterSwift 2| | 3| |#if os(iOS) || os(tvOS) 4| | 5| |@testable import SwifterSwift 6| |import XCTest 7| | 8| |final class UILayoutPriorityExtensionsTests: XCTestCase { 9| 1| func testFloatLiteralInitializer() { 10| 1| var priority: UILayoutPriority = 0.5 11| 1| XCTAssertEqual(UILayoutPriority(rawValue: 0.5), priority) 12| 1| 13| 1| priority = -0.5 14| 1| XCTAssertEqual(UILayoutPriority(rawValue: -0.5), priority) 15| 1| 16| 1| priority = 0.0 17| 1| XCTAssertEqual(UILayoutPriority(rawValue: 0.0), priority) 18| 1| } 19| | 20| 1| func testIntegerLiteralInitializer() { 21| 1| var priority: UILayoutPriority = 5 22| 1| XCTAssertEqual(UILayoutPriority(rawValue: 5), priority) 23| 1| 24| 1| priority = -5 25| 1| XCTAssertEqual(UILayoutPriority(rawValue: -5), priority) 26| 1| 27| 1| priority = 0 28| 1| XCTAssertEqual(UILayoutPriority(rawValue: 0), priority) 29| 1| } 30| |} 31| | 32| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Tests/UIKitTests/UINavigationBarExtensionTests.swift: 1| |// UINavigationBarExtensionTests.swift - Copyright 2020 SwifterSwift 2| | 3| |@testable import SwifterSwift 4| |import XCTest 5| | 6| |#if canImport(UIKit) && !os(watchOS) 7| |import UIKit 8| | 9| |final class UINavigationBarExtensionsTests: XCTestCase { 10| 1| func testSetTitleFont() { 11| 1| let navigationBar = UINavigationBar() 12| 1| let helveticaFont = UIFont(name: "HelveticaNeue", size: 14)! 13| 1| navigationBar.setTitleFont(helveticaFont, color: .green) 14| 1| let color = navigationBar.titleTextAttributes?[NSAttributedString.Key.foregroundColor] as? UIColor 15| 1| XCTAssertEqual(color, .green) 16| 1| let font = navigationBar.titleTextAttributes?[NSAttributedString.Key.font] as? UIFont 17| 1| XCTAssertEqual(font, helveticaFont) 18| 1| 19| 1| navigationBar.setTitleFont(helveticaFont) 20| 1| let defaultColor = navigationBar.titleTextAttributes?[NSAttributedString.Key.foregroundColor] as? UIColor 21| 1| XCTAssertEqual(defaultColor, .black) 22| 1| } 23| | 24| 1| func testMakeTransparent() { 25| 1| let navigationBar = UINavigationBar() 26| 1| navigationBar.makeTransparent(withTint: .red) 27| 1| XCTAssertNotNil(navigationBar.backgroundImage(for: .default)) 28| 1| XCTAssertNotNil(navigationBar.shadowImage) 29| 1| XCTAssert(navigationBar.isTranslucent) 30| 1| XCTAssertEqual(navigationBar.tintColor, .red) 31| 1| let color = navigationBar.titleTextAttributes?[NSAttributedString.Key.foregroundColor] as? UIColor 32| 1| XCTAssertEqual(color, .red) 33| 1| 34| 1| navigationBar.makeTransparent() 35| 1| let defaultColor = navigationBar.titleTextAttributes?[NSAttributedString.Key.foregroundColor] as? UIColor 36| 1| XCTAssertEqual(defaultColor, .white) 37| 1| } 38| | 39| 1| func testSetColors() { 40| 1| let navigationBar = UINavigationBar() 41| 1| navigationBar.setColors(background: .blue, text: .green) 42| 1| XCTAssertFalse(navigationBar.isTranslucent) 43| 1| XCTAssertEqual(navigationBar.backgroundColor, .blue) 44| 1| XCTAssertEqual(navigationBar.barTintColor, .blue) 45| 1| XCTAssertNotNil(navigationBar.backgroundImage(for: .default)) 46| 1| XCTAssertEqual(navigationBar.tintColor, .green) 47| 1| let color = navigationBar.titleTextAttributes?[NSAttributedString.Key.foregroundColor] as? UIColor 48| 1| XCTAssertEqual(color, .green) 49| 1| } 50| |} 51| | 52| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Tests/UIKitTests/UINavigationControllerExtensionsTests.swift: 1| |// UINavigationControllerExtensionsTests.swift - Copyright 2020 SwifterSwift 2| | 3| |@testable import SwifterSwift 4| |import XCTest 5| | 6| |#if canImport(UIKit) && !os(watchOS) 7| |import UIKit 8| | 9| |final class UINavigationControllerExtensionsTests: XCTestCase { 10| 1| func testPushViewController() { 11| 1| let navigationController = UINavigationController() 12| 1| let vcToPush = UIViewController() 13| 1| 14| 1| navigationController.pushViewController(vcToPush, animated: false) 15| 1| 16| 1| let exp = expectation(description: "pushCallback") 17| 1| 18| 1| navigationController.pushViewController(vcToPush) { 19| 1| XCTAssertEqual(navigationController.viewControllers.count, 1) 20| 1| XCTAssertEqual(navigationController.topViewController, vcToPush) 21| 1| exp.fulfill() 22| 1| } 23| 1| waitForExpectations(timeout: 5) 24| 1| } 25| | 26| 1| func testPopViewController() { 27| 1| let rootVC = UIViewController() 28| 1| let navigationController = UINavigationController(rootViewController: rootVC) 29| 1| let vcToPush = UIViewController() 30| 1| navigationController.pushViewController(vcToPush, animated: false) 31| 1| XCTAssertEqual(navigationController.viewControllers.count, 2) 32| 1| 33| 1| let exp = expectation(description: "pushCallback") 34| 1| navigationController.popViewController(animated: false) { 35| 1| XCTAssertEqual(navigationController.viewControllers.count, 1) 36| 1| XCTAssertEqual(navigationController.topViewController, rootVC) 37| 1| exp.fulfill() 38| 1| } 39| 1| waitForExpectations(timeout: 5) 40| 1| } 41| | 42| 1| func testMakeTransparent() { 43| 1| let navigationController = UINavigationController(rootViewController: UIViewController()) 44| 1| navigationController.makeTransparent(withTint: .red) 45| 1| let navBar = navigationController.navigationBar 46| 1| XCTAssertNotNil(navBar.shadowImage) 47| 1| XCTAssert(navBar.isTranslucent) 48| 1| XCTAssertEqual(navBar.tintColor, UIColor.red) 49| 1| 50| 1| let attrs = navBar.titleTextAttributes 51| 1| XCTAssertNotNil(attrs) 52| 1| let color = attrs![.foregroundColor] as? UIColor 53| 1| XCTAssertNotNil(color) 54| 1| XCTAssertEqual(color!, .red) 55| 1| } 56| |} 57| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Tests/UIKitTests/UINavigationItemExtensionsTests.swift: 1| |// UINavigationItemExtensionsTests.swift - Copyright 2020 SwifterSwift 2| | 3| |@testable import SwifterSwift 4| |import XCTest 5| | 6| |#if canImport(UIKit) && !os(watchOS) 7| |import UIKit 8| | 9| |final class UINavigationItemExtensionsTests: XCTestCase { 10| 1| func testReplaceTitle() { 11| 1| let navigationItem = UINavigationItem() 12| 1| let image = UIImage() 13| 1| navigationItem.replaceTitle(with: image) 14| 1| 15| 1| let imageView = navigationItem.titleView as? UIImageView 16| 1| XCTAssertNotNil(imageView) 17| 1| 18| 1| let frame = CGRect(x: 0, y: 0, width: 100, height: 30) 19| 1| XCTAssertEqual(imageView?.frame, frame) 20| 1| 21| 1| XCTAssertEqual(imageView?.contentMode, .scaleAspectFit) 22| 1| XCTAssertEqual(imageView?.image, image) 23| 1| } 24| |} 25| | 26| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Tests/UIKitTests/UIScrollViewExtensionsTests.swift: 1| |// UIScrollViewExtensionsTests.swift - Copyright 2020 SwifterSwift 2| | 3| |@testable import SwifterSwift 4| |import XCTest 5| | 6| |#if canImport(UIKit) && !os(watchOS) 7| |import UIKit 8| | 9| |final class UIScrollViewExtensionsTest: XCTestCase { 10| 10| let scroll = UIScrollView(frame: CGRect(origin: .zero, size: CGSize(width: 100, height: 100))) 11| | 12| 10| override func setUp() { 13| 10| super.setUp() 14| 10| 15| 10| scroll.contentSize = CGSize(width: 500, height: 500) 16| 10| scroll.contentInset = UIEdgeInsets(top: 10, left: 20, bottom: 30, right: 40) 17| 10| } 18| | 19| 1| func testSnapshot() { 20| 1| let snapshot = scroll.snapshot 21| 1| XCTAssertNotNil(snapshot) 22| 1| let view = UIScrollView() 23| 1| XCTAssertNil(view.snapshot) 24| 1| } 25| | 26| 1| func testVisibleRect() { 27| 1| XCTAssertEqual(scroll.visibleRect, CGRect(origin: .zero, size: CGSize(width: 100, height: 100))) 28| 1| 29| 1| scroll.contentOffset = CGPoint(x: 50, y: 50) 30| 1| XCTAssertEqual(scroll.visibleRect, CGRect(origin: CGPoint(x: 50, y: 50), size: CGSize(width: 100, height: 100))) 31| 1| 32| 1| let offset = CGPoint(x: 490, y: 480) 33| 1| scroll.contentOffset = offset 34| 1| XCTAssertEqual(scroll.visibleRect, CGRect(origin: offset, size: CGSize(width: 10, height: 20))) 35| 1| } 36| | 37| 1| func testScrollToTop() { 38| 1| scroll.contentOffset = CGPoint(x: 50, y: 50) 39| 1| scroll.scrollToTop(animated: false) 40| 1| XCTAssertEqual(scroll.contentOffset, CGPoint(x: 50, y: -10)) 41| 1| } 42| | 43| 1| func testScrollToLeft() { 44| 1| scroll.contentOffset = CGPoint(x: 50, y: 50) 45| 1| scroll.scrollToLeft(animated: false) 46| 1| XCTAssertEqual(scroll.contentOffset, CGPoint(x: -20, y: 50)) 47| 1| } 48| | 49| 1| func testScrollToBottom() { 50| 1| scroll.contentOffset = CGPoint(x: 50, y: 50) 51| 1| scroll.scrollToBottom(animated: false) 52| 1| XCTAssertEqual(scroll.contentOffset, CGPoint(x: 50, y: scroll.contentSize.height - scroll.bounds.height + 30)) 53| 1| } 54| | 55| 1| func testScrollToRight() { 56| 1| scroll.contentOffset = CGPoint(x: 50, y: 50) 57| 1| scroll.scrollToRight(animated: false) 58| 1| XCTAssertEqual(scroll.contentOffset, CGPoint(x: scroll.contentSize.width - scroll.bounds.height + 40, y: 50)) 59| 1| } 60| | 61| 1| func testScrollUp() { 62| 1| let offset = CGPoint(x: 50, y: 250) 63| 1| scroll.contentOffset = offset 64| 1| scroll.scrollUp(animated: false) 65| 1| XCTAssertEqual(scroll.contentOffset, CGPoint(x: offset.x, y: 150)) 66| 1| scroll.scrollUp(animated: false) 67| 1| scroll.scrollUp(animated: false) 68| 1| XCTAssertEqual(scroll.contentOffset, CGPoint(x: offset.x, y: -10)) 69| 1| 70| 1| let scrollView = UIScrollView() 71| 1| scrollView.scrollUp(animated: false) 72| 1| XCTAssertEqual(scrollView.contentOffset, .zero) 73| 1| 74| 1| #if !os(tvOS) 75| 1| scroll.isPagingEnabled = true 76| 1| scroll.contentOffset = offset 77| 1| scroll.scrollUp(animated: false) 78| 1| XCTAssertEqual(scroll.contentOffset, CGPoint(x: offset.x, y: 90)) 79| 1| 80| 1| scrollView.isPagingEnabled = true 81| 1| scrollView.scrollUp(animated: false) 82| 1| XCTAssertEqual(scrollView.contentOffset, .zero) 83| 1| #endif 84| 1| } 85| | 86| 1| func testScrollLeft() { 87| 1| let offset = CGPoint(x: 250, y: 50) 88| 1| scroll.contentOffset = offset 89| 1| scroll.scrollLeft(animated: false) 90| 1| XCTAssertEqual(scroll.contentOffset, CGPoint(x: 150, y: offset.y)) 91| 1| scroll.scrollLeft(animated: false) 92| 1| scroll.scrollLeft(animated: false) 93| 1| XCTAssertEqual(scroll.contentOffset, CGPoint(x: -20, y: offset.y)) 94| 1| 95| 1| let scrollView = UIScrollView() 96| 1| scrollView.scrollLeft(animated: false) 97| 1| XCTAssertEqual(scrollView.contentOffset, .zero) 98| 1| 99| 1| #if !os(tvOS) 100| 1| scroll.isPagingEnabled = true 101| 1| scroll.contentOffset = offset 102| 1| scroll.scrollLeft(animated: false) 103| 1| XCTAssertEqual(scroll.contentOffset, CGPoint(x: 80, y: offset.y)) 104| 1| 105| 1| scrollView.isPagingEnabled = true 106| 1| scrollView.scrollLeft(animated: false) 107| 1| XCTAssertEqual(scrollView.contentOffset, .zero) 108| 1| #endif 109| 1| } 110| | 111| 1| func testScrollDown() { 112| 1| let offset = CGPoint(x: 50, y: 250) 113| 1| scroll.contentOffset = offset 114| 1| scroll.scrollDown(animated: false) 115| 1| XCTAssertEqual(scroll.contentOffset, CGPoint(x: offset.x, y: 350)) 116| 1| scroll.scrollDown(animated: false) 117| 1| XCTAssertEqual(scroll.contentOffset, CGPoint(x: offset.x, y: 430)) 118| 1| 119| 1| let scrollView = UIScrollView() 120| 1| scrollView.scrollDown(animated: false) 121| 1| XCTAssertEqual(scrollView.contentOffset, .zero) 122| 1| 123| 1| #if !os(tvOS) 124| 1| scroll.isPagingEnabled = true 125| 1| scroll.contentOffset = offset 126| 1| scroll.scrollDown(animated: false) 127| 1| XCTAssertEqual(scroll.contentOffset, CGPoint(x: offset.x, y: 290)) 128| 1| 129| 1| scrollView.isPagingEnabled = true 130| 1| scrollView.scrollDown(animated: false) 131| 1| XCTAssertEqual(scrollView.contentOffset, .zero) 132| 1| #endif 133| 1| } 134| | 135| 1| func testScrollRight() { 136| 1| let offset = CGPoint(x: 250, y: 50) 137| 1| scroll.contentOffset = offset 138| 1| scroll.scrollRight(animated: false) 139| 1| XCTAssertEqual(scroll.contentOffset, CGPoint(x: 350, y: offset.y)) 140| 1| scroll.scrollRight(animated: false) 141| 1| XCTAssertEqual(scroll.contentOffset, CGPoint(x: 440, y: offset.y)) 142| 1| 143| 1| let scrollView = UIScrollView() 144| 1| scrollView.scrollRight(animated: false) 145| 1| XCTAssertEqual(scrollView.contentOffset, .zero) 146| 1| 147| 1| #if !os(tvOS) 148| 1| scroll.isPagingEnabled = true 149| 1| scroll.contentOffset = offset 150| 1| scroll.scrollRight(animated: false) 151| 1| XCTAssertEqual(scroll.contentOffset, CGPoint(x: 280, y: offset.y)) 152| 1| 153| 1| scrollView.isPagingEnabled = true 154| 1| scrollView.scrollRight(animated: false) 155| 1| XCTAssertEqual(scrollView.contentOffset, .zero) 156| 1| #endif 157| 1| } 158| |} 159| | 160| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Tests/UIKitTests/UISegmentedControlExtensionsTests.swift: 1| |// UISegmentedControlExtensionsTests.swift - Copyright 2020 SwifterSwift 2| | 3| |@testable import SwifterSwift 4| |import XCTest 5| | 6| |#if canImport(UIKit) && !os(watchOS) 7| |import UIKit 8| | 9| |final class UISegmentedControlExtensionsTests: XCTestCase { 10| 1| func testSegmentTitles() { 11| 1| let segmentControl = UISegmentedControl() 12| 1| XCTAssert(segmentControl.segmentTitles.isEmpty) 13| 1| let titles = ["Title1", "Title2"] 14| 1| segmentControl.segmentTitles = titles 15| 1| XCTAssertEqual(segmentControl.segmentTitles, titles) 16| 1| } 17| | 18| 1| func testSegmentImages() { 19| 1| let segmentControl = UISegmentedControl() 20| 1| XCTAssert(segmentControl.segmentImages.isEmpty) 21| 1| let images = [UIImage(), UIImage()] 22| 1| segmentControl.segmentImages = images 23| 1| XCTAssertEqual(segmentControl.segmentImages, images) 24| 1| } 25| |} 26| | 27| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Tests/UIKitTests/UITabBarExtensionsTests.swift: 1| |// UITabBarExtensionsTests.swift - Copyright 2020 SwifterSwift 2| | 3| |@testable import SwifterSwift 4| |import XCTest 5| | 6| |#if canImport(UIKit) && !os(watchOS) 7| |import UIKit 8| | 9| |final class UITabBarExtensionsTests: XCTestCase { 10| 1| func testSetColors() { 11| 1| let frame = CGRect(x: 0, y: 0, width: 300, height: 44) 12| 1| var tabBar = UITabBar(frame: frame) 13| 1| tabBar.setColors(background: .red, selectedBackground: .orange, item: .white, selectedItem: .black) 14| 1| 15| 1| XCTAssertEqual(tabBar.barTintColor, .red) 16| 1| 17| 1| tabBar = UITabBar(frame: frame) 18| 1| tabBar.setColors() 19| 1| XCTAssertNotEqual(tabBar.barTintColor, .red) 20| 1| 21| 1| let bundle = Bundle(for: UIImageExtensionsTests.self) 22| 1| let image = UIImage(named: "TestImage", in: bundle, compatibleWith: nil)! 23| 1| 24| 1| let item1 = UITabBarItem(title: "First", image: image, selectedImage: image) 25| 1| let item2 = UITabBarItem(title: "Second", image: nil, selectedImage: nil) 26| 1| tabBar.items = [item1, item2] 27| 1| tabBar.selectedItem = item1 28| 1| XCTAssertNotNil(tabBar.selectedItem) 29| 1| 30| 1| tabBar.setColors(selectedBackground: .orange, item: .white, selectedItem: .black) 31| 1| } 32| |} 33| | 34| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Tests/UIKitTests/UITableViewExtensionsTests.swift: 1| |// UITableViewExtensionsTests.swift - Copyright 2020 SwifterSwift 2| | 3| |@testable import SwifterSwift 4| |import XCTest 5| | 6| |#if canImport(UIKit) && !os(watchOS) 7| |import UIKit 8| | 9| |final class UITableViewExtensionsTests: XCTestCase { 10| 14| let tableView = UITableView() 11| 14| let emptyTableView = UITableView() 12| | 13| 14| override func setUp() { 14| 14| super.setUp() 15| 14| // Put setup code here. This method is called before the invocation of each test method in the class. 16| 14| tableView.dataSource = self 17| 14| emptyTableView.dataSource = self 18| 14| tableView.reloadData() 19| 14| } 20| | 21| 1| func testIndexPathForLastRow() { 22| 1| XCTAssertNotNil(tableView.indexPathForLastRow) 23| 1| XCTAssertEqual(tableView.indexPathForLastRow, IndexPath(row: 7, section: 1)) 24| 1| XCTAssertNil(emptyTableView.indexPathForLastRow) 25| 1| XCTAssertNil(emptyTableView.indexPathForLastRow(inSection: 0)) 26| 1| } 27| | 28| 1| func testLastSection() { 29| 1| XCTAssertEqual(tableView.lastSection, 1) 30| 1| XCTAssertNil(emptyTableView.lastSection) 31| 1| } 32| | 33| 1| func testNumberOfRows() { 34| 1| XCTAssertEqual(tableView.numberOfRows(), 13) 35| 1| XCTAssertEqual(emptyTableView.numberOfRows(), 0) 36| 1| } 37| | 38| 1| func testIndexPathForLastRowInSection() { 39| 1| XCTAssertNil(tableView.indexPathForLastRow(inSection: -1)) 40| 1| XCTAssertNil(emptyTableView.indexPathForLastRow(inSection: -1)) 41| 1| XCTAssertEqual(tableView.indexPathForLastRow(inSection: 0), IndexPath(row: 4, section: 0)) 42| 1| XCTAssertEqual(UITableView().indexPathForLastRow(inSection: 0), IndexPath(row: 0, section: 0)) 43| 1| } 44| | 45| 1| func testReloadData() { 46| 1| let exp = expectation(description: "reloadCallback") 47| 1| tableView.reloadData { 48| 1| XCTAssert(true) 49| 1| exp.fulfill() 50| 1| } 51| 1| waitForExpectations(timeout: 5) 52| 1| } 53| | 54| 1| func testRemoveTableFooterView() { 55| 1| tableView.tableFooterView = UIView() 56| 1| XCTAssertNotNil(tableView.tableFooterView) 57| 1| tableView.removeTableFooterView() 58| 1| XCTAssertNil(tableView.tableFooterView) 59| 1| } 60| | 61| 1| func testRemoveTableHeaderView() { 62| 1| tableView.tableHeaderView = UIView() 63| 1| XCTAssertNotNil(tableView.tableHeaderView) 64| 1| tableView.removeTableHeaderView() 65| 1| XCTAssertNil(tableView.tableHeaderView) 66| 1| } 67| | 68| 1| func testDequeueReusableCellWithClass() { 69| 1| tableView.register(UITableViewCell.self, forCellReuseIdentifier: "UITableViewCell") 70| 1| let cell = tableView.dequeueReusableCell(withClass: UITableViewCell.self) 71| 1| XCTAssertNotNil(cell) 72| 1| } 73| | 74| 1| func testDequeueReusableCellWithClassForIndexPath() { 75| 1| tableView.register(UITableViewCell.self, forCellReuseIdentifier: "UITableViewCell") 76| 1| let indexPath = tableView.indexPathForLastRow! 77| 1| let cell = tableView.dequeueReusableCell(withClass: UITableViewCell.self, for: indexPath) 78| 1| XCTAssertNotNil(cell) 79| 1| } 80| | 81| 1| func testDequeueReusableHeaderFooterView() { 82| 1| tableView.register(UITableViewHeaderFooterView.self, 83| 1| forHeaderFooterViewReuseIdentifier: "UITableViewHeaderFooterView") 84| 1| let headerFooterView = tableView.dequeueReusableHeaderFooterView(withClass: UITableViewHeaderFooterView.self) 85| 1| XCTAssertNotNil(headerFooterView) 86| 1| } 87| | 88| 1| func testIsValidIndexPath() { 89| 1| let validIndexPath = IndexPath(row: 0, section: 0) 90| 1| XCTAssert(tableView.isValidIndexPath(validIndexPath)) 91| 1| 92| 1| let invalidIndexPath = IndexPath(row: 10, section: 0) 93| 1| XCTAssertFalse(tableView.isValidIndexPath(invalidIndexPath)) 94| 1| 95| 1| let negativeIndexPath = IndexPath(row: -1, section: 0) 96| 1| XCTAssertFalse(tableView.isValidIndexPath(negativeIndexPath)) 97| 1| } 98| | 99| 1| func testSafeScrollToIndexPath() { 100| 1| let validIndexPathTop = IndexPath(row: 0, section: 0) 101| 1| 102| 1| tableView.contentOffset = .init(x: 0, y: 100) 103| 1| XCTAssertNotEqual(tableView.contentOffset, .zero) 104| 1| 105| 1| tableView.safeScrollToRow(at: validIndexPathTop, at: .top, animated: false) 106| 1| XCTAssertEqual(tableView.contentOffset, .zero) 107| 1| 108| 1| let validIndexPathBottom = IndexPath(row: 7, section: 1) 109| 1| let bottomOffset = CGPoint(x: 0, y: tableView.contentSize.height - tableView.bounds.size.height) 110| 1| 111| 1| tableView.contentOffset = .init(x: 0, y: 200) 112| 1| XCTAssertNotEqual(tableView.contentOffset, bottomOffset) 113| 1| 114| 1| tableView.safeScrollToRow(at: validIndexPathBottom, at: .bottom, animated: false) 115| 1| #if os(tvOS) 116| 1| XCTAssertEqual(bottomOffset.y, tableView.contentOffset.y, accuracy: 15.0) 117| 1| #else 118| 1| XCTAssertEqual(bottomOffset.y, tableView.contentOffset.y, accuracy: 2.0) 119| 1| #endif 120| 1| 121| 1| let invalidIndexPath = IndexPath(row: 213, section: 21) 122| 1| tableView.contentOffset = .zero 123| 1| 124| 1| tableView.safeScrollToRow(at: invalidIndexPath, at: .bottom, animated: false) 125| 1| XCTAssertEqual(tableView.contentOffset, .zero) 126| 1| } 127| | 128| | #if os(iOS) 129| | func testRegisterReusableViewWithClassAndNib() { 130| | let nib = UINib(nibName: "UITableViewHeaderFooterView", bundle: Bundle(for: UITableViewExtensionsTests.self)) 131| | tableView.register(nib: nib, withHeaderFooterViewClass: UITableViewHeaderFooterView.self) 132| | let view = tableView.dequeueReusableHeaderFooterView(withClass: UITableViewHeaderFooterView.self) 133| | XCTAssertNotNil(view) 134| | } 135| | #endif 136| | 137| 1| func testRegisterReusableViewWithClass() { 138| 1| tableView.register(headerFooterViewClassWith: UITableViewHeaderFooterView.self) 139| 1| let view = tableView.dequeueReusableHeaderFooterView(withClass: UITableViewHeaderFooterView.self) 140| 1| XCTAssertNotNil(view) 141| 1| } 142| | 143| 1| func testRegisterCellWithClass() { 144| 1| tableView.register(cellWithClass: UITableViewCell.self) 145| 1| let cell = tableView.dequeueReusableCell(withClass: UITableViewCell.self) 146| 1| XCTAssertNotNil(cell) 147| 1| } 148| | 149| | #if os(iOS) 150| | func testRegisterCellWithClassAndNib() { 151| | let nib = UINib(nibName: "UITableViewCell", bundle: Bundle(for: UITableViewExtensionsTests.self)) 152| | tableView.register(nib: nib, withCellClass: UITableViewCell.self) 153| | let cell = tableView.dequeueReusableCell(withClass: UITableViewCell.self) 154| | XCTAssertNotNil(cell) 155| | } 156| | #endif 157| | 158| | #if os(iOS) 159| | func testRegisterCellWithNibUsingClass() { 160| | tableView.register(nibWithCellClass: UITableViewCell.self, at: UITableViewExtensionsTests.self) 161| | let cell = tableView.dequeueReusableCell(withClass: UITableViewCell.self) 162| | XCTAssertNotNil(cell) 163| | } 164| | #endif 165| |} 166| | 167| |extension UITableViewExtensionsTests: UITableViewDataSource { 168| 19| func numberOfSections(in tableView: UITableView) -> Int { 169| 19| return tableView == emptyTableView ? 0 : 2 170| 19| } 171| | 172| 30| func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 173| 30| if tableView == emptyTableView { 174| 0| return 0 175| 30| } else { 176| 30| return section == 0 ? 5 : 8 177| 30| } 178| 0| } 179| | 180| 2| func tableView(_: UITableView, cellForRowAt _: IndexPath) -> UITableViewCell { 181| 2| return UITableViewCell() 182| 2| } 183| |} 184| | 185| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Tests/UIKitTests/UITextFieldExtensionsTests.swift: 1| |// UITextFieldExtensionsTests.swift - Copyright 2020 SwifterSwift 2| | 3| |@testable import SwifterSwift 4| |import XCTest 5| | 6| |#if canImport(UIKit) && !os(watchOS) 7| |import UIKit 8| | 9| |final class UITextFieldExtensionsTests: XCTestCase { 10| 1| func testIsEmpty() { 11| 1| let textField = UITextField() 12| 1| XCTAssert(textField.isEmpty) 13| 1| textField.text = "Hello" 14| 1| XCTAssertFalse(textField.isEmpty) 15| 1| textField.text = nil 16| 1| XCTAssert(textField.isEmpty) 17| 1| } 18| | 19| 1| func testTrimmedText() { 20| 1| let frame = CGRect(x: 0, y: 0, width: 100, height: 30) 21| 1| let textField = UITextField(frame: frame) 22| 1| textField.text = "Hello \n \n" 23| 1| XCTAssertNotNil(textField.trimmedText) 24| 1| XCTAssertEqual(textField.trimmedText!, "Hello") 25| 1| } 26| | 27| 1| func testTextType() { 28| 1| let tf1 = UITextField(frame: .zero) 29| 1| tf1.textType = .emailAddress 30| 1| XCTAssertEqual(tf1.textType, .emailAddress) 31| 1| XCTAssertEqual(tf1.keyboardType, .emailAddress) 32| 1| XCTAssertEqual(tf1.autocorrectionType, .no) 33| 1| XCTAssertEqual(tf1.autocapitalizationType, .none) 34| 1| XCTAssertFalse(tf1.isSecureTextEntry) 35| 1| XCTAssertEqual(tf1.placeholder, "Email Address") 36| 1| 37| 1| let tf2 = UITextField(frame: .zero) 38| 1| tf2.textType = .password 39| 1| XCTAssertEqual(tf2.textType, .password) 40| 1| XCTAssertEqual(tf2.keyboardType, .asciiCapable) 41| 1| XCTAssertEqual(tf2.autocorrectionType, .no) 42| 1| XCTAssertEqual(tf2.autocapitalizationType, .none) 43| 1| XCTAssert(tf2.isSecureTextEntry) 44| 1| XCTAssertEqual(tf2.placeholder, "Password") 45| 1| 46| 1| let tf3 = UITextField(frame: .zero) 47| 1| tf3.textType = .generic 48| 1| XCTAssertEqual(tf3.textType, .generic) 49| 1| XCTAssertFalse(tf3.isSecureTextEntry) 50| 1| } 51| | 52| 1| func testHasValidEmail() { 53| 1| let textField = UITextField(frame: .zero) 54| 1| textField.text = "john@doe.com" 55| 1| XCTAssert(textField.hasValidEmail) 56| 1| textField.text = "swifterswift" 57| 1| XCTAssertFalse(textField.hasValidEmail) 58| 1| textField.text = nil 59| 1| XCTAssertFalse(textField.hasValidEmail) 60| 1| } 61| | 62| 1| func testLeftViewTintColor() { 63| 1| let frame = CGRect(x: 0, y: 0, width: 100, height: 30) 64| 1| let textField = UITextField(frame: frame) 65| 1| 66| 1| let imageView = UIImageView() 67| 1| imageView.tintColor = .red 68| 1| 69| 1| textField.leftView = imageView 70| 1| XCTAssertEqual(textField.leftViewTintColor, .red) 71| 1| 72| 1| textField.leftViewTintColor = .blue 73| 1| XCTAssertEqual(textField.leftViewTintColor, .blue) 74| 1| 75| 1| textField.leftView = nil 76| 1| XCTAssertNil(textField.leftViewTintColor) 77| 1| 78| 1| textField.leftViewTintColor = .yellow 79| 1| XCTAssertNil(textField.leftViewTintColor) 80| 1| } 81| | 82| 1| func testRightViewTintColor() { 83| 1| let frame = CGRect(x: 0, y: 0, width: 100, height: 30) 84| 1| let textField = UITextField(frame: frame) 85| 1| 86| 1| let imageView = UIImageView() 87| 1| imageView.tintColor = .red 88| 1| 89| 1| textField.rightView = imageView 90| 1| XCTAssertEqual(textField.rightViewTintColor, .red) 91| 1| 92| 1| textField.rightViewTintColor = .blue 93| 1| XCTAssertEqual(textField.rightViewTintColor, .blue) 94| 1| 95| 1| textField.rightView = nil 96| 1| XCTAssertNil(textField.rightViewTintColor) 97| 1| 98| 1| textField.rightViewTintColor = .yellow 99| 1| XCTAssertNil(textField.rightViewTintColor) 100| 1| } 101| | 102| 1| func testClear() { 103| 1| let frame = CGRect(x: 0, y: 0, width: 100, height: 30) 104| 1| let textField = UITextField(frame: frame) 105| 1| textField.text = "Hello" 106| 1| textField.clear() 107| 1| XCTAssertEqual(textField.text!, "") 108| 1| } 109| | 110| 1| func testSetPlaceHolderTextColor() { 111| 1| let textField = UITextField() 112| 1| textField.attributedPlaceholder = NSAttributedString(string: "Attributed Placeholder") 113| 1| textField.setPlaceHolderTextColor(.blue) 114| 1| let color = textField.attributedPlaceholder?.attribute(.foregroundColor, at: 0, effectiveRange: nil) as? UIColor 115| 1| XCTAssertEqual(color, .blue) 116| 1| 117| 1| textField.placeholder = nil 118| 1| textField.setPlaceHolderTextColor(.yellow) 119| 1| let emptyColor = textField.attributedPlaceholder?.attribute( 120| 1| .foregroundColor, 121| 1| at: 0, 122| 1| effectiveRange: nil) as? UIColor 123| 1| XCTAssertNil(emptyColor) 124| 1| } 125| | 126| 1| func testAddPaddingLeft() { 127| 1| let textfield = UITextField() 128| 1| textfield.frame = CGRect(x: 0, y: 0, width: 100, height: 30) 129| 1| textfield.addPaddingLeft(40) 130| 1| XCTAssertEqual(textfield.leftView?.frame.width, 40) 131| 1| } 132| | 133| 1| func testAddPaddingRight() { 134| 1| let textfield = UITextField() 135| 1| textfield.frame = CGRect(x: 0, y: 0, width: 100, height: 30) 136| 1| textfield.addPaddingRight(40) 137| 1| XCTAssertEqual(textfield.rightView?.frame.width, 40) 138| 1| } 139| | 140| 1| func testAddPaddingImageLeftIcon() { 141| 1| let textfield = UITextField() 142| 1| textfield.frame = CGRect(x: 0, y: 0, width: 100, height: 44) 143| 1| 144| 1| let bundle = Bundle(for: UIImageExtensionsTests.self) 145| 1| let image = UIImage(named: "TestImage", in: bundle, compatibleWith: nil)! 146| 1| textfield.addPaddingLeftIcon(image, padding: 5) 147| 1| XCTAssertEqual(textfield.leftView?.frame.width, image.size.width + 5) 148| 1| } 149| | 150| 1| func testAddPaddingImageRightIcon() { 151| 1| let textfield = UITextField() 152| 1| textfield.frame = CGRect(x: 0, y: 0, width: 100, height: 44) 153| 1| 154| 1| let bundle = Bundle(for: UIImageExtensionsTests.self) 155| 1| let image = UIImage(named: "TestImage", in: bundle, compatibleWith: nil)! 156| 1| textfield.addPaddingRightIcon(image, padding: 5) 157| 1| XCTAssertEqual(textfield.rightView?.frame.width, image.size.width + 5) 158| 1| } 159| |} 160| | 161| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Tests/UIKitTests/UITextViewExtensionsTests.swift: 1| |// UITextViewExtensionsTests.swift - Copyright 2020 SwifterSwift 2| | 3| |@testable import SwifterSwift 4| |import XCTest 5| | 6| |#if canImport(UIKit) && !os(watchOS) 7| |import UIKit 8| | 9| |final class UITextViewExtensionsTests: XCTestCase { 10| 3| var textView = UITextView(frame: CGRect(x: 0, y: 0, width: 100, height: 100)) 11| | 12| 3| override func setUp() { 13| 3| super.setUp() 14| 3| } 15| | 16| 1| func testClear() { 17| 1| textView.text = "Hello" 18| 1| textView.clear() 19| 1| XCTAssertEqual(textView.text, "") 20| 1| XCTAssertEqual(textView.attributedText?.string, "") 21| 1| } 22| | 23| 1| func testScroll() { 24| 1| let text = 25| 1| "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." 26| 1| textView.text = text 27| 1| textView.scrollToBottom() 28| 1| XCTAssertGreaterThan(textView.contentOffset.y, 0.0) 29| 1| 30| 1| textView.scrollToTop() 31| 1| XCTAssertNotEqual(textView.contentOffset.y, 0.0) 32| 1| } 33| | 34| 1| func testWrapToContent() { 35| 1| let text = 36| 1| "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." 37| 1| 38| 1| // initial setting 39| 1| textView.frame = CGRect(origin: .zero, size: CGSize(width: 100, height: 20)) 40| 1| textView.font = UIFont.systemFont(ofSize: 20.0) 41| 1| textView.text = text 42| 1| 43| 1| // determining the text size 44| 1| let constraintRect = CGSize(width: 100, height: CGFloat.greatestFiniteMagnitude) 45| 1| let boundingBox = text.boundingRect(with: constraintRect, 46| 1| options: NSStringDrawingOptions.usesLineFragmentOrigin, 47| 1| attributes: [.font: textView.font!], 48| 1| context: nil) 49| 1| let textHeight = ceil(boundingBox.height) 50| 1| let textSize = CGSize(width: 100, height: textHeight) 51| 1| 52| 1| // before setting wrap, content won't be equal to bounds 53| 1| XCTAssertNotEqual(textView.bounds.size, textView.contentSize) 54| 1| 55| 1| // calling the wrap extension method 56| 1| textView.wrapToContent() 57| 1| 58| 1| // Setting the frame: 59| 1| // This is important to set the frame after calling the wrapToContent, otherwise 60| 1| // boundingRect can give you fractional value, and method call `sizeToFit` inside the 61| 1| // wrapToContent would change to the fractional value instead of the ceil value. 62| 1| textView.bounds = CGRect(origin: .zero, size: textSize) 63| 1| 64| 1| #if !targetEnvironment(macCatalyst) 65| 1| // after setting wrap, content size will be equal to bounds 66| 1| XCTAssertEqual(textView.bounds.size, textView.contentSize) 67| 1| #endif 68| 1| } 69| |} 70| | 71| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Tests/UIKitTests/UIViewControllerExtensionsTests.swift: 1| |// UIViewControllerExtensionsTests.swift - Copyright 2020 SwifterSwift 2| | 3| |@testable import SwifterSwift 4| |import XCTest 5| | 6| |#if canImport(UIKit) && !os(watchOS) 7| |import UIKit 8| | 9| |final class UIViewControllerExtensionsTests: XCTestCase { 10| | class MockNotificationViewController: UIViewController { 11| | var notificationFired = false 12| | 13| 1| @objc func testSelector() { 14| 1| notificationFired = true 15| 1| } 16| | } 17| | 18| 9| let notificationIdentifier = Notification.Name("MockNotification") 19| | 20| 1| func testAddNotificationObserver() { 21| 1| let viewController = MockNotificationViewController() 22| 1| let selector = #selector(MockNotificationViewController.testSelector) 23| 1| viewController.addNotificationObserver(name: notificationIdentifier, selector: selector) 24| 1| NotificationCenter.default.post(name: notificationIdentifier, object: nil) 25| 1| XCTAssert(viewController.notificationFired) 26| 1| } 27| | 28| 1| func testIsVisible() { 29| 1| let viewController = UIViewController() 30| 1| XCTAssertFalse(viewController.isVisible) 31| 1| } 32| | 33| 1| func testRemoveNotificationObserver() { 34| 1| let viewController = MockNotificationViewController() 35| 1| let selector = #selector(MockNotificationViewController.testSelector) 36| 1| viewController.addNotificationObserver(name: notificationIdentifier, selector: selector) 37| 1| viewController.removeNotificationObserver(name: notificationIdentifier) 38| 1| NotificationCenter.default.post(name: notificationIdentifier, object: nil) 39| 1| XCTAssertFalse(viewController.notificationFired) 40| 1| } 41| | 42| 1| func testRemoveNotificationsObserver() { 43| 1| let viewController = MockNotificationViewController() 44| 1| let selector = #selector(MockNotificationViewController.testSelector) 45| 1| viewController.addNotificationObserver(name: notificationIdentifier, selector: selector) 46| 1| viewController.removeNotificationsObserver() 47| 1| NotificationCenter.default.post(name: notificationIdentifier, object: nil) 48| 1| XCTAssertFalse(viewController.notificationFired) 49| 1| } 50| | 51| 1| func testInstantiate() { 52| 1| #if os(tvOS) 53| 1| let storyboard = "TestStoryboard-tvOS" 54| 1| #else 55| 1| let storyboard = "TestStoryboard" 56| 1| #endif 57| 1| 58| 1| let myViewController = MyViewController.instantiate(from: storyboard, 59| 1| bundle: Bundle(for: UIViewControllerExtensionsTests.self)) 60| 1| myViewController.loadViewIfNeeded() 61| 1| XCTAssertNotNil(myViewController.testLabel) 62| 1| 63| 1| let identifiedViewController = MyViewController.instantiate(from: storyboard, 64| 1| bundle: Bundle(for: UIViewControllerExtensionsTests 65| 1| .self), 66| 1| identifier: "MyViewController") 67| 1| identifiedViewController.loadViewIfNeeded() 68| 1| XCTAssertNotNil(identifiedViewController.testLabel) 69| 1| } 70| | 71| 1| func testShowAlert() { 72| 1| let viewController = UIViewController() 73| 1| let title = "test title" 74| 1| let message = "test message" 75| 1| let actionButtons = ["OK", "Cancel"] 76| 1| let preferredButtonIndex = 1 77| 1| let alertController = viewController.showAlert( 78| 1| title: title, 79| 1| message: message, 80| 1| buttonTitles: actionButtons, 81| 1| highlightedButtonIndex: preferredButtonIndex, 82| 1| completion: nil) 83| 1| 84| 1| XCTAssertEqual(alertController.preferredStyle, .alert) 85| 1| XCTAssertEqual(alertController.title, title) 86| 1| XCTAssertEqual(alertController.message, message) 87| 1| // check whether the buttons are added in the same order 88| 2| for action in 0.. UIModalPresentationStyle { 184| | return .popover 185| | } 186| | } 187| | 188| | let popover = UIViewController() 189| | let presentingViewController = UIViewController() 190| | let delegate = PopoverDelegate() 191| | 192| | // Putting the view controller in a window and running a RunLoop seems to be the only way to make 193| | // the presentedViewController and presentingViewController properties to be set. 194| | let window = UIWindow() 195| | window.rootViewController = presentingViewController 196| | window.addSubview(presentingViewController.view) 197| | RunLoop.current.run(until: Date(timeIntervalSinceNow: 5)) 198| | 199| | presentingViewController.presentPopover( 200| | popover, 201| | sourcePoint: presentingViewController.view.center, 202| | delegate: delegate) 203| | 204| | XCTAssertEqual(presentingViewController.presentedViewController, popover) 205| | XCTAssertEqual(popover.presentingViewController, presentingViewController) 206| | XCTAssertEqual(popover.modalPresentationStyle, .popover) 207| | 208| | let popoverDelegate = popover.popoverPresentationController?.delegate as? PopoverDelegate 209| | XCTAssertNotNil(popoverDelegate) 210| | XCTAssertEqual(popoverDelegate, delegate) 211| | } 212| | #endif 213| |} 214| | 215| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Tests/UIKitTests/UIViewExtensionsTests.swift: 1| |// UIViewExtensionsTests.swift - Copyright 2020 SwifterSwift 2| | 3| |@testable import SwifterSwift 4| |import XCTest 5| | 6| |#if canImport(UIKit) && !os(watchOS) 7| |import UIKit 8| | 9| |// swiftlint:disable:next type_body_length 10| |final class UIViewExtensionsTests: XCTestCase { 11| 1| func testBorderColor() { 12| 1| let frame = CGRect(x: 0, y: 0, width: 100, height: 100) 13| 1| let view = UIView(frame: frame) 14| 1| view.borderColor = nil 15| 1| XCTAssertNil(view.borderColor) 16| 1| view.borderColor = UIColor.red 17| 1| XCTAssertNotNil(view.layer.borderColor) 18| 1| XCTAssertEqual(view.borderColor!, UIColor.red) 19| 1| XCTAssertEqual(view.layer.borderColor!.uiColor, UIColor.red) 20| 1| } 21| | 22| 1| func testBorderWidth() { 23| 1| let frame = CGRect(x: 0, y: 0, width: 100, height: 100) 24| 1| let view = UIView(frame: frame) 25| 1| view.borderWidth = 0 26| 1| XCTAssertEqual(view.layer.borderWidth, 0) 27| 1| 28| 1| view.borderWidth = 5 29| 1| XCTAssertEqual(view.borderWidth, 5) 30| 1| } 31| | 32| 1| func testCornerRadius() { 33| 1| let frame = CGRect(x: 0, y: 0, width: 100, height: 100) 34| 1| let view = UIView(frame: frame) 35| 1| XCTAssertEqual(view.layer.cornerRadius, 0) 36| 1| 37| 1| view.cornerRadius = 50 38| 1| XCTAssertEqual(view.cornerRadius, 50) 39| 1| } 40| | 41| 1| func testFirstResponder() { 42| 1| // When there's no firstResponder 43| 1| XCTAssertNil(UIView().firstResponder()) 44| 1| 45| 1| let window = UIWindow() 46| 1| 47| 1| // When self is firstResponder 48| 1| let txtView = UITextField(frame: CGRect.zero) 49| 1| window.addSubview(txtView) 50| 1| txtView.becomeFirstResponder() 51| 1| XCTAssert(txtView.firstResponder() === txtView) 52| 1| 53| 1| // When a subview is firstResponder 54| 1| let superView = UIView() 55| 1| window.addSubview(superView) 56| 1| let subView = UITextField(frame: CGRect.zero) 57| 1| superView.addSubview(subView) 58| 1| subView.becomeFirstResponder() 59| 1| XCTAssert(superView.firstResponder() === subView) 60| 1| 61| 1| // When you have to find recursively 62| 1| XCTAssert(window.firstResponder() === subView) 63| 1| } 64| | 65| 1| func testHeight() { 66| 1| let frame = CGRect(x: 0, y: 0, width: 100, height: 100) 67| 1| let view = UIView(frame: frame) 68| 1| XCTAssertEqual(view.height, 100) 69| 1| view.height = 150 70| 1| XCTAssertEqual(view.frame.size.height, 150) 71| 1| } 72| | 73| 1| func testIsRightToLeft() { 74| 1| let view = UIView() 75| 1| XCTAssertFalse(view.isRightToLeft) 76| 1| } 77| | 78| 1| func testRoundCorners() { 79| 1| let frame = CGRect(x: 0, y: 0, width: 100, height: 100) 80| 1| let view = UIView(frame: frame) 81| 1| view.roundCorners([.allCorners], radius: 5.0) 82| 1| 83| 1| let maskPath = UIBezierPath(roundedRect: view.bounds, 84| 1| byRoundingCorners: [.allCorners], 85| 1| cornerRadii: CGSize(width: 5.0, height: 5.0)) 86| 1| let shape = CAShapeLayer() 87| 1| shape.path = maskPath.cgPath 88| 1| XCTAssertEqual(view.layer.mask?.bounds, shape.bounds) 89| 1| XCTAssertEqual(view.layer.mask?.cornerRadius, shape.cornerRadius) 90| 1| } 91| | 92| 1| func testShadowColor() { 93| 1| let frame = CGRect(x: 0, y: 0, width: 100, height: 100) 94| 1| let view = UIView(frame: frame) 95| 1| view.layer.shadowColor = nil 96| 1| XCTAssertNil(view.layerShadowColor) 97| 1| view.layerShadowColor = UIColor.orange 98| 1| XCTAssertNotNil(view.layer.shadowColor!) 99| 1| XCTAssertEqual(view.layerShadowColor, UIColor.orange) 100| 1| } 101| | 102| 1| func testScreenshot() { 103| 1| let frame = CGRect(x: 0, y: 0, width: 100, height: 100) 104| 1| let view = UIView(frame: frame) 105| 1| view.backgroundColor = .black 106| 1| let screenshot = view.screenshot 107| 1| XCTAssertNotNil(screenshot) 108| 1| 109| 1| let view2 = UIView() 110| 1| XCTAssertNil(view2.screenshot) 111| 1| } 112| | 113| 1| func testShadowOffset() { 114| 1| let frame = CGRect(x: 0, y: 0, width: 100, height: 100) 115| 1| let view = UIView(frame: frame) 116| 1| view.layer.shadowOffset = CGSize.zero 117| 1| XCTAssertEqual(view.layerShadowOffset, CGSize.zero) 118| 1| let size = CGSize(width: 5, height: 5) 119| 1| view.layerShadowOffset = size 120| 1| XCTAssertEqual(view.layer.shadowOffset, size) 121| 1| } 122| | 123| 1| func testShadowOpacity() { 124| 1| let frame = CGRect(x: 0, y: 0, width: 100, height: 100) 125| 1| let view = UIView(frame: frame) 126| 1| view.layer.shadowOpacity = 0 127| 1| XCTAssertEqual(view.layerShadowOpacity, 0) 128| 1| view.layerShadowOpacity = 0.5 129| 1| XCTAssertEqual(view.layer.shadowOpacity, 0.5) 130| 1| } 131| | 132| 1| func testShadowRadius() { 133| 1| let frame = CGRect(x: 0, y: 0, width: 100, height: 100) 134| 1| let view = UIView(frame: frame) 135| 1| view.layer.shadowRadius = 0 136| 1| XCTAssertEqual(view.layerShadowRadius, 0) 137| 1| view.layerShadowRadius = 0.5 138| 1| XCTAssertEqual(view.layer.shadowRadius, 0.5) 139| 1| } 140| | 141| 1| func testAddShadow() { 142| 1| let frame = CGRect(x: 0, y: 0, width: 100, height: 100) 143| 1| let view = UIView(frame: frame) 144| 1| view.addShadow(ofColor: .red, radius: 5.0, offset: .zero, opacity: 0.5) 145| 1| XCTAssertEqual(view.layerShadowColor, UIColor.red) 146| 1| XCTAssertEqual(view.layerShadowRadius, 5.0) 147| 1| XCTAssertEqual(view.layerShadowOffset, CGSize.zero) 148| 1| XCTAssertEqual(view.layerShadowOpacity, 0.5) 149| 1| } 150| | 151| 1| func testMasksToBounds() { 152| 1| let view = UIView(frame: .zero) 153| 1| view.layer.masksToBounds = true 154| 1| XCTAssertTrue(view.masksToBounds) 155| 1| view.masksToBounds = false 156| 1| XCTAssertFalse(view.masksToBounds) 157| 1| } 158| | 159| 1| func testSize() { 160| 1| let frame = CGRect(x: 0, y: 0, width: 100, height: 100) 161| 1| let view = UIView(frame: frame) 162| 1| XCTAssertEqual(view.size, view.frame.size) 163| 1| 164| 1| view.size = CGSize(width: 50, height: 50) 165| 1| XCTAssertEqual(view.frame.size.width, 50) 166| 1| XCTAssertEqual(view.frame.size.height, 50) 167| 1| } 168| | 169| 1| func testParentViewController() { 170| 1| let viewController = UIViewController() 171| 1| XCTAssertNotNil(viewController.view.parentViewController) 172| 1| XCTAssertEqual(viewController.view.parentViewController, viewController) 173| 1| 174| 1| let frame = CGRect(x: 0, y: 0, width: 100, height: 100) 175| 1| let view = UIView(frame: frame) 176| 1| XCTAssertNil(view.parentViewController) 177| 1| } 178| | 179| 1| func testX() { 180| 1| let frame = CGRect(x: 20, y: 20, width: 100, height: 100) 181| 1| let view = UIView(frame: frame) 182| 1| XCTAssertEqual(view.x, 20) 183| 1| view.x = 10 184| 1| XCTAssertEqual(view.frame.origin.x, 10) 185| 1| } 186| | 187| 1| func testY() { 188| 1| let frame = CGRect(x: 20, y: 20, width: 100, height: 100) 189| 1| let view = UIView(frame: frame) 190| 1| XCTAssertEqual(view.y, 20) 191| 1| view.y = 10 192| 1| XCTAssertEqual(view.frame.origin.y, 10) 193| 1| } 194| | 195| 1| func testWidth() { 196| 1| let frame = CGRect(x: 0, y: 0, width: 100, height: 100) 197| 1| let view = UIView(frame: frame) 198| 1| XCTAssertEqual(view.width, 100) 199| 1| view.width = 150 200| 1| XCTAssertEqual(view.frame.size.width, 150) 201| 1| } 202| | 203| 1| func testAddSubviews() { 204| 1| let frame = CGRect(x: 0, y: 0, width: 100, height: 100) 205| 1| let view = UIView(frame: frame) 206| 1| XCTAssertEqual(view.subviews.count, 0) 207| 1| 208| 1| view.addSubviews([UIView(), UIView()]) 209| 1| XCTAssertEqual(view.subviews.count, 2) 210| 1| } 211| | 212| 1| func testFadeIn() { 213| 1| let view1 = UIView() 214| 1| view1.isHidden = true 215| 1| view1.alpha = 0 216| 1| 217| 1| view1.fadeIn(duration: 0, completion: nil) 218| 1| XCTAssertFalse(view1.isHidden) 219| 1| XCTAssertEqual(view1.alpha, 1) 220| 1| 221| 1| let fadeInExpectation = expectation(description: "Faded in") 222| 1| 223| 1| let view2 = UIView() 224| 1| view2.alpha = 0 225| 1| XCTAssertFalse(view1.isHidden) 226| 1| 227| 1| view2.fadeIn(duration: 0.5) { _ in 228| 1| fadeInExpectation.fulfill() 229| 1| } 230| 1| 231| 1| XCTAssertEqual(view2.alpha, 1) 232| 1| waitForExpectations(timeout: 0.5) 233| 1| } 234| | 235| 1| func testFadeOut() { 236| 1| let view1 = UIView() 237| 1| view1.isHidden = true 238| 1| 239| 1| view1.fadeOut(duration: 0, completion: nil) 240| 1| XCTAssertFalse(view1.isHidden) 241| 1| XCTAssertEqual(view1.alpha, 0) 242| 1| 243| 1| let fadeOutExpectation = expectation(description: "Faded out") 244| 1| 245| 1| let view2 = UIView() 246| 1| XCTAssertFalse(view1.isHidden) 247| 1| 248| 1| view2.fadeOut(duration: 0.5) { _ in 249| 1| fadeOutExpectation.fulfill() 250| 1| } 251| 1| XCTAssertEqual(view2.alpha, 0) 252| 1| waitForExpectations(timeout: 0.5) 253| 1| } 254| | 255| 1| func testRotateByAngle() { 256| 1| let view1 = UIView() 257| 1| let transform1 = CGAffineTransform(rotationAngle: 2) 258| 1| view1.rotate(byAngle: 2, ofType: .radians, animated: false, duration: 0, completion: nil) 259| 1| XCTAssertEqual(view1.transform, transform1) 260| 1| 261| 1| let view2 = UIView() 262| 1| let transform2 = CGAffineTransform(rotationAngle: .pi * 90.0 / 180.0) 263| 1| view2.rotate(byAngle: 90, ofType: .degrees, animated: false, duration: 0, completion: nil) 264| 1| XCTAssertEqual(view2.transform, transform2) 265| 1| 266| 1| let rotateExpectation = expectation(description: "view rotated") 267| 1| 268| 1| let view3 = UIView() 269| 1| let transform3 = CGAffineTransform(rotationAngle: 2) 270| 1| 271| 1| view3.rotate(byAngle: 2, ofType: .radians, animated: true, duration: 0.5) { _ in 272| 1| rotateExpectation.fulfill() 273| 1| } 274| 1| XCTAssertEqual(view3.transform, transform3) 275| 1| waitForExpectations(timeout: 0.5) 276| 1| } 277| | 278| 1| func testRotateToAngle() { 279| 1| let view1 = UIView() 280| 1| let transform1 = CGAffineTransform(rotationAngle: 2) 281| 1| view1.rotate(toAngle: 2, ofType: .radians, animated: false, duration: 0, completion: nil) 282| 1| XCTAssertEqual(view1.transform, transform1) 283| 1| 284| 1| let view2 = UIView() 285| 1| let transform2 = CGAffineTransform(rotationAngle: .pi * 90.0 / 180.0) 286| 1| view2.rotate(toAngle: 90, ofType: .degrees, animated: false, duration: 0, completion: nil) 287| 1| XCTAssertEqual(view2.transform, transform2) 288| 1| 289| 1| let rotateExpectation = expectation(description: "view rotated") 290| 1| 291| 1| let view3 = UIView() 292| 1| let transform3 = CGAffineTransform(rotationAngle: 2) 293| 1| 294| 1| view3.rotate(toAngle: 2, ofType: .radians, animated: true, duration: 0.5) { _ in 295| 1| rotateExpectation.fulfill() 296| 1| } 297| 1| XCTAssertEqual(view3.transform, transform3) 298| 1| waitForExpectations(timeout: 0.5) 299| 1| } 300| | 301| 1| func testScale() { 302| 1| let frame = CGRect(x: 0, y: 0, width: 10, height: 10) 303| 1| let view1 = UIView(frame: frame) 304| 1| let view2 = UIView(frame: frame) 305| 1| let view3 = UIView(frame: frame) 306| 1| let scale = CGPoint(x: 0.5, y: 2.0) 307| 1| 308| 1| view1.transform = view2.transform.scaledBy(x: scale.x, y: scale.y) 309| 1| 310| 1| view2.scale(by: scale) 311| 1| view3.scale(by: scale, animated: true, duration: 0) 312| 1| 313| 1| XCTAssertEqual(view1.transform, view2.transform) 314| 1| XCTAssertEqual(view1.transform, view3.transform) 315| 1| } 316| | 317| 1| func testLoadFromNib() { 318| 1| let bundle = Bundle(for: UIViewExtensionsTests.self) 319| 1| XCTAssertNotNil(UIView.loadFromNib(named: "UIImageView", bundle: bundle)) 320| 1| XCTAssertNotNil(UIView.loadFromNib(withClass: UIImageView.self, bundle: bundle)) 321| 1| } 322| | 323| 1| func testRemoveSubviews() { 324| 1| let view = UIView() 325| 1| view.addSubviews([UIView(), UIView()]) 326| 1| view.removeSubviews() 327| 1| XCTAssertEqual(view.subviews.count, 0) 328| 1| } 329| | 330| 1| func testRemoveGestureRecognizers() { 331| 1| let view = UIView() 332| 1| XCTAssertNil(view.gestureRecognizers) 333| 1| view.removeGestureRecognizers() 334| 1| XCTAssertNil(view.gestureRecognizers) 335| 1| 336| 1| let tap = UIGestureRecognizer(target: nil, action: nil) 337| 1| view.addGestureRecognizer(tap) 338| 1| XCTAssertNotNil(view.gestureRecognizers) 339| 1| view.removeGestureRecognizers() 340| 1| XCTAssertEqual(view.gestureRecognizers!.count, 0) 341| 1| } 342| | 343| 1| func testAddGestureRecognizers() { 344| 1| let view = UIView() 345| 1| 346| 1| XCTAssertNil(view.gestureRecognizers) 347| 1| 348| 1| let tap = UITapGestureRecognizer(target: nil, action: nil) 349| 1| let pan = UIPanGestureRecognizer(target: nil, action: nil) 350| 1| let swipe = UISwipeGestureRecognizer(target: nil, action: nil) 351| 1| 352| 1| view.addGestureRecognizers([tap, pan, swipe]) 353| 1| 354| 1| XCTAssertNotNil(view.gestureRecognizers) 355| 1| XCTAssertEqual(view.gestureRecognizers!.count, 3) 356| 1| } 357| | 358| 1| func testRemoveGestureRecognizersVariadic() { 359| 1| let view = UIView() 360| 1| 361| 1| XCTAssertNil(view.gestureRecognizers) 362| 1| 363| 1| let tap = UITapGestureRecognizer(target: nil, action: nil) 364| 1| let pan = UIPanGestureRecognizer(target: nil, action: nil) 365| 1| let swipe = UISwipeGestureRecognizer(target: nil, action: nil) 366| 1| 367| 1| view.addGestureRecognizers([tap, pan, swipe]) 368| 1| 369| 1| XCTAssertNotNil(view.gestureRecognizers) 370| 1| XCTAssertEqual(view.gestureRecognizers!.count, 3) 371| 1| 372| 1| view.removeGestureRecognizers([tap, pan, swipe]) 373| 1| 374| 1| XCTAssertNotNil(view.gestureRecognizers) 375| 1| XCTAssert(view.gestureRecognizers!.isEmpty) 376| 1| } 377| | 378| 1| func testAnchor() { 379| 1| let view = UIView() 380| 1| let subview = UIView() 381| 1| view.addSubview(subview) 382| 1| subview.anchor(top: nil, left: nil, bottom: nil, right: nil) 383| 1| XCTAssert(subview.constraints.isEmpty) 384| 1| 385| 1| subview.anchor(top: view.topAnchor) 386| 1| XCTAssertNotNil(subview.topAnchor) 387| 1| 388| 1| subview.anchor(left: view.leftAnchor) 389| 1| XCTAssertNotNil(subview.leftAnchor) 390| 1| 391| 1| subview.anchor(bottom: view.bottomAnchor) 392| 1| XCTAssertNotNil(subview.bottomAnchor) 393| 1| 394| 1| subview.anchor(right: view.rightAnchor) 395| 1| XCTAssertNotNil(subview.rightAnchor) 396| 1| 397| 1| subview.anchor(widthConstant: 10.0, heightConstant: 10.0) 398| 1| XCTAssertNotNil(subview.widthAnchor) 399| 1| XCTAssertNotNil(subview.heightAnchor) 400| 1| } 401| | 402| 1| func testAnchorCenterXToSuperview() { 403| 1| let superview = UIView() 404| 1| let view = UIView() 405| 1| superview.addSubview(view) 406| 1| view.anchorCenterXToSuperview() 407| 1| let subview = UIView() 408| 1| view.addSubview(subview) 409| 1| view.anchorCenterXToSuperview(constant: 10.0) 410| 1| XCTAssertNotNil(subview.centerXAnchor) 411| 1| } 412| | 413| 1| func testAnchorCenterYToSuperview() { 414| 1| let superview = UIView() 415| 1| let view = UIView() 416| 1| superview.addSubview(view) 417| 1| view.anchorCenterXToSuperview() 418| 1| let subview = UIView() 419| 1| view.addSubview(subview) 420| 1| view.anchorCenterYToSuperview(constant: 10.0) 421| 1| XCTAssertNotNil(subview.centerYAnchor) 422| 1| } 423| | 424| 1| func testAnchorCenterToSuperview() { 425| 1| let superview = UIView() 426| 1| let view = UIView() 427| 1| superview.addSubview(view) 428| 1| view.anchorCenterSuperview() 429| 1| let subview = UIView() 430| 1| view.addSubview(subview) 431| 1| view.anchorCenterSuperview() 432| 1| XCTAssertNotNil(subview.centerXAnchor) 433| 1| XCTAssertNotNil(subview.centerYAnchor) 434| 1| } 435| | 436| 1| func testAncestorViewWhere() { 437| 1| let tableView = UITableView(frame: .zero) 438| 1| let tableViewCell = UITableViewCell(frame: .zero) 439| 1| tableView.addSubview(tableViewCell) 440| 1| tableView.tag = 2 441| 1| let button = UIButton(frame: .zero) 442| 1| tableViewCell.addSubview(button) 443| 1| button.tag = 1 444| 1| let buttonSubview = UIView(frame: .zero) 445| 1| button.addSubview(buttonSubview) 446| 1| 447| 1| XCTAssertEqual(buttonSubview.ancestorView(where: { $0?.tag == 1 }), button) 448| 3| XCTAssertEqual(buttonSubview.ancestorView(where: { $0?.tag == 2 }), tableView) 449| 4| XCTAssertNil(buttonSubview.ancestorView(where: { $0?.tag == 3 })) 450| 1| XCTAssertEqual(button.ancestorView(where: { $0 is UITableViewCell }), tableViewCell) 451| 2| XCTAssertEqual(button.ancestorView(where: { $0?.superview == nil }), tableView) 452| 1| } 453| | 454| 1| func testAncestorViewWithClass() { 455| 1| let tableView = UITableView(frame: .zero) 456| 1| let tableViewCell = UITableViewCell(frame: .zero) 457| 1| tableView.addSubview(tableViewCell) 458| 1| let button = UIButton(frame: .zero) 459| 1| tableViewCell.addSubview(button) 460| 1| let buttonSubview = UIView(frame: .zero) 461| 1| button.addSubview(buttonSubview) 462| 1| 463| 1| XCTAssertEqual(button.ancestorView(withClass: UITableViewCell.self), tableViewCell) 464| 1| XCTAssertEqual(button.ancestorView(withClass: UITableView.self), tableView) 465| 1| XCTAssertNil(button.ancestorView(withClass: UIButton.self)) 466| 1| XCTAssertNil(tableView.ancestorView(withClass: UIButton.self)) 467| 1| XCTAssertEqual(buttonSubview.ancestorView(withClass: UITableViewCell.self), tableViewCell) 468| 1| XCTAssertEqual(buttonSubview.ancestorView(withClass: UITableView.self), tableView) 469| 1| } 470| | 471| 1| func testFindConstraint() { 472| 1| let view = UIView() 473| 1| let container = UIView() 474| 1| container.addSubview(view) 475| 1| NSLayoutConstraint.activate([ 476| 1| view.widthAnchor.constraint(equalToConstant: 1), 477| 1| container.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 3) 478| 1| ]) 479| 1| XCTAssertNotNil(view.findConstraint(attribute: .width, for: view)) 480| 1| XCTAssertNil(view.findConstraint(attribute: .height, for: view)) 481| 1| 482| 1| // pathological case 483| 1| XCTAssertNil(view.findConstraint(attribute: .height, for: UIView())) 484| 1| } 485| | 486| 1| func testConstraintProperties() { 487| 1| let container = UIView() 488| 1| let view = UIView() 489| 1| container.addSubview(view) 490| 1| 491| 1| // setup constraints, some in container and some in view 492| 1| NSLayoutConstraint.activate([ 493| 1| view.widthAnchor.constraint(equalToConstant: 1), 494| 1| view.heightAnchor.constraint(equalToConstant: 2), 495| 1| container.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 3), 496| 1| container.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: 4), 497| 1| view.topAnchor.constraint(equalTo: container.topAnchor, constant: 5), 498| 1| view.bottomAnchor.constraint(equalTo: container.bottomAnchor, constant: 6) 499| 1| ]) 500| 1| 501| 1| // find them 502| 1| XCTAssertEqual(view.widthConstraint!.constant, 1) 503| 1| XCTAssertEqual(view.heightConstraint!.constant, 2) 504| 1| XCTAssertEqual(view.leadingConstraint!.constant, 3) 505| 1| XCTAssertEqual(view.trailingConstraint!.constant, 4) 506| 1| XCTAssertEqual(view.topConstraint!.constant, 5) 507| 1| XCTAssertEqual(view.bottomConstraint!.constant, 6) 508| 1| 509| 1| // simple empty case test 510| 1| XCTAssertNil(container.widthConstraint) 511| 1| } 512| |} 513| | 514| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Tests/XCTest/XCTestExtensions.swift: 1| |// XCTestExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(XCTest) 4| |import XCTest 5| | 6| |#if canImport(UIKit) 7| |import UIKit 8| |/// SwifterSwift: Color 9| |public typealias Color = UIColor 10| |#endif 11| | 12| |#if canImport(AppKit) && !targetEnvironment(macCatalyst) 13| |import AppKit 14| |/// SwifterSwift: Color 15| |public typealias Color = NSColor 16| |#endif 17| | 18| |#if canImport(AppKit) || canImport(UIKit) 19| |/// SwifterSwift: Asserts that the RGBA values of two `Color`s are equal within a certain accuracy. 20| |/// - Parameters: 21| |/// - expression1: A `Color`. 22| |/// - expression2: A `Color`. 23| |/// - accuracy: Describes the maximum difference between `expression1` and `expression2` for these values to be considered equal. 24| |/// - message: An optional description of the failure. 25| |/// - file: The file in which failure occurred. Defaults to the file name of the test case in which this function was called. 26| |/// - line: The line number on which failure occurred. Defaults to the line number on which this function was called. 27| |public func XCTAssertEqual(_ expression1: @autoclosure () throws -> Color, 28| | _ expression2: @autoclosure () throws -> Color, 29| | accuracy: CGFloat, 30| 0| _ message: @autoclosure () -> String = "", 31| | file: StaticString = #file, 32| 8| line: UInt = #line) { 33| 8| var color1: Color! 34| 8| XCTAssertNoThrow(color1 = try expression1(), message(), file: file, line: line) 35| 8| var color2: Color! 36| 8| XCTAssertNoThrow(color2 = try expression2(), message(), file: file, line: line) 37| 8| var red1: CGFloat = 0, green1: CGFloat = 0, blue1: CGFloat = 0, alpha1: CGFloat = 0 38| 8| var red2: CGFloat = 0, green2: CGFloat = 0, blue2: CGFloat = 0, alpha2: CGFloat = 0 39| 8| color1.getRed(&red1, green: &green1, blue: &blue1, alpha: &alpha1) 40| 8| color2.getRed(&red2, green: &green2, blue: &blue2, alpha: &alpha2) 41| 8| XCTAssertEqual(red1, red2, accuracy: accuracy, message(), file: file, line: line) 42| 8| XCTAssertEqual(green1, green2, accuracy: accuracy, message(), file: file, line: line) 43| 8| XCTAssertEqual(blue1, blue2, accuracy: accuracy, message(), file: file, line: line) 44| 8| XCTAssertEqual(alpha1, alpha2, accuracy: accuracy, message(), file: file, line: line) 45| 8|} 46| |#endif 47| | 48| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Tests/XCTestTests/XCTestExtensionsTests.swift: 1| |// XCTestExtensionsTests.swift - Copyright 2020 SwifterSwift 2| | 3| |@testable import SwifterSwift 4| |import XCTest 5| | 6| |final class XCTestExtensionsTests: XCTestCase { 7| | #if canImport(AppKit) || canImport(UIKit) 8| 1| func testAssertEqualColorsWithAccuracy() { 9| 1| XCTAssertEqual(.blue, .blue, accuracy: 0) 10| 1| 11| 1| let accuracy = CGFloat(0.1) 12| 1| XCTAssertEqual(Color(red: 0, green: 0, blue: 0, alpha: 0), 13| 1| Color(red: accuracy, green: 0, blue: 0, alpha: 0), 14| 1| accuracy: accuracy) 15| 1| XCTAssertEqual(Color(red: 0, green: 0, blue: 0, alpha: 0), 16| 1| Color(red: 0, green: accuracy, blue: 0, alpha: 0), 17| 1| accuracy: accuracy) 18| 1| XCTAssertEqual(Color(red: 0, green: 0, blue: 0, alpha: 0), 19| 1| Color(red: 0, green: 0, blue: accuracy, alpha: 0), 20| 1| accuracy: accuracy) 21| 1| XCTAssertEqual(Color(red: 0, green: 0, blue: 0, alpha: 0), 22| 1| Color(red: 0, green: 0, blue: 0, alpha: accuracy), 23| 1| accuracy: accuracy) 24| 1| } 25| | #endif 26| |} <<<<<< EOF # path=./SwifterSwift.framework.coverage.txt /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/CoreAnimation/CAGradientLayerExtensions.swift: 1| |// CAGradientLayerExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if !os(watchOS) && !os(Linux) && canImport(QuartzCore) 4| |import QuartzCore 5| | 6| |public extension CAGradientLayer { 7| | /// SwifterSwift: Creates a CAGradientLayer with the specified colors, location, startPoint, endPoint, and type. 8| | /// - Parameter colors: An array of colors defining the color of each gradient stop 9| | /// - Parameter locations: An array of NSNumber defining the location of each 10| | /// gradient stop as a value in the range [0,1]. The values must be 11| | /// monotonically increasing. If a nil array is given, the stops are 12| | /// assumed to spread uniformly across the [0,1] range. When rendered, 13| | /// the colors are mapped to the output colorspace before being 14| | /// interpolated. (default is nil) 15| | /// - 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.) 16| | /// - Parameter endPoint: end point corresponds to the last gradient stop 17| | /// - Parameter type: The kind of gradient that will be drawn. Currently, the only allowed values are `axial' (the default value), `radial', and `conic'. 18| | convenience init(colors: [Color], 19| | locations: [CGFloat]? = nil, 20| | startPoint: CGPoint = CGPoint(x: 0.5, y: 0), 21| | endPoint: CGPoint = CGPoint(x: 0.5, y: 1), 22| 1| type: CAGradientLayerType = .axial) { 23| 1| self.init() 24| 5| self.colors = colors.map(\.cgColor) ------------------ | $sSo15CAGradientLayerC12SwifterSwiftE6colors9locations10startPoint03endH04typeABSaySo7UIColorCG_Say12CoreGraphics7CGFloatVGSgSo7CGPointVARSo0aB4TypeatcfcSo10CGColorRefaAJcs7KeyPathCyAjVGcfu_: | 24| 1| self.colors = colors.map(\.cgColor) ------------------ | $sSo15CAGradientLayerC12SwifterSwiftE6colors9locations10startPoint03endH04typeABSaySo7UIColorCG_Say12CoreGraphics7CGFloatVGSgSo7CGPointVARSo0aB4TypeatcfcSo10CGColorRefaAJcs7KeyPathCyAjVGcfu_AvJcfu0_: | 24| 4| self.colors = colors.map(\.cgColor) ------------------ 25| 4| self.locations = locations?.map { NSNumber(value: Double($0)) } 26| 1| self.startPoint = startPoint 27| 1| self.endPoint = endPoint 28| 1| self.type = type 29| 1| } 30| |} 31| | 32| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/CoreAnimation/CATransform3DExtensions.swift: 1| |// CATransform3DExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |// swiftlint:disable identifier_name 4| | 5| |#if canImport(QuartzCore) 6| | 7| |import QuartzCore 8| | 9| |// MARK: - Equatable 10| | 11| |extension CATransform3D: Equatable { 12| | // swiftlint:disable missing_swifterswift_prefix 13| | 14| | /// Returns a Boolean value indicating whether two values are equal. 15| | /// 16| | /// Equality is the inverse of inequality. For any values `a` and `b`, 17| | /// `a == b` implies that `a != b` is `false`. 18| | /// 19| | /// - Parameters: 20| | /// - lhs: A value to compare. 21| | /// - rhs: Another value to compare. 22| | @inlinable 23| 31| public static func == (lhs: CATransform3D, rhs: CATransform3D) -> Bool { 24| 31| CATransform3DEqualToTransform(lhs, rhs) 25| 31| } 26| | 27| | // swiftlint:disable missing_swifterswift_prefix 28| |} 29| | 30| |// MARK: - Static Properties 31| | 32| |public extension CATransform3D { 33| | /// SwifterSwift: The identity transform: [1 0 0 0; 0 1 0 0; 0 0 1 0; 0 0 0 1]. 34| | @inlinable 35| 4| static var identity: CATransform3D { CATransform3DIdentity } 36| |} 37| | 38| |// MARK: - Codable 39| | 40| |extension CATransform3D: Codable { 41| | // swiftlint:disable missing_swifterswift_prefix 42| | 43| | /// Creates a new instance by decoding from the given decoder. 44| | /// 45| | /// This initializer throws an error if reading from the decoder fails, or if the data read is corrupted or otherwise invalid. 46| | /// - Parameter decoder: The decoder to read data from. 47| | @inlinable 48| 1| public init(from decoder: Decoder) throws { 49| 1| var container = try decoder.unkeyedContainer() 50| 1| self.init(m11: try container.decode(CGFloat.self), 51| 1| m12: try container.decode(CGFloat.self), 52| 1| m13: try container.decode(CGFloat.self), 53| 1| m14: try container.decode(CGFloat.self), 54| 1| m21: try container.decode(CGFloat.self), 55| 1| m22: try container.decode(CGFloat.self), 56| 1| m23: try container.decode(CGFloat.self), 57| 1| m24: try container.decode(CGFloat.self), 58| 1| m31: try container.decode(CGFloat.self), 59| 1| m32: try container.decode(CGFloat.self), 60| 1| m33: try container.decode(CGFloat.self), 61| 1| m34: try container.decode(CGFloat.self), 62| 1| m41: try container.decode(CGFloat.self), 63| 1| m42: try container.decode(CGFloat.self), 64| 1| m43: try container.decode(CGFloat.self), 65| 1| m44: try container.decode(CGFloat.self)) 66| 1| } 67| | 68| | /// Encodes this value into the given encoder. 69| | /// 70| | /// If the value fails to encode anything, encoder will encode an empty keyed container in its place. 71| | /// 72| | /// This function throws an error if any values are invalid for the given encoder’s format. 73| | /// - Parameter encoder: The encoder to write data to. 74| | @inlinable 75| 1| public func encode(to encoder: Encoder) throws { 76| 1| var container = encoder.unkeyedContainer() 77| 1| try container.encode(m11) 78| 1| try container.encode(m12) 79| 1| try container.encode(m13) 80| 1| try container.encode(m14) 81| 1| try container.encode(m21) 82| 1| try container.encode(m22) 83| 1| try container.encode(m23) 84| 1| try container.encode(m24) 85| 1| try container.encode(m31) 86| 1| try container.encode(m32) 87| 1| try container.encode(m33) 88| 1| try container.encode(m34) 89| 1| try container.encode(m41) 90| 1| try container.encode(m42) 91| 1| try container.encode(m43) 92| 1| try container.encode(m44) 93| 1| } 94| | 95| | // swiftlint:enable missing_swifterswift_prefix 96| |} 97| | 98| |// MARK: - Initializers 99| | 100| |public extension CATransform3D { 101| | /// SwifterSwift: Returns a transform that translates by `(tx, ty, tz)`. 102| | /// - Parameters: 103| | /// - tx: x-axis translation 104| | /// - ty: y-axis translation 105| | /// - tz: z-axis translation 106| | @inlinable 107| 1| init(translationX tx: CGFloat, y ty: CGFloat, z tz: CGFloat) { 108| 1| self = CATransform3DMakeTranslation(tx, ty, tz) 109| 1| } 110| | 111| | /// SwifterSwift: Returns a transform that scales by `(sx, sy, sz)`. 112| | /// - Parameters: 113| | /// - sx: x-axis scale 114| | /// - sy: y-axis scale 115| | /// - sz: z-axis scale 116| | @inlinable 117| 1| init(scaleX sx: CGFloat, y sy: CGFloat, z sz: CGFloat) { 118| 1| self = CATransform3DMakeScale(sx, sy, sz) 119| 1| } 120| | 121| | /// SwifterSwift: Returns a transform that rotates by `angle` radians about the vector `(x, y, z)`. 122| | /// 123| | /// If the vector has zero length the behavior is undefined. 124| | /// - Parameters: 125| | /// - angle: The angle of rotation 126| | /// - x: x position of the vector 127| | /// - y: y position of the vector 128| | /// - z: z position of the vector 129| | @inlinable 130| 1| init(rotationAngle angle: CGFloat, x: CGFloat, y: CGFloat, z: CGFloat) { 131| 1| self = CATransform3DMakeRotation(angle, x, y, z) 132| 1| } 133| |} 134| | 135| |// MARK: - Properties 136| | 137| |public extension CATransform3D { 138| | /// SwifterSwift: Returns `true` if the receiver is the identity transform. 139| | @inlinable 140| 4| var isIdentity: Bool { CATransform3DIsIdentity(self) } 141| |} 142| | 143| |// MARK: - Methods 144| | 145| |public extension CATransform3D { 146| | /// SwifterSwift: Translate the receiver by `(tx, ty, tz)`. 147| | /// - Parameters: 148| | /// - tx: x-axis translation 149| | /// - ty: y-axis translation 150| | /// - tz: z-axis translation 151| | /// - Returns: The translated matrix. 152| | @inlinable 153| | func translatedBy(x tx: CGFloat, y ty: CGFloat, 154| 1| z tz: CGFloat) -> CATransform3D { 155| 1| CATransform3DTranslate(self, tx, ty, tz) 156| 1| } 157| | 158| | /// SwifterSwift: Scale the receiver by `(sx, sy, sz)`. 159| | /// - Parameters: 160| | /// - sx: x-axis scale 161| | /// - sy: y-axis scale 162| | /// - sz: z-axis scale 163| | /// - Returns: The scaled matrix. 164| | @inlinable 165| | func scaledBy(x sx: CGFloat, y sy: CGFloat, 166| 1| z sz: CGFloat) -> CATransform3D { 167| 1| CATransform3DScale(self, sx, sy, sz) 168| 1| } 169| | 170| | /// SwifterSwift: Rotate the receiver by `angle` radians about the vector `(x, y, z)`. 171| | /// 172| | /// If the vector has zero length the behavior is undefined. 173| | /// - Parameters: 174| | /// - angle: The angle of rotation 175| | /// - x: x position of the vector 176| | /// - y: y position of the vector 177| | /// - z: z position of the vector 178| | /// - Returns: The rotated matrix. 179| | @inlinable 180| 1| func rotated(by angle: CGFloat, x: CGFloat, y: CGFloat, z: CGFloat) -> CATransform3D { 181| 1| CATransform3DRotate(self, angle, x, y, z) 182| 1| } 183| | 184| | /// SwifterSwift: Invert the receiver. 185| | /// 186| | /// Returns the original matrix if the receiver has no inverse. 187| | /// - Returns: The inverted matrix of the receiver. 188| | @inlinable 189| 4| func inverted() -> CATransform3D { 190| 4| CATransform3DInvert(self) 191| 4| } 192| | 193| | /// SwifterSwift: Concatenate `transform` to the receiver. 194| | /// - Parameter t2: The transform to concatenate on to the receiver 195| | /// - Returns: The concatenated matrix. 196| | @inlinable 197| 5| func concatenating(_ t2: CATransform3D) -> CATransform3D { 198| 5| CATransform3DConcat(self, t2) 199| 5| } 200| | 201| | /// SwifterSwift: Translate the receiver by `(tx, ty, tz)`. 202| | /// - Parameters: 203| | /// - tx: x-axis translation 204| | /// - ty: y-axis translation 205| | /// - tz: z-axis translation 206| | @inlinable 207| 1| mutating func translateBy(x tx: CGFloat, y ty: CGFloat, z tz: CGFloat) { 208| 1| self = CATransform3DTranslate(self, tx, ty, tz) 209| 1| } 210| | 211| | /// SwifterSwift: Scale the receiver by `(sx, sy, sz)`. 212| | /// - Parameters: 213| | /// - sx: x-axis scale 214| | /// - sy: y-axis scale 215| | /// - sz: z-axis scale 216| | @inlinable 217| 1| mutating func scaleBy(x sx: CGFloat, y sy: CGFloat, z sz: CGFloat) { 218| 1| self = CATransform3DScale(self, sx, sy, sz) 219| 1| } 220| | 221| | /// SwifterSwift: Rotate the receiver by `angle` radians about the vector `(x, y, z)`. 222| | /// 223| | /// If the vector has zero length the behavior is undefined. 224| | /// - Parameters: 225| | /// - angle: The angle of rotation 226| | /// - x: x position of the vector 227| | /// - y: y position of the vector 228| | /// - z: z position of the vector 229| | @inlinable 230| 1| mutating func rotate(by angle: CGFloat, x: CGFloat, y: CGFloat, z: CGFloat) { 231| 1| self = CATransform3DRotate(self, angle, x, y, z) 232| 1| } 233| | 234| | /// SwifterSwift: Invert the receiver. 235| | /// 236| | /// Returns the original matrix if the receiver has no inverse. 237| | @inlinable 238| 4| mutating func invert() { 239| 4| self = CATransform3DInvert(self) 240| 4| } 241| | 242| | /// SwifterSwift: Concatenate `transform` to the receiver. 243| | /// - Parameter t2: The transform to concatenate on to the receiver 244| | @inlinable 245| 3| mutating func concatenate(_ t2: CATransform3D) { 246| 3| self = CATransform3DConcat(self, t2) 247| 3| } 248| |} 249| | 250| |#if canImport(CoreGraphics) 251| | 252| |import CoreGraphics 253| | 254| |// MARK: - CGAffineTransform 255| | 256| |public extension CATransform3D { 257| | /// SwifterSwift: Returns true if the receiver can be represented exactly by an affine transform. 258| | @inlinable 259| 2| var isAffine: Bool { CATransform3DIsAffine(self) } 260| | 261| | /// SwifterSwift: Returns the affine transform represented by the receiver. 262| | /// 263| | /// If the receiver can not be represented exactly by an affine transform the returned value is undefined. 264| | @inlinable 265| 2| func affineTransform() -> CGAffineTransform { 266| 2| CATransform3DGetAffineTransform(self) 267| 2| } 268| |} 269| | 270| |#endif 271| | 272| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/CoreGraphics/CGAffineTransformExtensions.swift: 1| |// CGAffineTransformExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(CoreGraphics) 4| |import CoreGraphics 5| | 6| |#if canImport(QuartzCore) 7| | 8| |import QuartzCore 9| | 10| |// MARK: - Methods 11| | 12| |public extension CGAffineTransform { 13| | /// SwifterSwift: Returns a transform with the same effect as the receiver. 14| | @inlinable 15| 4| func transform3D() -> CATransform3D { CATransform3DMakeAffineTransform(self) } 16| |} 17| | 18| |#endif 19| | 20| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/CoreGraphics/CGColorExtensions.swift: 1| |// CGColorExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(CoreGraphics) 4| |import CoreGraphics 5| | 6| |#if canImport(UIKit) 7| |import UIKit 8| |#endif 9| | 10| |#if canImport(AppKit) 11| |import AppKit 12| |#endif 13| | 14| |// MARK: - Properties 15| | 16| |public extension CGColor { 17| | #if canImport(UIKit) 18| | /// SwifterSwift: UIColor. 19| 2| var uiColor: UIColor? { 20| 2| return UIColor(cgColor: self) 21| 2| } 22| | #endif 23| | 24| | #if canImport(AppKit) && !targetEnvironment(macCatalyst) 25| | /// SwifterSwift: NSColor. 26| | var nsColor: NSColor? { 27| | return NSColor(cgColor: self) 28| | } 29| | #endif 30| |} 31| | 32| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/CoreGraphics/CGFloatExtensions.swift: 1| |// CGFloatExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(CoreGraphics) 4| |import CoreGraphics 5| | 6| |#if canImport(Foundation) 7| |import Foundation 8| |#endif 9| | 10| |// MARK: - Properties 11| | 12| |public extension CGFloat { 13| | /// SwifterSwift: Absolute of CGFloat value. 14| 1| var abs: CGFloat { 15| 1| return Swift.abs(self) 16| 1| } 17| | 18| | #if canImport(Foundation) 19| | /// SwifterSwift: Ceil of CGFloat value. 20| 1| var ceil: CGFloat { 21| 1| return Foundation.ceil(self) 22| 1| } 23| | #endif 24| | 25| | /// SwifterSwift: Radian value of degree input. 26| 1| var degreesToRadians: CGFloat { 27| 1| return .pi * self / 180.0 28| 1| } 29| | 30| | #if canImport(Foundation) 31| | /// SwifterSwift: Floor of CGFloat value. 32| 1| var floor: CGFloat { 33| 1| return Foundation.floor(self) 34| 1| } 35| | #endif 36| | 37| | /// SwifterSwift: Check if CGFloat is positive. 38| 3| var isPositive: Bool { 39| 3| return self > 0 40| 3| } 41| | 42| | /// SwifterSwift: Check if CGFloat is negative. 43| 3| var isNegative: Bool { 44| 3| return self < 0 45| 3| } 46| | 47| | /// SwifterSwift: Int. 48| 1| var int: Int { 49| 1| return Int(self) 50| 1| } 51| | 52| | /// SwifterSwift: Float. 53| 1| var float: Float { 54| 1| return Float(self) 55| 1| } 56| | 57| | /// SwifterSwift: Double. 58| 1| var double: Double { 59| 1| return Double(self) 60| 1| } 61| | 62| | /// SwifterSwift: Degree value of radian input. 63| 1| var radiansToDegrees: CGFloat { 64| 1| return self * 180 / CGFloat.pi 65| 1| } 66| |} 67| | 68| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/CoreGraphics/CGPointExtensions.swift: 1| |// CGPointExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(CoreGraphics) 4| |import CoreGraphics 5| | 6| |// MARK: - Methods 7| | 8| |public extension CGPoint { 9| | /// SwifterSwift: Distance from another CGPoint. 10| | /// 11| | /// let point1 = CGPoint(x: 10, y: 10) 12| | /// let point2 = CGPoint(x: 30, y: 30) 13| | /// let distance = point1.distance(from: point2) 14| | /// // distance = 28.28 15| | /// 16| | /// - Parameter point: CGPoint to get distance from. 17| | /// - Returns: Distance between self and given CGPoint. 18| 1| func distance(from point: CGPoint) -> CGFloat { 19| 1| return CGPoint.distance(from: self, to: point) 20| 1| } 21| | 22| | /// SwifterSwift: Distance between two CGPoints. 23| | /// 24| | /// let point1 = CGPoint(x: 10, y: 10) 25| | /// let point2 = CGPoint(x: 30, y: 30) 26| | /// let distance = CGPoint.distance(from: point2, to: point1) 27| | /// // distance = 28.28 28| | /// 29| | /// - Parameters: 30| | /// - point1: first CGPoint. 31| | /// - point2: second CGPoint. 32| | /// - Returns: distance between the two given CGPoints. 33| 2| static func distance(from point1: CGPoint, to point2: CGPoint) -> CGFloat { 34| 2| // http://stackoverflow.com/questions/6416101/calculate-the-distance-between-two-cgpoints 35| 2| return sqrt(pow(point2.x - point1.x, 2) + pow(point2.y - point1.y, 2)) 36| 2| } 37| |} 38| | 39| |// MARK: - Operators 40| | 41| |public extension CGPoint { 42| | /// SwifterSwift: Add two CGPoints. 43| | /// 44| | /// let point1 = CGPoint(x: 10, y: 10) 45| | /// let point2 = CGPoint(x: 30, y: 30) 46| | /// let point = point1 + point2 47| | /// // point = CGPoint(x: 40, y: 40) 48| | /// 49| | /// - Parameters: 50| | /// - lhs: CGPoint to add to. 51| | /// - rhs: CGPoint to add. 52| | /// - Returns: result of addition of the two given CGPoints. 53| 1| static func + (lhs: CGPoint, rhs: CGPoint) -> CGPoint { 54| 1| return CGPoint(x: lhs.x + rhs.x, y: lhs.y + rhs.y) 55| 1| } 56| | 57| | /// SwifterSwift: Add a CGPoints to self. 58| | /// 59| | /// let point1 = CGPoint(x: 10, y: 10) 60| | /// let point2 = CGPoint(x: 30, y: 30) 61| | /// point1 += point2 62| | /// // point1 = CGPoint(x: 40, y: 40) 63| | /// 64| | /// - Parameters: 65| | /// - lhs: self 66| | /// - rhs: CGPoint to add. 67| 1| static func += (lhs: inout CGPoint, rhs: CGPoint) { 68| 1| lhs.x += rhs.x 69| 1| lhs.y += rhs.y 70| 1| } 71| | 72| | /// SwifterSwift: Subtract two CGPoints. 73| | /// 74| | /// let point1 = CGPoint(x: 10, y: 10) 75| | /// let point2 = CGPoint(x: 30, y: 30) 76| | /// let point = point1 - point2 77| | /// // point = CGPoint(x: -20, y: -20) 78| | /// 79| | /// - Parameters: 80| | /// - lhs: CGPoint to subtract from. 81| | /// - rhs: CGPoint to subtract. 82| | /// - Returns: result of subtract of the two given CGPoints. 83| 1| static func - (lhs: CGPoint, rhs: CGPoint) -> CGPoint { 84| 1| return CGPoint(x: lhs.x - rhs.x, y: lhs.y - rhs.y) 85| 1| } 86| | 87| | /// SwifterSwift: Subtract a CGPoints from self. 88| | /// 89| | /// let point1 = CGPoint(x: 10, y: 10) 90| | /// let point2 = CGPoint(x: 30, y: 30) 91| | /// point1 -= point2 92| | /// // point1 = CGPoint(x: -20, y: -20) 93| | /// 94| | /// - Parameters: 95| | /// - lhs: self 96| | /// - rhs: CGPoint to subtract. 97| 1| static func -= (lhs: inout CGPoint, rhs: CGPoint) { 98| 1| lhs.x -= rhs.x 99| 1| lhs.y -= rhs.y 100| 1| } 101| | 102| | /// SwifterSwift: Multiply a CGPoint with a scalar 103| | /// 104| | /// let point1 = CGPoint(x: 10, y: 10) 105| | /// let scalar = point1 * 5 106| | /// // scalar = CGPoint(x: 50, y: 50) 107| | /// 108| | /// - Parameters: 109| | /// - point: CGPoint to multiply. 110| | /// - scalar: scalar value. 111| | /// - Returns: result of multiplication of the given CGPoint with the scalar. 112| 0| static func * (point: CGPoint, scalar: CGFloat) -> CGPoint { 113| 0| return CGPoint(x: point.x * scalar, y: point.y * scalar) 114| 0| } 115| | 116| | /// SwifterSwift: Multiply self with a scalar 117| | /// 118| | /// let point1 = CGPoint(x: 10, y: 10) 119| | /// point *= 5 120| | /// // point1 = CGPoint(x: 50, y: 50) 121| | /// 122| | /// - Parameters: 123| | /// - point: self. 124| | /// - scalar: scalar value. 125| | /// - Returns: result of multiplication of the given CGPoint with the scalar. 126| 1| static func *= (point: inout CGPoint, scalar: CGFloat) { 127| 1| point.x *= scalar 128| 1| point.y *= scalar 129| 1| } 130| | 131| | /// SwifterSwift: Multiply a CGPoint with a scalar 132| | /// 133| | /// let point1 = CGPoint(x: 10, y: 10) 134| | /// let scalar = 5 * point1 135| | /// // scalar = CGPoint(x: 50, y: 50) 136| | /// 137| | /// - Parameters: 138| | /// - scalar: scalar value. 139| | /// - point: CGPoint to multiply. 140| | /// - Returns: result of multiplication of the given CGPoint with the scalar. 141| 2| static func * (scalar: CGFloat, point: CGPoint) -> CGPoint { 142| 2| return CGPoint(x: point.x * scalar, y: point.y * scalar) 143| 2| } 144| |} 145| | 146| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/CoreGraphics/CGRectExtensions.swift: 1| |// CGRectExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(CoreGraphics) 4| |import CoreGraphics 5| | 6| |// MARK: - Properties 7| | 8| |public extension CGRect { 9| | /// SwifterSwift: Return center of rect 10| 1| var center: CGPoint { CGPoint(x: midX, y: midY) } 11| |} 12| | 13| |// MARK: - Initializers 14| | 15| |public extension CGRect { 16| | /// SwifterSwift: Create a `CGRect` instance with center and size 17| | /// - Parameters: 18| | /// - center: center of the new rect 19| | /// - size: size of the new rect 20| 1| init(center: CGPoint, size: CGSize) { 21| 1| let origin = CGPoint(x: center.x - size.width / 2.0, y: center.y - size.height / 2.0) 22| 1| self.init(origin: origin, size: size) 23| 1| } 24| |} 25| | 26| |// MARK: - Methods 27| | 28| |public extension CGRect { 29| | /// SwifterSwift: Create a new `CGRect` by resizing with specified anchor 30| | /// - Parameters: 31| | /// - size: new size to be applied 32| | /// - anchor: specified anchor, a point in normalized coordinates - 33| | /// '(0, 0)' is the top left corner of rect,'(1, 1)' is the bottom right corner of rect, 34| | /// defaults to '(0.5, 0.5)'. excample: 35| | /// 36| | /// anchor = CGPoint(x: 0.0, y: 1.0): 37| | /// 38| | /// A2------B2 39| | /// A----B | | 40| | /// | | --> | | 41| | /// C----D C-------D2 42| | /// 43| 6| func resizing(to size: CGSize, anchor: CGPoint = CGPoint(x: 0.5, y: 0.5)) -> CGRect { 44| 6| let sizeDelta = CGSize(width: size.width - width, height: size.height - height) 45| 6| return CGRect(origin: CGPoint(x: minX - sizeDelta.width * anchor.x, 46| 6| y: minY - sizeDelta.height * anchor.y), 47| 6| size: size) 48| 6| } 49| |} 50| | 51| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/CoreGraphics/CGSizeExtensions.swift: 1| |// CGSizeExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(CoreGraphics) 4| |import CoreGraphics 5| | 6| |// MARK: - Methods 7| | 8| |public extension CGSize { 9| | /// SwifterSwift: Returns the aspect ratio. 10| 2| var aspectRatio: CGFloat { 11| 2| guard height != 0 else { return 0 } 12| 1| return width / height 13| 2| } 14| | 15| | /// SwifterSwift: Returns width or height, whichever is the bigger value. 16| 2| var maxDimension: CGFloat { 17| 2| return max(width, height) 18| 2| } 19| | 20| | /// SwifterSwift: Returns width or height, whichever is the smaller value. 21| 2| var minDimension: CGFloat { 22| 2| return min(width, height) 23| 2| } 24| |} 25| | 26| |// MARK: - Methods 27| | 28| |public extension CGSize { 29| | /// SwifterSwift: Aspect fit CGSize. 30| | /// 31| | /// let rect = CGSize(width: 120, height: 80) 32| | /// let parentRect = CGSize(width: 100, height: 50) 33| | /// let newRect = rect.aspectFit(to: parentRect) 34| | /// // newRect.width = 75 , newRect = 50 35| | /// 36| | /// - Parameter boundingSize: bounding size to fit self to. 37| | /// - Returns: self fitted into given bounding size 38| 1| func aspectFit(to boundingSize: CGSize) -> CGSize { 39| 1| let minRatio = min(boundingSize.width / width, boundingSize.height / height) 40| 1| return CGSize(width: width * minRatio, height: height * minRatio) 41| 1| } 42| | 43| | /// SwifterSwift: Aspect fill CGSize. 44| | /// 45| | /// let rect = CGSize(width: 20, height: 120) 46| | /// let parentRect = CGSize(width: 100, height: 60) 47| | /// let newRect = rect.aspectFit(to: parentRect) 48| | /// // newRect.width = 100 , newRect = 60 49| | /// 50| | /// - Parameter boundingSize: bounding size to fill self to. 51| | /// - Returns: self filled into given bounding size 52| 1| func aspectFill(to boundingSize: CGSize) -> CGSize { 53| 1| let minRatio = max(boundingSize.width / width, boundingSize.height / height) 54| 1| let aWidth = min(width * minRatio, boundingSize.width) 55| 1| let aHeight = min(height * minRatio, boundingSize.height) 56| 1| return CGSize(width: aWidth, height: aHeight) 57| 1| } 58| |} 59| | 60| |// MARK: - Operators 61| | 62| |public extension CGSize { 63| | /// SwifterSwift: Add two CGSize 64| | /// 65| | /// let sizeA = CGSize(width: 5, height: 10) 66| | /// let sizeB = CGSize(width: 3, height: 4) 67| | /// let result = sizeA + sizeB 68| | /// // result = CGSize(width: 8, height: 14) 69| | /// 70| | /// - Parameters: 71| | /// - lhs: CGSize to add to. 72| | /// - rhs: CGSize to add. 73| | /// - Returns: The result comes from the addition of the two given CGSize struct. 74| 1| static func + (lhs: CGSize, rhs: CGSize) -> CGSize { 75| 1| return CGSize(width: lhs.width + rhs.width, height: lhs.height + rhs.height) 76| 1| } 77| | 78| | /// SwifterSwift: Add a tuple to CGSize. 79| | /// 80| | /// let sizeA = CGSize(width: 5, height: 10) 81| | /// let result = sizeA + (5, 4) 82| | /// // result = CGSize(width: 10, height: 14) 83| | /// 84| | /// - Parameters: 85| | /// - lhs: CGSize to add to. 86| | /// - tuple: tuple value. 87| | /// - Returns: The result comes from the addition of the given CGSize and tuple. 88| 1| static func + (lhs: CGSize, tuple: (width: CGFloat, height: CGFloat)) -> CGSize { 89| 1| return CGSize(width: lhs.width + tuple.width, height: lhs.height + tuple.height) 90| 1| } 91| | 92| | /// SwifterSwift: Add a CGSize to self. 93| | /// 94| | /// var sizeA = CGSize(width: 5, height: 10) 95| | /// let sizeB = CGSize(width: 3, height: 4) 96| | /// sizeA += sizeB 97| | /// // sizeA = CGPoint(width: 8, height: 14) 98| | /// 99| | /// - Parameters: 100| | /// - lhs: self 101| | /// - rhs: CGSize to add. 102| 1| static func += (lhs: inout CGSize, rhs: CGSize) { 103| 1| lhs.width += rhs.width 104| 1| lhs.height += rhs.height 105| 1| } 106| | 107| | /// SwifterSwift: Add a tuple to self. 108| | /// 109| | /// var sizeA = CGSize(width: 5, height: 10) 110| | /// sizeA += (3, 4) 111| | /// // result = CGSize(width: 8, height: 14) 112| | /// 113| | /// - Parameters: 114| | /// - lhs: self. 115| | /// - tuple: tuple value. 116| 1| static func += (lhs: inout CGSize, tuple: (width: CGFloat, height: CGFloat)) { 117| 1| lhs.width += tuple.width 118| 1| lhs.height += tuple.height 119| 1| } 120| | 121| | /// SwifterSwift: Subtract two CGSize 122| | /// 123| | /// let sizeA = CGSize(width: 5, height: 10) 124| | /// let sizeB = CGSize(width: 3, height: 4) 125| | /// let result = sizeA - sizeB 126| | /// // result = CGSize(width: 2, height: 6) 127| | /// 128| | /// - Parameters: 129| | /// - lhs: CGSize to subtract from. 130| | /// - rhs: CGSize to subtract. 131| | /// - Returns: The result comes from the subtract of the two given CGSize struct. 132| 1| static func - (lhs: CGSize, rhs: CGSize) -> CGSize { 133| 1| return CGSize(width: lhs.width - rhs.width, height: lhs.height - rhs.height) 134| 1| } 135| | 136| | /// SwifterSwift: Subtract a tuple from CGSize. 137| | /// 138| | /// let sizeA = CGSize(width: 5, height: 10) 139| | /// let result = sizeA - (3, 2) 140| | /// // result = CGSize(width: 2, height: 8) 141| | /// 142| | /// - Parameters: 143| | /// - lhs: CGSize to subtract from. 144| | /// - tuple: tuple value. 145| | /// - Returns: The result comes from the subtract of the given CGSize and tuple. 146| 1| static func - (lhs: CGSize, tuple: (width: CGFloat, heoght: CGFloat)) -> CGSize { 147| 1| return CGSize(width: lhs.width - tuple.width, height: lhs.height - tuple.heoght) 148| 1| } 149| | 150| | /// SwifterSwift: Subtract a CGSize from self. 151| | /// 152| | /// var sizeA = CGSize(width: 5, height: 10) 153| | /// let sizeB = CGSize(width: 3, height: 4) 154| | /// sizeA -= sizeB 155| | /// // sizeA = CGPoint(width: 2, height: 6) 156| | /// 157| | /// - Parameters: 158| | /// - lhs: self 159| | /// - rhs: CGSize to subtract. 160| 1| static func -= (lhs: inout CGSize, rhs: CGSize) { 161| 1| lhs.width -= rhs.width 162| 1| lhs.height -= rhs.height 163| 1| } 164| | 165| | /// SwifterSwift: Subtract a tuple from self. 166| | /// 167| | /// var sizeA = CGSize(width: 5, height: 10) 168| | /// sizeA -= (2, 4) 169| | /// // result = CGSize(width: 3, height: 6) 170| | /// 171| | /// - Parameters: 172| | /// - lhs: self. 173| | /// - tuple: tuple value. 174| 1| static func -= (lhs: inout CGSize, tuple: (width: CGFloat, height: CGFloat)) { 175| 1| lhs.width -= tuple.width 176| 1| lhs.height -= tuple.height 177| 1| } 178| | 179| | /// SwifterSwift: Multiply two CGSize 180| | /// 181| | /// let sizeA = CGSize(width: 5, height: 10) 182| | /// let sizeB = CGSize(width: 3, height: 4) 183| | /// let result = sizeA * sizeB 184| | /// // result = CGSize(width: 15, height: 40) 185| | /// 186| | /// - Parameters: 187| | /// - lhs: CGSize to multiply. 188| | /// - rhs: CGSize to multiply with. 189| | /// - Returns: The result comes from the multiplication of the two given CGSize structs. 190| 1| static func * (lhs: CGSize, rhs: CGSize) -> CGSize { 191| 1| return CGSize(width: lhs.width * rhs.width, height: lhs.height * rhs.height) 192| 1| } 193| | 194| | /// SwifterSwift: Multiply a CGSize with a scalar. 195| | /// 196| | /// let sizeA = CGSize(width: 5, height: 10) 197| | /// let result = sizeA * 5 198| | /// // result = CGSize(width: 25, height: 50) 199| | /// 200| | /// - Parameters: 201| | /// - lhs: CGSize to multiply. 202| | /// - scalar: scalar value. 203| | /// - Returns: The result comes from the multiplication of the given CGSize and scalar. 204| 1| static func * (lhs: CGSize, scalar: CGFloat) -> CGSize { 205| 1| return CGSize(width: lhs.width * scalar, height: lhs.height * scalar) 206| 1| } 207| | 208| | /// SwifterSwift: Multiply a CGSize with a scalar. 209| | /// 210| | /// let sizeA = CGSize(width: 5, height: 10) 211| | /// let result = 5 * sizeA 212| | /// // result = CGSize(width: 25, height: 50) 213| | /// 214| | /// - Parameters: 215| | /// - scalar: scalar value. 216| | /// - rhs: CGSize to multiply. 217| | /// - Returns: The result comes from the multiplication of the given scalar and CGSize. 218| 1| static func * (scalar: CGFloat, rhs: CGSize) -> CGSize { 219| 1| return CGSize(width: scalar * rhs.width, height: scalar * rhs.height) 220| 1| } 221| | 222| | /// SwifterSwift: Multiply self with a CGSize. 223| | /// 224| | /// var sizeA = CGSize(width: 5, height: 10) 225| | /// let sizeB = CGSize(width: 3, height: 4) 226| | /// sizeA *= sizeB 227| | /// // result = CGSize(width: 15, height: 40) 228| | /// 229| | /// - Parameters: 230| | /// - lhs: self. 231| | /// - rhs: CGSize to multiply. 232| 1| static func *= (lhs: inout CGSize, rhs: CGSize) { 233| 1| lhs.width *= rhs.width 234| 1| lhs.height *= rhs.height 235| 1| } 236| | 237| | /// SwifterSwift: Multiply self with a scalar. 238| | /// 239| | /// var sizeA = CGSize(width: 5, height: 10) 240| | /// sizeA *= 3 241| | /// // result = CGSize(width: 15, height: 30) 242| | /// 243| | /// - Parameters: 244| | /// - lhs: self. 245| | /// - scalar: scalar value. 246| 1| static func *= (lhs: inout CGSize, scalar: CGFloat) { 247| 1| lhs.width *= scalar 248| 1| lhs.height *= scalar 249| 1| } 250| |} 251| | 252| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/CoreGraphics/CGVectorExtensions.swift: 1| |// CGVectorExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(CoreGraphics) 4| |import CoreGraphics 5| | 6| |// MARK: - Properties 7| | 8| |public extension CGVector { 9| | /// 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. 10| | /// 11| | /// https://en.wikipedia.org/wiki/Atan2 12| 8| var angle: CGFloat { 13| 8| return atan2(dy, dx) 14| 8| } 15| | 16| | /// SwifterSwift: The magnitude (or length) of the vector. 17| | /// 18| | /// https://en.wikipedia.org/wiki/Euclidean_vector#Length 19| 5| var magnitude: CGFloat { 20| 5| return sqrt((dx * dx) + (dy * dy)) 21| 5| } 22| |} 23| | 24| |// MARK: - Initializers 25| | 26| |public extension CGVector { 27| | /// SwifterSwift: Creates a vector with the given magnitude and angle. 28| | /// 29| | /// let vector = CGVector(angle: .pi, magnitude: 1) 30| | /// 31| | /// - Parameters: 32| | /// - angle: The angle of rotation (in radians) counterclockwise from the positive x-axis. 33| | /// - magnitude: The lenth of the vector. 34| | /// 35| 4| init(angle: CGFloat, magnitude: CGFloat) { 36| 4| // https://www.grc.nasa.gov/WWW/K-12/airplane/vectpart.html 37| 4| self.init(dx: magnitude * cos(angle), dy: magnitude * sin(angle)) 38| 4| } 39| |} 40| | 41| |// MARK: - Operators 42| | 43| |public extension CGVector { 44| | /// SwifterSwift: Multiplies a scalar and a vector (commutative). 45| | /// 46| | /// let vector = CGVector(dx: 1, dy: 1) 47| | /// let largerVector = vector * 2 48| | /// 49| | /// - Parameters: 50| | /// - vector: The vector to be multiplied 51| | /// - scalar: The scale by which the vector will be multiplied 52| | /// - Returns: The vector with its magnitude scaled 53| 2| static func * (vector: CGVector, scalar: CGFloat) -> CGVector { 54| 2| return CGVector(dx: vector.dx * scalar, dy: vector.dy * scalar) 55| 2| } 56| | 57| | /// SwifterSwift: Multiplies a scalar and a vector (commutative). 58| | /// 59| | /// let vector = CGVector(dx: 1, dy: 1) 60| | /// let largerVector = 2 * vector 61| | /// 62| | /// - Parameters: 63| | /// - scalar: The scalar by which the vector will be multiplied 64| | /// - vector: The vector to be multiplied 65| | /// - Returns: The vector with its magnitude scaled 66| 5| static func * (scalar: CGFloat, vector: CGVector) -> CGVector { 67| 5| return CGVector(dx: scalar * vector.dx, dy: scalar * vector.dy) 68| 5| } 69| | 70| | /// SwifterSwift: Compound assignment operator for vector-scalr multiplication 71| | /// 72| | /// var vector = CGVector(dx: 1, dy: 1) 73| | /// vector *= 2 74| | /// 75| | /// - Parameters: 76| | /// - vector: The vector to be multiplied 77| | /// - scalar: The scale by which the vector will be multiplied 78| 1| static func *= (vector: inout CGVector, scalar: CGFloat) { 79| 1| vector.dx *= scalar 80| 1| vector.dy *= scalar 81| 1| } 82| | 83| | /// SwifterSwift: Negates the vector. The direction is reversed, but magnitude remains the same. 84| | /// 85| | /// let vector = CGVector(dx: 1, dy: 1) 86| | /// let reversedVector = -vector 87| | /// 88| | /// - Parameter vector: The vector to be negated 89| | /// - Returns: The negated vector 90| 1| static prefix func - (vector: CGVector) -> CGVector { 91| 1| return CGVector(dx: -vector.dx, dy: -vector.dy) 92| 1| } 93| |} 94| | 95| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/CoreLocation/CLLocationArrayExtensions.swift: 1| |// CLLocationArrayExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(CoreLocation) 4| |import CoreLocation 5| | 6| |// MARK: - Methods 7| | 8| |public extension Array where Element: CLLocation { 9| | /// SwifterSwift: Calculates the sum of distances between each location in the array based on the curvature of the earth. 10| | /// 11| | /// - Parameter unitLength: The unit of length to return the distance in. 12| | /// - Returns: The distance in the specified unit. 13| | @available(tvOS 10.0, macOS 10.12, watchOS 3.0, *) 14| 3| func distance(unitLength unit: UnitLength) -> Measurement { 15| 3| guard count > 1 else { 16| 2| return Measurement(value: 0.0, unit: unit) 17| 2| } 18| 1| var distance: CLLocationDistance = 0.0 19| 2| for idx in 0.. CLLocation { 16| 3| let lat1 = Double.pi * start.coordinate.latitude / 180.0 17| 3| let long1 = Double.pi * start.coordinate.longitude / 180.0 18| 3| let lat2 = Double.pi * end.coordinate.latitude / 180.0 19| 3| let long2 = Double.pi * end.coordinate.longitude / 180.0 20| 3| 21| 3| // Formula 22| 3| // Bx = cos φ2 ⋅ cos Δλ 23| 3| // By = cos φ2 ⋅ sin Δλ 24| 3| // φm = atan2( sin φ1 + sin φ2, √(cos φ1 + Bx)² + By² ) 25| 3| // λm = λ1 + atan2(By, cos(φ1)+Bx) 26| 3| // Source: http://www.movable-type.co.uk/scripts/latlong.html 27| 3| 28| 3| let bxLoc = cos(lat2) * cos(long2 - long1) 29| 3| let byLoc = cos(lat2) * sin(long2 - long1) 30| 3| let mlat = atan2(sin(lat1) + sin(lat2), sqrt((cos(lat1) + bxLoc) * (cos(lat1) + bxLoc) + (byLoc * byLoc))) 31| 3| let mlong = long1 + atan2(byLoc, cos(lat1) + bxLoc) 32| 3| 33| 3| return CLLocation(latitude: mlat * 180 / Double.pi, longitude: mlong * 180 / Double.pi) 34| 3| } 35| | 36| | /// SwifterSwift: Calculate the half-way point along a great circle path between self and another points. 37| | /// 38| | /// - Parameter point: End location. 39| | /// - Returns: Location that represents the half-way point. 40| 2| func midLocation(to point: CLLocation) -> CLLocation { 41| 2| return CLLocation.midLocation(start: self, end: point) 42| 2| } 43| | 44| | /// SwifterSwift: Calculates the bearing to another CLLocation. 45| | /// 46| | /// - Parameters: 47| | /// - destination: Location to calculate bearing. 48| | /// - Returns: Calculated bearing degrees in the range 0° ... 360° 49| 1| func bearing(to destination: CLLocation) -> Double { 50| 1| // http://stackoverflow.com/questions/3925942/cllocation-category-for-calculating-bearing-w-haversine-function 51| 1| let lat1 = Double.pi * coordinate.latitude / 180.0 52| 1| let long1 = Double.pi * coordinate.longitude / 180.0 53| 1| let lat2 = Double.pi * destination.coordinate.latitude / 180.0 54| 1| let long2 = Double.pi * destination.coordinate.longitude / 180.0 55| 1| 56| 1| // Formula: θ = atan2( sin Δλ ⋅ cos φ2 , cos φ1 ⋅ sin φ2 − sin φ1 ⋅ cos φ2 ⋅ cos Δλ ) 57| 1| // Source: http://www.movable-type.co.uk/scripts/latlong.html 58| 1| 59| 1| let rads = atan2( 60| 1| sin(long2 - long1) * cos(lat2), 61| 1| cos(lat1) * sin(lat2) - sin(lat1) * cos(lat2) * cos(long2 - long1)) 62| 1| let degrees = rads * 180 / Double.pi 63| 1| 64| 1| return (degrees + 360).truncatingRemainder(dividingBy: 360) 65| 1| } 66| |} 67| | 68| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/Dispatch/DispatchQueueExtensions.swift: 1| |// DispatchQueueExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(Dispatch) 4| |import Dispatch 5| | 6| |// MARK: - Properties 7| | 8| |public extension DispatchQueue { 9| | /// SwifterSwift: A Boolean value indicating whether the current dispatch queue is the main queue. 10| 2| static var isMainQueue: Bool { 11| 2| enum Static { 12| 2| static var key: DispatchSpecificKey = { 13| 1| let key = DispatchSpecificKey() 14| 1| DispatchQueue.main.setSpecific(key: key, value: ()) 15| 1| return key 16| 1| }() 17| 2| } 18| 2| return DispatchQueue.getSpecific(key: Static.key) != nil 19| 2| } 20| |} 21| | 22| |// MARK: - Methods 23| | 24| |public extension DispatchQueue { 25| | /// SwifterSwift: Returns a Boolean value indicating whether the current dispatch queue is the specified queue. 26| | /// 27| | /// - Parameter queue: The queue to compare against. 28| | /// - Returns: `true` if the current queue is the specified queue, otherwise `false`. 29| 3| static func isCurrent(_ queue: DispatchQueue) -> Bool { 30| 3| let key = DispatchSpecificKey() 31| 3| 32| 3| queue.setSpecific(key: key, value: ()) 33| 3| defer { queue.setSpecific(key: key, value: nil) } 34| 3| 35| 3| return DispatchQueue.getSpecific(key: key) != nil 36| 3| } 37| | 38| | /// SwifterSwift: Runs passed closure asynchronous after certain time interval 39| | /// 40| | /// - Parameters: 41| | /// - delay: The time inverval after which the closure will run. 42| | /// - qos: Quality of service at which the work item should be executed. 43| | /// - flags: Flags that control the execution environment of the work item. 44| | /// - work: The closure to run after certain time interval. 45| | func asyncAfter(delay: Double, 46| | qos: DispatchQoS = .unspecified, 47| | flags: DispatchWorkItemFlags = [], 48| 1| execute work: @escaping () -> Void) { 49| 1| asyncAfter(deadline: .now() + delay, qos: qos, flags: flags, execute: work) 50| 1| } 51| | 52| 1| func debounce(delay: Double, action: @escaping () -> Void) -> () -> Void { 53| 1| // http://stackoverflow.com/questions/27116684/how-can-i-debounce-a-method-call 54| 1| var lastFireTime = DispatchTime.now() 55| 20| let deadline = { lastFireTime + delay } 56| 10| return { 57| 10| self.asyncAfter(deadline: deadline()) { 58| 10| let now = DispatchTime.now() 59| 10| if now >= deadline() { 60| 1| lastFireTime = now 61| 1| action() 62| 10| } 63| 10| } 64| 10| } 65| 1| } 66| |} 67| | 68| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/Foundation/CalendarExtensions.swift: 1| |// CalendarExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(Foundation) 4| |import Foundation 5| | 6| |// MARK: - Methods 7| | 8| |public extension Calendar { 9| | /// SwifterSwift: Return the number of days in the month for a specified 'Date'. 10| | /// 11| | /// let date = Date() // "Jan 12, 2017, 7:07 PM" 12| | /// Calendar.current.numberOfDaysInMonth(for: date) -> 31 13| | /// 14| | /// - Parameter date: the date form which the number of days in month is calculated. 15| | /// - Returns: The number of days in the month of 'Date'. 16| 13| func numberOfDaysInMonth(for date: Date) -> Int { 17| 13| return range(of: .day, in: .month, for: date)!.count 18| 13| } 19| |} 20| | 21| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/Foundation/DataExtensions.swift: 1| |// DataExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(Foundation) 4| |import Foundation 5| | 6| |// MARK: - Properties 7| | 8| |public extension Data { 9| | /// SwifterSwift: Return data as an array of bytes. 10| 1| var bytes: [UInt8] { 11| 1| // http://stackoverflow.com/questions/38097710/swift-3-changes-for-getbytes-method 12| 1| return [UInt8](self) 13| 1| } 14| |} 15| | 16| |// MARK: - Methods 17| | 18| |public extension Data { 19| | /// SwifterSwift: String by encoding Data using the given encoding (if applicable). 20| | /// 21| | /// - Parameter encoding: encoding. 22| | /// - Returns: String by encoding Data using the given encoding (if applicable). 23| 2| func string(encoding: String.Encoding) -> String? { 24| 2| return String(data: self, encoding: encoding) 25| 2| } 26| | 27| | /// SwifterSwift: Returns a Foundation object from given JSON data. 28| | /// 29| | /// - Parameter options: Options for reading the JSON data and creating the Foundation object. 30| | /// 31| | /// For possible values, see `JSONSerialization.ReadingOptions`. 32| | /// - Returns: A Foundation object from the JSON data in the receiver, or `nil` if an error occurs. 33| | /// - Throws: An `NSError` if the receiver does not represent a valid JSON object. 34| 6| func jsonObject(options: JSONSerialization.ReadingOptions = []) throws -> Any { 35| 6| return try JSONSerialization.jsonObject(with: self, options: options) 36| 6| } 37| |} 38| | 39| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/Foundation/DateExtensions.swift: 1| |// DateExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(Foundation) 4| |import Foundation 5| | 6| |#if os(macOS) || os(iOS) 7| |import Darwin 8| |#elseif os(Linux) 9| |import Glibc 10| |#endif 11| | 12| |// MARK: - Enums 13| | 14| |public extension Date { 15| | /// SwifterSwift: Day name format. 16| | /// 17| | /// - threeLetters: 3 letter day abbreviation of day name. 18| | /// - oneLetter: 1 letter day abbreviation of day name. 19| | /// - full: Full day name. 20| | enum DayNameStyle { 21| | /// SwifterSwift: 3 letter day abbreviation of day name. 22| | case threeLetters 23| | 24| | /// SwifterSwift: 1 letter day abbreviation of day name. 25| | case oneLetter 26| | 27| | /// SwifterSwift: Full day name. 28| | case full 29| | } 30| | 31| | /// SwifterSwift: Month name format. 32| | /// 33| | /// - threeLetters: 3 letter month abbreviation of month name. 34| | /// - oneLetter: 1 letter month abbreviation of month name. 35| | /// - full: Full month name. 36| | enum MonthNameStyle { 37| | /// SwifterSwift: 3 letter month abbreviation of month name. 38| | case threeLetters 39| | 40| | /// SwifterSwift: 1 letter month abbreviation of month name. 41| | case oneLetter 42| | 43| | /// SwifterSwift: Full month name. 44| | case full 45| | } 46| |} 47| | 48| |// MARK: - Properties 49| | 50| |public extension Date { 51| | /// SwifterSwift: User’s current calendar. 52| 642| var calendar: Calendar { 53| 642| // Workaround to segfault on corelibs foundation https://bugs.swift.org/browse/SR-10147 54| 642| return Calendar(identifier: Calendar.current.identifier) 55| 642| } 56| | 57| | /// SwifterSwift: Era. 58| | /// 59| | /// Date().era -> 1 60| | /// 61| 5| var era: Int { 62| 5| return calendar.component(.era, from: self) 63| 5| } 64| | 65| | #if !os(Linux) 66| | /// SwifterSwift: Quarter. 67| | /// 68| | /// Date().quarter -> 3 // date in third quarter of the year. 69| | /// 70| 4| var quarter: Int { 71| 4| let month = Double(calendar.component(.month, from: self)) 72| 4| let numberOfMonths = Double(calendar.monthSymbols.count) 73| 4| let numberOfMonthsInQuarter = numberOfMonths / 4 74| 4| return Int(ceil(month / numberOfMonthsInQuarter)) 75| 4| } 76| | #endif 77| | 78| | /// SwifterSwift: Week of year. 79| | /// 80| | /// Date().weekOfYear -> 2 // second week in the year. 81| | /// 82| 3| var weekOfYear: Int { 83| 3| return calendar.component(.weekOfYear, from: self) 84| 3| } 85| | 86| | /// SwifterSwift: Week of month. 87| | /// 88| | /// Date().weekOfMonth -> 3 // date is in third week of the month. 89| | /// 90| 3| var weekOfMonth: Int { 91| 3| return calendar.component(.weekOfMonth, from: self) 92| 3| } 93| | 94| | /// SwifterSwift: Year. 95| | /// 96| | /// Date().year -> 2017 97| | /// 98| | /// var someDate = Date() 99| | /// someDate.year = 2000 // sets someDate's year to 2000 100| | /// 101| | var year: Int { 102| 15| get { 103| 15| return calendar.component(.year, from: self) 104| 15| } 105| 5| set { 106| 5| guard newValue > 0 else { return } 107| 3| let currentYear = calendar.component(.year, from: self) 108| 3| let yearsToAdd = newValue - currentYear 109| 3| if let date = calendar.date(byAdding: .year, value: yearsToAdd, to: self) { 110| 3| self = date 111| 3| } 112| 3| } 113| | } 114| | 115| | /// SwifterSwift: Month. 116| | /// 117| | /// Date().month -> 1 118| | /// 119| | /// var someDate = Date() 120| | /// someDate.month = 10 // sets someDate's month to 10. 121| | /// 122| | var month: Int { 123| 20| get { 124| 20| return calendar.component(.month, from: self) 125| 20| } 126| 6| set { 127| 6| let allowedRange = calendar.range(of: .month, in: .year, for: self)! 128| 6| guard allowedRange.contains(newValue) else { return } 129| 3| 130| 3| let currentMonth = calendar.component(.month, from: self) 131| 3| let monthsToAdd = newValue - currentMonth 132| 3| if let date = calendar.date(byAdding: .month, value: monthsToAdd, to: self) { 133| 3| self = date 134| 3| } 135| 3| } 136| | } 137| | 138| | /// SwifterSwift: Day. 139| | /// 140| | /// Date().day -> 12 141| | /// 142| | /// var someDate = Date() 143| | /// someDate.day = 1 // sets someDate's day of month to 1. 144| | /// 145| | var day: Int { 146| 38| get { 147| 38| return calendar.component(.day, from: self) 148| 38| } 149| 6| set { 150| 6| let allowedRange = calendar.range(of: .day, in: .month, for: self)! 151| 6| guard allowedRange.contains(newValue) else { return } 152| 3| 153| 3| let currentDay = calendar.component(.day, from: self) 154| 3| let daysToAdd = newValue - currentDay 155| 3| if let date = calendar.date(byAdding: .day, value: daysToAdd, to: self) { 156| 3| self = date 157| 3| } 158| 3| } 159| | } 160| | 161| | /// SwifterSwift: Weekday. 162| | /// 163| | /// Date().weekday -> 5 // fifth day in the current week. 164| | /// 165| 1| var weekday: Int { 166| 1| return calendar.component(.weekday, from: self) 167| 1| } 168| | 169| | /// SwifterSwift: Hour. 170| | /// 171| | /// Date().hour -> 17 // 5 pm 172| | /// 173| | /// var someDate = Date() 174| | /// someDate.hour = 13 // sets someDate's hour to 1 pm. 175| | /// 176| | var hour: Int { 177| 33| get { 178| 33| return calendar.component(.hour, from: self) 179| 33| } 180| 5| set { 181| 5| let allowedRange = calendar.range(of: .hour, in: .day, for: self)! 182| 5| guard allowedRange.contains(newValue) else { return } 183| 3| 184| 3| let currentHour = calendar.component(.hour, from: self) 185| 3| let hoursToAdd = newValue - currentHour 186| 3| if let date = calendar.date(byAdding: .hour, value: hoursToAdd, to: self) { 187| 3| self = date 188| 3| } 189| 3| } 190| | } 191| | 192| | /// SwifterSwift: Minutes. 193| | /// 194| | /// Date().minute -> 39 195| | /// 196| | /// var someDate = Date() 197| | /// someDate.minute = 10 // sets someDate's minutes to 10. 198| | /// 199| | var minute: Int { 200| 40| get { 201| 40| return calendar.component(.minute, from: self) 202| 40| } 203| 5| set { 204| 5| let allowedRange = calendar.range(of: .minute, in: .hour, for: self)! 205| 5| guard allowedRange.contains(newValue) else { return } 206| 3| 207| 3| let currentMinutes = calendar.component(.minute, from: self) 208| 3| let minutesToAdd = newValue - currentMinutes 209| 3| if let date = calendar.date(byAdding: .minute, value: minutesToAdd, to: self) { 210| 3| self = date 211| 3| } 212| 3| } 213| | } 214| | 215| | /// SwifterSwift: Seconds. 216| | /// 217| | /// Date().second -> 55 218| | /// 219| | /// var someDate = Date() 220| | /// someDate.second = 15 // sets someDate's seconds to 15. 221| | /// 222| | var second: Int { 223| 45| get { 224| 45| return calendar.component(.second, from: self) 225| 45| } 226| 5| set { 227| 5| let allowedRange = calendar.range(of: .second, in: .minute, for: self)! 228| 5| guard allowedRange.contains(newValue) else { return } 229| 3| 230| 3| let currentSeconds = calendar.component(.second, from: self) 231| 3| let secondsToAdd = newValue - currentSeconds 232| 3| if let date = calendar.date(byAdding: .second, value: secondsToAdd, to: self) { 233| 3| self = date 234| 3| } 235| 3| } 236| | } 237| | 238| | /// SwifterSwift: Nanoseconds. 239| | /// 240| | /// Date().nanosecond -> 981379985 241| | /// 242| | /// var someDate = Date() 243| | /// someDate.nanosecond = 981379985 // sets someDate's seconds to 981379985. 244| | /// 245| | var nanosecond: Int { 246| 37| get { 247| 37| return calendar.component(.nanosecond, from: self) 248| 37| } 249| 2| set { 250| 2| #if targetEnvironment(macCatalyst) 251| 2| // The `Calendar` implementation in `macCatalyst` does not know that a nanosecond is 1/1,000,000,000th of a second 252| 2| let allowedRange = 0..<1_000_000_000 253| 2| #else 254| 2| let allowedRange = calendar.range(of: .nanosecond, in: .second, for: self)! 255| 2| #endif 256| 2| guard allowedRange.contains(newValue) else { return } 257| 1| 258| 1| let currentNanoseconds = calendar.component(.nanosecond, from: self) 259| 1| let nanosecondsToAdd = newValue - currentNanoseconds 260| 1| 261| 1| if let date = calendar.date(byAdding: .nanosecond, value: nanosecondsToAdd, to: self) { 262| 1| self = date 263| 1| } 264| 1| } 265| | } 266| | 267| | /// SwifterSwift: Milliseconds. 268| | /// 269| | /// Date().millisecond -> 68 270| | /// 271| | /// var someDate = Date() 272| | /// someDate.millisecond = 68 // sets someDate's nanosecond to 68000000. 273| | /// 274| | var millisecond: Int { 275| 6| get { 276| 6| return calendar.component(.nanosecond, from: self) / 1_000_000 277| 6| } 278| 3| set { 279| 3| let nanoSeconds = newValue * 1_000_000 280| 3| #if targetEnvironment(macCatalyst) 281| 3| // The `Calendar` implementation in `macCatalyst` does not know that a nanosecond is 1/1,000,000,000th of a second 282| 3| let allowedRange = 0..<1_000_000_000 283| 3| #else 284| 3| let allowedRange = calendar.range(of: .nanosecond, in: .second, for: self)! 285| 3| #endif 286| 3| guard allowedRange.contains(nanoSeconds) else { return } 287| 2| 288| 2| if let date = calendar.date(bySetting: .nanosecond, value: nanoSeconds, of: self) { 289| 2| self = date 290| 2| } 291| 2| } 292| | } 293| | 294| | /// SwifterSwift: Check if date is in future. 295| | /// 296| | /// Date(timeInterval: 100, since: Date()).isInFuture -> true 297| | /// 298| 2| var isInFuture: Bool { 299| 2| return self > Date() 300| 2| } 301| | 302| | /// SwifterSwift: Check if date is in past. 303| | /// 304| | /// Date(timeInterval: -100, since: Date()).isInPast -> true 305| | /// 306| 2| var isInPast: Bool { 307| 2| return self < Date() 308| 2| } 309| | 310| | /// SwifterSwift: Check if date is within today. 311| | /// 312| | /// Date().isInToday -> true 313| | /// 314| 4| var isInToday: Bool { 315| 4| return calendar.isDateInToday(self) 316| 4| } 317| | 318| | /// SwifterSwift: Check if date is within yesterday. 319| | /// 320| | /// Date().isInYesterday -> false 321| | /// 322| 3| var isInYesterday: Bool { 323| 3| return calendar.isDateInYesterday(self) 324| 3| } 325| | 326| | /// SwifterSwift: Check if date is within tomorrow. 327| | /// 328| | /// Date().isInTomorrow -> false 329| | /// 330| 3| var isInTomorrow: Bool { 331| 3| return calendar.isDateInTomorrow(self) 332| 3| } 333| | 334| | /// SwifterSwift: Check if date is within a weekend period. 335| 1| var isInWeekend: Bool { 336| 1| return calendar.isDateInWeekend(self) 337| 1| } 338| | 339| | /// SwifterSwift: Check if date is within a weekday period. 340| 1| var isWorkday: Bool { 341| 1| return !calendar.isDateInWeekend(self) 342| 1| } 343| | 344| | /// SwifterSwift: Check if date is within the current week. 345| 2| var isInCurrentWeek: Bool { 346| 2| return calendar.isDate(self, equalTo: Date(), toGranularity: .weekOfYear) 347| 2| } 348| | 349| | /// SwifterSwift: Check if date is within the current month. 350| 2| var isInCurrentMonth: Bool { 351| 2| return calendar.isDate(self, equalTo: Date(), toGranularity: .month) 352| 2| } 353| | 354| | /// SwifterSwift: Check if date is within the current year. 355| 2| var isInCurrentYear: Bool { 356| 2| return calendar.isDate(self, equalTo: Date(), toGranularity: .year) 357| 2| } 358| | 359| | /// SwifterSwift: ISO8601 string of format (yyyy-MM-dd'T'HH:mm:ss.SSS) from date. 360| | /// 361| | /// Date().iso8601String -> "2017-01-12T14:51:29.574Z" 362| | /// 363| 1| var iso8601String: String { 364| 1| // https://github.com/justinmakaila/NSDate-ISO-8601/blob/master/NSDateISO8601.swift 365| 1| let dateFormatter = DateFormatter() 366| 1| dateFormatter.locale = Locale(identifier: "en_US_POSIX") 367| 1| dateFormatter.timeZone = TimeZone(abbreviation: "GMT") 368| 1| dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSS" 369| 1| 370| 1| return dateFormatter.string(from: self).appending("Z") 371| 1| } 372| | 373| | /// SwifterSwift: Nearest five minutes to date. 374| | /// 375| | /// var date = Date() // "5:54 PM" 376| | /// date.minute = 32 // "5:32 PM" 377| | /// date.nearestFiveMinutes // "5:30 PM" 378| | /// 379| | /// date.minute = 44 // "5:44 PM" 380| | /// date.nearestFiveMinutes // "5:45 PM" 381| | /// 382| 5| var nearestFiveMinutes: Date { 383| 5| var components = calendar.dateComponents( 384| 5| [.year, .month, .day, .hour, .minute, .second, .nanosecond], 385| 5| from: self) 386| 5| let min = components.minute! 387| 5| components.minute! = min % 5 < 3 ? min - min % 5 : min + 5 - (min % 5) 388| 5| components.second = 0 389| 5| components.nanosecond = 0 390| 5| return calendar.date(from: components)! 391| 5| } 392| | 393| | /// SwifterSwift: Nearest ten minutes to date. 394| | /// 395| | /// var date = Date() // "5:57 PM" 396| | /// date.minute = 34 // "5:34 PM" 397| | /// date.nearestTenMinutes // "5:30 PM" 398| | /// 399| | /// date.minute = 48 // "5:48 PM" 400| | /// date.nearestTenMinutes // "5:50 PM" 401| | /// 402| 4| var nearestTenMinutes: Date { 403| 4| var components = calendar.dateComponents( 404| 4| [.year, .month, .day, .hour, .minute, .second, .nanosecond], 405| 4| from: self) 406| 4| let min = components.minute! 407| 4| components.minute? = min % 10 < 6 ? min - min % 10 : min + 10 - (min % 10) 408| 4| components.second = 0 409| 4| components.nanosecond = 0 410| 4| return calendar.date(from: components)! 411| 4| } 412| | 413| | /// SwifterSwift: Nearest quarter hour to date. 414| | /// 415| | /// var date = Date() // "5:57 PM" 416| | /// date.minute = 34 // "5:34 PM" 417| | /// date.nearestQuarterHour // "5:30 PM" 418| | /// 419| | /// date.minute = 40 // "5:40 PM" 420| | /// date.nearestQuarterHour // "5:45 PM" 421| | /// 422| 4| var nearestQuarterHour: Date { 423| 4| var components = calendar.dateComponents( 424| 4| [.year, .month, .day, .hour, .minute, .second, .nanosecond], 425| 4| from: self) 426| 4| let min = components.minute! 427| 4| components.minute! = min % 15 < 8 ? min - min % 15 : min + 15 - (min % 15) 428| 4| components.second = 0 429| 4| components.nanosecond = 0 430| 4| return calendar.date(from: components)! 431| 4| } 432| | 433| | /// SwifterSwift: Nearest half hour to date. 434| | /// 435| | /// var date = Date() // "6:07 PM" 436| | /// date.minute = 41 // "6:41 PM" 437| | /// date.nearestHalfHour // "6:30 PM" 438| | /// 439| | /// date.minute = 51 // "6:51 PM" 440| | /// date.nearestHalfHour // "7:00 PM" 441| | /// 442| 4| var nearestHalfHour: Date { 443| 4| var components = calendar.dateComponents( 444| 4| [.year, .month, .day, .hour, .minute, .second, .nanosecond], 445| 4| from: self) 446| 4| let min = components.minute! 447| 4| components.minute! = min % 30 < 15 ? min - min % 30 : min + 30 - (min % 30) 448| 4| components.second = 0 449| 4| components.nanosecond = 0 450| 4| return calendar.date(from: components)! 451| 4| } 452| | 453| | /// SwifterSwift: Nearest hour to date. 454| | /// 455| | /// var date = Date() // "6:17 PM" 456| | /// date.nearestHour // "6:00 PM" 457| | /// 458| | /// date.minute = 36 // "6:36 PM" 459| | /// date.nearestHour // "7:00 PM" 460| | /// 461| 3| var nearestHour: Date { 462| 3| let min = calendar.component(.minute, from: self) 463| 3| let components: Set = [.year, .month, .day, .hour] 464| 3| let date = calendar.date(from: calendar.dateComponents(components, from: self))! 465| 3| 466| 3| if min < 30 { 467| 2| return date 468| 2| } 469| 1| return calendar.date(byAdding: .hour, value: 1, to: date)! 470| 3| } 471| | 472| | /// SwifterSwift: Yesterday date. 473| | /// 474| | /// let date = Date() // "Oct 3, 2018, 10:57:11" 475| | /// let yesterday = date.yesterday // "Oct 2, 2018, 10:57:11" 476| | /// 477| 1| var yesterday: Date { 478| 1| return calendar.date(byAdding: .day, value: -1, to: self) ?? Date() 479| 1| } 480| | 481| | /// SwifterSwift: Tomorrow's date. 482| | /// 483| | /// let date = Date() // "Oct 3, 2018, 10:57:11" 484| | /// let tomorrow = date.tomorrow // "Oct 4, 2018, 10:57:11" 485| | /// 486| 1| var tomorrow: Date { 487| 1| return calendar.date(byAdding: .day, value: 1, to: self) ?? Date() 488| 1| } 489| | 490| | /// SwifterSwift: UNIX timestamp from date. 491| | /// 492| | /// Date().unixTimestamp -> 1484233862.826291 493| | /// 494| 2| var unixTimestamp: Double { 495| 2| return timeIntervalSince1970 496| 2| } 497| |} 498| | 499| |// MARK: - Methods 500| | 501| |public extension Date { 502| | /// SwifterSwift: Date by adding multiples of calendar component. 503| | /// 504| | /// let date = Date() // "Jan 12, 2017, 7:07 PM" 505| | /// let date2 = date.adding(.minute, value: -10) // "Jan 12, 2017, 6:57 PM" 506| | /// let date3 = date.adding(.day, value: 4) // "Jan 16, 2017, 7:07 PM" 507| | /// let date4 = date.adding(.month, value: 2) // "Mar 12, 2017, 7:07 PM" 508| | /// let date5 = date.adding(.year, value: 13) // "Jan 12, 2030, 7:07 PM" 509| | /// 510| | /// - Parameters: 511| | /// - component: component type. 512| | /// - value: multiples of components to add. 513| | /// - Returns: original date + multiples of component added. 514| 88| func adding(_ component: Calendar.Component, value: Int) -> Date { 515| 88| return calendar.date(byAdding: component, value: value, to: self)! 516| 88| } 517| | 518| | /// SwifterSwift: Add calendar component to date. 519| | /// 520| | /// var date = Date() // "Jan 12, 2017, 7:07 PM" 521| | /// date.add(.minute, value: -10) // "Jan 12, 2017, 6:57 PM" 522| | /// date.add(.day, value: 4) // "Jan 16, 2017, 7:07 PM" 523| | /// date.add(.month, value: 2) // "Mar 12, 2017, 7:07 PM" 524| | /// date.add(.year, value: 13) // "Jan 12, 2030, 7:07 PM" 525| | /// 526| | /// - Parameters: 527| | /// - component: component type. 528| | /// - value: multiples of compnenet to add. 529| 24| mutating func add(_ component: Calendar.Component, value: Int) { 530| 24| if let date = calendar.date(byAdding: component, value: value, to: self) { 531| 24| self = date 532| 24| } 533| 24| } 534| | 535| | // swiftlint:disable cyclomatic_complexity function_body_length 536| | /// SwifterSwift: Date by changing value of calendar component. 537| | /// 538| | /// let date = Date() // "Jan 12, 2017, 7:07 PM" 539| | /// let date2 = date.changing(.minute, value: 10) // "Jan 12, 2017, 6:10 PM" 540| | /// let date3 = date.changing(.day, value: 4) // "Jan 4, 2017, 7:07 PM" 541| | /// let date4 = date.changing(.month, value: 2) // "Feb 12, 2017, 7:07 PM" 542| | /// let date5 = date.changing(.year, value: 2000) // "Jan 12, 2000, 7:07 PM" 543| | /// 544| | /// - Parameters: 545| | /// - component: component type. 546| | /// - value: new value of compnenet to change. 547| | /// - Returns: original date after changing given component to given value. 548| 28| func changing(_ component: Calendar.Component, value: Int) -> Date? { 549| 28| switch component { 550| 28| case .nanosecond: 551| 3| #if targetEnvironment(macCatalyst) 552| 3| // The `Calendar` implementation in `macCatalyst` does not know that a nanosecond is 1/1,000,000,000th of a second 553| 3| let allowedRange = 0..<1_000_000_000 554| 3| #else 555| 3| let allowedRange = calendar.range(of: .nanosecond, in: .second, for: self)! 556| 3| #endif 557| 3| guard allowedRange.contains(value) else { return nil } 558| 2| let currentNanoseconds = calendar.component(.nanosecond, from: self) 559| 2| let nanosecondsToAdd = value - currentNanoseconds 560| 2| return calendar.date(byAdding: .nanosecond, value: nanosecondsToAdd, to: self) 561| 28| 562| 28| case .second: 563| 4| let allowedRange = calendar.range(of: .second, in: .minute, for: self)! 564| 4| guard allowedRange.contains(value) else { return nil } 565| 2| let currentSeconds = calendar.component(.second, from: self) 566| 2| let secondsToAdd = value - currentSeconds 567| 2| return calendar.date(byAdding: .second, value: secondsToAdd, to: self) 568| 28| 569| 28| case .minute: 570| 4| let allowedRange = calendar.range(of: .minute, in: .hour, for: self)! 571| 4| guard allowedRange.contains(value) else { return nil } 572| 2| let currentMinutes = calendar.component(.minute, from: self) 573| 2| let minutesToAdd = value - currentMinutes 574| 2| return calendar.date(byAdding: .minute, value: minutesToAdd, to: self) 575| 28| 576| 28| case .hour: 577| 4| let allowedRange = calendar.range(of: .hour, in: .day, for: self)! 578| 4| guard allowedRange.contains(value) else { return nil } 579| 2| let currentHour = calendar.component(.hour, from: self) 580| 2| let hoursToAdd = value - currentHour 581| 2| return calendar.date(byAdding: .hour, value: hoursToAdd, to: self) 582| 28| 583| 28| case .day: 584| 4| let allowedRange = calendar.range(of: .day, in: .month, for: self)! 585| 4| guard allowedRange.contains(value) else { return nil } 586| 2| let currentDay = calendar.component(.day, from: self) 587| 2| let daysToAdd = value - currentDay 588| 2| return calendar.date(byAdding: .day, value: daysToAdd, to: self) 589| 28| 590| 28| case .month: 591| 4| let allowedRange = calendar.range(of: .month, in: .year, for: self)! 592| 4| guard allowedRange.contains(value) else { return nil } 593| 2| let currentMonth = calendar.component(.month, from: self) 594| 2| let monthsToAdd = value - currentMonth 595| 2| return calendar.date(byAdding: .month, value: monthsToAdd, to: self) 596| 28| 597| 28| case .year: 598| 4| guard value > 0 else { return nil } 599| 2| let currentYear = calendar.component(.year, from: self) 600| 2| let yearsToAdd = value - currentYear 601| 2| return calendar.date(byAdding: .year, value: yearsToAdd, to: self) 602| 28| 603| 28| default: 604| 1| return calendar.date(bySetting: component, value: value, of: self) 605| 28| } 606| 28| } 607| | 608| | #if !os(Linux) 609| | // swiftlint:enable cyclomatic_complexity, function_body_length 610| | 611| | /// SwifterSwift: Data at the beginning of calendar component. 612| | /// 613| | /// let date = Date() // "Jan 12, 2017, 7:14 PM" 614| | /// let date2 = date.beginning(of: .hour) // "Jan 12, 2017, 7:00 PM" 615| | /// let date3 = date.beginning(of: .month) // "Jan 1, 2017, 12:00 AM" 616| | /// let date4 = date.beginning(of: .year) // "Jan 1, 2017, 12:00 AM" 617| | /// 618| | /// - Parameter component: calendar component to get date at the beginning of. 619| | /// - Returns: date at the beginning of calendar component (if applicable). 620| 17| func beginning(of component: Calendar.Component) -> Date? { 621| 17| if component == .day { 622| 3| return calendar.startOfDay(for: self) 623| 14| } 624| 14| 625| 27| var components: Set { 626| 27| switch component { 627| 27| case .second: 628| 4| return [.year, .month, .day, .hour, .minute, .second] 629| 27| 630| 27| case .minute: 631| 4| return [.year, .month, .day, .hour, .minute] 632| 27| 633| 27| case .hour: 634| 4| return [.year, .month, .day, .hour] 635| 27| 636| 27| case .weekOfYear, .weekOfMonth: 637| 6| return [.yearForWeekOfYear, .weekOfYear] 638| 27| 639| 27| case .month: 640| 4| return [.year, .month] 641| 27| 642| 27| case .year: 643| 4| return [.year] 644| 27| 645| 27| default: 646| 1| return [] 647| 27| } 648| 27| } 649| 14| 650| 14| guard !components.isEmpty else { return nil } 651| 13| return calendar.date(from: calendar.dateComponents(components, from: self)) 652| 14| } 653| | #endif 654| | 655| | // swiftlint:disable function_body_length 656| | /// SwifterSwift: Date at the end of calendar component. 657| | /// 658| | /// let date = Date() // "Jan 12, 2017, 7:27 PM" 659| | /// let date2 = date.end(of: .day) // "Jan 12, 2017, 11:59 PM" 660| | /// let date3 = date.end(of: .month) // "Jan 31, 2017, 11:59 PM" 661| | /// let date4 = date.end(of: .year) // "Dec 31, 2017, 11:59 PM" 662| | /// 663| | /// - Parameter component: calendar component to get date at the end of. 664| | /// - Returns: date at the end of calendar component (if applicable). 665| 17| func end(of component: Calendar.Component) -> Date? { 666| 17| switch component { 667| 17| case .second: 668| 1| var date = adding(.second, value: 1) 669| 1| date = calendar.date(from: 670| 1| calendar.dateComponents([.year, .month, .day, .hour, .minute, .second], from: date))! 671| 1| date.add(.second, value: -1) 672| 1| return date 673| 17| 674| 17| case .minute: 675| 1| var date = adding(.minute, value: 1) 676| 1| let after = calendar.date(from: 677| 1| calendar.dateComponents([.year, .month, .day, .hour, .minute], from: date))! 678| 1| date = after.adding(.second, value: -1) 679| 1| return date 680| 17| 681| 17| case .hour: 682| 1| var date = adding(.hour, value: 1) 683| 1| let after = calendar.date(from: 684| 1| calendar.dateComponents([.year, .month, .day, .hour], from: date))! 685| 1| date = after.adding(.second, value: -1) 686| 1| return date 687| 17| 688| 17| case .day: 689| 3| var date = adding(.day, value: 1) 690| 3| date = calendar.startOfDay(for: date) 691| 3| date.add(.second, value: -1) 692| 3| return date 693| 17| 694| 17| case .weekOfYear, .weekOfMonth: 695| 1| var date = self 696| 1| let beginningOfWeek = calendar.date(from: 697| 1| calendar.dateComponents([.yearForWeekOfYear, .weekOfYear], from: date))! 698| 1| date = beginningOfWeek.adding(.day, value: 7).adding(.second, value: -1) 699| 1| return date 700| 17| 701| 17| case .month: 702| 4| var date = adding(.month, value: 1) 703| 4| let after = calendar.date(from: 704| 4| calendar.dateComponents([.year, .month], from: date))! 705| 4| date = after.adding(.second, value: -1) 706| 4| return date 707| 17| 708| 17| case .year: 709| 5| var date = adding(.year, value: 1) 710| 5| let after = calendar.date(from: 711| 5| calendar.dateComponents([.year], from: date))! 712| 5| date = after.adding(.second, value: -1) 713| 5| return date 714| 17| 715| 17| default: 716| 1| return nil 717| 17| } 718| 17| } 719| | 720| | // swiftlint:enable function_body_length 721| | 722| | /// SwifterSwift: Check if date is in current given calendar component. 723| | /// 724| | /// Date().isInCurrent(.day) -> true 725| | /// Date().isInCurrent(.year) -> true 726| | /// 727| | /// - Parameter component: calendar component to check. 728| | /// - Returns: true if date is in current given calendar component. 729| 15| func isInCurrent(_ component: Calendar.Component) -> Bool { 730| 15| return calendar.isDate(self, equalTo: Date(), toGranularity: component) 731| 15| } 732| | 733| | /// SwifterSwift: Date string from date. 734| | /// 735| | /// Date().string(withFormat: "dd/MM/yyyy") -> "1/12/17" 736| | /// Date().string(withFormat: "HH:mm") -> "23:50" 737| | /// Date().string(withFormat: "dd/MM/yyyy HH:mm") -> "1/12/17 23:50" 738| | /// 739| | /// - Parameter format: Date format (default is "dd/MM/yyyy"). 740| | /// - Returns: date string. 741| 4| func string(withFormat format: String = "dd/MM/yyyy HH:mm") -> String { 742| 4| let dateFormatter = DateFormatter() 743| 4| dateFormatter.dateFormat = format 744| 4| return dateFormatter.string(from: self) 745| 4| } 746| | 747| | /// SwifterSwift: Date string from date. 748| | /// 749| | /// Date().dateString(ofStyle: .short) -> "1/12/17" 750| | /// Date().dateString(ofStyle: .medium) -> "Jan 12, 2017" 751| | /// Date().dateString(ofStyle: .long) -> "January 12, 2017" 752| | /// Date().dateString(ofStyle: .full) -> "Thursday, January 12, 2017" 753| | /// 754| | /// - Parameter style: DateFormatter style (default is .medium). 755| | /// - Returns: date string. 756| 4| func dateString(ofStyle style: DateFormatter.Style = .medium) -> String { 757| 4| let dateFormatter = DateFormatter() 758| 4| dateFormatter.timeStyle = .none 759| 4| dateFormatter.dateStyle = style 760| 4| return dateFormatter.string(from: self) 761| 4| } 762| | 763| | /// SwifterSwift: Date and time string from date. 764| | /// 765| | /// Date().dateTimeString(ofStyle: .short) -> "1/12/17, 7:32 PM" 766| | /// Date().dateTimeString(ofStyle: .medium) -> "Jan 12, 2017, 7:32:00 PM" 767| | /// Date().dateTimeString(ofStyle: .long) -> "January 12, 2017 at 7:32:00 PM GMT+3" 768| | /// Date().dateTimeString(ofStyle: .full) -> "Thursday, January 12, 2017 at 7:32:00 PM GMT+03:00" 769| | /// 770| | /// - Parameter style: DateFormatter style (default is .medium). 771| | /// - Returns: date and time string. 772| 4| func dateTimeString(ofStyle style: DateFormatter.Style = .medium) -> String { 773| 4| let dateFormatter = DateFormatter() 774| 4| dateFormatter.timeStyle = style 775| 4| dateFormatter.dateStyle = style 776| 4| return dateFormatter.string(from: self) 777| 4| } 778| | 779| | /// SwifterSwift: Time string from date 780| | /// 781| | /// Date().timeString(ofStyle: .short) -> "7:37 PM" 782| | /// Date().timeString(ofStyle: .medium) -> "7:37:02 PM" 783| | /// Date().timeString(ofStyle: .long) -> "7:37:02 PM GMT+3" 784| | /// Date().timeString(ofStyle: .full) -> "7:37:02 PM GMT+03:00" 785| | /// 786| | /// - Parameter style: DateFormatter style (default is .medium). 787| | /// - Returns: time string. 788| 4| func timeString(ofStyle style: DateFormatter.Style = .medium) -> String { 789| 4| let dateFormatter = DateFormatter() 790| 4| dateFormatter.timeStyle = style 791| 4| dateFormatter.dateStyle = .none 792| 4| return dateFormatter.string(from: self) 793| 4| } 794| | 795| | /// SwifterSwift: Day name from date. 796| | /// 797| | /// Date().dayName(ofStyle: .oneLetter) -> "T" 798| | /// Date().dayName(ofStyle: .threeLetters) -> "Thu" 799| | /// Date().dayName(ofStyle: .full) -> "Thursday" 800| | /// 801| | /// - Parameter Style: style of day name (default is DayNameStyle.full). 802| | /// - Returns: day name string (example: W, Wed, Wednesday). 803| 3| func dayName(ofStyle style: DayNameStyle = .full) -> String { 804| 3| // http://www.codingexplorer.com/swiftly-getting-human-readable-date-nsdateformatter/ 805| 3| let dateFormatter = DateFormatter() 806| 3| var format: String { 807| 3| switch style { 808| 3| case .oneLetter: 809| 1| return "EEEEE" 810| 3| case .threeLetters: 811| 1| return "EEE" 812| 3| case .full: 813| 1| return "EEEE" 814| 3| } 815| 3| } 816| 3| dateFormatter.setLocalizedDateFormatFromTemplate(format) 817| 3| return dateFormatter.string(from: self) 818| 3| } 819| | 820| | /// SwifterSwift: Month name from date. 821| | /// 822| | /// Date().monthName(ofStyle: .oneLetter) -> "J" 823| | /// Date().monthName(ofStyle: .threeLetters) -> "Jan" 824| | /// Date().monthName(ofStyle: .full) -> "January" 825| | /// 826| | /// - Parameter Style: style of month name (default is MonthNameStyle.full). 827| | /// - Returns: month name string (example: D, Dec, December). 828| 3| func monthName(ofStyle style: MonthNameStyle = .full) -> String { 829| 3| // http://www.codingexplorer.com/swiftly-getting-human-readable-date-nsdateformatter/ 830| 3| let dateFormatter = DateFormatter() 831| 3| var format: String { 832| 3| switch style { 833| 3| case .oneLetter: 834| 1| return "MMMMM" 835| 3| case .threeLetters: 836| 1| return "MMM" 837| 3| case .full: 838| 1| return "MMMM" 839| 3| } 840| 3| } 841| 3| dateFormatter.setLocalizedDateFormatFromTemplate(format) 842| 3| return dateFormatter.string(from: self) 843| 3| } 844| | 845| | /// SwifterSwift: get number of seconds between two date 846| | /// 847| | /// - Parameter date: date to compate self to. 848| | /// - Returns: number of seconds between self and given date. 849| 2| func secondsSince(_ date: Date) -> Double { 850| 2| return timeIntervalSince(date) 851| 2| } 852| | 853| | /// SwifterSwift: get number of minutes between two date 854| | /// 855| | /// - Parameter date: date to compate self to. 856| | /// - Returns: number of minutes between self and given date. 857| 2| func minutesSince(_ date: Date) -> Double { 858| 2| return timeIntervalSince(date) / 60 859| 2| } 860| | 861| | /// SwifterSwift: get number of hours between two date 862| | /// 863| | /// - Parameter date: date to compate self to. 864| | /// - Returns: number of hours between self and given date. 865| 2| func hoursSince(_ date: Date) -> Double { 866| 2| return timeIntervalSince(date) / 3600 867| 2| } 868| | 869| | /// SwifterSwift: get number of days between two date 870| | /// 871| | /// - Parameter date: date to compate self to. 872| | /// - Returns: number of days between self and given date. 873| 2| func daysSince(_ date: Date) -> Double { 874| 2| return timeIntervalSince(date) / (3600 * 24) 875| 2| } 876| | 877| | /// SwifterSwift: check if a date is between two other dates 878| | /// 879| | /// - Parameters: 880| | /// - startDate: start date to compare self to. 881| | /// - endDate: endDate date to compare self to. 882| | /// - includeBounds: true if the start and end date should be included (default is false) 883| | /// - Returns: true if the date is between the two given dates. 884| 18| func isBetween(_ startDate: Date, _ endDate: Date, includeBounds: Bool = false) -> Bool { 885| 18| if includeBounds { 886| 8| return startDate.compare(self).rawValue * compare(endDate).rawValue >= 0 887| 10| } 888| 10| return startDate.compare(self).rawValue * compare(endDate).rawValue > 0 889| 18| } 890| | 891| | /// SwifterSwift: check if a date is a number of date components of another date 892| | /// 893| | /// - Parameters: 894| | /// - value: number of times component is used in creating range 895| | /// - component: Calendar.Component to use. 896| | /// - date: Date to compare self to. 897| | /// - Returns: true if the date is within a number of components of another date 898| 13| func isWithin(_ value: UInt, _ component: Calendar.Component, of date: Date) -> Bool { 899| 13| let components = calendar.dateComponents([component], from: self, to: date) 900| 13| let componentValue = components.value(for: component)! 901| 13| return abs(componentValue) <= value 902| 13| } 903| | 904| | /// SwifterSwift: Returns a random date within the specified range. 905| | /// 906| | /// - Parameter range: The range in which to create a random date. `range` must not be empty. 907| | /// - Returns: A random date within the bounds of `range`. 908| 4| static func random(in range: Range) -> Date { 909| 4| return Date(timeIntervalSinceReferenceDate: 910| 4| TimeInterval 911| 4| .random(in: range.lowerBound.timeIntervalSinceReferenceDate..) -> Date { 920| 6| return Date(timeIntervalSinceReferenceDate: 921| 6| TimeInterval 922| 6| .random(in: range.lowerBound.timeIntervalSinceReferenceDate...range.upperBound 923| 6| .timeIntervalSinceReferenceDate)) 924| 6| } 925| | 926| | /// SwifterSwift: Returns a random date within the specified range, using the given generator as a source for randomness. 927| | /// 928| | /// - Parameters: 929| | /// - range: The range in which to create a random date. `range` must not be empty. 930| | /// - generator: The random number generator to use when creating the new random date. 931| | /// - Returns: A random date within the bounds of `range`. 932| 1| static func random(in range: Range, using generator: inout T) -> Date where T: RandomNumberGenerator { 933| 1| return Date(timeIntervalSinceReferenceDate: 934| 1| TimeInterval.random( 935| 1| in: range.lowerBound.timeIntervalSinceReferenceDate..(in range: ClosedRange, using generator: inout T) -> Date 946| 3| where T: RandomNumberGenerator { 947| 3| return Date(timeIntervalSinceReferenceDate: 948| 3| TimeInterval.random( 949| 3| in: range.lowerBound.timeIntervalSinceReferenceDate...range.upperBound.timeIntervalSinceReferenceDate, 950| 3| using: &generator)) 951| 3| } 952| |} 953| | 954| |// MARK: - Initializers 955| | 956| |public extension Date { 957| | /// SwifterSwift: Create a new date form calendar components. 958| | /// 959| | /// let date = Date(year: 2010, month: 1, day: 12) // "Jan 12, 2010, 7:45 PM" 960| | /// 961| | /// - Parameters: 962| | /// - calendar: Calendar (default is current). 963| | /// - timeZone: TimeZone (default is current). 964| | /// - era: Era (default is current era). 965| | /// - year: Year (default is current year). 966| | /// - month: Month (default is current month). 967| | /// - day: Day (default is today). 968| | /// - hour: Hour (default is current hour). 969| | /// - minute: Minute (default is current minute). 970| | /// - second: Second (default is current second). 971| | /// - nanosecond: Nanosecond (default is current nanosecond). 972| | init?( 973| | calendar: Calendar? = Calendar.current, 974| | timeZone: TimeZone? = NSTimeZone.default, 975| | era: Int? = Date().era, 976| | year: Int? = Date().year, 977| | month: Int? = Date().month, 978| | day: Int? = Date().day, 979| | hour: Int? = Date().hour, 980| | minute: Int? = Date().minute, 981| | second: Int? = Date().second, 982| 4| nanosecond: Int? = Date().nanosecond) { 983| 4| var components = DateComponents() 984| 4| components.calendar = calendar 985| 4| components.timeZone = timeZone 986| 4| components.era = era 987| 4| components.year = year 988| 4| components.month = month 989| 4| components.day = day 990| 4| components.hour = hour 991| 4| components.minute = minute 992| 4| components.second = second 993| 4| components.nanosecond = nanosecond 994| 4| 995| 4| guard let date = calendar?.date(from: components) else { return nil } 996| 3| self = date 997| 3| } 998| | 999| | /// SwifterSwift: Create date object from ISO8601 string. 1000| | /// 1001| | /// let date = Date(iso8601String: "2017-01-12T16:48:00.959Z") // "Jan 12, 2017, 7:48 PM" 1002| | /// 1003| | /// - Parameter iso8601String: ISO8601 string of format (yyyy-MM-dd'T'HH:mm:ss.SSSZ). 1004| 2| init?(iso8601String: String) { 1005| 2| // https://github.com/justinmakaila/NSDate-ISO-8601/blob/master/NSDateISO8601.swift 1006| 2| let dateFormatter = DateFormatter() 1007| 2| dateFormatter.locale = Locale(identifier: "en_US_POSIX") 1008| 2| dateFormatter.timeZone = TimeZone.current 1009| 2| dateFormatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss.SSSZ" 1010| 2| guard let date = dateFormatter.date(from: iso8601String) else { return nil } 1011| 1| self = date 1012| 1| } 1013| | 1014| | /// SwifterSwift: Create new date object from UNIX timestamp. 1015| | /// 1016| | /// let date = Date(unixTimestamp: 1484239783.922743) // "Jan 12, 2017, 7:49 PM" 1017| | /// 1018| | /// - Parameter unixTimestamp: UNIX timestamp. 1019| 1| init(unixTimestamp: Double) { 1020| 1| self.init(timeIntervalSince1970: unixTimestamp) 1021| 1| } 1022| | 1023| | /// SwifterSwift: Create date object from Int literal 1024| | /// 1025| | /// let date = Date(integerLiteral: 2017_12_25) // "2017-12-25 00:00:00 +0000" 1026| | /// - Parameter value: Int value, e.g. 20171225, or 2017_12_25 etc. 1027| 2| init?(integerLiteral value: Int) { 1028| 2| let formatter = DateFormatter() 1029| 2| formatter.dateFormat = "yyyyMMdd" 1030| 2| guard let date = formatter.date(from: String(value)) else { return nil } 1031| 1| self = date 1032| 1| } 1033| |} 1034| | 1035| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/Foundation/FileManagerExtensions.swift: 1| |// FileManagerExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(Foundation) 4| |import Foundation 5| | 6| |public extension FileManager { 7| | /// SwifterSwift: Read from a JSON file at a given path. 8| | /// 9| | /// - Parameters: 10| | /// - path: JSON file path. 11| | /// - readingOptions: JSONSerialization reading options. 12| | /// - Returns: Optional dictionary. 13| | /// - Throws: Throws any errors thrown by Data creation or JSON serialization. 14| | func jsonFromFile( 15| | atPath path: String, 16| 1| readingOptions: JSONSerialization.ReadingOptions = .allowFragments) throws -> [String: Any]? { 17| 1| let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe) 18| 1| let json = try JSONSerialization.jsonObject(with: data, options: readingOptions) 19| 1| 20| 1| return json as? [String: Any] 21| 1| } 22| | 23| | #if !os(Linux) 24| | /// SwifterSwift: Read from a JSON file with a given filename. 25| | /// 26| | /// - Parameters: 27| | /// - filename: File to read. 28| | /// - bundleClass: Bundle where the file is associated. 29| | /// - readingOptions: JSONSerialization reading options. 30| | /// - Returns: Optional dictionary. 31| | /// - Throws: Throws any errors thrown by Data creation or JSON serialization. 32| | func jsonFromFile( 33| | withFilename filename: String, 34| | at bundleClass: AnyClass? = nil, 35| 3| readingOptions: JSONSerialization.ReadingOptions = .allowFragments) throws -> [String: Any]? { 36| 3| // https://stackoverflow.com/questions/24410881/reading-in-a-json-file-using-swift 37| 3| 38| 3| // To handle cases that provided filename has an extension 39| 3| let name = filename.components(separatedBy: ".")[0] 40| 3| let bundle = bundleClass != nil ? Bundle(for: bundleClass!) : Bundle.main 41| 3| 42| 3| if let path = bundle.path(forResource: name, ofType: "json") { 43| 2| let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe) 44| 2| let json = try JSONSerialization.jsonObject(with: data, options: readingOptions) 45| 2| 46| 2| return json as? [String: Any] 47| 2| } 48| 1| 49| 1| return nil 50| 3| } 51| | #endif 52| | 53| | /// SwifterSwift: Creates a unique directory for saving temporary files. The directory can be used to create multiple temporary files used for a common purpose. 54| | /// 55| | /// let tempDirectory = try fileManager.createTemporaryDirectory() 56| | /// let tempFile1URL = tempDirectory.appendingPathComponent(ProcessInfo().globallyUniqueString) 57| | /// let tempFile2URL = tempDirectory.appendingPathComponent(ProcessInfo().globallyUniqueString) 58| | /// 59| | /// - Returns: A URL to a new directory for saving temporary files. 60| | /// - Throws: An error if a temporary directory cannot be found or created. 61| 1| func createTemporaryDirectory() throws -> URL { 62| 1| #if !os(Linux) 63| 1| let temporaryDirectoryURL: URL 64| 1| if #available(OSX 10.12, tvOS 10.0, watchOS 3.0, *) { 65| 1| temporaryDirectoryURL = temporaryDirectory 66| 1| } else { 67| 0| temporaryDirectoryURL = URL(fileURLWithPath: NSTemporaryDirectory(), isDirectory: true) 68| 1| } 69| 1| return try url(for: .itemReplacementDirectory, 70| 1| in: .userDomainMask, 71| 1| appropriateFor: temporaryDirectoryURL, 72| 1| create: true) 73| 1| #else 74| 1| let envs = ProcessInfo.processInfo.environment 75| 1| let env = envs["TMPDIR"] ?? envs["TEMP"] ?? envs["TMP"] ?? "/tmp" 76| 1| let dir = "/\(env)/file-temp.XXXXXX" 77| 1| var template = [UInt8](dir.utf8).map { Int8($0) } + [Int8(0)] 78| 1| guard mkdtemp(&template) != nil else { throw CocoaError.error(.featureUnsupported) } 79| 1| return URL(fileURLWithPath: String(cString: template)) 80| 1| #endif 81| 1| } 82| |} 83| | 84| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/Foundation/LocaleExtensions.swift: 1| |// LocaleExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(Foundation) 4| |import Foundation 5| | 6| |// MARK: - Properties 7| | 8| |public extension Locale { 9| | /// SwifterSwift: UNIX representation of locale usually used for normalizing. 10| 1| static var posix: Locale { 11| 1| return Locale(identifier: "en_US_POSIX") 12| 1| } 13| | 14| | /// SwifterSwift: Returns bool value indicating if locale has 12h format. 15| 2| var is12HourTimeFormat: Bool { 16| 2| let dateFormatter = DateFormatter() 17| 2| dateFormatter.timeStyle = .short 18| 2| dateFormatter.dateStyle = .none 19| 2| dateFormatter.locale = self 20| 2| let dateString = dateFormatter.string(from: Date()) 21| 2| return dateString.contains(dateFormatter.amSymbol) || dateString.contains(dateFormatter.pmSymbol) 22| 2| } 23| |} 24| | 25| |// MARK: - Functions 26| | 27| |public extension Locale { 28| | /// SwifterSwift: Get the flag emoji for a given country region code. 29| | /// - Parameter isoRegionCode: The IOS region code. 30| | /// 31| | /// Adapted from https://stackoverflow.com/a/30403199/1627511 32| 260| static func flagEmoji(forRegionCode isoRegionCode: String) -> String? { 33| 260| #if !os(Linux) 34| 260| guard isoRegionCodes.contains(isoRegionCode) else { return nil } 35| 258| #endif 36| 258| 37| 516| return isoRegionCode.unicodeScalars.reduce(into: String()) { 38| 516| guard let flagScalar = UnicodeScalar(UInt32(127_397) + $1.value) else { return } 39| 516| $0.unicodeScalars.append(flagScalar) 40| 516| } 41| 260| } 42| |} 43| | 44| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/Foundation/NSAttributedStringExtensions.swift: 1| |// NSAttributedStringExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(Foundation) 4| |import Foundation 5| | 6| |#if canImport(UIKit) 7| |import UIKit 8| |#endif 9| | 10| |#if canImport(AppKit) 11| |import AppKit 12| |#endif 13| | 14| |// MARK: - Properties 15| | 16| |public extension NSAttributedString { 17| | /// SwifterSwift: Bolded string using the system font. 18| | #if !os(Linux) 19| 2| var bolded: NSAttributedString { 20| 2| guard !string.isEmpty else { return self } 21| 2| 22| 2| let pointSize: CGFloat 23| 2| if let font = attribute(.font, at: 0, effectiveRange: nil) as? Font { 24| 1| pointSize = font.pointSize 25| 2| } else { 26| 1| #if os(tvOS) || os(watchOS) 27| 1| pointSize = Font.preferredFont(forTextStyle: .headline).pointSize 28| 1| #else 29| 1| pointSize = Font.systemFontSize 30| 1| #endif 31| 2| } 32| 2| return applying(attributes: [.font: Font.boldSystemFont(ofSize: pointSize)]) 33| 2| } 34| | #endif 35| | 36| | #if !os(Linux) 37| | /// SwifterSwift: Underlined string. 38| 1| var underlined: NSAttributedString { 39| 1| return applying(attributes: [.underlineStyle: NSUnderlineStyle.single.rawValue]) 40| 1| } 41| | #endif 42| | 43| | #if canImport(UIKit) 44| | /// SwifterSwift: Italicized string using the system font. 45| 2| var italicized: NSAttributedString { 46| 2| guard !string.isEmpty else { return self } 47| 2| 48| 2| let pointSize: CGFloat 49| 2| if let font = attribute(.font, at: 0, effectiveRange: nil) as? UIFont { 50| 1| pointSize = font.pointSize 51| 2| } else { 52| 1| #if os(tvOS) || os(watchOS) 53| 1| pointSize = UIFont.preferredFont(forTextStyle: .headline).pointSize 54| 1| #else 55| 1| pointSize = UIFont.systemFontSize 56| 1| #endif 57| 2| } 58| 2| return applying(attributes: [.font: UIFont.italicSystemFont(ofSize: pointSize)]) 59| 2| } 60| | #endif 61| | 62| | #if !os(Linux) 63| | /// SwifterSwift: Struckthrough string. 64| 1| var struckthrough: NSAttributedString { 65| 1| return applying(attributes: [.strikethroughStyle: NSNumber(value: NSUnderlineStyle.single.rawValue)]) 66| 1| } 67| | #endif 68| | 69| | /// SwifterSwift: Dictionary of the attributes applied across the whole string 70| 6| var attributes: [Key: Any] { 71| 6| guard length > 0 else { return [:] } 72| 6| return attributes(at: 0, effectiveRange: nil) 73| 6| } 74| |} 75| | 76| |// MARK: - Methods 77| | 78| |public extension NSAttributedString { 79| | /// SwifterSwift: Applies given attributes to the new instance of NSAttributedString initialized with self object 80| | /// 81| | /// - Parameter attributes: Dictionary of attributes 82| | /// - Returns: NSAttributedString with applied attributes 83| 10| func applying(attributes: [Key: Any]) -> NSAttributedString { 84| 10| guard !string.isEmpty else { return self } 85| 10| 86| 10| let copy = NSMutableAttributedString(attributedString: self) 87| 10| copy.addAttributes(attributes, range: NSRange(0.. NSAttributedString { 97| 2| return applying(attributes: [.foregroundColor: color]) 98| 2| } 99| | #endif 100| | 101| | /// SwifterSwift: Apply attributes to substrings matching a regular expression 102| | /// 103| | /// - Parameters: 104| | /// - attributes: Dictionary of attributes 105| | /// - pattern: a regular expression to target 106| | /// - options: The regular expression options that are applied to the expression during matching. See NSRegularExpression.Options for possible values. 107| | /// - Returns: An NSAttributedString with attributes applied to substrings matching the pattern 108| | func applying(attributes: [Key: Any], 109| | toRangesMatching pattern: String, 110| 0| options: NSRegularExpression.Options = []) -> NSAttributedString { 111| 0| guard let pattern = try? NSRegularExpression(pattern: pattern, options: options) else { return self } 112| 0| 113| 0| let matches = pattern.matches(in: string, options: [], range: NSRange(0..(attributes: [Key: Any], 130| 0| toOccurrencesOf target: T) -> NSAttributedString { 131| 0| let pattern = "\\Q\(target)\\E" 132| 0| 133| 0| return applying(attributes: attributes, toRangesMatching: pattern) 134| 0| } 135| |} 136| | 137| |// MARK: - Operators 138| | 139| |public extension NSAttributedString { 140| | /// SwifterSwift: Add a NSAttributedString to another NSAttributedString. 141| | /// 142| | /// - Parameters: 143| | /// - lhs: NSAttributedString to add to. 144| | /// - rhs: NSAttributedString to add. 145| 0| static func += (lhs: inout NSAttributedString, rhs: NSAttributedString) { 146| 0| let string = NSMutableAttributedString(attributedString: lhs) 147| 0| string.append(rhs) 148| 0| lhs = string 149| 0| } 150| | 151| | /// SwifterSwift: Add a NSAttributedString to another NSAttributedString and return a new NSAttributedString instance. 152| | /// 153| | /// - Parameters: 154| | /// - lhs: NSAttributedString to add. 155| | /// - rhs: NSAttributedString to add. 156| | /// - Returns: New instance with added NSAttributedString. 157| 0| static func + (lhs: NSAttributedString, rhs: NSAttributedString) -> NSAttributedString { 158| 0| let string = NSMutableAttributedString(attributedString: lhs) 159| 0| string.append(rhs) 160| 0| return NSAttributedString(attributedString: string) 161| 0| } 162| | 163| | /// SwifterSwift: Add a NSAttributedString to another NSAttributedString. 164| | /// 165| | /// - Parameters: 166| | /// - lhs: NSAttributedString to add to. 167| | /// - rhs: String to add. 168| 0| static func += (lhs: inout NSAttributedString, rhs: String) { 169| 0| lhs += NSAttributedString(string: rhs) 170| 0| } 171| | 172| | /// SwifterSwift: Add a NSAttributedString to another NSAttributedString and return a new NSAttributedString instance. 173| | /// 174| | /// - Parameters: 175| | /// - lhs: NSAttributedString to add. 176| | /// - rhs: String to add. 177| | /// - Returns: New instance with added NSAttributedString. 178| 0| static func + (lhs: NSAttributedString, rhs: String) -> NSAttributedString { 179| 0| return lhs + NSAttributedString(string: rhs) 180| 0| } 181| |} 182| | 183| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/Foundation/NSPredicateExtensions.swift: 1| |// NSPredicateExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(Foundation) 4| |import Foundation 5| | 6| |// MARK: - Properties 7| | 8| |public extension NSPredicate { 9| | /// SwifterSwift: Returns a new predicate formed by NOT-ing the predicate. 10| 3| var not: NSCompoundPredicate { 11| 3| return NSCompoundPredicate(notPredicateWithSubpredicate: self) 12| 3| } 13| |} 14| | 15| |// MARK: - Methods 16| | 17| |public extension NSPredicate { 18| | /// SwifterSwift: Returns a new predicate formed by AND-ing the argument to the predicate. 19| | /// 20| | /// - Parameter predicate: NSPredicate 21| | /// - Returns: NSCompoundPredicate 22| 3| func and(_ predicate: NSPredicate) -> NSCompoundPredicate { 23| 3| return NSCompoundPredicate(andPredicateWithSubpredicates: [self, predicate]) 24| 3| } 25| | 26| | /// SwifterSwift: Returns a new predicate formed by OR-ing the argument to the predicate. 27| | /// 28| | /// - Parameter predicate: NSPredicate 29| | /// - Returns: NSCompoundPredicate 30| 2| func or(_ predicate: NSPredicate) -> NSCompoundPredicate { 31| 2| return NSCompoundPredicate(orPredicateWithSubpredicates: [self, predicate]) 32| 2| } 33| |} 34| | 35| |// MARK: - Operators 36| | 37| |public extension NSPredicate { 38| | /// SwifterSwift: Returns a new predicate formed by NOT-ing the predicate. 39| | /// - Parameters: rhs: NSPredicate to convert. 40| | /// - Returns: NSCompoundPredicate 41| 2| static prefix func ! (rhs: NSPredicate) -> NSCompoundPredicate { 42| 2| return rhs.not 43| 2| } 44| | 45| | /// SwifterSwift: Returns a new predicate formed by AND-ing the argument to the predicate. 46| | /// 47| | /// - Parameters: 48| | /// - lhs: NSPredicate. 49| | /// - rhs: NSPredicate. 50| | /// - Returns: NSCompoundPredicate 51| 2| static func + (lhs: NSPredicate, rhs: NSPredicate) -> NSCompoundPredicate { 52| 2| return lhs.and(rhs) 53| 2| } 54| | 55| | /// SwifterSwift: Returns a new predicate formed by OR-ing the argument to the predicate. 56| | /// 57| | /// - Parameters: 58| | /// - lhs: NSPredicate. 59| | /// - rhs: NSPredicate. 60| | /// - Returns: NSCompoundPredicate 61| 1| static func | (lhs: NSPredicate, rhs: NSPredicate) -> NSCompoundPredicate { 62| 1| return lhs.or(rhs) 63| 1| } 64| | 65| | /// SwifterSwift: Returns a new predicate formed by remove the argument to the predicate. 66| | /// 67| | /// - Parameters: 68| | /// - lhs: NSPredicate. 69| | /// - rhs: NSPredicate. 70| | /// - Returns: NSCompoundPredicate 71| 1| static func - (lhs: NSPredicate, rhs: NSPredicate) -> NSCompoundPredicate { 72| 1| return lhs + !rhs 73| 1| } 74| |} 75| | 76| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/Foundation/NSRegularExpressionExtensions.swift: 1| |// NSRegularExpressionExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(Foundation) 4| |import Foundation 5| | 6| |public extension NSRegularExpression { 7| | /// SwifterSwift: Enumerates the string allowing the Block to handle each regular expression match. 8| | /// 9| | /// - Parameters: 10| | /// - string: The string. 11| | /// - options: The matching options to report. See `NSRegularExpression.MatchingOptions` for the supported values. 12| | /// - range: The range of the string to test. 13| | /// - block: The Block enumerates the matches of the regular expression in the string. 14| | /// The block takes three arguments and returns `Void`: 15| | /// - result: 16| | /// 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. 17| | /// - flags: 18| | /// The current state of the matching progress. See `NSRegularExpression.MatchingFlags` for the possible values. 19| | /// - stop: 20| | /// 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. 21| | #if os(Linux) 22| | func enumerateMatches(in string: String, 23| | options: MatchingOptions = [], 24| | range: Range, 25| | using block: @escaping ( 26| | _ result: NSTextCheckingResult?, 27| | _ flags: MatchingFlags, 28| | _ stop: inout Bool) -> Void) { 29| | enumerateMatches(in: string, 30| | options: options, 31| | range: NSRange(range, in: string)) { result, flags, stop in 32| | var shouldStop = false 33| | block(result, flags, &shouldStop) 34| | if shouldStop { 35| | stop.pointee = true 36| | } 37| | } 38| | } 39| | #else 40| | func enumerateMatches(in string: String, 41| | options: MatchingOptions = [], 42| | range: Range, 43| | using block: (_ result: NSTextCheckingResult?, _ flags: MatchingFlags, _ stop: inout Bool) 44| 2| -> Void) { 45| 2| enumerateMatches(in: string, 46| 2| options: options, 47| 5| range: NSRange(range, in: string)) { result, flags, stop in 48| 5| var shouldStop = false 49| 5| block(result, flags, &shouldStop) 50| 5| if shouldStop { 51| 1| stop.pointee = true 52| 5| } 53| 5| } 54| 2| } 55| | #endif 56| | 57| | /// SwifterSwift: Returns an array containing all the matches of the regular expression in the string. 58| | /// 59| | /// - Parameters: 60| | /// - string: The string to search. 61| | /// - options: The matching options to use. See NSRegularExpression.MatchingOptions for possible values. 62| | /// - range: The range of the string to search. 63| | /// - 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. 64| | func matches(in string: String, 65| | options: MatchingOptions = [], 66| 1| range: Range) -> [NSTextCheckingResult] { 67| 1| return matches(in: string, 68| 1| options: options, 69| 1| range: NSRange(range, in: string)) 70| 1| } 71| | 72| | /// SwifterSwift: Returns the number of matches of the regular expression within the specified range of the string. 73| | /// 74| | /// - Parameters: 75| | /// - string: The string to search. 76| | /// - options: The matching options to use. See NSRegularExpression.MatchingOptions for possible values. 77| | /// - range: The range of the string to search. 78| | /// - Returns: The number of matches of the regular expression. 79| | func numberOfMatches(in string: String, 80| | options: MatchingOptions = [], 81| 1| range: Range) -> Int { 82| 1| return numberOfMatches(in: string, 83| 1| options: options, 84| 1| range: NSRange(range, in: string)) 85| 1| } 86| | 87| | /// SwifterSwift: Returns the first match of the regular expression within the specified range of the string. 88| | /// 89| | /// - Parameters: 90| | /// - string: The string to search. 91| | /// - options: The matching options to use. See `NSRegularExpression.MatchingOptions` for possible values. 92| | /// - range: The range of the string to search. 93| | /// - 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. 94| | func firstMatch(in string: String, 95| | options: MatchingOptions = [], 96| 1| range: Range) -> NSTextCheckingResult? { 97| 1| return firstMatch(in: string, 98| 1| options: options, 99| 1| range: NSRange(range, in: string)) 100| 1| } 101| | 102| | /// SwifterSwift: Returns the range of the first match of the regular expression within the specified range of the string. 103| | /// 104| | /// - Parameters: 105| | /// - string: The string to search. 106| | /// - options: The matching options to use. See `NSRegularExpression.MatchingOptions` for possible values. 107| | /// - range: The range of the string to search. 108| | /// - Returns: The range of the first match. Returns `nil` if no match is found. 109| | func rangeOfFirstMatch(in string: String, 110| | options: MatchingOptions = [], 111| 2| range: Range) -> Range? { 112| 2| return Range(rangeOfFirstMatch(in: string, 113| 2| options: options, 114| 2| range: NSRange(range, in: string)), 115| 2| in: string) 116| 2| } 117| | 118| | /// SwifterSwift: Returns a new string containing matching regular expressions replaced with the template string. 119| | /// 120| | /// - Parameters: 121| | /// - string: The string to search for values within. 122| | /// - options: The matching options to use. See `NSRegularExpression.MatchingOptions` for possible values. 123| | /// - range: The range of the string to search. 124| | /// - templ: The substitution template used when replacing matching instances. 125| | /// - Returns: A string with matching regular expressions replaced by the template string. 126| | func stringByReplacingMatches(in string: String, 127| | options: MatchingOptions = [], 128| | range: Range, 129| 1| withTemplate templ: String) -> String { 130| 1| return stringByReplacingMatches(in: string, 131| 1| options: options, 132| 1| range: NSRange(range, in: string), 133| 1| withTemplate: templ) 134| 1| } 135| | 136| | /// SwifterSwift: Replaces regular expression matches within the mutable string using the template string. 137| | /// 138| | /// - Parameters: 139| | /// - string: The mutable string to search and replace values within. 140| | /// - options: The matching options to use. See `NSRegularExpression.MatchingOptions` for possible values. 141| | /// - range: The range of the string to search. 142| | /// - templ: The substitution template used when replacing matching instances. 143| | /// - Returns: The number of matches. 144| | @discardableResult 145| | func replaceMatches(in string: inout String, 146| | options: MatchingOptions = [], 147| | range: Range, 148| 1| withTemplate templ: String) -> Int { 149| 1| let mutableString = NSMutableString(string: string) 150| 1| let matches = replaceMatches(in: mutableString, 151| 1| options: options, 152| 1| range: NSRange(range, in: string), 153| 1| withTemplate: templ) 154| 1| string = mutableString.copy() as! String // swiftlint:disable:this force_cast 155| 1| return matches 156| 1| } 157| |} 158| | 159| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/Foundation/NotificationCenterExtensions.swift: 1| |// NotificationCenterExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(Foundation) 4| |import Foundation 5| | 6| |public extension NotificationCenter { 7| | /// 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. 8| | /// - Parameters: 9| | /// - 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. 10| | /// 11| | /// 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. 12| | /// - obj: The object whose notifications the observer wants to receive; that is, only notifications sent by this sender are delivered to the observer. 13| | /// 14| | /// If you pass `nil`, the notification center doesn’t use a notification’s sender to decide whether to deliver it to the observer. 15| | /// - queue: The operation queue to which block should be added. 16| | /// 17| | /// If you pass `nil`, the block is run synchronously on the posting thread. 18| | /// - block: The block to be executed when the notification is received. 19| | /// 20| | /// The block is copied by the notification center and (the copy) held until the observer registration is removed. 21| | /// 22| | /// The block takes one argument: 23| | /// - notification: The notification. 24| | func observeOnce(forName name: NSNotification.Name?, 25| | object obj: Any? = nil, 26| | queue: OperationQueue? = nil, 27| 4| using block: @escaping (_ notification: Notification) -> Void) { 28| 4| var handler: NSObjectProtocol! 29| 4| handler = addObserver(forName: name, object: obj, queue: queue) { [unowned self] in 30| 4| self.removeObserver(handler!) 31| 4| block($0) 32| 4| } 33| 4| } 34| |} 35| | 36| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/Foundation/URLExtensions.swift: 1| |// URLExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(Foundation) 4| |import Foundation 5| | 6| |#if canImport(UIKit) && canImport(AVFoundation) 7| |import AVFoundation 8| |import UIKit 9| |#endif 10| | 11| |// MARK: - Properties 12| | 13| |public extension URL { 14| | /// SwifterSwift: Dictionary of the URL's query parameters 15| 1| var queryParameters: [String: String]? { 16| 1| guard let components = URLComponents(url: self, resolvingAgainstBaseURL: false), 17| 1| let queryItems = components.queryItems else { return nil } 18| 1| 19| 1| var items: [String: String] = [:] 20| 1| 21| 3| for queryItem in queryItems { 22| 3| items[queryItem.name] = queryItem.value 23| 3| } 24| 1| 25| 1| return items 26| 1| } 27| |} 28| | 29| |// MARK: - Initializers 30| | 31| |public extension URL { 32| | /// SwifterSwift: Initializes an `URL` object with a base URL and a relative string. If `string` was malformed, returns `nil`. 33| | /// - Parameters: 34| | /// - string: The URL string with which to initialize the `URL` object. Must conform to RFC 2396. `string` is interpreted relative to `url`. 35| | /// - url: The base URL for the `URL` object. 36| 5| init?(string: String?, relativeTo url: URL? = nil) { 37| 5| guard let string = string else { return nil } 38| 2| self.init(string: string, relativeTo: url) 39| 2| } 40| |} 41| | 42| |// MARK: - Methods 43| | 44| |public extension URL { 45| | /// SwifterSwift: URL with appending query parameters. 46| | /// 47| | /// let url = URL(string: "https://google.com")! 48| | /// let param = ["q": "Swifter Swift"] 49| | /// url.appendingQueryParameters(params) -> "https://google.com?q=Swifter%20Swift" 50| | /// 51| | /// - Parameter parameters: parameters dictionary. 52| | /// - Returns: URL with appending given query parameters. 53| 2| func appendingQueryParameters(_ parameters: [String: String]) -> URL { 54| 2| var urlComponents = URLComponents(url: self, resolvingAgainstBaseURL: true)! 55| 2| urlComponents.queryItems = (urlComponents.queryItems ?? []) + parameters 56| 2| .map { URLQueryItem(name: $0, value: $1) } 57| 2| return urlComponents.url! 58| 2| } 59| | 60| | /// SwifterSwift: Append query parameters to URL. 61| | /// 62| | /// var url = URL(string: "https://google.com")! 63| | /// let param = ["q": "Swifter Swift"] 64| | /// url.appendQueryParameters(params) 65| | /// print(url) // prints "https://google.com?q=Swifter%20Swift" 66| | /// 67| | /// - Parameter parameters: parameters dictionary. 68| 1| mutating func appendQueryParameters(_ parameters: [String: String]) { 69| 1| self = appendingQueryParameters(parameters) 70| 1| } 71| | 72| | /// SwifterSwift: Get value of a query key. 73| | /// 74| | /// var url = URL(string: "https://google.com?code=12345")! 75| | /// queryValue(for: "code") -> "12345" 76| | /// 77| | /// - Parameter key: The key of a query value. 78| 3| func queryValue(for key: String) -> String? { 79| 3| return URLComponents(string: absoluteString)? 80| 3| .queryItems? 81| 5| .first(where: { $0.name == key })? 82| 3| .value 83| 3| } 84| | 85| | /// SwifterSwift: Returns a new URL by removing all the path components. 86| | /// 87| | /// let url = URL(string: "https://domain.com/path/other")! 88| | /// print(url.deletingAllPathComponents()) // prints "https://domain.com/" 89| | /// 90| | /// - Returns: URL with all path components removed. 91| 1| func deletingAllPathComponents() -> URL { 92| 1| var url: URL = self 93| 2| for _ in 0.. URL? { 115| 9| if let scheme = scheme { 116| 6| let droppedScheme = String(absoluteString.dropFirst(scheme.count + 3)) 117| 6| return URL(string: droppedScheme) 118| 6| } 119| 3| 120| 3| guard host != nil else { return self } 121| 1| 122| 1| let droppedScheme = String(absoluteString.dropFirst(2)) 123| 1| return URL(string: droppedScheme) 124| 3| } 125| |} 126| | 127| |// MARK: - Methods 128| | 129| |public extension URL { 130| | #if os(iOS) || os(tvOS) 131| | /// SwifterSwift: Generate a thumbnail image from given url. Returns nil if no thumbnail could be created. This function may take some time to complete. It's recommended to dispatch the call if the thumbnail is not generated from a local resource. 132| | /// 133| | /// var url = URL(string: "https://video.golem.de/files/1/1/20637/wrkw0718-sd.mp4")! 134| | /// var thumbnail = url.thumbnail() 135| | /// thumbnail = url.thumbnail(fromTime: 5) 136| | /// 137| | /// DisptachQueue.main.async { 138| | /// someImageView.image = url.thumbnail() 139| | /// } 140| | /// 141| | /// - Parameter time: Seconds into the video where the image should be generated. 142| | /// - Returns: The UIImage result of the AVAssetImageGenerator 143| 3| func thumbnail(fromTime time: Float64 = 0) -> UIImage? { 144| 3| let imageGenerator = AVAssetImageGenerator(asset: AVAsset(url: self)) 145| 3| let time = CMTimeMakeWithSeconds(time, preferredTimescale: 1) 146| 3| var actualTime = CMTimeMake(value: 0, timescale: 0) 147| 3| 148| 3| guard let cgImage = try? imageGenerator.copyCGImage(at: time, actualTime: &actualTime) else { 149| 1| return nil 150| 2| } 151| 2| return UIImage(cgImage: cgImage) 152| 3| } 153| | #endif 154| |} 155| | 156| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/Foundation/URLRequestExtensions.swift: 1| |// URLRequestExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(Foundation) 4| |import Foundation 5| | 6| |#if canImport(FoundationNetworking) 7| |import FoundationNetworking 8| |#endif 9| | 10| |// MARK: - Initializers 11| | 12| |public extension URLRequest { 13| | /// SwifterSwift: Create URLRequest from URL string. 14| | /// 15| | /// - Parameter urlString: URL string to initialize URL request from 16| 2| init?(urlString: String) { 17| 2| guard let url = URL(string: urlString) else { return nil } 18| 1| self.init(url: url) 19| 1| } 20| | 21| | /// SwifterSwift: cURL command representation of this URL request. 22| 2| var curlString: String { 23| 2| guard let url = url else { return "" } 24| 2| 25| 2| var baseCommand = "curl \(url.absoluteString)" 26| 2| if httpMethod == "HEAD" { 27| 0| baseCommand += " --head" 28| 2| } 29| 2| 30| 2| var command = [baseCommand] 31| 2| if let method = httpMethod, method != "GET", method != "HEAD" { 32| 0| command.append("-X \(method)") 33| 2| } 34| 2| 35| 2| if let headers = allHTTPHeaderFields { 36| 0| for (key, value) in headers where key != "Cookie" { 37| 0| command.append("-H '\(key): \(value)'") 38| 0| } 39| 2| } 40| 2| 41| 2| if let data = httpBody, 42| 2| let body = String(data: data, encoding: .utf8) { 43| 0| command.append("-d '\(body)'") 44| 2| } 45| 2| 46| 2| return command.joined(separator: " \\\n\t") 47| 2| } 48| |} 49| | 50| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/Foundation/UserDefaultsExtensions.swift: 1| |// UserDefaultsExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(Foundation) && !os(Linux) 4| |import Foundation 5| | 6| |// MARK: - Methods 7| | 8| |public extension UserDefaults { 9| | /// SwifterSwift: get object from UserDefaults by using subscript 10| | /// 11| | /// - Parameter key: key in the current user's defaults database. 12| | subscript(key: String) -> Any? { 13| 4| get { 14| 4| return object(forKey: key) 15| 4| } 16| 1| set { 17| 1| set(newValue, forKey: key) 18| 1| } 19| | } 20| | 21| | /// SwifterSwift: Float from UserDefaults. 22| | /// 23| | /// - Parameter forKey: key to find float for. 24| | /// - Returns: Float object for key (if exists). 25| 2| func float(forKey key: String) -> Float? { 26| 2| return object(forKey: key) as? Float 27| 2| } 28| | 29| | /// SwifterSwift: Date from UserDefaults. 30| | /// 31| | /// - Parameter forKey: key to find date for. 32| | /// - Returns: Date object for key (if exists). 33| 2| func date(forKey key: String) -> Date? { 34| 2| return object(forKey: key) as? Date 35| 2| } 36| | 37| | /// SwifterSwift: Retrieves a Codable object from UserDefaults. 38| | /// 39| | /// - Parameters: 40| | /// - type: Class that conforms to the Codable protocol. 41| | /// - key: Identifier of the object. 42| | /// - decoder: Custom JSONDecoder instance. Defaults to `JSONDecoder()`. 43| | /// - Returns: Codable object for key (if exists). 44| 2| func object(_ type: T.Type, with key: String, usingDecoder decoder: JSONDecoder = JSONDecoder()) -> T? { 45| 2| guard let data = value(forKey: key) as? Data else { return nil } 46| 2| return try? decoder.decode(type.self, from: data) 47| 2| } 48| | 49| | /// SwifterSwift: Allows storing of Codable objects to UserDefaults. 50| | /// 51| | /// - Parameters: 52| | /// - object: Codable object to store. 53| | /// - key: Identifier of the object. 54| | /// - encoder: Custom JSONEncoder instance. Defaults to `JSONEncoder()`. 55| 2| func set(object: T, forKey key: String, usingEncoder encoder: JSONEncoder = JSONEncoder()) { 56| 2| let data = try? encoder.encode(object) 57| 2| set(data, forKey: key) 58| 2| } 59| |} 60| | 61| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/MapKit/MKMapViewExtensions.swift: 1| |// MKMapViewExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(MapKit) 4| |import MapKit 5| | 6| |#if !os(watchOS) 7| |@available(tvOS 9.2, *) 8| |public extension MKMapView { 9| | /// SwifterSwift: Dequeue reusable MKAnnotationView using class type 10| | /// 11| | /// - Parameters: 12| | /// - name: MKAnnotationView type. 13| | /// - Returns: optional MKAnnotationView object. 14| 0| func dequeueReusableAnnotationView(withClass name: T.Type) -> T? { 15| 0| return dequeueReusableAnnotationView(withIdentifier: String(describing: name)) as? T 16| 0| } 17| | 18| | /// SwifterSwift: Register MKAnnotationView using class type 19| | /// 20| | /// - Parameter name: MKAnnotationView type. 21| | @available(iOS 11.0, tvOS 11.0, macOS 10.13, *) 22| 0| func register(annotationViewWithClass name: T.Type) { 23| 0| register(T.self, forAnnotationViewWithReuseIdentifier: String(describing: name)) 24| 0| } 25| | 26| | /// SwifterSwift: Dequeue reusable MKAnnotationView using class type 27| | /// 28| | /// - Parameters: 29| | /// - name: MKAnnotationView type. 30| | /// - annotation: annotation of the mapView. 31| | /// - Returns: optional MKAnnotationView object. 32| | @available(iOS 11.0, tvOS 11.0, macOS 10.13, *) 33| | func dequeueReusableAnnotationView(withClass name: T.Type, 34| 0| for annotation: MKAnnotation) -> T? { 35| 0| guard let annotationView = dequeueReusableAnnotationView( 36| 0| withIdentifier: String(describing: name), 37| 0| for: annotation) as? T else { 38| 0| fatalError("Couldn't find MKAnnotationView for \(String(describing: name))") 39| 0| } 40| 0| 41| 0| return annotationView 42| 0| } 43| | 44| | /// SwifterSwift: Zooms in on multiple mapView coordinates. 45| | /// 46| | /// - Parameters: 47| | /// - coordinates: Gets the array of type CLLocationCoordinate2D. 48| | /// - meter: If arrays have a single item, they take the value of meters (Double). The map zooms in at the given meters. 49| | /// - edgePadding: The amount of additional space (measured in screen points) to make visible around the specified rectangle 50| | /// - animated: The animation control takes the Boolean value. Enter the true value for zooming with the animation. 51| 0| func zoom(to coordinates: [CLLocationCoordinate2D], meter: Double, edgePadding: EdgeInsets, animated: Bool) { 52| 0| guard !coordinates.isEmpty else { return } 53| 0| 54| 0| if coordinates.count == 1 { 55| 0| let coordinateRegion = MKCoordinateRegion( 56| 0| center: coordinates.first!, 57| 0| latitudinalMeters: meter, 58| 0| longitudinalMeters: meter) 59| 0| setRegion(coordinateRegion, animated: true) 60| 0| } else { 61| 0| let mkPolygon = MKPolygon(coordinates: coordinates, count: coordinates.count) 62| 0| setVisibleMapRect(mkPolygon.boundingMapRect, edgePadding: edgePadding, animated: animated) 63| 0| } 64| 0| } 65| |} 66| | 67| |#endif 68| | 69| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/MapKit/MKPolylineExtensions.swift: 1| |// MKPolylineExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(MapKit) && !os(watchOS) 4| |import MapKit 5| | 6| |// MARK: - Initializers 7| | 8| |@available(tvOS 9.2, *) 9| |public extension MKPolyline { 10| | /// SwifterSwift: Create a new MKPolyline from a provided Array of coordinates. 11| | /// 12| | /// - Parameter coordinates: Array of CLLocationCoordinate2D(s). 13| 2| convenience init(coordinates: [CLLocationCoordinate2D]) { 14| 2| var refCoordinates = coordinates 15| 2| self.init(coordinates: &refCoordinates, count: refCoordinates.count) 16| 2| } 17| |} 18| | 19| |// MARK: - Properties 20| | 21| |@available(tvOS 9.2, *) 22| |public extension MKPolyline { 23| | /// SwifterSwift: Return an Array of coordinates representing the provided polyline. 24| 4| var coordinates: [CLLocationCoordinate2D] { 25| 4| var coords = [CLLocationCoordinate2D](repeating: kCLLocationCoordinate2DInvalid, count: pointCount) 26| 4| getCoordinates(&coords, range: NSRange(location: 0, length: pointCount)) 27| 4| return coords 28| 4| } 29| |} 30| | 31| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/SceneKit/SCNBoxExtensions.swift: 1| |// SCNBoxExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(SceneKit) 4| |import SceneKit 5| | 6| |// MARK: - Methods 7| | 8| |public extension SCNBox { 9| | /// SwifterSwift: Creates a box geometry with the specified width, height, and length. 10| | /// 11| | /// - Parameters: 12| | /// - width: The width of the box along the x-axis of its local coordinate space. 13| | /// - height: The height of the box along the y-axis of its local coordinate space. 14| | /// - length: The length of the box along the z-axis of its local coordinate space. 15| 1| convenience init(width: CGFloat, height: CGFloat, length: CGFloat) { 16| 1| self.init(width: width, height: height, length: length, chamferRadius: 0) 17| 1| } 18| | 19| | /// SwifterSwift: Creates a cube geometry with the specified side length, and chamfer radius. 20| | /// 21| | /// - Parameters: 22| | /// - sideLength: The width, height, and length of the box in its local coordinate space. 23| | /// - chamferRadius: The radius of curvature for the edges and corners of the box. 24| 1| convenience init(sideLength: CGFloat, chamferRadius: CGFloat = 0) { 25| 1| self.init(width: sideLength, height: sideLength, length: sideLength, chamferRadius: chamferRadius) 26| 1| } 27| | 28| | /// SwifterSwift: Creates a box geometry with the specified width, height, length, chamfer radius, and material. 29| | /// 30| | /// - Parameters: 31| | /// - width: The width of the box along the x-axis of its local coordinate space. 32| | /// - height: The height of the box along the y-axis of its local coordinate space. 33| | /// - length: The length of the box along the z-axis of its local coordinate space. 34| | /// - chamferRadius: The radius of curvature for the edges and corners of the box. 35| | /// - material: The material of the geometry. 36| | convenience init( 37| | width: CGFloat, 38| | height: CGFloat, 39| | length: CGFloat, 40| | chamferRadius: CGFloat = 0, 41| 1| material: SCNMaterial) { 42| 1| self.init(width: width, height: height, length: length, chamferRadius: chamferRadius) 43| 1| materials = [material] 44| 1| } 45| | 46| | /// SwifterSwift: Creates a cube geometry with the specified side length, chamfer radius, and material. 47| | /// 48| | /// - Parameters: 49| | /// - sideLength: The width, height, and length of the box in its local coordinate space. 50| | /// - chamferRadius: The radius of curvature for the edges and corners of the box. 51| | /// - material: The material of the geometry. 52| 1| convenience init(sideLength: CGFloat, chamferRadius: CGFloat = 0, material: SCNMaterial) { 53| 1| self.init(width: sideLength, height: sideLength, length: sideLength, chamferRadius: chamferRadius) 54| 1| materials = [material] 55| 1| } 56| | 57| | /// SwifterSwift: Creates a box geometry with the specified width, height, length, chamfer radius, and material color. 58| | /// 59| | /// - Parameters: 60| | /// - width: The width of the box along the x-axis of its local coordinate space. 61| | /// - height: The height of the box along the y-axis of its local coordinate space. 62| | /// - length: The length of the box along the z-axis of its local coordinate space. 63| | /// - chamferRadius: The radius of curvature for the edges and corners of the box. 64| | /// - color: The color of the geometry's material. 65| 1| convenience init(width: CGFloat, height: CGFloat, length: CGFloat, chamferRadius: CGFloat = 0, color: Color) { 66| 1| self.init(width: width, height: height, length: length, chamferRadius: chamferRadius) 67| 1| materials = [SCNMaterial(color: color)] 68| 1| } 69| | 70| | /// SwifterSwift: Creates a cube geometry with the specified side length, chamfer radius, and material color. 71| | /// 72| | /// - Parameters: 73| | /// - sideLength: The width, height, and length of the box in its local coordinate space. 74| | /// - chamferRadius: The radius of curvature for the edges and corners of the box. 75| | /// - color: The color of the geometry's material. 76| 1| convenience init(sideLength: CGFloat, chamferRadius: CGFloat = 0, color: Color) { 77| 1| self.init(width: sideLength, height: sideLength, length: sideLength, chamferRadius: chamferRadius) 78| 1| materials = [SCNMaterial(color: color)] 79| 1| } 80| |} 81| | 82| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/SceneKit/SCNCapsuleExtensions.swift: 1| |// SCNCapsuleExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(SceneKit) 4| |import SceneKit 5| | 6| |// MARK: - Methods 7| | 8| |public extension SCNCapsule { 9| | /// SwifterSwift: Creates a capsule geometry with the specified diameter and height. 10| | /// 11| | /// - Parameters: 12| | /// - capDiameter: The diameter both of the capsule’s cylindrical body and of its hemispherical ends. 13| | /// - height: The height of the capsule along the y-axis of its local coordinate space. 14| 1| convenience init(capDiameter: CGFloat, height: CGFloat) { 15| 1| self.init(capRadius: capDiameter / 2, height: height) 16| 1| } 17| | 18| | /// SwifterSwift: Creates a capsule geometry with the specified radius and height. 19| | /// 20| | /// - Parameters: 21| | /// - capRadius: The radius both of the capsule’s cylindrical body and of its hemispherical ends. 22| | /// - height: The height of the capsule along the y-axis of its local coordinate space. 23| | /// - material: The material of the geometry. 24| 1| convenience init(capRadius: CGFloat, height: CGFloat, material: SCNMaterial) { 25| 1| self.init(capRadius: capRadius, height: height) 26| 1| materials = [material] 27| 1| } 28| | 29| | /// SwifterSwift: Creates a capsule geometry with the specified diameter and height. 30| | /// 31| | /// - Parameters: 32| | /// - capDiameter: The diameter both of the capsule’s cylindrical body and of its hemispherical ends. 33| | /// - height: The height of the capsule along the y-axis of its local coordinate space. 34| | /// - material: The material of the geometry. 35| 1| convenience init(capDiameter: CGFloat, height: CGFloat, material: SCNMaterial) { 36| 1| self.init(capRadius: capDiameter / 2, height: height) 37| 1| materials = [material] 38| 1| } 39| | 40| | /// SwifterSwift: Creates a capsule geometry with the specified radius and height. 41| | /// 42| | /// - Parameters: 43| | /// - capRadius: The radius both of the capsule’s cylindrical body and of its hemispherical ends. 44| | /// - height: The height of the capsule along the y-axis of its local coordinate space. 45| | /// - material: The material of the geometry. 46| 1| convenience init(capRadius: CGFloat, height: CGFloat, color: Color) { 47| 1| self.init(capRadius: capRadius, height: height) 48| 1| materials = [SCNMaterial(color: color)] 49| 1| } 50| | 51| | /// SwifterSwift: Creates a capsule geometry with the specified diameter and height. 52| | /// 53| | /// - Parameters: 54| | /// - capDiameter: The diameter both of the capsule’s cylindrical body and of its hemispherical ends. 55| | /// - height: The height of the capsule along the y-axis of its local coordinate space. 56| | /// - material: The material of the geometry. 57| 1| convenience init(capDiameter: CGFloat, height: CGFloat, color: Color) { 58| 1| self.init(capRadius: capDiameter / 2, height: height) 59| 1| materials = [SCNMaterial(color: color)] 60| 1| } 61| |} 62| | 63| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/SceneKit/SCNConeExtensions.swift: 1| |// SCNConeExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(SceneKit) 4| |import SceneKit 5| | 6| |// MARK: - Methods 7| | 8| |public extension SCNCone { 9| | /// SwifterSwift: Creates a cone geometry with the given top diameter, bottom diameter, and height. 10| | /// 11| | /// - Parameters: 12| | /// - topDiameter: The diameter of the cone’s top, forming a circle in the x- and z-axis dimensions of its local coordinate space. 13| | /// - bottomDiameter: The diameter of the cone’s base, forming a circle in the x- and z-axis dimensions of its local coordinate space. 14| | /// - height: The height of the cone along the y-axis of its local coordinate space. 15| 1| convenience init(topDiameter: CGFloat, bottomDiameter: CGFloat, height: CGFloat) { 16| 1| self.init(topRadius: topDiameter / 2, bottomRadius: bottomDiameter / 2, height: height) 17| 1| } 18| | 19| | /// SwifterSwift: Creates a cone geometry with the given top radius, bottom radius, height, and material. 20| | /// 21| | /// - Parameters: 22| | /// - topRadius: The radius of the cone’s top, forming a circle in the x- and z-axis dimensions of its local coordinate space. 23| | /// - bottomRadius: The radius of the cone’s base, forming a circle in the x- and z-axis dimensions of its local coordinate space. 24| | /// - height: The height of the cone along the y-axis of its local coordinate space. 25| | /// - material: The material of the geometry. 26| 1| convenience init(topRadius: CGFloat, bottomRadius: CGFloat, height: CGFloat, material: SCNMaterial) { 27| 1| self.init(topRadius: topRadius, bottomRadius: bottomRadius, height: height) 28| 1| materials = [material] 29| 1| } 30| | 31| | /// SwifterSwift: Creates a cone geometry with the given top diameter, bottom diameter, height, and material. 32| | /// 33| | /// - Parameters: 34| | /// - topDiameter: The diameter of the cone’s top, forming a circle in the x- and z-axis dimensions of its local coordinate space. 35| | /// - bottomDiameter: The diameter of the cone’s base, forming a circle in the x- and z-axis dimensions of its local coordinate space. 36| | /// - height: The height of the cone along the y-axis of its local coordinate space. 37| | /// - material: The material of the geometry. 38| 1| convenience init(topDiameter: CGFloat, bottomDiameter: CGFloat, height: CGFloat, material: SCNMaterial) { 39| 1| self.init(topRadius: topDiameter / 2, bottomRadius: bottomDiameter / 2, height: height) 40| 1| materials = [material] 41| 1| } 42| | 43| | /// SwifterSwift: Creates a cone geometry with the given top radius, bottom radius, height, and material. 44| | /// 45| | /// - Parameters: 46| | /// - topRadius: The radius of the cone’s top, forming a circle in the x- and z-axis dimensions of its local coordinate space. 47| | /// - bottomRadius: The radius of the cone’s base, forming a circle in the x- and z-axis dimensions of its local coordinate space. 48| | /// - height: The height of the cone along the y-axis of its local coordinate space. 49| | /// - color: The color of the geometry's material. 50| 1| convenience init(topRadius: CGFloat, bottomRadius: CGFloat, height: CGFloat, color: Color) { 51| 1| self.init(topRadius: topRadius, bottomRadius: bottomRadius, height: height) 52| 1| materials = [SCNMaterial(color: color)] 53| 1| } 54| | 55| | /// SwifterSwift: Creates a cone geometry with the given top diameter, bottom diameter, height, and material. 56| | /// 57| | /// - Parameters: 58| | /// - topDiameter: The diameter of the cone’s top, forming a circle in the x- and z-axis dimensions of its local coordinate space. 59| | /// - bottomDiameter: The diameter of the cone’s base, forming a circle in the x- and z-axis dimensions of its local coordinate space. 60| | /// - height: The height of the cone along the y-axis of its local coordinate space. 61| | /// - color: The color of the geometry's material. 62| 1| convenience init(topDiameter: CGFloat, bottomDiameter: CGFloat, height: CGFloat, color: Color) { 63| 1| self.init(topRadius: topDiameter / 2, bottomRadius: bottomDiameter / 2, height: height) 64| 1| materials = [SCNMaterial(color: color)] 65| 1| } 66| |} 67| | 68| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/SceneKit/SCNCylinderExtensions.swift: 1| |// SCNCylinderExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(SceneKit) 4| |import SceneKit 5| | 6| |// MARK: - Methods 7| | 8| |public extension SCNCylinder { 9| | /// SwifterSwift: Creates a cylinder geometry with the specified diameter and height. 10| | /// 11| | /// - Parameters: 12| | /// - radius: The radius of the cylinder’s circular cross section in the x- and z-axis dimensions of its local coordinate space. 13| | /// - height: The height of the cylinder along the y-axis of its local coordinate space. 14| | /// - material: The material of the geometry. 15| 1| convenience init(diameter: CGFloat, height: CGFloat) { 16| 1| self.init(radius: diameter / 2, height: height) 17| 1| } 18| | 19| | /// SwifterSwift: Creates a cylinder geometry with the specified radius, height and material. 20| | /// 21| | /// - Parameters: 22| | /// - radius: The radius of the cylinder’s circular cross section in the x- and z-axis dimensions of its local coordinate space. 23| | /// - height: The height of the cylinder along the y-axis of its local coordinate space. 24| | /// - material: The material of the geometry. 25| 0| convenience init(radius: CGFloat, height: CGFloat, material: SCNMaterial) { 26| 0| self.init(radius: radius, height: height) 27| 0| materials = [material] 28| 0| } 29| | 30| | /// SwifterSwift: Creates a cylinder geometry with the specified diameter, height and material. 31| | /// 32| | /// - Parameters: 33| | /// - radius: The radius of the cylinder’s circular cross section in the x- and z-axis dimensions of its local coordinate space. 34| | /// - height: The height of the cylinder along the y-axis of its local coordinate space. 35| | /// - material: The material of the geometry. 36| 2| convenience init(diameter: CGFloat, height: CGFloat, material: SCNMaterial) { 37| 2| self.init(radius: diameter / 2, height: height) 38| 2| materials = [material] 39| 2| } 40| | 41| | /// SwifterSwift: Creates a cylinder geometry with the specified radius, height, and material color. 42| | /// 43| | /// - Parameters: 44| | /// - radius: The radius of the cylinder’s circular cross section in the x- and z-axis dimensions of its local coordinate space. 45| | /// - height: The height of the cylinder along the y-axis of its local coordinate space. 46| | /// - color: The color of the geometry's material. 47| 1| convenience init(radius: CGFloat, height: CGFloat, color: Color) { 48| 1| self.init(radius: radius, height: height) 49| 1| materials = [SCNMaterial(color: color)] 50| 1| } 51| | 52| | /// SwifterSwift: Creates a cylinder geometry with the specified diameter, height, and material color. 53| | /// 54| | /// - Parameters: 55| | /// - diameter: The diameter of the cylinder’s circular cross section in the x- and z-axis dimensions of its local coordinate space. 56| | /// - height: The height of the cylinder along the y-axis of its local coordinate space. 57| | /// - color: The color of the geometry's material. 58| 1| convenience init(diameter: CGFloat, height: CGFloat, color: Color) { 59| 1| self.init(radius: diameter / 2, height: height) 60| 1| materials = [SCNMaterial(color: color)] 61| 1| } 62| |} 63| | 64| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/SceneKit/SCNGeometryExtensions.swift: 1| |// SCNGeometryExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(SceneKit) 4| |import SceneKit 5| | 6| |// MARK: - Properties 7| | 8| |public extension SCNGeometry { 9| | /// SwifterSwift: Returns the size of the geometry's bounding box. 10| 20| var boundingSize: SCNVector3 { 11| 20| return (boundingBox.max - boundingBox.min).absolute 12| 20| } 13| |} 14| | 15| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/SceneKit/SCNMaterialExtensions.swift: 1| |// SCNMaterialExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(SceneKit) 4| |import SceneKit 5| | 6| |// MARK: - Methods 7| | 8| |public extension SCNMaterial { 9| | /// SwifterSwift: Initializes a SCNMaterial with a specific diffuse color 10| | /// 11| | /// - Parameter color: diffuse color 12| 27| convenience init(color: Color) { 13| 27| self.init() 14| 27| diffuse.contents = color 15| 27| } 16| |} 17| | 18| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/SceneKit/SCNPlaneExtensions.swift: 1| |// SCNPlaneExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(SceneKit) 4| |import SceneKit 5| | 6| |// MARK: - Methods 7| | 8| |public extension SCNPlane { 9| | /// SwifterSwift: Creates a square plane geometry with the specified width. 10| | /// 11| | /// - Parameter width: The width and height of the plane along the x-axis and y-axis of its local coordinate space. 12| 1| convenience init(width: CGFloat) { 13| 1| self.init(width: width, height: width) 14| 1| } 15| | 16| | /// SwifterSwift: Creates a plane geometry with the specified width, height and material. 17| | /// 18| | /// - Parameters: 19| | /// - width: The width of the plane along the x-axis of its local coordinate space. 20| | /// - height: The height of the plane along the y-axis of its local coordinate space. 21| | /// - material: The material of the geometry. 22| 1| convenience init(width: CGFloat, height: CGFloat, material: SCNMaterial) { 23| 1| self.init(width: width, height: height) 24| 1| materials = [material] 25| 1| } 26| | 27| | /// SwifterSwift: Creates a square plane geometry with the specified width and material. 28| | /// 29| | /// - Parameters: 30| | /// - width: The width and height of the plane along the x-axis and y-axis of its local coordinate space. 31| | /// - material: The material of the geometry. 32| 1| convenience init(width: CGFloat, material: SCNMaterial) { 33| 1| self.init(width: width, height: width) 34| 1| materials = [material] 35| 1| } 36| | 37| | /// SwifterSwift: Creates a plane geometry with the specified width, height and material color. 38| | /// 39| | /// - Parameters: 40| | /// - width: The width of the plane along the x-axis of its local coordinate space. 41| | /// - height: The height of the plane along the y-axis of its local coordinate space. 42| | /// - color: The color of the geometry's material. 43| 1| convenience init(width: CGFloat, height: CGFloat, color: Color) { 44| 1| self.init(width: width, height: height) 45| 1| materials = [SCNMaterial(color: color)] 46| 1| } 47| | 48| | /// SwifterSwift: Creates a square plane geometry with the specified width and material color. 49| | /// 50| | /// - Parameters: 51| | /// - width: The width and height of the plane along the x-axis and y-axis of its local coordinate space. 52| | /// - color: The color of the geometry's material. 53| 1| convenience init(width: CGFloat, color: Color) { 54| 1| self.init(width: width, height: width) 55| 1| materials = [SCNMaterial(color: color)] 56| 1| } 57| |} 58| | 59| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/SceneKit/SCNShapeExtensions.swift: 1| |// SCNShapeExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(SceneKit) 4| |import SceneKit 5| | 6| |#if canImport(UIKit) 7| |import UIKit 8| |#endif 9| | 10| |// MARK: - Methods 11| | 12| |public extension SCNShape { 13| | #if canImport(UIKit) 14| | 15| | /// SwifterSwift: Creates a shape geometry with the specified path, extrusion depth, and material. 16| | /// 17| | /// - Parameters: 18| | /// - path: The two-dimensional path forming the basis of the shape. 19| | /// - extrusionDepth: The thickness of the extruded shape along the z-axis. 20| | /// - material: The material of the geometry. 21| 1| convenience init(path: UIBezierPath, extrusionDepth: CGFloat, material: SCNMaterial) { 22| 1| self.init(path: path, extrusionDepth: extrusionDepth) 23| 1| materials = [material] 24| 1| } 25| | 26| | /// SwifterSwift: Creates a shape geometry with the specified path, extrusion depth, and material. 27| | /// 28| | /// - Parameters: 29| | /// - path: The two-dimensional path forming the basis of the shape. 30| | /// - extrusionDepth: The thickness of the extruded shape along the z-axis. 31| | /// - color: The color of the geometry's material. 32| 1| convenience init(path: UIBezierPath, extrusionDepth: CGFloat, color: Color) { 33| 1| self.init(path: path, extrusionDepth: extrusionDepth) 34| 1| materials = [SCNMaterial(color: color)] 35| 1| } 36| | 37| | #endif 38| |} 39| | 40| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/SceneKit/SCNSphereExtensions.swift: 1| |// SCNSphereExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(SceneKit) 4| |import SceneKit 5| | 6| |// MARK: - Methods 7| | 8| |public extension SCNSphere { 9| | /// SwifterSwift: Creates a sphere geometry with the specified diameter. 10| | /// 11| | /// - Parameter diameter: The diameter of the sphere in its local coordinate space. 12| 1| convenience init(diameter: CGFloat) { 13| 1| self.init(radius: diameter / 2) 14| 1| } 15| | 16| | /// SwifterSwift: Creates a sphere geometry with the specified radius and material. 17| | /// 18| | /// - Parameters: 19| | /// - radius: The radius of the sphere in its local coordinate space. 20| | /// - material: The material of the geometry. 21| 2| convenience init(radius: CGFloat, material: SCNMaterial) { 22| 2| self.init(radius: radius) 23| 2| materials = [material] 24| 2| } 25| | 26| | /// SwifterSwift: Creates a sphere geometry with the specified radius and material color. 27| | /// 28| | /// - Parameters: 29| | /// - radius: The radius of the sphere in its local coordinate space. 30| | /// - color: The color of the geometry's material. 31| 1| convenience init(radius: CGFloat, color: Color) { 32| 1| self.init(radius: radius, material: SCNMaterial(color: color)) 33| 1| } 34| | 35| | /// SwifterSwift: Creates a sphere geometry with the specified diameter and material. 36| | /// 37| | /// - Parameters: 38| | /// - diameter: The diameter of the sphere in its local coordinate space. 39| | /// - material: The material of the geometry. 40| 2| convenience init(diameter: CGFloat, material: SCNMaterial) { 41| 2| self.init(radius: diameter / 2) 42| 2| materials = [material] 43| 2| } 44| | 45| | /// SwifterSwift: Creates a sphere geometry with the specified diameter and material color. 46| | /// 47| | /// - Parameters: 48| | /// - diameter: The diameter of the sphere in its local coordinate space. 49| | /// - color: The color of the geometry's material. 50| 1| convenience init(diameter: CGFloat, color: Color) { 51| 1| self.init(diameter: diameter, material: SCNMaterial(color: color)) 52| 1| } 53| |} 54| | 55| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/SceneKit/SCNVector3Extensions.swift: 1| |// SCNVector3Extensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if os(OSX) 4| |/// SwifterSwift: CGFloat. 5| |public typealias SceneKitFloat = CGFloat 6| |#else 7| |/// SwifterSwift: Float. 8| |public typealias SceneKitFloat = Float 9| |#endif 10| | 11| |#if canImport(SceneKit) 12| |import SceneKit 13| | 14| |// MARK: - Methods 15| | 16| |public extension SCNVector3 { 17| | /// SwifterSwift: Returns the absolute values of the vector's components. 18| | /// 19| | /// SCNVector3(2, -3, -6).abs -> SCNVector3(2, 3, 6) 20| | /// 21| 21| var absolute: SCNVector3 { 22| 21| return SCNVector3(abs(x), abs(y), abs(z)) 23| 21| } 24| | 25| | /// SwifterSwift: Returns the length of the vector. 26| | /// 27| | /// SCNVector3(2, 3, 6).length -> 7 28| | /// 29| 2| var length: SceneKitFloat { 30| 2| return sqrt(pow(x, 2) + pow(y, 2) + pow(z, 2)) 31| 2| } 32| | 33| | /// SwifterSwift: Returns the unit or normalized vector where `length = 1`. 34| | /// 35| | /// SCNVector3(2, 3, 6).normalized -> SCNVector3(2/7, 3/7, 6/7) 36| | /// 37| 2| var normalized: SCNVector3 { 38| 2| let length = sqrt(pow(x, 2) + pow(y, 2) + pow(z, 2)) 39| 2| return SCNVector3(x / length, y / length, z / length) 40| 2| } 41| |} 42| | 43| |// MARK: - Operators 44| | 45| |public extension SCNVector3 { 46| | /// SwifterSwift: Add two SCNVector3s. 47| | /// 48| | /// SCNVector3(10, 10, 10) + SCNVector3(10, 20, -30) -> SCNVector3(20, 30, -20) 49| | /// 50| | /// - Parameters: 51| | /// - lhs: SCNVector3 to add to. 52| | /// - rhs: SCNVector3 to add. 53| | /// - Returns: result of addition of the two given SCNVector3s. 54| 1| static func + (lhs: SCNVector3, rhs: SCNVector3) -> SCNVector3 { 55| 1| return SCNVector3(lhs.x + rhs.x, lhs.y + rhs.y, lhs.z + rhs.z) 56| 1| } 57| | 58| | /// SwifterSwift: Add a SCNVector3 to self. 59| | /// 60| | /// SCNVector3(10, 10, 10) += SCNVector3(10, 20, -30) -> SCNVector3(20, 30, -20) 61| | /// 62| | /// - Parameters: 63| | /// - lhs: self 64| | /// - rhs: SCNVector3 to add. 65| 1| static func += (lhs: inout SCNVector3, rhs: SCNVector3) { 66| 1| lhs.x += rhs.x 67| 1| lhs.y += rhs.y 68| 1| lhs.z += rhs.z 69| 1| } 70| | 71| | /// SwifterSwift: Subtract two SCNVector3s. 72| | /// 73| | /// SCNVector3(10, 10, 10) - SCNVector3(10, 20, -30) -> SCNVector3(0, -10, 40) 74| | /// 75| | /// - Parameters: 76| | /// - lhs: SCNVector3 to subtract from. 77| | /// - rhs: SCNVector3 to subtract. 78| | /// - Returns: result of subtract of the two given SCNVector3s. 79| 21| static func - (lhs: SCNVector3, rhs: SCNVector3) -> SCNVector3 { 80| 21| return SCNVector3(lhs.x - rhs.x, lhs.y - rhs.y, lhs.z - rhs.z) 81| 21| } 82| | 83| | /// SwifterSwift: Subtract a SCNVector3 from self. 84| | /// 85| | /// SCNVector3(10, 10, 10) -= SCNVector3(10, 20, -30) -> SCNVector3(0, -10, 40) 86| | /// 87| | /// - Parameters: 88| | /// - lhs: self 89| | /// - rhs: SCNVector3 to subtract. 90| 1| static func -= (lhs: inout SCNVector3, rhs: SCNVector3) { 91| 1| lhs.x -= rhs.x 92| 1| lhs.y -= rhs.y 93| 1| lhs.z -= rhs.z 94| 1| } 95| | 96| | /// SwifterSwift: Multiply a SCNVector3 with a scalar 97| | /// 98| | /// SCNVector3(10, 20, -30) * 3 -> SCNVector3(30, 60, -90) 99| | /// 100| | /// - Parameters: 101| | /// - vector: SCNVector3 to multiply. 102| | /// - scalar: scalar value. 103| | /// - Returns: result of multiplication of the given SCNVector3 with the scalar. 104| 1| static func * (vector: SCNVector3, scalar: SceneKitFloat) -> SCNVector3 { 105| 1| return SCNVector3(vector.x * scalar, vector.y * scalar, vector.z * scalar) 106| 1| } 107| | 108| | /// SwifterSwift: Multiply self with a scalar 109| | /// 110| | /// SCNVector3(10, 20, -30) *= 3 -> SCNVector3(30, 60, -90) 111| | /// 112| | /// - Parameters: 113| | /// - vector: self. 114| | /// - scalar: scalar value. 115| | /// - Returns: result of multiplication of the given CGPoint with the scalar. 116| 1| static func *= (vector: inout SCNVector3, scalar: SceneKitFloat) { 117| 1| vector.x *= scalar 118| 1| vector.y *= scalar 119| 1| vector.z *= scalar 120| 1| } 121| | 122| | /// SwifterSwift: Multiply a scalar with a SCNVector3 123| | /// 124| | /// 3 * SCNVector3(10, 20, -30) -> SCNVector3(30, 60, -90) 125| | /// 126| | /// - Parameters: 127| | /// - scalar: scalar value. 128| | /// - vector: SCNVector3 to multiply. 129| | /// - Returns: result of multiplication of the given CGPoint with the scalar. 130| 1| static func * (scalar: SceneKitFloat, vector: SCNVector3) -> SCNVector3 { 131| 1| return SCNVector3(vector.x * scalar, vector.y * scalar, vector.z * scalar) 132| 1| } 133| | 134| | /// SwifterSwift: Divide a SCNVector3 with a scalar 135| | /// 136| | /// SCNVector3(10, 20, -30) / 3 -> SCNVector3(3/10, 0.15, -30) 137| | /// 138| | /// - Parameters: 139| | /// - vector: SCNVector3 to divide. 140| | /// - scalar: scalar value. 141| | /// - Returns: result of division of the given SCNVector3 with the scalar. 142| 1| static func / (vector: SCNVector3, scalar: SceneKitFloat) -> SCNVector3 { 143| 1| return SCNVector3(vector.x / scalar, vector.y / scalar, vector.z / scalar) 144| 1| } 145| | 146| | /// SwifterSwift: Divide self with a scalar 147| | /// 148| | /// SCNVector3(10, 20, -30) /= 3 -> SCNVector3(3/10, 0.15, -30) 149| | /// 150| | /// - Parameters: 151| | /// - vector: self. 152| | /// - scalar: scalar value. 153| | /// - Returns: result of division of the given CGPoint with the scalar. 154| 1| static func /= (vector: inout SCNVector3, scalar: SceneKitFloat) { 155| 1| vector = SCNVector3(vector.x / scalar, vector.y / scalar, vector.z / scalar) 156| 1| } 157| |} 158| | 159| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/Shared/ColorExtensions.swift: 1| |// ColorExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if !os(Linux) 4| | 5| |#if canImport(UIKit) 6| |import UIKit 7| |/// SwifterSwift: Color 8| |public typealias Color = UIColor 9| |#endif 10| | 11| |#if canImport(AppKit) && !targetEnvironment(macCatalyst) 12| |import AppKit 13| |/// SwifterSwift: Color 14| |public typealias Color = NSColor 15| |#endif 16| | 17| |#if !os(watchOS) 18| |import CoreImage 19| |#endif 20| | 21| |// MARK: - Properties 22| | 23| |public extension Color { 24| | /// SwifterSwift: Random color. 25| 2| static var random: Color { 26| 2| let red = Int.random(in: 0...255) 27| 2| let green = Int.random(in: 0...255) 28| 2| let blue = Int.random(in: 0...255) 29| 2| return Color(red: red, green: green, blue: blue)! 30| 2| } 31| | 32| | // swiftlint:disable large_tuple 33| | /// SwifterSwift: RGB components for a Color (between 0 and 255). 34| | /// 35| | /// UIColor.red.rgbComponents.red -> 255 36| | /// NSColor.green.rgbComponents.green -> 255 37| | /// UIColor.blue.rgbComponents.blue -> 255 38| | /// 39| 46| var rgbComponents: (red: Int, green: Int, blue: Int) { 40| 46| let components: [CGFloat] = { 41| 46| let comps: [CGFloat] = cgColor.components! 42| 46| guard comps.count != 4 else { return comps } 43| 6| return [comps[0], comps[0], comps[0], comps[1]] 44| 46| }() 45| 46| let red = components[0] 46| 46| let green = components[1] 47| 46| let blue = components[2] 48| 46| return (red: Int(red * 255.0), green: Int(green * 255.0), blue: Int(blue * 255.0)) 49| 46| } 50| | 51| | // swiftlint:enable large_tuple 52| | 53| | // swiftlint:disable large_tuple 54| | /// SwifterSwift: RGB components for a Color represented as CGFloat numbers (between 0 and 1) 55| | /// 56| | /// UIColor.red.rgbComponents.red -> 1.0 57| | /// NSColor.green.rgbComponents.green -> 1.0 58| | /// UIColor.blue.rgbComponents.blue -> 1.0 59| | /// 60| 16| var cgFloatComponents: (red: CGFloat, green: CGFloat, blue: CGFloat) { 61| 16| let components: [CGFloat] = { 62| 16| let comps: [CGFloat] = cgColor.components! 63| 16| guard comps.count != 4 else { return comps } 64| 6| return [comps[0], comps[0], comps[0], comps[1]] 65| 16| }() 66| 16| let red = components[0] 67| 16| let green = components[1] 68| 16| let blue = components[2] 69| 16| return (red: red, green: green, blue: blue) 70| 16| } 71| | 72| | // swiftlint:enable large_tuple 73| | 74| | // swiftlint:disable large_tuple 75| | /// SwifterSwift: Get components of hue, saturation, and brightness, and alpha (read-only). 76| 21| var hsbaComponents: (hue: CGFloat, saturation: CGFloat, brightness: CGFloat, alpha: CGFloat) { 77| 21| var hue: CGFloat = 0.0 78| 21| var saturation: CGFloat = 0.0 79| 21| var brightness: CGFloat = 0.0 80| 21| var alpha: CGFloat = 0.0 81| 21| 82| 21| getHue(&hue, saturation: &saturation, brightness: &brightness, alpha: &alpha) 83| 21| return (hue: hue, saturation: saturation, brightness: brightness, alpha: alpha) 84| 21| } 85| | 86| | // swiftlint:enable large_tuple 87| | 88| | /// SwifterSwift: Hexadecimal value string (read-only). 89| 9| var hexString: String { 90| 9| let components: [Int] = { 91| 34| let comps = cgColor.components!.map { Int($0 * 255.0) } 92| 9| guard comps.count != 4 else { return comps } 93| 1| return [comps[0], comps[0], comps[0], comps[1]] 94| 9| }() 95| 9| return String(format: "#%02X%02X%02X", components[0], components[1], components[2]) 96| 9| } 97| | 98| | /// SwifterSwift: Short hexadecimal value string (read-only, if applicable). 99| 4| var shortHexString: String? { 100| 4| let string = hexString.replacingOccurrences(of: "#", with: "") 101| 4| let chrs = Array(string) 102| 4| guard chrs[0] == chrs[1], chrs[2] == chrs[3], chrs[4] == chrs[5] else { return nil } 103| 2| return "#\(chrs[0])\(chrs[2])\(chrs[4])" 104| 4| } 105| | 106| | /// SwifterSwift: Short hexadecimal value string, or full hexadecimal string if not possible (read-only). 107| 4| var shortHexOrHexString: String { 108| 4| let components: [Int] = { 109| 16| let comps = cgColor.components!.map { Int($0 * 255.0) } 110| 4| guard comps.count != 4 else { return comps } 111| 0| return [comps[0], comps[0], comps[0], comps[1]] 112| 4| }() 113| 4| let hexString = String(format: "#%02X%02X%02X", components[0], components[1], components[2]) 114| 4| let string = hexString.replacingOccurrences(of: "#", with: "") 115| 4| let chrs = Array(string) 116| 4| guard chrs[0] == chrs[1], chrs[2] == chrs[3], chrs[4] == chrs[5] else { return hexString } 117| 2| return "#\(chrs[0])\(chrs[2])\(chrs[4])" 118| 4| } 119| | 120| | /// SwifterSwift: Alpha of Color (read-only). 121| 13| var alpha: CGFloat { 122| 13| return cgColor.alpha 123| 13| } 124| | 125| | #if !os(watchOS) 126| | /// SwifterSwift: CoreImage.CIColor (read-only) 127| 3| var coreImageColor: CoreImage.CIColor? { 128| 3| return CoreImage.CIColor(color: self) 129| 3| } 130| | #endif 131| | 132| | /// SwifterSwift: Get UInt representation of a Color (read-only). 133| 9| var uInt: UInt { 134| 9| let components: [CGFloat] = { 135| 9| let comps: [CGFloat] = cgColor.components! 136| 9| guard comps.count != 4 else { return comps } 137| 0| return [comps[0], comps[0], comps[0], comps[1]] 138| 9| }() 139| 9| 140| 9| var colorAsUInt32: UInt32 = 0 141| 9| colorAsUInt32 += UInt32(components[0] * 255.0) << 16 142| 9| colorAsUInt32 += UInt32(components[1] * 255.0) << 8 143| 9| colorAsUInt32 += UInt32(components[2] * 255.0) 144| 9| 145| 9| return UInt(colorAsUInt32) 146| 9| } 147| | 148| | /// SwifterSwift: Get color complementary (read-only, if applicable). 149| 3| var complementary: Color? { 150| 3| let colorSpaceRGB = CGColorSpaceCreateDeviceRGB() 151| 3| let convertColorToRGBSpace: ((_ color: Color) -> Color?) = { _ -> Color? in 152| 3| if self.cgColor.colorSpace!.model == CGColorSpaceModel.monochrome { 153| 2| let oldComponents = self.cgColor.components 154| 2| let components: [CGFloat] = [oldComponents![0], oldComponents![0], oldComponents![0], oldComponents![1]] 155| 2| let colorRef = CGColor(colorSpace: colorSpaceRGB, components: components) 156| 2| let colorOut = Color(cgColor: colorRef!) 157| 2| return colorOut 158| 2| } else { 159| 1| return self 160| 1| } 161| 0| } 162| 3| 163| 3| let color = convertColorToRGBSpace(self) 164| 3| guard let componentColors = color?.cgColor.components else { return nil } 165| 3| 166| 3| let red: CGFloat = sqrt(pow(255.0, 2.0) - pow(componentColors[0] * 255, 2.0)) / 255 167| 3| let green: CGFloat = sqrt(pow(255.0, 2.0) - pow(componentColors[1] * 255, 2.0)) / 255 168| 3| let blue: CGFloat = sqrt(pow(255.0, 2.0) - pow(componentColors[2] * 255, 2.0)) / 255 169| 3| 170| 3| return Color(red: red, green: green, blue: blue, alpha: 1.0) 171| 3| } 172| |} 173| | 174| |// MARK: - Methods 175| | 176| |public extension Color { 177| | /// SwifterSwift: Blend two Colors 178| | /// 179| | /// - Parameters: 180| | /// - color1: first color to blend 181| | /// - intensity1: intensity of first color (default is 0.5) 182| | /// - color2: second color to blend 183| | /// - intensity2: intensity of second color (default is 0.5) 184| | /// - Returns: Color created by blending first and seond colors. 185| | static func blend(_ color1: Color, intensity1: CGFloat = 0.5, with color2: Color, 186| 5| intensity2: CGFloat = 0.5) -> Color { 187| 5| // http://stackoverflow.com/questions/27342715/blend-uicolors-in-swift 188| 5| 189| 5| let total = intensity1 + intensity2 190| 5| let level1 = intensity1 / total 191| 5| let level2 = intensity2 / total 192| 5| 193| 5| guard level1 > 0 else { return color2 } 194| 4| guard level2 > 0 else { return color1 } 195| 3| 196| 3| let components1: [CGFloat] = { 197| 3| let comps: [CGFloat] = color1.cgColor.components! 198| 3| guard comps.count != 4 else { return comps } 199| 1| return [comps[0], comps[0], comps[0], comps[1]] 200| 3| }() 201| 3| 202| 3| let components2: [CGFloat] = { 203| 3| let comps: [CGFloat] = color2.cgColor.components! 204| 3| guard comps.count != 4 else { return comps } 205| 1| return [comps[0], comps[0], comps[0], comps[1]] 206| 3| }() 207| 3| 208| 3| let red1 = components1[0] 209| 3| let red2 = components2[0] 210| 3| 211| 3| let green1 = components1[1] 212| 3| let green2 = components2[1] 213| 3| 214| 3| let blue1 = components1[2] 215| 3| let blue2 = components2[2] 216| 3| 217| 3| let alpha1 = color1.cgColor.alpha 218| 3| let alpha2 = color2.cgColor.alpha 219| 3| 220| 3| let red = level1 * red1 + level2 * red2 221| 3| let green = level1 * green1 + level2 * green2 222| 3| let blue = level1 * blue1 + level2 * blue2 223| 3| let alpha = level1 * alpha1 + level2 * alpha2 224| 3| 225| 3| return Color(red: red, green: green, blue: blue, alpha: alpha) 226| 4| } 227| | 228| | /// SwifterSwift: Lighten a color 229| | /// 230| | /// let color = Color(red: r, green: g, blue: b, alpha: a) 231| | /// let lighterColor: Color = color.lighten(by: 0.2) 232| | /// 233| | /// - Parameter percentage: Percentage by which to lighten the color 234| | /// - Returns: A lightened color 235| 1| func lighten(by percentage: CGFloat = 0.2) -> Color { 236| 1| // https://stackoverflow.com/questions/38435308/swift-get-lighter-and-darker-color-variations-for-a-given-uicolor 237| 1| var red: CGFloat = 0, green: CGFloat = 0, blue: CGFloat = 0, alpha: CGFloat = 0 238| 1| getRed(&red, green: &green, blue: &blue, alpha: &alpha) 239| 1| return Color(red: min(red + percentage, 1.0), 240| 1| green: min(green + percentage, 1.0), 241| 1| blue: min(blue + percentage, 1.0), 242| 1| alpha: alpha) 243| 1| } 244| | 245| | /// SwifterSwift: Darken a color 246| | /// 247| | /// let color = Color(red: r, green: g, blue: b, alpha: a) 248| | /// let darkerColor: Color = color.darken(by: 0.2) 249| | /// 250| | /// - Parameter percentage: Percentage by which to darken the color 251| | /// - Returns: A darkened color 252| 1| func darken(by percentage: CGFloat = 0.2) -> Color { 253| 1| // https://stackoverflow.com/questions/38435308/swift-get-lighter-and-darker-color-variations-for-a-given-uicolor 254| 1| var red: CGFloat = 0, green: CGFloat = 0, blue: CGFloat = 0, alpha: CGFloat = 0 255| 1| getRed(&red, green: &green, blue: &blue, alpha: &alpha) 256| 1| return Color(red: max(red - percentage, 0), 257| 1| green: max(green - percentage, 0), 258| 1| blue: max(blue - percentage, 0), 259| 1| alpha: alpha) 260| 1| } 261| |} 262| | 263| |// MARK: - Initializers 264| | 265| |public extension Color { 266| | /// SwifterSwift: Create Color from RGB values with optional transparency. 267| | /// 268| | /// - Parameters: 269| | /// - red: red component. 270| | /// - green: green component. 271| | /// - blue: blue component. 272| | /// - transparency: optional transparency value (default is 1). 273| 53| convenience init?(red: Int, green: Int, blue: Int, transparency: CGFloat = 1) { 274| 53| guard red >= 0, red <= 255 else { return nil } 275| 51| guard green >= 0, green <= 255 else { return nil } 276| 50| guard blue >= 0, blue <= 255 else { return nil } 277| 49| 278| 49| var trans = transparency 279| 49| if trans < 0 { trans = 0 } 280| 49| if trans > 1 { trans = 1 } 281| 49| 282| 49| self.init(red: CGFloat(red) / 255.0, green: CGFloat(green) / 255.0, blue: CGFloat(blue) / 255.0, alpha: trans) 283| 49| } 284| | 285| | /// SwifterSwift: Create Color from hexadecimal value with optional transparency. 286| | /// 287| | /// - Parameters: 288| | /// - hex: hex Int (example: 0xDECEB5). 289| | /// - transparency: optional transparency value (default is 1). 290| 32| convenience init?(hex: Int, transparency: CGFloat = 1) { 291| 32| var trans = transparency 292| 32| if trans < 0 { trans = 0 } 293| 32| if trans > 1 { trans = 1 } 294| 32| 295| 32| let red = (hex >> 16) & 0xFF 296| 32| let green = (hex >> 8) & 0xFF 297| 32| let blue = hex & 0xFF 298| 32| self.init(red: red, green: green, blue: blue, transparency: trans) 299| 32| } 300| | 301| | /// SwifterSwift: Create Color from hexadecimal string with optional transparency (if applicable). 302| | /// 303| | /// - Parameters: 304| | /// - hexString: hexadecimal string (examples: EDE7F6, 0xEDE7F6, #EDE7F6, #0ff, 0xF0F, ..). 305| | /// - transparency: optional transparency value (default is 1). 306| 12| convenience init?(hexString: String, transparency: CGFloat = 1) { 307| 12| var string = "" 308| 12| if hexString.lowercased().hasPrefix("0x") { 309| 1| string = hexString.replacingOccurrences(of: "0x", with: "") 310| 12| } else if hexString.hasPrefix("#") { 311| 8| string = hexString.replacingOccurrences(of: "#", with: "") 312| 12| } else { 313| 4| string = hexString 314| 12| } 315| 12| 316| 12| if string.count == 3 { // convert hex to 6 digit format if in short format 317| 2| var str = "" 318| 6| string.forEach { str.append(String(repeating: String($0), count: 2)) } 319| 2| string = str 320| 12| } 321| 12| 322| 12| guard let hexValue = Int(string, radix: 16) else { return nil } 323| 10| 324| 10| var trans = transparency 325| 10| if trans < 0 { trans = 0 } 326| 10| if trans > 1 { trans = 1 } 327| 10| 328| 10| let red = (hexValue >> 16) & 0xFF 329| 10| let green = (hexValue >> 8) & 0xFF 330| 10| let blue = hexValue & 0xFF 331| 10| self.init(red: red, green: green, blue: blue, transparency: trans) 332| 10| } 333| | 334| | /// SwifterSwift: Create Color from a complementary of a Color (if applicable). 335| | /// 336| | /// - Parameter color: color of which opposite color is desired. 337| 2| convenience init?(complementaryFor color: Color) { 338| 2| let colorSpaceRGB = CGColorSpaceCreateDeviceRGB() 339| 2| let convertColorToRGBSpace: ((_ color: Color) -> Color?) = { color -> Color? in 340| 2| if color.cgColor.colorSpace!.model == CGColorSpaceModel.monochrome { 341| 1| let oldComponents = color.cgColor.components 342| 1| let components: [CGFloat] = [oldComponents![0], oldComponents![0], oldComponents![0], oldComponents![1]] 343| 1| let colorRef = CGColor(colorSpace: colorSpaceRGB, components: components) 344| 1| let colorOut = Color(cgColor: colorRef!) 345| 1| return colorOut 346| 1| } else { 347| 1| return color 348| 1| } 349| 0| } 350| 2| 351| 2| let color = convertColorToRGBSpace(color) 352| 2| guard let componentColors = color?.cgColor.components else { return nil } 353| 2| 354| 2| let red: CGFloat = sqrt(pow(255.0, 2.0) - pow(componentColors[0] * 255, 2.0)) / 255 355| 2| let green: CGFloat = sqrt(pow(255.0, 2.0) - pow(componentColors[1] * 255, 2.0)) / 255 356| 2| let blue: CGFloat = sqrt(pow(255.0, 2.0) - pow(componentColors[2] * 255, 2.0)) / 255 357| 2| self.init(red: red, green: green, blue: blue, alpha: 1.0) 358| 2| } 359| |} 360| | 361| |// MARK: - Social 362| | 363| |public extension Color { 364| | /// SwifterSwift: Brand identity color of popular social media platform. 365| | enum Social { 366| | // https://www.lockedowndesign.com/social-media-colors/ 367| | 368| | /// SwifterSwift: red: 59, green: 89, blue: 152 369| | public static let facebook = Color(red: 59, green: 89, blue: 152)! 370| | 371| | /// SwifterSwift: red: 0, green: 182, blue: 241 372| | public static let twitter = Color(red: 0, green: 182, blue: 241)! 373| | 374| | /// SwifterSwift: red: 223, green: 74, blue: 50 375| | public static let googlePlus = Color(red: 223, green: 74, blue: 50)! 376| | 377| | /// SwifterSwift: red: 0, green: 123, blue: 182 378| | public static let linkedIn = Color(red: 0, green: 123, blue: 182)! 379| | 380| | /// SwifterSwift: red: 69, green: 187, blue: 255 381| | public static let vimeo = Color(red: 69, green: 187, blue: 255)! 382| | 383| | /// SwifterSwift: red: 179, green: 18, blue: 23 384| | public static let youtube = Color(red: 179, green: 18, blue: 23)! 385| | 386| | /// SwifterSwift: red: 195, green: 42, blue: 163 387| | public static let instagram = Color(red: 195, green: 42, blue: 163)! 388| | 389| | /// SwifterSwift: red: 203, green: 32, blue: 39 390| | public static let pinterest = Color(red: 203, green: 32, blue: 39)! 391| | 392| | /// SwifterSwift: red: 244, green: 0, blue: 131 393| | public static let flickr = Color(red: 244, green: 0, blue: 131)! 394| | 395| | /// SwifterSwift: red: 67, green: 2, blue: 151 396| | public static let yahoo = Color(red: 67, green: 2, blue: 151)! 397| | 398| | /// SwifterSwift: red: 67, green: 2, blue: 151 399| | public static let soundCloud = Color(red: 67, green: 2, blue: 151)! 400| | 401| | /// SwifterSwift: red: 44, green: 71, blue: 98 402| | public static let tumblr = Color(red: 44, green: 71, blue: 98)! 403| | 404| | /// SwifterSwift: red: 252, green: 69, blue: 117 405| | public static let foursquare = Color(red: 252, green: 69, blue: 117)! 406| | 407| | /// SwifterSwift: red: 255, green: 176, blue: 0 408| | public static let swarm = Color(red: 255, green: 176, blue: 0)! 409| | 410| | /// SwifterSwift: red: 234, green: 76, blue: 137 411| | public static let dribbble = Color(red: 234, green: 76, blue: 137)! 412| | 413| | /// SwifterSwift: red: 255, green: 87, blue: 0 414| | public static let reddit = Color(red: 255, green: 87, blue: 0)! 415| | 416| | /// SwifterSwift: red: 74, green: 93, blue: 78 417| | public static let devianArt = Color(red: 74, green: 93, blue: 78)! 418| | 419| | /// SwifterSwift: red: 238, green: 64, blue: 86 420| | public static let pocket = Color(red: 238, green: 64, blue: 86)! 421| | 422| | /// SwifterSwift: red: 170, green: 34, blue: 182 423| | public static let quora = Color(red: 170, green: 34, blue: 182)! 424| | 425| | /// SwifterSwift: red: 247, green: 146, blue: 30 426| | public static let slideShare = Color(red: 247, green: 146, blue: 30)! 427| | 428| | /// SwifterSwift: red: 0, green: 153, blue: 229 429| | public static let px500 = Color(red: 0, green: 153, blue: 229)! 430| | 431| | /// SwifterSwift: red: 223, green: 109, blue: 70 432| | public static let listly = Color(red: 223, green: 109, blue: 70)! 433| | 434| | /// SwifterSwift: red: 0, green: 180, blue: 137 435| | public static let vine = Color(red: 0, green: 180, blue: 137)! 436| | 437| | /// SwifterSwift: red: 0, green: 175, blue: 240 438| | public static let skype = Color(red: 0, green: 175, blue: 240)! 439| | 440| | /// SwifterSwift: red: 235, green: 73, blue: 36 441| | public static let stumbleUpon = Color(red: 235, green: 73, blue: 36)! 442| | 443| | /// SwifterSwift: red: 255, green: 252, blue: 0 444| | public static let snapchat = Color(red: 255, green: 252, blue: 0)! 445| | 446| | /// SwifterSwift: red: 37, green: 211, blue: 102 447| | public static let whatsApp = Color(red: 37, green: 211, blue: 102)! 448| | } 449| |} 450| | 451| |// MARK: - Material colors 452| | 453| |public extension Color { 454| | // swiftlint:disable type_body_length 455| | /// SwifterSwift: Google Material design colors palette. 456| | enum Material { 457| | // https://material.google.com/style/color.html 458| | 459| | /// SwifterSwift: color red500 460| | public static let red = red500 461| | 462| | /// SwifterSwift: hex #FFEBEE 463| | public static let red50 = Color(hex: 0xFFEBEE)! 464| | 465| | /// SwifterSwift: hex #FFCDD2 466| | public static let red100 = Color(hex: 0xFFCDD2)! 467| | 468| | /// SwifterSwift: hex #EF9A9A 469| | public static let red200 = Color(hex: 0xEF9A9A)! 470| | 471| | /// SwifterSwift: hex #E57373 472| | public static let red300 = Color(hex: 0xE57373)! 473| | 474| | /// SwifterSwift: hex #EF5350 475| | public static let red400 = Color(hex: 0xEF5350)! 476| | 477| | /// SwifterSwift: hex #F44336 478| | public static let red500 = Color(hex: 0xF44336)! 479| | 480| | /// SwifterSwift: hex #E53935 481| | public static let red600 = Color(hex: 0xE53935)! 482| | 483| | /// SwifterSwift: hex #D32F2F 484| | public static let red700 = Color(hex: 0xD32F2F)! 485| | 486| | /// SwifterSwift: hex #C62828 487| | public static let red800 = Color(hex: 0xC62828)! 488| | 489| | /// SwifterSwift: hex #B71C1C 490| | public static let red900 = Color(hex: 0xB71C1C)! 491| | 492| | /// SwifterSwift: hex #FF8A80 493| | public static let redA100 = Color(hex: 0xFF8A80)! 494| | 495| | /// SwifterSwift: hex #FF5252 496| | public static let redA200 = Color(hex: 0xFF5252)! 497| | 498| | /// SwifterSwift: hex #FF1744 499| | public static let redA400 = Color(hex: 0xFF1744)! 500| | 501| | /// SwifterSwift: hex #D50000 502| | public static let redA700 = Color(hex: 0xD50000)! 503| | 504| | /// SwifterSwift: color pink500 505| | public static let pink = pink500 506| | 507| | /// SwifterSwift: hex #FCE4EC 508| | public static let pink50 = Color(hex: 0xFCE4EC)! 509| | 510| | /// SwifterSwift: hex #F8BBD0 511| | public static let pink100 = Color(hex: 0xF8BBD0)! 512| | 513| | /// SwifterSwift: hex #F48FB1 514| | public static let pink200 = Color(hex: 0xF48FB1)! 515| | 516| | /// SwifterSwift: hex #F06292 517| | public static let pink300 = Color(hex: 0xF06292)! 518| | 519| | /// SwifterSwift: hex #EC407A 520| | public static let pink400 = Color(hex: 0xEC407A)! 521| | 522| | /// SwifterSwift: hex #E91E63 523| | public static let pink500 = Color(hex: 0xE91E63)! 524| | 525| | /// SwifterSwift: hex #D81B60 526| | public static let pink600 = Color(hex: 0xD81B60)! 527| | 528| | /// SwifterSwift: hex #C2185B 529| | public static let pink700 = Color(hex: 0xC2185B)! 530| | 531| | /// SwifterSwift: hex #AD1457 532| | public static let pink800 = Color(hex: 0xAD1457)! 533| | 534| | /// SwifterSwift: hex #880E4F 535| | public static let pink900 = Color(hex: 0x880E4F)! 536| | 537| | /// SwifterSwift: hex #FF80AB 538| | public static let pinkA100 = Color(hex: 0xFF80AB)! 539| | 540| | /// SwifterSwift: hex #FF4081 541| | public static let pinkA200 = Color(hex: 0xFF4081)! 542| | 543| | /// SwifterSwift: hex #F50057 544| | public static let pinkA400 = Color(hex: 0xF50057)! 545| | 546| | /// SwifterSwift: hex #C51162 547| | public static let pinkA700 = Color(hex: 0xC51162)! 548| | 549| | /// SwifterSwift: color purple500 550| | public static let purple = purple500 551| | 552| | /// SwifterSwift: hex #F3E5F5 553| | public static let purple50 = Color(hex: 0xF3E5F5)! 554| | 555| | /// SwifterSwift: hex #E1BEE7 556| | public static let purple100 = Color(hex: 0xE1BEE7)! 557| | 558| | /// SwifterSwift: hex #CE93D8 559| | public static let purple200 = Color(hex: 0xCE93D8)! 560| | 561| | /// SwifterSwift: hex #BA68C8 562| | public static let purple300 = Color(hex: 0xBA68C8)! 563| | 564| | /// SwifterSwift: hex #AB47BC 565| | public static let purple400 = Color(hex: 0xAB47BC)! 566| | 567| | /// SwifterSwift: hex #9C27B0 568| | public static let purple500 = Color(hex: 0x9C27B0)! 569| | 570| | /// SwifterSwift: hex #8E24AA 571| | public static let purple600 = Color(hex: 0x8E24AA)! 572| | 573| | /// SwifterSwift: hex #7B1FA2 574| | public static let purple700 = Color(hex: 0x7B1FA2)! 575| | 576| | /// SwifterSwift: hex #6A1B9A 577| | public static let purple800 = Color(hex: 0x6A1B9A)! 578| | 579| | /// SwifterSwift: hex #4A148C 580| | public static let purple900 = Color(hex: 0x4A148C)! 581| | 582| | /// SwifterSwift: hex #EA80FC 583| | public static let purpleA100 = Color(hex: 0xEA80FC)! 584| | 585| | /// SwifterSwift: hex #E040FB 586| | public static let purpleA200 = Color(hex: 0xE040FB)! 587| | 588| | /// SwifterSwift: hex #D500F9 589| | public static let purpleA400 = Color(hex: 0xD500F9)! 590| | 591| | /// SwifterSwift: hex #AA00FF 592| | public static let purpleA700 = Color(hex: 0xAA00FF)! 593| | 594| | /// SwifterSwift: color deepPurple500 595| | public static let deepPurple = deepPurple500 596| | 597| | /// SwifterSwift: hex #EDE7F6 598| | public static let deepPurple50 = Color(hex: 0xEDE7F6)! 599| | 600| | /// SwifterSwift: hex #D1C4E9 601| | public static let deepPurple100 = Color(hex: 0xD1C4E9)! 602| | 603| | /// SwifterSwift: hex #B39DDB 604| | public static let deepPurple200 = Color(hex: 0xB39DDB)! 605| | 606| | /// SwifterSwift: hex #9575CD 607| | public static let deepPurple300 = Color(hex: 0x9575CD)! 608| | 609| | /// SwifterSwift: hex #7E57C2 610| | public static let deepPurple400 = Color(hex: 0x7E57C2)! 611| | 612| | /// SwifterSwift: hex #673AB7 613| | public static let deepPurple500 = Color(hex: 0x673AB7)! 614| | 615| | /// SwifterSwift: hex #5E35B1 616| | public static let deepPurple600 = Color(hex: 0x5E35B1)! 617| | 618| | /// SwifterSwift: hex #512DA8 619| | public static let deepPurple700 = Color(hex: 0x512DA8)! 620| | 621| | /// SwifterSwift: hex #4527A0 622| | public static let deepPurple800 = Color(hex: 0x4527A0)! 623| | 624| | /// SwifterSwift: hex #311B92 625| | public static let deepPurple900 = Color(hex: 0x311B92)! 626| | 627| | /// SwifterSwift: hex #B388FF 628| | public static let deepPurpleA100 = Color(hex: 0xB388FF)! 629| | 630| | /// SwifterSwift: hex #7C4DFF 631| | public static let deepPurpleA200 = Color(hex: 0x7C4DFF)! 632| | 633| | /// SwifterSwift: hex #651FFF 634| | public static let deepPurpleA400 = Color(hex: 0x651FFF)! 635| | 636| | /// SwifterSwift: hex #6200EA 637| | public static let deepPurpleA700 = Color(hex: 0x6200EA)! 638| | 639| | /// SwifterSwift: color indigo500 640| | public static let indigo = indigo500 641| | 642| | /// SwifterSwift: hex #E8EAF6 643| | public static let indigo50 = Color(hex: 0xE8EAF6)! 644| | 645| | /// SwifterSwift: hex #C5CAE9 646| | public static let indigo100 = Color(hex: 0xC5CAE9)! 647| | 648| | /// SwifterSwift: hex #9FA8DA 649| | public static let indigo200 = Color(hex: 0x9FA8DA)! 650| | 651| | /// SwifterSwift: hex #7986CB 652| | public static let indigo300 = Color(hex: 0x7986CB)! 653| | 654| | /// SwifterSwift: hex #5C6BC0 655| | public static let indigo400 = Color(hex: 0x5C6BC0)! 656| | 657| | /// SwifterSwift: hex #3F51B5 658| | public static let indigo500 = Color(hex: 0x3F51B5)! 659| | 660| | /// SwifterSwift: hex #3949AB 661| | public static let indigo600 = Color(hex: 0x3949AB)! 662| | 663| | /// SwifterSwift: hex #303F9F 664| | public static let indigo700 = Color(hex: 0x303F9F)! 665| | 666| | /// SwifterSwift: hex #283593 667| | public static let indigo800 = Color(hex: 0x283593)! 668| | 669| | /// SwifterSwift: hex #1A237E 670| | public static let indigo900 = Color(hex: 0x1A237E)! 671| | 672| | /// SwifterSwift: hex #8C9EFF 673| | public static let indigoA100 = Color(hex: 0x8C9EFF)! 674| | 675| | /// SwifterSwift: hex #536DFE 676| | public static let indigoA200 = Color(hex: 0x536DFE)! 677| | 678| | /// SwifterSwift: hex #3D5AFE 679| | public static let indigoA400 = Color(hex: 0x3D5AFE)! 680| | 681| | /// SwifterSwift: hex #304FFE 682| | public static let indigoA700 = Color(hex: 0x304FFE)! 683| | 684| | /// SwifterSwift: color blue500 685| | public static let blue = blue500 686| | 687| | /// SwifterSwift: hex #E3F2FD 688| | public static let blue50 = Color(hex: 0xE3F2FD)! 689| | 690| | /// SwifterSwift: hex #BBDEFB 691| | public static let blue100 = Color(hex: 0xBBDEFB)! 692| | 693| | /// SwifterSwift: hex #90CAF9 694| | public static let blue200 = Color(hex: 0x90CAF9)! 695| | 696| | /// SwifterSwift: hex #64B5F6 697| | public static let blue300 = Color(hex: 0x64B5F6)! 698| | 699| | /// SwifterSwift: hex #42A5F5 700| | public static let blue400 = Color(hex: 0x42A5F5)! 701| | 702| | /// SwifterSwift: hex #2196F3 703| | public static let blue500 = Color(hex: 0x2196F3)! 704| | 705| | /// SwifterSwift: hex #1E88E5 706| | public static let blue600 = Color(hex: 0x1E88E5)! 707| | 708| | /// SwifterSwift: hex #1976D2 709| | public static let blue700 = Color(hex: 0x1976D2)! 710| | 711| | /// SwifterSwift: hex #1565C0 712| | public static let blue800 = Color(hex: 0x1565C0)! 713| | 714| | /// SwifterSwift: hex #0D47A1 715| | public static let blue900 = Color(hex: 0x0D47A1)! 716| | 717| | /// SwifterSwift: hex #82B1FF 718| | public static let blueA100 = Color(hex: 0x82B1FF)! 719| | 720| | /// SwifterSwift: hex #448AFF 721| | public static let blueA200 = Color(hex: 0x448AFF)! 722| | 723| | /// SwifterSwift: hex #2979FF 724| | public static let blueA400 = Color(hex: 0x2979FF)! 725| | 726| | /// SwifterSwift: hex #2962FF 727| | public static let blueA700 = Color(hex: 0x2962FF)! 728| | 729| | /// SwifterSwift: color lightBlue500 730| | public static let lightBlue = lightBlue500 731| | 732| | /// SwifterSwift: hex #E1F5FE 733| | public static let lightBlue50 = Color(hex: 0xE1F5FE)! 734| | 735| | /// SwifterSwift: hex #B3E5FC 736| | public static let lightBlue100 = Color(hex: 0xB3E5FC)! 737| | 738| | /// SwifterSwift: hex #81D4FA 739| | public static let lightBlue200 = Color(hex: 0x81D4FA)! 740| | 741| | /// SwifterSwift: hex #4FC3F7 742| | public static let lightBlue300 = Color(hex: 0x4FC3F7)! 743| | 744| | /// SwifterSwift: hex #29B6F6 745| | public static let lightBlue400 = Color(hex: 0x29B6F6)! 746| | 747| | /// SwifterSwift: hex #03A9F4 748| | public static let lightBlue500 = Color(hex: 0x03A9F4)! 749| | 750| | /// SwifterSwift: hex #039BE5 751| | public static let lightBlue600 = Color(hex: 0x039BE5)! 752| | 753| | /// SwifterSwift: hex #0288D1 754| | public static let lightBlue700 = Color(hex: 0x0288D1)! 755| | 756| | /// SwifterSwift: hex #0277BD 757| | public static let lightBlue800 = Color(hex: 0x0277BD)! 758| | 759| | /// SwifterSwift: hex #01579B 760| | public static let lightBlue900 = Color(hex: 0x01579B)! 761| | 762| | /// SwifterSwift: hex #80D8FF 763| | public static let lightBlueA100 = Color(hex: 0x80D8FF)! 764| | 765| | /// SwifterSwift: hex #40C4FF 766| | public static let lightBlueA200 = Color(hex: 0x40C4FF)! 767| | 768| | /// SwifterSwift: hex #00B0FF 769| | public static let lightBlueA400 = Color(hex: 0x00B0FF)! 770| | 771| | /// SwifterSwift: hex #0091EA 772| | public static let lightBlueA700 = Color(hex: 0x0091EA)! 773| | 774| | /// SwifterSwift: color cyan500 775| | public static let cyan = cyan500 776| | 777| | /// SwifterSwift: hex #E0F7FA 778| | public static let cyan50 = Color(hex: 0xE0F7FA)! 779| | 780| | /// SwifterSwift: hex #B2EBF2 781| | public static let cyan100 = Color(hex: 0xB2EBF2)! 782| | 783| | /// SwifterSwift: hex #80DEEA 784| | public static let cyan200 = Color(hex: 0x80DEEA)! 785| | 786| | /// SwifterSwift: hex #4DD0E1 787| | public static let cyan300 = Color(hex: 0x4DD0E1)! 788| | 789| | /// SwifterSwift: hex #26C6DA 790| | public static let cyan400 = Color(hex: 0x26C6DA)! 791| | 792| | /// SwifterSwift: hex #00BCD4 793| | public static let cyan500 = Color(hex: 0x00BCD4)! 794| | 795| | /// SwifterSwift: hex #00ACC1 796| | public static let cyan600 = Color(hex: 0x00ACC1)! 797| | 798| | /// SwifterSwift: hex #0097A7 799| | public static let cyan700 = Color(hex: 0x0097A7)! 800| | 801| | /// SwifterSwift: hex #00838F 802| | public static let cyan800 = Color(hex: 0x00838F)! 803| | 804| | /// SwifterSwift: hex #006064 805| | public static let cyan900 = Color(hex: 0x006064)! 806| | 807| | /// SwifterSwift: hex #84FFFF 808| | public static let cyanA100 = Color(hex: 0x84FFFF)! 809| | 810| | /// SwifterSwift: hex #18FFFF 811| | public static let cyanA200 = Color(hex: 0x18FFFF)! 812| | 813| | /// SwifterSwift: hex #00E5FF 814| | public static let cyanA400 = Color(hex: 0x00E5FF)! 815| | 816| | /// SwifterSwift: hex #00B8D4 817| | public static let cyanA700 = Color(hex: 0x00B8D4)! 818| | 819| | /// SwifterSwift: color teal500 820| | public static let teal = teal500 821| | 822| | /// SwifterSwift: hex #E0F2F1 823| | public static let teal50 = Color(hex: 0xE0F2F1)! 824| | 825| | /// SwifterSwift: hex #B2DFDB 826| | public static let teal100 = Color(hex: 0xB2DFDB)! 827| | 828| | /// SwifterSwift: hex #80CBC4 829| | public static let teal200 = Color(hex: 0x80CBC4)! 830| | 831| | /// SwifterSwift: hex #4DB6AC 832| | public static let teal300 = Color(hex: 0x4DB6AC)! 833| | 834| | /// SwifterSwift: hex #26A69A 835| | public static let teal400 = Color(hex: 0x26A69A)! 836| | 837| | /// SwifterSwift: hex #009688 838| | public static let teal500 = Color(hex: 0x009688)! 839| | 840| | /// SwifterSwift: hex #00897B 841| | public static let teal600 = Color(hex: 0x00897B)! 842| | 843| | /// SwifterSwift: hex #00796B 844| | public static let teal700 = Color(hex: 0x00796B)! 845| | 846| | /// SwifterSwift: hex #00695C 847| | public static let teal800 = Color(hex: 0x00695C)! 848| | 849| | /// SwifterSwift: hex #004D40 850| | public static let teal900 = Color(hex: 0x004D40)! 851| | 852| | /// SwifterSwift: hex #A7FFEB 853| | public static let tealA100 = Color(hex: 0xA7FFEB)! 854| | 855| | /// SwifterSwift: hex #64FFDA 856| | public static let tealA200 = Color(hex: 0x64FFDA)! 857| | 858| | /// SwifterSwift: hex #1DE9B6 859| | public static let tealA400 = Color(hex: 0x1DE9B6)! 860| | 861| | /// SwifterSwift: hex #00BFA5 862| | public static let tealA700 = Color(hex: 0x00BFA5)! 863| | 864| | /// SwifterSwift: color green500 865| | public static let green = green500 866| | 867| | /// SwifterSwift: hex #E8F5E9 868| | public static let green50 = Color(hex: 0xE8F5E9)! 869| | 870| | /// SwifterSwift: hex #C8E6C9 871| | public static let green100 = Color(hex: 0xC8E6C9)! 872| | 873| | /// SwifterSwift: hex #A5D6A7 874| | public static let green200 = Color(hex: 0xA5D6A7)! 875| | 876| | /// SwifterSwift: hex #81C784 877| | public static let green300 = Color(hex: 0x81C784)! 878| | 879| | /// SwifterSwift: hex #66BB6A 880| | public static let green400 = Color(hex: 0x66BB6A)! 881| | 882| | /// SwifterSwift: hex #4CAF50 883| | public static let green500 = Color(hex: 0x4CAF50)! 884| | 885| | /// SwifterSwift: hex #43A047 886| | public static let green600 = Color(hex: 0x43A047)! 887| | 888| | /// SwifterSwift: hex #388E3C 889| | public static let green700 = Color(hex: 0x388E3C)! 890| | 891| | /// SwifterSwift: hex #2E7D32 892| | public static let green800 = Color(hex: 0x2E7D32)! 893| | 894| | /// SwifterSwift: hex #1B5E20 895| | public static let green900 = Color(hex: 0x1B5E20)! 896| | 897| | /// SwifterSwift: hex #B9F6CA 898| | public static let greenA100 = Color(hex: 0xB9F6CA)! 899| | 900| | /// SwifterSwift: hex #69F0AE 901| | public static let greenA200 = Color(hex: 0x69F0AE)! 902| | 903| | /// SwifterSwift: hex #00E676 904| | public static let greenA400 = Color(hex: 0x00E676)! 905| | 906| | /// SwifterSwift: hex #00C853 907| | public static let greenA700 = Color(hex: 0x00C853)! 908| | 909| | /// SwifterSwift: color lightGreen500 910| | public static let lightGreen = lightGreen500 911| | 912| | /// SwifterSwift: hex #F1F8E9 913| | public static let lightGreen50 = Color(hex: 0xF1F8E9)! 914| | 915| | /// SwifterSwift: hex #DCEDC8 916| | public static let lightGreen100 = Color(hex: 0xDCEDC8)! 917| | 918| | /// SwifterSwift: hex #C5E1A5 919| | public static let lightGreen200 = Color(hex: 0xC5E1A5)! 920| | 921| | /// SwifterSwift: hex #AED581 922| | public static let lightGreen300 = Color(hex: 0xAED581)! 923| | 924| | /// SwifterSwift: hex #9CCC65 925| | public static let lightGreen400 = Color(hex: 0x9CCC65)! 926| | 927| | /// SwifterSwift: hex #8BC34A 928| | public static let lightGreen500 = Color(hex: 0x8BC34A)! 929| | 930| | /// SwifterSwift: hex #7CB342 931| | public static let lightGreen600 = Color(hex: 0x7CB342)! 932| | 933| | /// SwifterSwift: hex #689F38 934| | public static let lightGreen700 = Color(hex: 0x689F38)! 935| | 936| | /// SwifterSwift: hex #558B2F 937| | public static let lightGreen800 = Color(hex: 0x558B2F)! 938| | 939| | /// SwifterSwift: hex #33691E 940| | public static let lightGreen900 = Color(hex: 0x33691E)! 941| | 942| | /// SwifterSwift: hex #CCFF90 943| | public static let lightGreenA100 = Color(hex: 0xCCFF90)! 944| | 945| | /// SwifterSwift: hex #B2FF59 946| | public static let lightGreenA200 = Color(hex: 0xB2FF59)! 947| | 948| | /// SwifterSwift: hex #76FF03 949| | public static let lightGreenA400 = Color(hex: 0x76FF03)! 950| | 951| | /// SwifterSwift: hex #64DD17 952| | public static let lightGreenA700 = Color(hex: 0x64DD17)! 953| | 954| | /// SwifterSwift: color lime500 955| | public static let lime = lime500 956| | 957| | /// SwifterSwift: hex #F9FBE7 958| | public static let lime50 = Color(hex: 0xF9FBE7)! 959| | 960| | /// SwifterSwift: hex #F0F4C3 961| | public static let lime100 = Color(hex: 0xF0F4C3)! 962| | 963| | /// SwifterSwift: hex #E6EE9C 964| | public static let lime200 = Color(hex: 0xE6EE9C)! 965| | 966| | /// SwifterSwift: hex #DCE775 967| | public static let lime300 = Color(hex: 0xDCE775)! 968| | 969| | /// SwifterSwift: hex #D4E157 970| | public static let lime400 = Color(hex: 0xD4E157)! 971| | 972| | /// SwifterSwift: hex #CDDC39 973| | public static let lime500 = Color(hex: 0xCDDC39)! 974| | 975| | /// SwifterSwift: hex #C0CA33 976| | public static let lime600 = Color(hex: 0xC0CA33)! 977| | 978| | /// SwifterSwift: hex #AFB42B 979| | public static let lime700 = Color(hex: 0xAFB42B)! 980| | 981| | /// SwifterSwift: hex #9E9D24 982| | public static let lime800 = Color(hex: 0x9E9D24)! 983| | 984| | /// SwifterSwift: hex #827717 985| | public static let lime900 = Color(hex: 0x827717)! 986| | 987| | /// SwifterSwift: hex #F4FF81 988| | public static let limeA100 = Color(hex: 0xF4FF81)! 989| | 990| | /// SwifterSwift: hex #EEFF41 991| | public static let limeA200 = Color(hex: 0xEEFF41)! 992| | 993| | /// SwifterSwift: hex #C6FF00 994| | public static let limeA400 = Color(hex: 0xC6FF00)! 995| | 996| | /// SwifterSwift: hex #AEEA00 997| | public static let limeA700 = Color(hex: 0xAEEA00)! 998| | 999| | /// SwifterSwift: color yellow500 1000| | public static let yellow = yellow500 1001| | 1002| | /// SwifterSwift: hex #FFFDE7 1003| | public static let yellow50 = Color(hex: 0xFFFDE7)! 1004| | 1005| | /// SwifterSwift: hex #FFF9C4 1006| | public static let yellow100 = Color(hex: 0xFFF9C4)! 1007| | 1008| | /// SwifterSwift: hex #FFF59D 1009| | public static let yellow200 = Color(hex: 0xFFF59D)! 1010| | 1011| | /// SwifterSwift: hex #FFF176 1012| | public static let yellow300 = Color(hex: 0xFFF176)! 1013| | 1014| | /// SwifterSwift: hex #FFEE58 1015| | public static let yellow400 = Color(hex: 0xFFEE58)! 1016| | 1017| | /// SwifterSwift: hex #FFEB3B 1018| | public static let yellow500 = Color(hex: 0xFFEB3B)! 1019| | 1020| | /// SwifterSwift: hex #FDD835 1021| | public static let yellow600 = Color(hex: 0xFDD835)! 1022| | 1023| | /// SwifterSwift: hex #FBC02D 1024| | public static let yellow700 = Color(hex: 0xFBC02D)! 1025| | 1026| | /// SwifterSwift: hex #F9A825 1027| | public static let yellow800 = Color(hex: 0xF9A825)! 1028| | 1029| | /// SwifterSwift: hex #F57F17 1030| | public static let yellow900 = Color(hex: 0xF57F17)! 1031| | 1032| | /// SwifterSwift: hex #FFFF8D 1033| | public static let yellowA100 = Color(hex: 0xFFFF8D)! 1034| | 1035| | /// SwifterSwift: hex #FFFF00 1036| | public static let yellowA200 = Color(hex: 0xFFFF00)! 1037| | 1038| | /// SwifterSwift: hex #FFEA00 1039| | public static let yellowA400 = Color(hex: 0xFFEA00)! 1040| | 1041| | /// SwifterSwift: hex #FFD600 1042| | public static let yellowA700 = Color(hex: 0xFFD600)! 1043| | 1044| | /// SwifterSwift: color amber500 1045| | public static let amber = amber500 1046| | 1047| | /// SwifterSwift: hex #FFF8E1 1048| | public static let amber50 = Color(hex: 0xFFF8E1)! 1049| | 1050| | /// SwifterSwift: hex #FFECB3 1051| | public static let amber100 = Color(hex: 0xFFECB3)! 1052| | 1053| | /// SwifterSwift: hex #FFE082 1054| | public static let amber200 = Color(hex: 0xFFE082)! 1055| | 1056| | /// SwifterSwift: hex #FFD54F 1057| | public static let amber300 = Color(hex: 0xFFD54F)! 1058| | 1059| | /// SwifterSwift: hex #FFCA28 1060| | public static let amber400 = Color(hex: 0xFFCA28)! 1061| | 1062| | /// SwifterSwift: hex #FFC107 1063| | public static let amber500 = Color(hex: 0xFFC107)! 1064| | 1065| | /// SwifterSwift: hex #FFB300 1066| | public static let amber600 = Color(hex: 0xFFB300)! 1067| | 1068| | /// SwifterSwift: hex #FFA000 1069| | public static let amber700 = Color(hex: 0xFFA000)! 1070| | 1071| | /// SwifterSwift: hex #FF8F00 1072| | public static let amber800 = Color(hex: 0xFF8F00)! 1073| | 1074| | /// SwifterSwift: hex #FF6F00 1075| | public static let amber900 = Color(hex: 0xFF6F00)! 1076| | 1077| | /// SwifterSwift: hex #FFE57F 1078| | public static let amberA100 = Color(hex: 0xFFE57F)! 1079| | 1080| | /// SwifterSwift: hex #FFD740 1081| | public static let amberA200 = Color(hex: 0xFFD740)! 1082| | 1083| | /// SwifterSwift: hex #FFC400 1084| | public static let amberA400 = Color(hex: 0xFFC400)! 1085| | 1086| | /// SwifterSwift: hex #FFAB00 1087| | public static let amberA700 = Color(hex: 0xFFAB00)! 1088| | 1089| | /// SwifterSwift: color orange500 1090| | public static let orange = orange500 1091| | 1092| | /// SwifterSwift: hex #FFF3E0 1093| | public static let orange50 = Color(hex: 0xFFF3E0)! 1094| | 1095| | /// SwifterSwift: hex #FFE0B2 1096| | public static let orange100 = Color(hex: 0xFFE0B2)! 1097| | 1098| | /// SwifterSwift: hex #FFCC80 1099| | public static let orange200 = Color(hex: 0xFFCC80)! 1100| | 1101| | /// SwifterSwift: hex #FFB74D 1102| | public static let orange300 = Color(hex: 0xFFB74D)! 1103| | 1104| | /// SwifterSwift: hex #FFA726 1105| | public static let orange400 = Color(hex: 0xFFA726)! 1106| | 1107| | /// SwifterSwift: hex #FF9800 1108| | public static let orange500 = Color(hex: 0xFF9800)! 1109| | 1110| | /// SwifterSwift: hex #FB8C00 1111| | public static let orange600 = Color(hex: 0xFB8C00)! 1112| | 1113| | /// SwifterSwift: hex #F57C00 1114| | public static let orange700 = Color(hex: 0xF57C00)! 1115| | 1116| | /// SwifterSwift: hex #EF6C00 1117| | public static let orange800 = Color(hex: 0xEF6C00)! 1118| | 1119| | /// SwifterSwift: hex #E65100 1120| | public static let orange900 = Color(hex: 0xE65100)! 1121| | 1122| | /// SwifterSwift: hex #FFD180 1123| | public static let orangeA100 = Color(hex: 0xFFD180)! 1124| | 1125| | /// SwifterSwift: hex #FFAB40 1126| | public static let orangeA200 = Color(hex: 0xFFAB40)! 1127| | 1128| | /// SwifterSwift: hex #FF9100 1129| | public static let orangeA400 = Color(hex: 0xFF9100)! 1130| | 1131| | /// SwifterSwift: hex #FF6D00 1132| | public static let orangeA700 = Color(hex: 0xFF6D00)! 1133| | 1134| | /// SwifterSwift: color deepOrange500 1135| | public static let deepOrange = deepOrange500 1136| | 1137| | /// SwifterSwift: hex #FBE9E7 1138| | public static let deepOrange50 = Color(hex: 0xFBE9E7)! 1139| | 1140| | /// SwifterSwift: hex #FFCCBC 1141| | public static let deepOrange100 = Color(hex: 0xFFCCBC)! 1142| | 1143| | /// SwifterSwift: hex #FFAB91 1144| | public static let deepOrange200 = Color(hex: 0xFFAB91)! 1145| | 1146| | /// SwifterSwift: hex #FF8A65 1147| | public static let deepOrange300 = Color(hex: 0xFF8A65)! 1148| | 1149| | /// SwifterSwift: hex #FF7043 1150| | public static let deepOrange400 = Color(hex: 0xFF7043)! 1151| | 1152| | /// SwifterSwift: hex #FF5722 1153| | public static let deepOrange500 = Color(hex: 0xFF5722)! 1154| | 1155| | /// SwifterSwift: hex #F4511E 1156| | public static let deepOrange600 = Color(hex: 0xF4511E)! 1157| | 1158| | /// SwifterSwift: hex #E64A19 1159| | public static let deepOrange700 = Color(hex: 0xE64A19)! 1160| | 1161| | /// SwifterSwift: hex #D84315 1162| | public static let deepOrange800 = Color(hex: 0xD84315)! 1163| | 1164| | /// SwifterSwift: hex #BF360C 1165| | public static let deepOrange900 = Color(hex: 0xBF360C)! 1166| | 1167| | /// SwifterSwift: hex #FF9E80 1168| | public static let deepOrangeA100 = Color(hex: 0xFF9E80)! 1169| | 1170| | /// SwifterSwift: hex #FF6E40 1171| | public static let deepOrangeA200 = Color(hex: 0xFF6E40)! 1172| | 1173| | /// SwifterSwift: hex #FF3D00 1174| | public static let deepOrangeA400 = Color(hex: 0xFF3D00)! 1175| | 1176| | /// SwifterSwift: hex #DD2C00 1177| | public static let deepOrangeA700 = Color(hex: 0xDD2C00)! 1178| | 1179| | /// SwifterSwift: color brown500 1180| | public static let brown = brown500 1181| | 1182| | /// SwifterSwift: hex #EFEBE9 1183| | public static let brown50 = Color(hex: 0xEFEBE9)! 1184| | 1185| | /// SwifterSwift: hex #D7CCC8 1186| | public static let brown100 = Color(hex: 0xD7CCC8)! 1187| | 1188| | /// SwifterSwift: hex #BCAAA4 1189| | public static let brown200 = Color(hex: 0xBCAAA4)! 1190| | 1191| | /// SwifterSwift: hex #A1887F 1192| | public static let brown300 = Color(hex: 0xA1887F)! 1193| | 1194| | /// SwifterSwift: hex #8D6E63 1195| | public static let brown400 = Color(hex: 0x8D6E63)! 1196| | 1197| | /// SwifterSwift: hex #795548 1198| | public static let brown500 = Color(hex: 0x795548)! 1199| | 1200| | /// SwifterSwift: hex #6D4C41 1201| | public static let brown600 = Color(hex: 0x6D4C41)! 1202| | 1203| | /// SwifterSwift: hex #5D4037 1204| | public static let brown700 = Color(hex: 0x5D4037)! 1205| | 1206| | /// SwifterSwift: hex #4E342E 1207| | public static let brown800 = Color(hex: 0x4E342E)! 1208| | 1209| | /// SwifterSwift: hex #3E2723 1210| | public static let brown900 = Color(hex: 0x3E2723)! 1211| | 1212| | /// SwifterSwift: color grey500 1213| | public static let grey = grey500 1214| | 1215| | /// SwifterSwift: hex #FAFAFA 1216| | public static let grey50 = Color(hex: 0xFAFAFA)! 1217| | 1218| | /// SwifterSwift: hex #F5F5F5 1219| | public static let grey100 = Color(hex: 0xF5F5F5)! 1220| | 1221| | /// SwifterSwift: hex #EEEEEE 1222| | public static let grey200 = Color(hex: 0xEEEEEE)! 1223| | 1224| | /// SwifterSwift: hex #E0E0E0 1225| | public static let grey300 = Color(hex: 0xE0E0E0)! 1226| | 1227| | /// SwifterSwift: hex #BDBDBD 1228| | public static let grey400 = Color(hex: 0xBDBDBD)! 1229| | 1230| | /// SwifterSwift: hex #9E9E9E 1231| | public static let grey500 = Color(hex: 0x9E9E9E)! 1232| | 1233| | /// SwifterSwift: hex #757575 1234| | public static let grey600 = Color(hex: 0x757575)! 1235| | 1236| | /// SwifterSwift: hex #616161 1237| | public static let grey700 = Color(hex: 0x616161)! 1238| | 1239| | /// SwifterSwift: hex #424242 1240| | public static let grey800 = Color(hex: 0x424242)! 1241| | 1242| | /// SwifterSwift: hex #212121 1243| | public static let grey900 = Color(hex: 0x212121)! 1244| | 1245| | /// SwifterSwift: color blueGrey500 1246| | public static let blueGrey = blueGrey500 1247| | 1248| | /// SwifterSwift: hex #ECEFF1 1249| | public static let blueGrey50 = Color(hex: 0xECEFF1)! 1250| | 1251| | /// SwifterSwift: hex #CFD8DC 1252| | public static let blueGrey100 = Color(hex: 0xCFD8DC)! 1253| | 1254| | /// SwifterSwift: hex #B0BEC5 1255| | public static let blueGrey200 = Color(hex: 0xB0BEC5)! 1256| | 1257| | /// SwifterSwift: hex #90A4AE 1258| | public static let blueGrey300 = Color(hex: 0x90A4AE)! 1259| | 1260| | /// SwifterSwift: hex #78909C 1261| | public static let blueGrey400 = Color(hex: 0x78909C)! 1262| | 1263| | /// SwifterSwift: hex #607D8B 1264| | public static let blueGrey500 = Color(hex: 0x607D8B)! 1265| | 1266| | /// SwifterSwift: hex #546E7A 1267| | public static let blueGrey600 = Color(hex: 0x546E7A)! 1268| | 1269| | /// SwifterSwift: hex #455A64 1270| | public static let blueGrey700 = Color(hex: 0x455A64)! 1271| | 1272| | /// SwifterSwift: hex #37474F 1273| | public static let blueGrey800 = Color(hex: 0x37474F)! 1274| | 1275| | /// SwifterSwift: hex #263238 1276| | public static let blueGrey900 = Color(hex: 0x263238)! 1277| | 1278| | /// SwifterSwift: hex #000000 1279| | public static let black = Color(hex: 0x000000)! 1280| | 1281| | /// SwifterSwift: hex #FFFFFF 1282| | public static let white = Color(hex: 0xFFFFFF)! 1283| | } 1284| |} 1285| | 1286| |// MARK: - CSS colors 1287| | 1288| |public extension Color { 1289| | /// SwifterSwift: CSS colors. 1290| | enum CSS { 1291| | // http://www.w3schools.com/colors/colors_names.asp 1292| | 1293| | /// SwifterSwift: hex #F0F8FF 1294| | public static let aliceBlue = Color(hex: 0xF0F8FF)! 1295| | 1296| | /// SwifterSwift: hex #FAEBD7 1297| | public static let antiqueWhite = Color(hex: 0xFAEBD7)! 1298| | 1299| | /// SwifterSwift: hex #00FFFF 1300| | public static let aqua = Color(hex: 0x00FFFF)! 1301| | 1302| | /// SwifterSwift: hex #7FFFD4 1303| | public static let aquamarine = Color(hex: 0x7FFFD4)! 1304| | 1305| | /// SwifterSwift: hex #F0FFFF 1306| | public static let azure = Color(hex: 0xF0FFFF)! 1307| | 1308| | /// SwifterSwift: hex #F5F5DC 1309| | public static let beige = Color(hex: 0xF5F5DC)! 1310| | 1311| | /// SwifterSwift: hex #FFE4C4 1312| | public static let bisque = Color(hex: 0xFFE4C4)! 1313| | 1314| | /// SwifterSwift: hex #000000 1315| | public static let black = Color(hex: 0x000000)! 1316| | 1317| | /// SwifterSwift: hex #FFEBCD 1318| | public static let blanchedAlmond = Color(hex: 0xFFEBCD)! 1319| | 1320| | /// SwifterSwift: hex #0000FF 1321| | public static let blue = Color(hex: 0x0000FF)! 1322| | 1323| | /// SwifterSwift: hex #8A2BE2 1324| | public static let blueViolet = Color(hex: 0x8A2BE2)! 1325| | 1326| | /// SwifterSwift: hex #A52A2A 1327| | public static let brown = Color(hex: 0xA52A2A)! 1328| | 1329| | /// SwifterSwift: hex #DEB887 1330| | public static let burlyWood = Color(hex: 0xDEB887)! 1331| | 1332| | /// SwifterSwift: hex #5F9EA0 1333| | public static let cadetBlue = Color(hex: 0x5F9EA0)! 1334| | 1335| | /// SwifterSwift: hex #7FFF00 1336| | public static let chartreuse = Color(hex: 0x7FFF00)! 1337| | 1338| | /// SwifterSwift: hex #D2691E 1339| | public static let chocolate = Color(hex: 0xD2691E)! 1340| | 1341| | /// SwifterSwift: hex #FF7F50 1342| | public static let coral = Color(hex: 0xFF7F50)! 1343| | 1344| | /// SwifterSwift: hex #6495ED 1345| | public static let cornflowerBlue = Color(hex: 0x6495ED)! 1346| | 1347| | /// SwifterSwift: hex #FFF8DC 1348| | public static let cornsilk = Color(hex: 0xFFF8DC)! 1349| | 1350| | /// SwifterSwift: hex #DC143C 1351| | public static let crimson = Color(hex: 0xDC143C)! 1352| | 1353| | /// SwifterSwift: hex #00FFFF 1354| | public static let cyan = Color(hex: 0x00FFFF)! 1355| | 1356| | /// SwifterSwift: hex #00008B 1357| | public static let darkBlue = Color(hex: 0x00008B)! 1358| | 1359| | /// SwifterSwift: hex #008B8B 1360| | public static let darkCyan = Color(hex: 0x008B8B)! 1361| | 1362| | /// SwifterSwift: hex #B8860B 1363| | public static let darkGoldenRod = Color(hex: 0xB8860B)! 1364| | 1365| | /// SwifterSwift: hex #A9A9A9 1366| | public static let darkGray = Color(hex: 0xA9A9A9)! 1367| | 1368| | /// SwifterSwift: hex #A9A9A9 1369| | public static let darkGrey = Color(hex: 0xA9A9A9)! 1370| | 1371| | /// SwifterSwift: hex #006400 1372| | public static let darkGreen = Color(hex: 0x006400)! 1373| | 1374| | /// SwifterSwift: hex #BDB76B 1375| | public static let darkKhaki = Color(hex: 0xBDB76B)! 1376| | 1377| | /// SwifterSwift: hex #8B008B 1378| | public static let darkMagenta = Color(hex: 0x8B008B)! 1379| | 1380| | /// SwifterSwift: hex #556B2F 1381| | public static let darkOliveGreen = Color(hex: 0x556B2F)! 1382| | 1383| | /// SwifterSwift: hex #FF8C00 1384| | public static let darkOrange = Color(hex: 0xFF8C00)! 1385| | 1386| | /// SwifterSwift: hex #9932CC 1387| | public static let darkOrchid = Color(hex: 0x9932CC)! 1388| | 1389| | /// SwifterSwift: hex #8B0000 1390| | public static let darkRed = Color(hex: 0x8B0000)! 1391| | 1392| | /// SwifterSwift: hex #E9967A 1393| | public static let darkSalmon = Color(hex: 0xE9967A)! 1394| | 1395| | /// SwifterSwift: hex #8FBC8F 1396| | public static let darkSeaGreen = Color(hex: 0x8FBC8F)! 1397| | 1398| | /// SwifterSwift: hex #483D8B 1399| | public static let darkSlateBlue = Color(hex: 0x483D8B)! 1400| | 1401| | /// SwifterSwift: hex #2F4F4F 1402| | public static let darkSlateGray = Color(hex: 0x2F4F4F)! 1403| | 1404| | /// SwifterSwift: hex #2F4F4F 1405| | public static let darkSlateGrey = Color(hex: 0x2F4F4F)! 1406| | 1407| | /// SwifterSwift: hex #00CED1 1408| | public static let darkTurquoise = Color(hex: 0x00CED1)! 1409| | 1410| | /// SwifterSwift: hex #9400D3 1411| | public static let darkViolet = Color(hex: 0x9400D3)! 1412| | 1413| | /// SwifterSwift: hex #FF1493 1414| | public static let deepPink = Color(hex: 0xFF1493)! 1415| | 1416| | /// SwifterSwift: hex #00BFFF 1417| | public static let deepSkyBlue = Color(hex: 0x00BFFF)! 1418| | 1419| | /// SwifterSwift: hex #696969 1420| | public static let dimGray = Color(hex: 0x696969)! 1421| | 1422| | /// SwifterSwift: hex #696969 1423| | public static let dimGrey = Color(hex: 0x696969)! 1424| | 1425| | /// SwifterSwift: hex #1E90FF 1426| | public static let dodgerBlue = Color(hex: 0x1E90FF)! 1427| | 1428| | /// SwifterSwift: hex #B22222 1429| | public static let fireBrick = Color(hex: 0xB22222)! 1430| | 1431| | /// SwifterSwift: hex #FFFAF0 1432| | public static let floralWhite = Color(hex: 0xFFFAF0)! 1433| | 1434| | /// SwifterSwift: hex #228B22 1435| | public static let forestGreen = Color(hex: 0x228B22)! 1436| | 1437| | /// SwifterSwift: hex #FF00FF 1438| | public static let fuchsia = Color(hex: 0xFF00FF)! 1439| | 1440| | /// SwifterSwift: hex #DCDCDC 1441| | public static let gainsboro = Color(hex: 0xDCDCDC)! 1442| | 1443| | /// SwifterSwift: hex #F8F8FF 1444| | public static let ghostWhite = Color(hex: 0xF8F8FF)! 1445| | 1446| | /// SwifterSwift: hex #FFD700 1447| | public static let gold = Color(hex: 0xFFD700)! 1448| | 1449| | /// SwifterSwift: hex #DAA520 1450| | public static let goldenRod = Color(hex: 0xDAA520)! 1451| | 1452| | /// SwifterSwift: hex #808080 1453| | public static let gray = Color(hex: 0x808080)! 1454| | 1455| | /// SwifterSwift: hex #808080 1456| | public static let grey = Color(hex: 0x808080)! 1457| | 1458| | /// SwifterSwift: hex #008000 1459| | public static let green = Color(hex: 0x008000)! 1460| | 1461| | /// SwifterSwift: hex #ADFF2F 1462| | public static let greenYellow = Color(hex: 0xADFF2F)! 1463| | 1464| | /// SwifterSwift: hex #F0FFF0 1465| | public static let honeyDew = Color(hex: 0xF0FFF0)! 1466| | 1467| | /// SwifterSwift: hex #FF69B4 1468| | public static let hotPink = Color(hex: 0xFF69B4)! 1469| | 1470| | /// SwifterSwift: hex #CD5C5C 1471| | public static let indianRed = Color(hex: 0xCD5C5C)! 1472| | 1473| | /// SwifterSwift: hex #4B0082 1474| | public static let indigo = Color(hex: 0x4B0082)! 1475| | 1476| | /// SwifterSwift: hex #FFFFF0 1477| | public static let ivory = Color(hex: 0xFFFFF0)! 1478| | 1479| | /// SwifterSwift: hex #F0E68C 1480| | public static let khaki = Color(hex: 0xF0E68C)! 1481| | 1482| | /// SwifterSwift: hex #E6E6FA 1483| | public static let lavender = Color(hex: 0xE6E6FA)! 1484| | 1485| | /// SwifterSwift: hex #FFF0F5 1486| | public static let lavenderBlush = Color(hex: 0xFFF0F5)! 1487| | 1488| | /// SwifterSwift: hex #7CFC00 1489| | public static let lawnGreen = Color(hex: 0x7CFC00)! 1490| | 1491| | /// SwifterSwift: hex #FFFACD 1492| | public static let lemonChiffon = Color(hex: 0xFFFACD)! 1493| | 1494| | /// SwifterSwift: hex #ADD8E6 1495| | public static let lightBlue = Color(hex: 0xADD8E6)! 1496| | 1497| | /// SwifterSwift: hex #F08080 1498| | public static let lightCoral = Color(hex: 0xF08080)! 1499| | 1500| | /// SwifterSwift: hex #E0FFFF 1501| | public static let lightCyan = Color(hex: 0xE0FFFF)! 1502| | 1503| | /// SwifterSwift: hex #FAFAD2 1504| | public static let lightGoldenRodYellow = Color(hex: 0xFAFAD2)! 1505| | 1506| | /// SwifterSwift: hex #D3D3D3 1507| | public static let lightGray = Color(hex: 0xD3D3D3)! 1508| | 1509| | /// SwifterSwift: hex #D3D3D3 1510| | public static let lightGrey = Color(hex: 0xD3D3D3)! 1511| | 1512| | /// SwifterSwift: hex #90EE90 1513| | public static let lightGreen = Color(hex: 0x90EE90)! 1514| | 1515| | /// SwifterSwift: hex #FFB6C1 1516| | public static let lightPink = Color(hex: 0xFFB6C1)! 1517| | 1518| | /// SwifterSwift: hex #FFA07A 1519| | public static let lightSalmon = Color(hex: 0xFFA07A)! 1520| | 1521| | /// SwifterSwift: hex #20B2AA 1522| | public static let lightSeaGreen = Color(hex: 0x20B2AA)! 1523| | 1524| | /// SwifterSwift: hex #87CEFA 1525| | public static let lightSkyBlue = Color(hex: 0x87CEFA)! 1526| | 1527| | /// SwifterSwift: hex #778899 1528| | public static let lightSlateGray = Color(hex: 0x778899)! 1529| | 1530| | /// SwifterSwift: hex #778899 1531| | public static let lightSlateGrey = Color(hex: 0x778899)! 1532| | 1533| | /// SwifterSwift: hex #B0C4DE 1534| | public static let lightSteelBlue = Color(hex: 0xB0C4DE)! 1535| | 1536| | /// SwifterSwift: hex #FFFFE0 1537| | public static let lightYellow = Color(hex: 0xFFFFE0)! 1538| | 1539| | /// SwifterSwift: hex #00FF00 1540| | public static let lime = Color(hex: 0x00FF00)! 1541| | 1542| | /// SwifterSwift: hex #32CD32 1543| | public static let limeGreen = Color(hex: 0x32CD32)! 1544| | 1545| | /// SwifterSwift: hex #FAF0E6 1546| | public static let linen = Color(hex: 0xFAF0E6)! 1547| | 1548| | /// SwifterSwift: hex #FF00FF 1549| | public static let magenta = Color(hex: 0xFF00FF)! 1550| | 1551| | /// SwifterSwift: hex #800000 1552| | public static let maroon = Color(hex: 0x800000)! 1553| | 1554| | /// SwifterSwift: hex #66CDAA 1555| | public static let mediumAquaMarine = Color(hex: 0x66CDAA)! 1556| | 1557| | /// SwifterSwift: hex #0000CD 1558| | public static let mediumBlue = Color(hex: 0x0000CD)! 1559| | 1560| | /// SwifterSwift: hex #BA55D3 1561| | public static let mediumOrchid = Color(hex: 0xBA55D3)! 1562| | 1563| | /// SwifterSwift: hex #9370DB 1564| | public static let mediumPurple = Color(hex: 0x9370DB)! 1565| | 1566| | /// SwifterSwift: hex #3CB371 1567| | public static let mediumSeaGreen = Color(hex: 0x3CB371)! 1568| | 1569| | /// SwifterSwift: hex #7B68EE 1570| | public static let mediumSlateBlue = Color(hex: 0x7B68EE)! 1571| | 1572| | /// SwifterSwift: hex #00FA9A 1573| | public static let mediumSpringGreen = Color(hex: 0x00FA9A)! 1574| | 1575| | /// SwifterSwift: hex #48D1CC 1576| | public static let mediumTurquoise = Color(hex: 0x48D1CC)! 1577| | 1578| | /// SwifterSwift: hex #C71585 1579| | public static let mediumVioletRed = Color(hex: 0xC71585)! 1580| | 1581| | /// SwifterSwift: hex #191970 1582| | public static let midnightBlue = Color(hex: 0x191970)! 1583| | 1584| | /// SwifterSwift: hex #F5FFFA 1585| | public static let mintCream = Color(hex: 0xF5FFFA)! 1586| | 1587| | /// SwifterSwift: hex #FFE4E1 1588| | public static let mistyRose = Color(hex: 0xFFE4E1)! 1589| | 1590| | /// SwifterSwift: hex #FFE4B5 1591| | public static let moccasin = Color(hex: 0xFFE4B5)! 1592| | 1593| | /// SwifterSwift: hex #FFDEAD 1594| | public static let navajoWhite = Color(hex: 0xFFDEAD)! 1595| | 1596| | /// SwifterSwift: hex #000080 1597| | public static let navy = Color(hex: 0x000080)! 1598| | 1599| | /// SwifterSwift: hex #FDF5E6 1600| | public static let oldLace = Color(hex: 0xFDF5E6)! 1601| | 1602| | /// SwifterSwift: hex #808000 1603| | public static let olive = Color(hex: 0x808000)! 1604| | 1605| | /// SwifterSwift: hex #6B8E23 1606| | public static let oliveDrab = Color(hex: 0x6B8E23)! 1607| | 1608| | /// SwifterSwift: hex #FFA500 1609| | public static let orange = Color(hex: 0xFFA500)! 1610| | 1611| | /// SwifterSwift: hex #FF4500 1612| | public static let orangeRed = Color(hex: 0xFF4500)! 1613| | 1614| | /// SwifterSwift: hex #DA70D6 1615| | public static let orchid = Color(hex: 0xDA70D6)! 1616| | 1617| | /// SwifterSwift: hex #EEE8AA 1618| | public static let paleGoldenRod = Color(hex: 0xEEE8AA)! 1619| | 1620| | /// SwifterSwift: hex #98FB98 1621| | public static let paleGreen = Color(hex: 0x98FB98)! 1622| | 1623| | /// SwifterSwift: hex #AFEEEE 1624| | public static let paleTurquoise = Color(hex: 0xAFEEEE)! 1625| | 1626| | /// SwifterSwift: hex #DB7093 1627| | public static let paleVioletRed = Color(hex: 0xDB7093)! 1628| | 1629| | /// SwifterSwift: hex #FFEFD5 1630| | public static let papayaWhip = Color(hex: 0xFFEFD5)! 1631| | 1632| | /// SwifterSwift: hex #FFDAB9 1633| | public static let peachPuff = Color(hex: 0xFFDAB9)! 1634| | 1635| | /// SwifterSwift: hex #CD853F 1636| | public static let peru = Color(hex: 0xCD853F)! 1637| | 1638| | /// SwifterSwift: hex #FFC0CB 1639| | public static let pink = Color(hex: 0xFFC0CB)! 1640| | 1641| | /// SwifterSwift: hex #DDA0DD 1642| | public static let plum = Color(hex: 0xDDA0DD)! 1643| | 1644| | /// SwifterSwift: hex #B0E0E6 1645| | public static let powderBlue = Color(hex: 0xB0E0E6)! 1646| | 1647| | /// SwifterSwift: hex #800080 1648| | public static let purple = Color(hex: 0x800080)! 1649| | 1650| | /// SwifterSwift: hex #663399 1651| | public static let rebeccaPurple = Color(hex: 0x663399)! 1652| | 1653| | /// SwifterSwift: hex #FF0000 1654| | public static let red = Color(hex: 0xFF0000)! 1655| | 1656| | /// SwifterSwift: hex #BC8F8F 1657| | public static let rosyBrown = Color(hex: 0xBC8F8F)! 1658| | 1659| | /// SwifterSwift: hex #4169E1 1660| | public static let royalBlue = Color(hex: 0x4169E1)! 1661| | 1662| | /// SwifterSwift: hex #8B4513 1663| | public static let saddleBrown = Color(hex: 0x8B4513)! 1664| | 1665| | /// SwifterSwift: hex #FA8072 1666| | public static let salmon = Color(hex: 0xFA8072)! 1667| | 1668| | /// SwifterSwift: hex #F4A460 1669| | public static let sandyBrown = Color(hex: 0xF4A460)! 1670| | 1671| | /// SwifterSwift: hex #2E8B57 1672| | public static let seaGreen = Color(hex: 0x2E8B57)! 1673| | 1674| | /// SwifterSwift: hex #FFF5EE 1675| | public static let seaShell = Color(hex: 0xFFF5EE)! 1676| | 1677| | /// SwifterSwift: hex #A0522D 1678| | public static let sienna = Color(hex: 0xA0522D)! 1679| | 1680| | /// SwifterSwift: hex #C0C0C0 1681| | public static let silver = Color(hex: 0xC0C0C0)! 1682| | 1683| | /// SwifterSwift: hex #87CEEB 1684| | public static let skyBlue = Color(hex: 0x87CEEB)! 1685| | 1686| | /// SwifterSwift: hex #6A5ACD 1687| | public static let slateBlue = Color(hex: 0x6A5ACD)! 1688| | 1689| | /// SwifterSwift: hex #708090 1690| | public static let slateGray = Color(hex: 0x708090)! 1691| | 1692| | /// SwifterSwift: hex #708090 1693| | public static let slateGrey = Color(hex: 0x708090)! 1694| | 1695| | /// SwifterSwift: hex #FFFAFA 1696| | public static let snow = Color(hex: 0xFFFAFA)! 1697| | 1698| | /// SwifterSwift: hex #00FF7F 1699| | public static let springGreen = Color(hex: 0x00FF7F)! 1700| | 1701| | /// SwifterSwift: hex #4682B4 1702| | public static let steelBlue = Color(hex: 0x4682B4)! 1703| | 1704| | /// SwifterSwift: hex #D2B48C 1705| | public static let tan = Color(hex: 0xD2B48C)! 1706| | 1707| | /// SwifterSwift: hex #008080 1708| | public static let teal = Color(hex: 0x008080)! 1709| | 1710| | /// SwifterSwift: hex #D8BFD8 1711| | public static let thistle = Color(hex: 0xD8BFD8)! 1712| | 1713| | /// SwifterSwift: hex #FF6347 1714| | public static let tomato = Color(hex: 0xFF6347)! 1715| | 1716| | /// SwifterSwift: hex #40E0D0 1717| | public static let turquoise = Color(hex: 0x40E0D0)! 1718| | 1719| | /// SwifterSwift: hex #EE82EE 1720| | public static let violet = Color(hex: 0xEE82EE)! 1721| | 1722| | /// SwifterSwift: hex #F5DEB3 1723| | public static let wheat = Color(hex: 0xF5DEB3)! 1724| | 1725| | /// SwifterSwift: hex #FFFFFF 1726| | public static let white = Color(hex: 0xFFFFFF)! 1727| | 1728| | /// SwifterSwift: hex #F5F5F5 1729| | public static let whiteSmoke = Color(hex: 0xF5F5F5)! 1730| | 1731| | /// SwifterSwift: hex #FFFF00 1732| | public static let yellow = Color(hex: 0xFFFF00)! 1733| | 1734| | /// SwifterSwift: hex #9ACD32 1735| | public static let yellowGreen = Color(hex: 0x9ACD32)! 1736| | } 1737| |} 1738| | 1739| |// MARK: - Flat UI colors 1740| | 1741| |public extension Color { 1742| | /// SwifterSwift: Flat UI colors 1743| | enum FlatUI { 1744| | // http://flatuicolors.com. 1745| | 1746| | /// SwifterSwift: hex #1ABC9C 1747| | public static let turquoise = Color(hex: 0x1ABC9C)! 1748| | 1749| | /// SwifterSwift: hex #16A085 1750| | public static let greenSea = Color(hex: 0x16A085)! 1751| | 1752| | /// SwifterSwift: hex #2ECC71 1753| | public static let emerald = Color(hex: 0x2ECC71)! 1754| | 1755| | /// SwifterSwift: hex #27AE60 1756| | public static let nephritis = Color(hex: 0x27AE60)! 1757| | 1758| | /// SwifterSwift: hex #3498DB 1759| | public static let peterRiver = Color(hex: 0x3498DB)! 1760| | 1761| | /// SwifterSwift: hex #2980B9 1762| | public static let belizeHole = Color(hex: 0x2980B9)! 1763| | 1764| | /// SwifterSwift: hex #9B59B6 1765| | public static let amethyst = Color(hex: 0x9B59B6)! 1766| | 1767| | /// SwifterSwift: hex #8E44AD 1768| | public static let wisteria = Color(hex: 0x8E44AD)! 1769| | 1770| | /// SwifterSwift: hex #34495E 1771| | public static let wetAsphalt = Color(hex: 0x34495E)! 1772| | 1773| | /// SwifterSwift: hex #2C3E50 1774| | public static let midnightBlue = Color(hex: 0x2C3E50)! 1775| | 1776| | /// SwifterSwift: hex #F1C40F 1777| | public static let sunFlower = Color(hex: 0xF1C40F)! 1778| | 1779| | /// SwifterSwift: hex #F39C12 1780| | public static let flatOrange = Color(hex: 0xF39C12)! 1781| | 1782| | /// SwifterSwift: hex #E67E22 1783| | public static let carrot = Color(hex: 0xE67E22)! 1784| | 1785| | /// SwifterSwift: hex #D35400 1786| | public static let pumkin = Color(hex: 0xD35400)! 1787| | 1788| | /// SwifterSwift: hex #E74C3C 1789| | public static let alizarin = Color(hex: 0xE74C3C)! 1790| | 1791| | /// SwifterSwift: hex #C0392B 1792| | public static let pomegranate = Color(hex: 0xC0392B)! 1793| | 1794| | /// SwifterSwift: hex #ECF0F1 1795| | public static let clouds = Color(hex: 0xECF0F1)! 1796| | 1797| | /// SwifterSwift: hex #BDC3C7 1798| | public static let silver = Color(hex: 0xBDC3C7)! 1799| | 1800| | /// SwifterSwift: hex #7F8C8D 1801| | public static let asbestos = Color(hex: 0x7F8C8D)! 1802| | 1803| | /// SwifterSwift: hex #95A5A6 1804| | public static let concerte = Color(hex: 0x95A5A6)! 1805| | } 1806| | 1807| | // swiftlint:enable type_body_length 1808| |} 1809| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/Shared/EdgeInsetsExtensions.swift: 1| |// EdgeInsetsExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if os(iOS) || os(tvOS) || os(watchOS) 4| |import UIKit 5| |/// SwifterSwift: EdgeInsets 6| |public typealias EdgeInsets = UIEdgeInsets 7| |#elseif os(macOS) 8| |import Foundation 9| |/// SwifterSwift: EdgeInsets 10| |public typealias EdgeInsets = NSEdgeInsets 11| | 12| |public extension NSEdgeInsets { 13| | /// SwifterSwift: An edge insets struct whose top, left, bottom, and right fields are all set to 0. 14| | static let zero = NSEdgeInsets() 15| |} 16| | 17| |// swiftlint:disable missing_swifterswift_prefix 18| |extension NSEdgeInsets: Equatable { 19| | /// Returns a Boolean value indicating whether two values are equal. 20| | /// 21| | /// Equality is the inverse of inequality. For any values `a` and `b`, 22| | /// `a == b` implies that `a != b` is `false`. 23| | /// 24| | /// - Parameters: 25| | /// - lhs: A value to compare. 26| | /// - rhs: Another value to compare. 27| | public static func == (lhs: NSEdgeInsets, rhs: NSEdgeInsets) -> Bool { 28| | return lhs.top == rhs.top && 29| | lhs.left == rhs.left && 30| | lhs.bottom == rhs.bottom && 31| | lhs.right == rhs.right 32| | } 33| |} 34| | 35| |// swiftlint:enable missing_swifterswift_prefix 36| |#endif 37| | 38| |#if os(iOS) || os(tvOS) || os(watchOS) || os(macOS) 39| | 40| |// MARK: - Properties 41| | 42| |public extension EdgeInsets { 43| | /// SwifterSwift: Return the vertical insets. The vertical insets is composed by top + bottom. 44| | /// 45| 1| var vertical: CGFloat { 46| 1| // Source: https://github.com/MessageKit/MessageKit/blob/master/Sources/SwifterSwift/EdgeInsets%2BExtensions.swift 47| 1| return top + bottom 48| 1| } 49| | 50| | /// SwifterSwift: Return the horizontal insets. The horizontal insets is composed by left + right. 51| | /// 52| 1| var horizontal: CGFloat { 53| 1| // Source: https://github.com/MessageKit/MessageKit/blob/master/Sources/SwifterSwift/EdgeInsets%2BExtensions.swift 54| 1| return left + right 55| 1| } 56| |} 57| | 58| |// MARK: - Methods 59| | 60| |public extension EdgeInsets { 61| | /// SwifterSwift: Creates an `EdgeInsets` with the inset value applied to all (top, bottom, right, left) 62| | /// 63| | /// - Parameter inset: Inset to be applied in all the edges. 64| 1| init(inset: CGFloat) { 65| 1| self.init(top: inset, left: inset, bottom: inset, right: inset) 66| 1| } 67| | 68| | /// SwifterSwift: Creates an `EdgeInsets` with the horizontal value equally divided and applied to right and left. 69| | /// And the vertical value equally divided and applied to top and bottom. 70| | /// 71| | /// 72| | /// - Parameter horizontal: Inset to be applied to right and left. 73| | /// - Parameter vertical: Inset to be applied to top and bottom. 74| 1| init(horizontal: CGFloat, vertical: CGFloat) { 75| 1| self.init(top: vertical / 2, left: horizontal / 2, bottom: vertical / 2, right: horizontal / 2) 76| 1| } 77| | 78| | /// SwifterSwift: Creates an `EdgeInsets` based on current value and top offset. 79| | /// 80| | /// - Parameters: 81| | /// - top: Offset to be applied in to the top edge. 82| | /// - Returns: EdgeInsets offset with given offset. 83| 2| func insetBy(top: CGFloat) -> EdgeInsets { 84| 2| return EdgeInsets(top: self.top + top, left: left, bottom: bottom, right: right) 85| 2| } 86| | 87| | /// SwifterSwift: Creates an `EdgeInsets` based on current value and left offset. 88| | /// 89| | /// - Parameters: 90| | /// - left: Offset to be applied in to the left edge. 91| | /// - Returns: EdgeInsets offset with given offset. 92| 2| func insetBy(left: CGFloat) -> EdgeInsets { 93| 2| return EdgeInsets(top: top, left: self.left + left, bottom: bottom, right: right) 94| 2| } 95| | 96| | /// SwifterSwift: Creates an `EdgeInsets` based on current value and bottom offset. 97| | /// 98| | /// - Parameters: 99| | /// - bottom: Offset to be applied in to the bottom edge. 100| | /// - Returns: EdgeInsets offset with given offset. 101| 4| func insetBy(bottom: CGFloat) -> EdgeInsets { 102| 4| return EdgeInsets(top: top, left: left, bottom: self.bottom + bottom, right: right) 103| 4| } 104| | 105| | /// SwifterSwift: Creates an `EdgeInsets` based on current value and right offset. 106| | /// 107| | /// - Parameters: 108| | /// - right: Offset to be applied in to the right edge. 109| | /// - Returns: EdgeInsets offset with given offset. 110| 2| func insetBy(right: CGFloat) -> EdgeInsets { 111| 2| return EdgeInsets(top: top, left: left, bottom: bottom, right: self.right + right) 112| 2| } 113| | 114| | /// SwifterSwift: Creates an `EdgeInsets` based on current value and horizontal value equally divided and applied to right offset and left offset. 115| | /// 116| | /// - Parameters: 117| | /// - horizontal: Offset to be applied to right and left. 118| | /// - Returns: EdgeInsets offset with given offset. 119| 4| func insetBy(horizontal: CGFloat) -> EdgeInsets { 120| 4| return EdgeInsets(top: top, left: left + horizontal / 2, bottom: bottom, right: right + horizontal / 2) 121| 4| } 122| | 123| | /// SwifterSwift: Creates an `EdgeInsets` based on current value and vertical value equally divided and applied to top and bottom. 124| | /// 125| | /// - Parameters: 126| | /// - vertical: Offset to be applied to top and bottom. 127| | /// - Returns: EdgeInsets offset with given offset. 128| 2| func insetBy(vertical: CGFloat) -> EdgeInsets { 129| 2| return EdgeInsets(top: top + vertical / 2, left: left, bottom: bottom + vertical / 2, right: right) 130| 2| } 131| |} 132| | 133| |// MARK: - Operators 134| | 135| |public extension EdgeInsets { 136| | /// SwifterSwift: Add all the properties of two `EdgeInsets` to create their addition. 137| | /// 138| | /// - Parameters: 139| | /// - lhs: The left-hand expression 140| | /// - rhs: The right-hand expression 141| | /// - Returns: A new `EdgeInsets` instance where the values of `lhs` and `rhs` are added together. 142| 3| static func + (_ lhs: EdgeInsets, _ rhs: EdgeInsets) -> EdgeInsets { 143| 3| return EdgeInsets(top: lhs.top + rhs.top, 144| 3| left: lhs.left + rhs.left, 145| 3| bottom: lhs.bottom + rhs.bottom, 146| 3| right: lhs.right + rhs.right) 147| 3| } 148| | 149| | /// SwifterSwift: Add all the properties of two `EdgeInsets` to the left-hand instance. 150| | /// 151| | /// - Parameters: 152| | /// - lhs: The left-hand expression to be mutated 153| | /// - rhs: The right-hand expression 154| 3| static func += (_ lhs: inout EdgeInsets, _ rhs: EdgeInsets) { 155| 3| lhs.top += rhs.top 156| 3| lhs.left += rhs.left 157| 3| lhs.bottom += rhs.bottom 158| 3| lhs.right += rhs.right 159| 3| } 160| |} 161| | 162| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/SpriteKit/SKNodeExtensions.swift: 1| |// SKNodeExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(SpriteKit) 4| |import SpriteKit 5| | 6| |// MARK: - Methods 7| | 8| |public extension SKNode { 9| | /// SwifterSwift: Return an array of all SKNode descendants 10| | /// 11| | /// mySKNode.descendants() -> [childNodeOne, childNodeTwo] 12| | /// 13| 6| func descendants() -> [SKNode] { 14| 6| var children = self.children 15| 6| children.append(contentsOf: children.reduce(into: [SKNode]()) { $0.append(contentsOf: $1.descendants()) }) 16| 6| return children 17| 6| } 18| | 19| | /// SwifterSwift: The center anchor of the node in its parent's coordinate system. 20| | /// 21| | /// mySKNode.center = CGPoint(x: frame.midX, y: frame.midY) 22| | /// 23| | var center: CGPoint { 24| 1| get { 25| 1| let contents = calculateAccumulatedFrame() 26| 1| return CGPoint(x: contents.midX, y: contents.midY) 27| 1| } 28| 1| set { 29| 1| let contents = calculateAccumulatedFrame() 30| 1| position = CGPoint(x: newValue.x - contents.midX, y: newValue.y - contents.midY) 31| 1| } 32| | } 33| | 34| | /// SwifterSwift: The top left anchor of the node in its parent's coordinate system. 35| | /// 36| | /// mySKNode.topLeft = CGPoint(x: frame.minX, y: frame.maxY) 37| | /// 38| | var topLeft: CGPoint { 39| 1| get { 40| 1| let contents = calculateAccumulatedFrame() 41| 1| return CGPoint(x: contents.minX, y: contents.maxY) 42| 1| } 43| 1| set { 44| 1| let contents = calculateAccumulatedFrame() 45| 1| position = CGPoint(x: newValue.x - contents.minX, y: newValue.y - contents.maxY) 46| 1| } 47| | } 48| | 49| | /// SwifterSwift: The top right anchor of the node in its parent's coordinate system. 50| | /// 51| | /// mySKNode.topRight = CGPoint(x: frame.maxX, y: frame.maxY) 52| | /// 53| | var topRight: CGPoint { 54| 1| get { 55| 1| let contents = calculateAccumulatedFrame() 56| 1| return CGPoint(x: contents.maxX, y: contents.maxY) 57| 1| } 58| 1| set { 59| 1| let contents = calculateAccumulatedFrame() 60| 1| position = CGPoint(x: newValue.x - contents.maxX, y: newValue.y - contents.maxY) 61| 1| } 62| | } 63| | 64| | /// SwifterSwift: The bottom left anchor of the node in its parent's coordinate system. 65| | /// 66| | /// mySKNode.center = GPoint(x: frame.minX, y: frame.minY) 67| | /// 68| | var bottomLeft: CGPoint { 69| 1| get { 70| 1| let contents = calculateAccumulatedFrame() 71| 1| return CGPoint(x: contents.minX, y: contents.minY) 72| 1| } 73| 1| set { 74| 1| let contents = calculateAccumulatedFrame() 75| 1| position = CGPoint(x: newValue.x - contents.minX, y: newValue.y - contents.minY) 76| 1| } 77| | } 78| | 79| | /// SwifterSwift: The bottom right anchor of the node in its parent's coordinate system. 80| | /// 81| | /// mySKNode.bottomRight = CGPoint(x: frame.maxX, y: frame.minY) 82| | /// 83| | var bottomRight: CGPoint { 84| 1| get { 85| 1| let contents = calculateAccumulatedFrame() 86| 1| return CGPoint(x: contents.maxX, y: contents.minY) 87| 1| } 88| 1| set { 89| 1| let contents = calculateAccumulatedFrame() 90| 1| position = CGPoint(x: newValue.x - contents.maxX, y: newValue.y - contents.minY) 91| 1| } 92| | } 93| |} 94| | 95| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/StoreKit/SKProductExtensions.swift: 1| |// SKProductExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(StoreKit) 4| |import StoreKit 5| | 6| |@available(watchOS 6.2, *) 7| |public extension SKProduct { 8| 1| private static let priceFormatter: NumberFormatter = { 9| 1| let priceFormatter = NumberFormatter() 10| 1| priceFormatter.numberStyle = .currency 11| 1| return priceFormatter 12| 1| }() 13| | 14| | /// SwifterSwift: Localized price of SKProduct 15| 4| var localizedPrice: String? { 16| 4| let formatter = SKProduct.priceFormatter 17| 4| formatter.locale = priceLocale 18| 4| return formatter.string(from: price) 19| 4| } 20| |} 21| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/ArrayExtensions.swift: 1| |// ArrayExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |// MARK: - Methods 4| | 5| |public extension Array { 6| | /// SwifterSwift: Insert an element at the beginning of array. 7| | /// 8| | /// [2, 3, 4, 5].prepend(1) -> [1, 2, 3, 4, 5] 9| | /// ["e", "l", "l", "o"].prepend("h") -> ["h", "e", "l", "l", "o"] 10| | /// 11| | /// - Parameter newElement: element to insert. 12| 1| mutating func prepend(_ newElement: Element) { 13| 1| insert(newElement, at: 0) 14| 1| } 15| | 16| | /// SwifterSwift: Safely swap values at given index positions. 17| | /// 18| | /// [1, 2, 3, 4, 5].safeSwap(from: 3, to: 0) -> [4, 2, 3, 1, 5] 19| | /// ["h", "e", "l", "l", "o"].safeSwap(from: 1, to: 0) -> ["e", "h", "l", "l", "o"] 20| | /// 21| | /// - Parameters: 22| | /// - index: index of first element. 23| | /// - otherIndex: index of other element. 24| 4| mutating func safeSwap(from index: Index, to otherIndex: Index) { 25| 4| guard index != otherIndex else { return } 26| 3| guard startIndex.. [MyStruct(x: 1), MyStruct(x: 2), MyStruct(x: 3)] 35| | /// 36| | /// - Parameters: 37| | /// - otherArray: array containing elements in the desired order. 38| | /// - keyPath: keyPath indiciating the property that the array should be sorted by 39| | /// - Returns: sorted array. 40| 3| func sorted(like otherArray: [T], keyPath: KeyPath) -> [Element] { 41| 15| let dict = otherArray.enumerated().reduce(into: [:]) { $0[$1.element] = $1.offset } 42| 26| return sorted { 43| 26| guard let thisIndex = dict[$0[keyPath: keyPath]] else { return false } 44| 25| guard let otherIndex = dict[$1[keyPath: keyPath]] else { return true } 45| 23| return thisIndex < otherIndex 46| 25| } 47| 3| } 48| |} 49| | 50| |// MARK: - Methods (Equatable) 51| | 52| |public extension Array where Element: Equatable { 53| | /// SwifterSwift: Remove all instances of an item from array. 54| | /// 55| | /// [1, 2, 2, 3, 4, 5].removeAll(2) -> [1, 3, 4, 5] 56| | /// ["h", "e", "l", "l", "o"].removeAll("l") -> ["h", "e", "o"] 57| | /// 58| | /// - Parameter item: item to remove. 59| | /// - Returns: self after removing all instances of item. 60| | @discardableResult 61| 2| mutating func removeAll(_ item: Element) -> [Element] { 62| 9| removeAll(where: { $0 == item }) 63| 2| return self 64| 2| } 65| | 66| | /// SwifterSwift: Remove all instances contained in items parameter from array. 67| | /// 68| | /// [1, 2, 2, 3, 4, 5].removeAll([2,5]) -> [1, 3, 4] 69| | /// ["h", "e", "l", "l", "o"].removeAll(["l", "h"]) -> ["e", "o"] 70| | /// 71| | /// - Parameter items: items to remove. 72| | /// - Returns: self after removing all instances of all items in given array. 73| | @discardableResult 74| 3| mutating func removeAll(_ items: [Element]) -> [Element] { 75| 3| guard !items.isEmpty else { return self } 76| 10| removeAll(where: { items.contains($0) }) 77| 1| return self 78| 3| } 79| | 80| | /// SwifterSwift: Remove all duplicate elements from Array. 81| | /// 82| | /// [1, 2, 2, 3, 4, 5].removeDuplicates() -> [1, 2, 3, 4, 5] 83| | /// ["h", "e", "l", "l", "o"]. removeDuplicates() -> ["h", "e", "l", "o"] 84| | /// 85| | /// - Returns: Return array with all duplicate elements removed. 86| | @discardableResult 87| 1| mutating func removeDuplicates() -> [Element] { 88| 1| // Thanks to https://github.com/sairamkotha for improving the method 89| 9| self = reduce(into: [Element]()) { 90| 9| if !$0.contains($1) { 91| 5| $0.append($1) 92| 9| } 93| 9| } 94| 1| return self 95| 1| } 96| | 97| | /// SwifterSwift: Return array with all duplicate elements removed. 98| | /// 99| | /// [1, 1, 2, 2, 3, 3, 3, 4, 5].withoutDuplicates() -> [1, 2, 3, 4, 5]) 100| | /// ["h", "e", "l", "l", "o"].withoutDuplicates() -> ["h", "e", "l", "o"]) 101| | /// 102| | /// - Returns: an array of unique elements. 103| | /// 104| 2| func withoutDuplicates() -> [Element] { 105| 2| // Thanks to https://github.com/sairamkotha for improving the method 106| 14| return reduce(into: [Element]()) { 107| 14| if !$0.contains($1) { 108| 9| $0.append($1) 109| 14| } 110| 14| } 111| 2| } 112| | 113| | /// SwifterSwift: Returns an array with all duplicate elements removed using KeyPath to compare. 114| | /// 115| | /// - Parameter path: Key path to compare, the value must be Equatable. 116| | /// - Returns: an array of unique elements. 117| 1| func withoutDuplicates(keyPath path: KeyPath) -> [Element] { 118| 7| return reduce(into: [Element]()) { result, element in 119| 12| if !result.contains(where: { $0[keyPath: path] == element[keyPath: path] }) { 120| 4| result.append(element) 121| 7| } 122| 7| } 123| 1| } 124| | 125| | /// SwifterSwift: Returns an array with all duplicate elements removed using KeyPath to compare. 126| | /// 127| | /// - Parameter path: Key path to compare, the value must be Hashable. 128| | /// - Returns: an array of unique elements. 129| 1| func withoutDuplicates(keyPath path: KeyPath) -> [Element] { 130| 1| var set = Set() 131| 7| return filter { set.insert($0[keyPath: path]).inserted } 132| 1| } 133| |} /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/BidirectionalCollectionExtensions.swift: 1| |// BidirectionalCollectionExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |// MARK: - Methods 4| | 5| |public extension BidirectionalCollection { 6| | /// 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)`. 7| | /// 8| | /// let arr = [1, 2, 3, 4, 5] 9| | /// arr[offset: 1] -> 2 10| | /// arr[offset: -2] -> 4 11| | /// 12| | /// - Parameter distance: The distance to offset. 13| 3| subscript(offset distance: Int) -> Element { 14| 3| let index = distance >= 0 ? startIndex : endIndex 15| 3| return self[indices.index(index, offsetBy: distance)] 16| 3| } 17| | 18| | /// SwifterSwift: Returns the last element of the sequence with having property by given key path equals to given `value`. 19| | /// 20| | /// - Parameters: 21| | /// - keyPath: The `KeyPath` of property for `Element` to compare. 22| | /// - value: The value to compare with `Element` property 23| | /// - 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. 24| 2| func last(where keyPath: KeyPath, equals value: T) -> Element? { 25| 4| return last { $0[keyPath: keyPath] == value } 26| 2| } 27| |} /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/BinaryFloatingPointExtensions.swift: 1| |// BinaryFloatingPointExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(Foundation) 4| |import Foundation 5| | 6| |// MARK: - Methods 7| | 8| |public extension BinaryFloatingPoint { 9| | #if canImport(Foundation) 10| | /// SwifterSwift: Returns a rounded value with the specified number of decimal places and rounding rule. If `numberOfDecimalPlaces` is negative, `0` will be used. 11| | /// 12| | /// let num = 3.1415927 13| | /// num.rounded(numberOfDecimalPlaces: 3, rule: .up) -> 3.142 14| | /// num.rounded(numberOfDecimalPlaces: 3, rule: .down) -> 3.141 15| | /// num.rounded(numberOfDecimalPlaces: 2, rule: .awayFromZero) -> 3.15 16| | /// num.rounded(numberOfDecimalPlaces: 4, rule: .towardZero) -> 3.1415 17| | /// num.rounded(numberOfDecimalPlaces: -1, rule: .toNearestOrEven) -> 3 18| | /// 19| | /// - Parameters: 20| | /// - numberOfDecimalPlaces: The expected number of decimal places. 21| | /// - rule: The rounding rule to use. 22| | /// - Returns: The rounded value. 23| 6| func rounded(numberOfDecimalPlaces: Int, rule: FloatingPointRoundingRule) -> Self { 24| 6| let factor = Self(pow(10.0, Double(max(0, numberOfDecimalPlaces)))) 25| 6| return (self * factor).rounded(rule) / factor 26| 6| } 27| | #endif 28| |} 29| | 30| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/BoolExtensions.swift: 1| |// BoolExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |// MARK: - Properties 4| | 5| |public extension Bool { 6| | /// SwifterSwift: Return 1 if true, or 0 if false. 7| | /// 8| | /// false.int -> 0 9| | /// true.int -> 1 10| | /// 11| 2| var int: Int { 12| 2| return self ? 1 : 0 13| 2| } 14| | 15| | /// SwifterSwift: Return "true" if true, or "false" if false. 16| | /// 17| | /// false.string -> "false" 18| | /// true.string -> "true" 19| | /// 20| 2| var string: String { 21| 2| return self ? "true" : "false" 22| 2| } 23| |} /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/CharacterExtensions.swift: 1| |// CharacterExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |// MARK: - Properties 4| | 5| |public extension Character { 6| | /// SwifterSwift: Check if character is emoji. 7| | /// 8| | /// Character("😀").isEmoji -> true 9| | /// 10| 2| var isEmoji: Bool { 11| 2| // http://stackoverflow.com/questions/30757193/find-out-if-character-in-string-is-emoji 12| 2| let scalarValue = String(self).unicodeScalars.first!.value 13| 2| switch scalarValue { 14| 2| case 0x1F600...0x1F64F, // Emoticons 15| 1| 0x1F300...0x1F5FF, // Misc Symbols and Pictographs 16| 1| 0x1F680...0x1F6FF, // Transport and Map 17| 1| 0x1F1E6...0x1F1FF, // Regional country flags 18| 1| 0x2600...0x26FF, // Misc symbols 19| 1| 0x2700...0x27BF, // Dingbats 20| 1| 0xE0020...0xE007F, // Tags 21| 1| 0xFE00...0xFE0F, // Variation Selectors 22| 1| 0x1F900...0x1F9FF, // Supplemental Symbols and Pictographs 23| 1| 127_000...127_600, // Various asian characters 24| 1| 65024...65039, // Variation selector 25| 1| 9100...9300, // Misc items 26| 1| 8400...8447: // Combining Diacritical Marks for Symbols 27| 1| return true 28| 2| default: 29| 1| return false 30| 2| } 31| 2| } 32| | 33| | /// SwifterSwift: Integer from character (if applicable). 34| | /// 35| | /// Character("1").int -> 1 36| | /// Character("A").int -> nil 37| | /// 38| 3| var int: Int? { 39| 3| return Int(String(self)) 40| 3| } 41| | 42| | /// SwifterSwift: String from character. 43| | /// 44| | /// Character("a").string -> "a" 45| | /// 46| 1| var string: String { 47| 1| return String(self) 48| 1| } 49| | 50| | /// SwifterSwift: Return the character lowercased. 51| | /// 52| | /// Character("A").lowercased -> Character("a") 53| | /// 54| 1| var lowercased: Character { 55| 1| return String(self).lowercased().first! 56| 1| } 57| | 58| | /// SwifterSwift: Return the character uppercased. 59| | /// 60| | /// Character("a").uppercased -> Character("A") 61| | /// 62| 1| var uppercased: Character { 63| 1| return String(self).uppercased().first! 64| 1| } 65| |} 66| | 67| |// MARK: - Methods 68| | 69| |public extension Character { 70| | /// SwifterSwift: Random character. 71| | /// 72| | /// Character.random() -> k 73| | /// 74| | /// - Returns: A random character. 75| 20| static func randomAlphanumeric() -> Character { 76| 20| return "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".randomElement()! 77| 20| } 78| |} 79| | 80| |// MARK: - Operators 81| | 82| |public extension Character { 83| | /// SwifterSwift: Repeat character multiple times. 84| | /// 85| | /// Character("-") * 10 -> "----------" 86| | /// 87| | /// - Parameters: 88| | /// - lhs: character to repeat. 89| | /// - rhs: number of times to repeat character. 90| | /// - Returns: string with character repeated n times. 91| 3| static func * (lhs: Character, rhs: Int) -> String { 92| 3| guard rhs > 0 else { return "" } 93| 1| return String(repeating: String(lhs), count: rhs) 94| 3| } 95| | 96| | /// SwifterSwift: Repeat character multiple times. 97| | /// 98| | /// 10 * Character("-") -> "----------" 99| | /// 100| | /// - Parameters: 101| | /// - lhs: number of times to repeat character. 102| | /// - rhs: character to repeat. 103| | /// - Returns: string with character repeated n times. 104| 3| static func * (lhs: Int, rhs: Character) -> String { 105| 3| guard lhs > 0 else { return "" } 106| 1| return String(repeating: String(rhs), count: lhs) 107| 3| } 108| |} /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/CollectionExtensions.swift: 1| |// CollectionExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(Dispatch) 4| |import Dispatch 5| |#endif 6| | 7| |// MARK: - Properties 8| | 9| |public extension Collection { 10| | /// SwifterSwift: The full range of the collection. 11| 2| var fullRange: Range { startIndex.. Void) { 26| 5| DispatchQueue.concurrentPerform(iterations: count) { 27| 5| each(self[index(startIndex, offsetBy: $0)]) 28| 5| } 29| 1| } 30| | #endif 31| | 32| | /// SwifterSwift: Safe protects the array from out of bounds by use of optional. 33| | /// 34| | /// let arr = [1, 2, 3, 4, 5] 35| | /// arr[safe: 1] -> 2 36| | /// arr[safe: 10] -> nil 37| | /// 38| | /// - Parameter index: index of element to access element. 39| 3| subscript(safe index: Index) -> Element? { 40| 3| return indices.contains(index) ? self[index] : nil 41| 3| } 42| | 43| | /// 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. 44| | /// 45| | /// [0, 2, 4, 7].group(by: 2) -> [[0, 2], [4, 7]] 46| | /// [0, 2, 4, 7, 6].group(by: 2) -> [[0, 2], [4, 7], [6]] 47| | /// 48| | /// - Parameter size: The size of the slices to be returned. 49| | /// - Returns: grouped self. 50| 4| func group(by size: Int) -> [[Element]]? { 51| 4| // Inspired by: https://lodash.com/docs/4.17.4#chunk 52| 4| guard size > 0, !isEmpty else { return nil } 53| 3| var start = startIndex 54| 3| var slices = [[Element]]() 55| 11| while start != endIndex { 56| 8| let end = index(start, offsetBy: size, limitedBy: endIndex) ?? endIndex 57| 8| slices.append(Array(self[start.. [0, 2, 5] 66| | /// 67| | /// - Parameter condition: condition to evaluate each element against. 68| | /// - Returns: all indices where the specified condition evaluates to true. (optional) 69| 2| func indices(where condition: (Element) throws -> Bool) rethrows -> [Index]? { 70| 10| let indices = try self.indices.filter { try condition(self[$0]) } 71| 2| return indices.isEmpty ? nil : indices 72| 2| } 73| | 74| | /// SwifterSwift: Calls the given closure with an array of size of the parameter slice. 75| | /// 76| | /// [0, 2, 4, 7].forEach(slice: 2) { print($0) } -> // print: [0, 2], [4, 7] 77| | /// [0, 2, 4, 7, 6].forEach(slice: 2) { print($0) } -> // print: [0, 2], [4, 7], [6] 78| | /// 79| | /// - Parameters: 80| | /// - slice: size of array in each interation. 81| | /// - body: a closure that takes an array of slice size as a parameter. 82| 5| func forEach(slice: Int, body: ([Element]) throws -> Void) rethrows { 83| 5| var start = startIndex 84| 6| while case let end = index(start, offsetBy: slice, limitedBy: endIndex) ?? endIndex, 85| 8| start != end { 86| 8| try body(Array(self[start.. [1, 2, 5] 98| | /// [1.2, 2.3, 4.5, 3.4, 4.5].indices(of 2.3) -> [1] 99| | /// ["h", "e", "l", "l", "o"].indices(of "l") -> [2, 3] 100| | /// 101| | /// - Parameter item: item to check. 102| | /// - Returns: an array with all indices of the given item. 103| 4| func indices(of item: Element) -> [Index] { 104| 24| return indices.filter { self[$0] == item } 105| 4| } 106| |} 107| | 108| |// MARK: - Methods (BinaryInteger) 109| | 110| |public extension Collection where Element: BinaryInteger { 111| | /// SwifterSwift: Average of all elements in array. 112| | /// 113| | /// - Returns: the average of the array's elements. 114| 2| func average() -> Double { 115| 2| // http://stackoverflow.com/questions/28288148/making-my-function-calculate-average-of-array-swift 116| 2| guard !isEmpty else { return .zero } 117| 1| return Double(reduce(.zero, +)) / Double(count) 118| 2| } 119| |} 120| | 121| |// MARK: - Methods (FloatingPoint) 122| | 123| |public extension Collection where Element: FloatingPoint { 124| | /// SwifterSwift: Average of all elements in array. 125| | /// 126| | /// [1.2, 2.3, 4.5, 3.4, 4.5].average() = 3.18 127| | /// 128| | /// - Returns: average of the array's elements. 129| 2| func average() -> Element { 130| 2| guard !isEmpty else { return .zero } 131| 1| return reduce(.zero, +) / Element(count) 132| 2| } 133| |} /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/ComparableExtensions.swift: 1| |// ComparableExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |// MARK: - Methods 4| | 5| |public extension Comparable { 6| | /// SwifterSwift: Returns true if value is in the provided range. 7| | /// 8| | /// 1.isBetween(5...7) // false 9| | /// 7.isBetween(6...12) // true 10| | /// date.isBetween(date1...date2) 11| | /// "c".isBetween(a...d) // true 12| | /// 0.32.isBetween(0.31...0.33) // true 13| | /// 14| | /// - parameter min: Minimum comparable value. 15| | /// - parameter max: Maximum comparable value. 16| | /// 17| | /// - returns: `true` if value is between `min` and `max`, `false` otherwise. 18| 5| func isBetween(_ range: ClosedRange) -> Bool { 19| 5| return range ~= self 20| 5| } 21| | 22| | /// SwifterSwift: Returns value limited within the provided range. 23| | /// 24| | /// 1.clamped(to: 3...8) // 3 25| | /// 4.clamped(to: 3...7) // 4 26| | /// "c".clamped(to: "e"..."g") // "e" 27| | /// 0.32.clamped(to: 0.1...0.29) // 0.29 28| | /// 29| | /// - parameter min: Lower bound to limit the value to. 30| | /// - parameter max: Upper bound to limit the value to. 31| | /// 32| | /// - returns: A value limited to the range between `min` and `max`. 33| 4| func clamped(to range: ClosedRange) -> Self { 34| 4| return max(range.lowerBound, min(self, range.upperBound)) 35| 4| } 36| |} /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/DecodableExtensions.swift: 1| |// DecodableExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(Foundation) 4| |import Foundation 5| |#endif 6| | 7| |public extension Decodable { 8| | #if canImport(Foundation) 9| | /// SwifterSwift: Parsing the model in Decodable type 10| | /// - Parameters: 11| | /// - data: Data. 12| | /// - decoder: JSONDecoder. Initialized by default 13| 2| init?(from data: Data, using decoder: JSONDecoder = .init()) { 14| 2| guard let parsed = try? decoder.decode(Self.self, from: data) else { return nil } 15| 1| self = parsed 16| 1| } 17| | #endif 18| |} /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/Deprecated/StdlibDeprecated.swift: 1| |// StdlibDeprecated.swift - Copyright 2020 SwifterSwift 2| | 3| 0|private func optionalCompareAscending(path1: T?, path2: T?) -> Bool { 4| 0| guard let path1 = path1, let path2 = path2 else { return false } 5| 0| return path1 < path2 6| 0|} 7| | 8| 0|private func optionalCompareDescending(path1: T?, path2: T?) -> Bool { 9| 0| guard let path1 = path1, let path2 = path2 else { return false } 10| 0| return path1 > path2 11| 0|} 12| | 13| |public extension Array { 14| | /// SwifterSwift: Returns a sorted array based on an optional keypath. 15| | /// 16| | /// - Parameter path: Key path to sort. The key path type must be Comparable. 17| | /// - Parameter ascending: If order must be ascending. 18| | /// - Returns: Sorted array based on keyPath. 19| | @available(*, deprecated, message: "Use sorted(by:with:) instead.") 20| 0| func sorted(by path: KeyPath, ascending: Bool) -> [Element] { 21| 0| if ascending { 22| 0| return sorted(by: path, with: optionalCompareAscending) 23| 0| } 24| 0| return sorted(by: path, with: optionalCompareDescending) 25| 0| } 26| | 27| | /// SwifterSwift: Returns a sorted array based on a keypath. 28| | /// 29| | /// - Parameter path: Key path to sort. The key path type must be Comparable. 30| | /// - Parameter ascending: If order must be ascending. 31| | /// - Returns: Sorted array based on keyPath. 32| | @available(*, deprecated, message: "Use sorted(by:with:) instead.") 33| 0| func sorted(by path: KeyPath, ascending: Bool) -> [Element] { 34| 0| if ascending { 35| 0| return sorted(by: path, with: <) 36| 0| } 37| 0| return sorted(by: path, with: >) 38| 0| } 39| | 40| | /// SwifterSwift: Sort the array based on an optional keypath. 41| | /// 42| | /// - Parameters: 43| | /// - path: Key path to sort, must be Comparable. 44| | /// - ascending: whether order is ascending or not. 45| | /// - Returns: self after sorting. 46| | @available(*, deprecated, message: "Use sort(by:with:) instead.") 47| | @discardableResult 48| 0| mutating func sort(by path: KeyPath, ascending: Bool) -> [Element] { 49| 0| if ascending { 50| 0| sort(by: path, with: optionalCompareAscending) 51| 0| } else { 52| 0| sort(by: path, with: optionalCompareDescending) 53| 0| } 54| 0| return self 55| 0| } 56| | 57| | /// SwifterSwift: Sort the array based on a keypath. 58| | /// 59| | /// - Parameters: 60| | /// - path: Key path to sort, must be Comparable. 61| | /// - ascending: whether order is ascending or not. 62| | /// - Returns: self after sorting. 63| | @available(*, deprecated, message: "Use sort(by:with:) instead.") 64| | @discardableResult 65| 0| mutating func sort(by path: KeyPath, ascending: Bool) -> [Element] { 66| 0| if ascending { 67| 0| sort(by: path, with: <) 68| 0| } else { 69| 0| sort(by: path, with: >) 70| 0| } 71| 0| return self 72| 0| } 73| |} 74| | 75| |public extension Sequence { 76| | /// SwifterSwift: Returns an array containing the results of mapping the given key path over the sequence’s elements. 77| | /// 78| | /// - Parameter keyPath: Key path to map. 79| | /// - Returns: An array containing the results of mapping. 80| | @available(*, deprecated, message: "Please use map() with a key path instead.") 81| 0| func map(by keyPath: KeyPath) -> [T] { 82| 0| return map { $0[keyPath: keyPath] } 83| 0| } 84| | 85| | /// SwifterSwift: Returns an array containing the non-nil results of mapping the given key path over the sequence’s elements. 86| | /// 87| | /// - Parameter keyPath: Key path to map. 88| | /// - Returns: An array containing the non-nil results of mapping. 89| | @available(*, deprecated, message: "Please use compactMap() with a key path instead.") 90| 0| func compactMap(by keyPath: KeyPath) -> [T] { 91| 0| return compactMap { $0[keyPath: keyPath] } 92| 0| } 93| | 94| | /// SwifterSwift: Returns an array containing the results of filtering the sequence’s elements by a boolean key path. 95| | /// 96| | /// - Parameter keyPath: Boolean key path. If it's value is `true` the element will be added to result. 97| | /// - Returns: An array containing filtered elements. 98| | @available(*, deprecated, message: "Please use filter() with a key path instead.") 99| 0| func filter(by keyPath: KeyPath) -> [Element] { 100| 0| return filter { $0[keyPath: keyPath] } 101| 0| } 102| | 103| | /// SwifterSwift: Get last element that satisfies a conditon. 104| | /// 105| | /// [2, 2, 4, 7].last(where: {$0 % 2 == 0}) -> 4 106| | /// 107| | /// - Parameter condition: condition to evaluate each element against. 108| | /// - Returns: the last element in the array matching the specified condition. (optional) 109| | @available(*, deprecated, message: "For an unordered sequence using `last` instead of `first` is equal.") 110| 0| func last(where condition: (Element) throws -> Bool) rethrows -> Element? { 111| 0| for element in reversed() { 112| 0| if try condition(element) { return element } 113| 0| } 114| 0| return nil 115| 0| } 116| |} /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/DictionaryExtensions.swift: 1| |// DictionaryExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(Foundation) 4| |import Foundation 5| |#endif 6| | 7| |// MARK: - Methods 8| | 9| |public extension Dictionary { 10| | /// SwifterSwift: Creates a Dictionary from a given sequence grouped by a given key path. 11| | /// 12| | /// - Parameters: 13| | /// - sequence: Sequence being grouped 14| | /// - keypath: The key path to group by. 15| 4| init(grouping sequence: S, by keyPath: KeyPath) where Value == [S.Element] { 16| 14| self.init(grouping: sequence, by: { $0[keyPath: keyPath] }) 17| 4| } 18| | 19| | /// SwifterSwift: Check if key exists in dictionary. 20| | /// 21| | /// let dict: [String: Any] = ["testKey": "testValue", "testArrayKey": [1, 2, 3, 4, 5]] 22| | /// dict.has(key: "testKey") -> true 23| | /// dict.has(key: "anotherKey") -> false 24| | /// 25| | /// - Parameter key: key to search for 26| | /// - Returns: true if key exists in dictionary. 27| 2| func has(key: Key) -> Bool { 28| 2| return index(forKey: key) != nil 29| 2| } 30| | 31| | /// SwifterSwift: Remove all keys contained in the keys parameter from the dictionary. 32| | /// 33| | /// var dict : [String: String] = ["key1" : "value1", "key2" : "value2", "key3" : "value3"] 34| | /// dict.removeAll(keys: ["key1", "key2"]) 35| | /// dict.keys.contains("key3") -> true 36| | /// dict.keys.contains("key1") -> false 37| | /// dict.keys.contains("key2") -> false 38| | /// 39| | /// - Parameter keys: keys to be removed 40| 3| mutating func removeAll(keys: S) where S.Element == Key { 41| 3| keys.forEach { removeValue(forKey: $0) } 42| 3| } 43| | 44| | /// SwifterSwift: Remove a value for a random key from the dictionary. 45| | @discardableResult 46| 2| mutating func removeValueForRandomKey() -> Value? { 47| 2| guard let randomKey = keys.randomElement() else { return nil } 48| 1| return removeValue(forKey: randomKey) 49| 2| } 50| | 51| | #if canImport(Foundation) 52| | /// SwifterSwift: JSON Data from dictionary. 53| | /// 54| | /// - Parameter prettify: set true to prettify data (default is false). 55| | /// - Returns: optional JSON Data (if applicable). 56| 4| func jsonData(prettify: Bool = false) -> Data? { 57| 4| guard JSONSerialization.isValidJSONObject(self) else { 58| 2| return nil 59| 2| } 60| 2| let options = (prettify == true) ? JSONSerialization.WritingOptions.prettyPrinted : JSONSerialization 61| 1| .WritingOptions() 62| 2| return try? JSONSerialization.data(withJSONObject: self, options: options) 63| 4| } 64| | #endif 65| | 66| | #if canImport(Foundation) 67| | /// SwifterSwift: JSON String from dictionary. 68| | /// 69| | /// dict.jsonString() -> "{"testKey":"testValue","testArrayKey":[1,2,3,4,5]}" 70| | /// 71| | /// dict.jsonString(prettify: true) 72| | /// /* 73| | /// returns the following string: 74| | /// 75| | /// "{ 76| | /// "testKey" : "testValue", 77| | /// "testArrayKey" : [ 78| | /// 1, 79| | /// 2, 80| | /// 3, 81| | /// 4, 82| | /// 5 83| | /// ] 84| | /// }" 85| | /// 86| | /// */ 87| | /// 88| | /// - Parameter prettify: set true to prettify string (default is false). 89| | /// - Returns: optional JSON String (if applicable). 90| 6| func jsonString(prettify: Bool = false) -> String? { 91| 6| guard JSONSerialization.isValidJSONObject(self) else { return nil } 92| 4| let options = (prettify == true) ? JSONSerialization.WritingOptions.prettyPrinted : JSONSerialization 93| 3| .WritingOptions() 94| 4| guard let jsonData = try? JSONSerialization.data(withJSONObject: self, options: options) else { return nil } 95| 4| return String(data: jsonData, encoding: .utf8) 96| 4| } 97| | #endif 98| | 99| | /// SwifterSwift: Returns a dictionary containing the results of mapping the given closure over the sequence’s elements. 100| | /// - 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. 101| | /// - Returns: A dictionary containing the transformed elements of this sequence. 102| 1| func mapKeysAndValues(_ transform: ((key: Key, value: Value)) throws -> (K, V)) rethrows -> [K: V] { 103| 1| return [K: V](uniqueKeysWithValues: try map(transform)) 104| 1| } 105| | 106| | /// SwifterSwift: Returns a dictionary containing the non-`nil` results of calling the given transformation with each element of this sequence. 107| | /// - Parameter transform: A closure that accepts an element of this sequence as its argument and returns an optional value. 108| | /// - Returns: A dictionary of the non-`nil` results of calling `transform` with each element of the sequence. 109| | /// - Complexity: *O(m + n)*, where _m_ is the length of this sequence and _n_ is the length of the result. 110| 1| func compactMapKeysAndValues(_ transform: ((key: Key, value: Value)) throws -> (K, V)?) rethrows -> [K: V] { 111| 1| return [K: V](uniqueKeysWithValues: try compactMap(transform)) 112| 1| } 113| | 114| | /// SwifterSwift: Creates a new dictionary using specified keys 115| | /// 116| | /// var dict = ["key1": 1, "key2": 2, "key3": 3, "key4": 4] 117| | /// dict.pick(keys: ["key1", "key3", "key4"]) -> ["key1": 1, "key3": 3, "key4": 4] 118| | /// dict.pick(keys: ["key2"]) -> ["key2": 2] 119| | /// 120| | /// - Complexity: O(K), where _K_ is the length of the keys array. 121| | /// 122| | /// - Parameter keys: An array of keys that will be the entries in the resulting dictionary. 123| | /// 124| | /// - Returns: A new dictionary that contains the specified keys only. If none of the keys exist, an empty dictionary will be returned. 125| 4| func pick(keys: [Key]) -> [Key: Value] { 126| 11| keys.reduce(into: [Key: Value]()) { result, item in 127| 11| result[item] = self[item] 128| 11| } 129| 4| } 130| |} 131| | 132| |// MARK: - Methods (Value: Equatable) 133| | 134| |public extension Dictionary where Value: Equatable { 135| | /// SwifterSwift: Returns an array of all keys that have the given value in dictionary. 136| | /// 137| | /// let dict = ["key1": "value1", "key2": "value1", "key3": "value2"] 138| | /// dict.keys(forValue: "value1") -> ["key1", "key2"] 139| | /// dict.keys(forValue: "value2") -> ["key3"] 140| | /// dict.keys(forValue: "value3") -> [] 141| | /// 142| | /// - Parameter value: Value for which keys are to be fetched. 143| | /// - Returns: An array containing keys that have the given value. 144| 1| func keys(forValue value: Value) -> [Key] { 145| 3| return keys.filter { self[$0] == value } 146| 1| } 147| |} 148| | 149| |// MARK: - Methods (ExpressibleByStringLiteral) 150| | 151| |public extension Dictionary where Key: StringProtocol { 152| | /// SwifterSwift: Lowercase all keys in dictionary. 153| | /// 154| | /// var dict = ["tEstKeY": "value"] 155| | /// dict.lowercaseAllKeys() 156| | /// print(dict) // prints "["testkey": "value"]" 157| | /// 158| 1| mutating func lowercaseAllKeys() { 159| 1| // http://stackoverflow.com/questions/33180028/extend-dictionary-where-key-is-of-type-string 160| 1| for key in keys { 161| 1| if let lowercaseKey = String(describing: key).lowercased() as? Key { 162| 1| self[lowercaseKey] = removeValue(forKey: key) 163| 1| } 164| 1| } 165| 1| } 166| |} 167| | 168| |// MARK: - Subscripts 169| | 170| |public extension Dictionary { 171| | /// SwifterSwift: Deep fetch or set a value from nested dictionaries. 172| | /// 173| | /// var dict = ["key": ["key1": ["key2": "value"]]] 174| | /// dict[path: ["key", "key1", "key2"]] = "newValue" 175| | /// dict[path: ["key", "key1", "key2"]] -> "newValue" 176| | /// 177| | /// - Note: Value fetching is iterative, while setting is recursive. 178| | /// 179| | /// - Complexity: O(N), _N_ being the length of the path passed in. 180| | /// 181| | /// - Parameter path: An array of keys to the desired value. 182| | /// 183| | /// - Returns: The value for the key-path passed in. `nil` if no value is found. 184| | subscript(path path: [Key]) -> Any? { 185| 4| get { 186| 4| guard !path.isEmpty else { return nil } 187| 3| var result: Any? = self 188| 8| for key in path { 189| 8| if let element = (result as? [Key: Any])?[key] { 190| 8| result = element 191| 8| } else { 192| 0| return nil 193| 8| } 194| 8| } 195| 3| return result 196| 3| } 197| 3| set { 198| 3| if let first = path.first { 199| 3| if path.count == 1, let new = newValue as? Value { 200| 1| return self[first] = new 201| 2| } 202| 2| if var nested = self[first] as? [Key: Any] { 203| 2| nested[path: Array(path.dropFirst())] = newValue 204| 2| return self[first] = nested as? Value 205| 2| } 206| 0| } 207| 0| } 208| | } 209| |} 210| | 211| |// MARK: - Operators 212| | 213| |public extension Dictionary { 214| | /// SwifterSwift: Merge the keys/values of two dictionaries. 215| | /// 216| | /// let dict: [String: String] = ["key1": "value1"] 217| | /// let dict2: [String: String] = ["key2": "value2"] 218| | /// let result = dict + dict2 219| | /// result["key1"] -> "value1" 220| | /// result["key2"] -> "value2" 221| | /// 222| | /// - Parameters: 223| | /// - lhs: dictionary 224| | /// - rhs: dictionary 225| | /// - Returns: An dictionary with keys and values from both. 226| 1| static func + (lhs: [Key: Value], rhs: [Key: Value]) -> [Key: Value] { 227| 1| var result = lhs 228| 1| rhs.forEach { result[$0] = $1 } 229| 1| return result 230| 1| } 231| | 232| | // MARK: - Operators 233| | 234| | /// SwifterSwift: Append the keys and values from the second dictionary into the first one. 235| | /// 236| | /// var dict: [String: String] = ["key1": "value1"] 237| | /// let dict2: [String: String] = ["key2": "value2"] 238| | /// dict += dict2 239| | /// dict["key1"] -> "value1" 240| | /// dict["key2"] -> "value2" 241| | /// 242| | /// - Parameters: 243| | /// - lhs: dictionary 244| | /// - rhs: dictionary 245| 1| static func += (lhs: inout [Key: Value], rhs: [Key: Value]) { 246| 1| rhs.forEach { lhs[$0] = $1 } 247| 1| } 248| | 249| | /// SwifterSwift: Remove keys contained in the sequence from the dictionary 250| | /// 251| | /// let dict: [String: String] = ["key1": "value1", "key2": "value2", "key3": "value3"] 252| | /// let result = dict-["key1", "key2"] 253| | /// result.keys.contains("key3") -> true 254| | /// result.keys.contains("key1") -> false 255| | /// result.keys.contains("key2") -> false 256| | /// 257| | /// - Parameters: 258| | /// - lhs: dictionary 259| | /// - keys: array with the keys to be removed. 260| | /// - Returns: a new dictionary with keys removed. 261| 1| static func - (lhs: [Key: Value], keys: S) -> [Key: Value] where S.Element == Key { 262| 1| var result = lhs 263| 1| result.removeAll(keys: keys) 264| 1| return result 265| 1| } 266| | 267| | /// SwifterSwift: Remove keys contained in the sequence from the dictionary 268| | /// 269| | /// var dict: [String: String] = ["key1": "value1", "key2": "value2", "key3": "value3"] 270| | /// dict-=["key1", "key2"] 271| | /// dict.keys.contains("key3") -> true 272| | /// dict.keys.contains("key1") -> false 273| | /// dict.keys.contains("key2") -> false 274| | /// 275| | /// - Parameters: 276| | /// - lhs: dictionary 277| | /// - keys: array with the keys to be removed. 278| 1| static func -= (lhs: inout [Key: Value], keys: S) where S.Element == Key { 279| 1| lhs.removeAll(keys: keys) 280| 1| } 281| |} /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/DoubleExtensions.swift: 1| |// DoubleExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(CoreGraphics) 4| |import CoreGraphics 5| |#endif 6| | 7| |#if os(macOS) || os(iOS) 8| |import Darwin 9| |#elseif os(Linux) 10| |import Glibc 11| |#endif 12| | 13| |// MARK: - Properties 14| | 15| |public extension Double { 16| | /// SwifterSwift: Int. 17| 3| var int: Int { 18| 3| return Int(self) 19| 3| } 20| | 21| | /// SwifterSwift: Float. 22| 3| var float: Float { 23| 3| return Float(self) 24| 3| } 25| | 26| | #if canImport(CoreGraphics) 27| | /// SwifterSwift: CGFloat. 28| 1| var cgFloat: CGFloat { 29| 1| return CGFloat(self) 30| 1| } 31| | #endif 32| |} 33| | 34| |// MARK: - Operators 35| | 36| |precedencegroup PowerPrecedence { higherThan: MultiplicationPrecedence } 37| |infix operator **: PowerPrecedence 38| |/// SwifterSwift: Value of exponentiation. 39| |/// 40| |/// - Parameters: 41| |/// - lhs: base double. 42| |/// - rhs: exponent double. 43| |/// - Returns: exponentiation result (example: 4.4 ** 0.5 = 2.0976176963). 44| 1|func ** (lhs: Double, rhs: Double) -> Double { 45| 1| // http://nshipster.com/swift-operators/ 46| 1| return pow(lhs, rhs) 47| 1|} /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/FloatExtensions.swift: 1| |// FloatExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(CoreGraphics) 4| |import CoreGraphics 5| |#endif 6| | 7| |#if os(macOS) || os(iOS) 8| |import Darwin 9| |#elseif os(Linux) 10| |import Glibc 11| |#endif 12| | 13| |// MARK: - Properties 14| | 15| |public extension Float { 16| | /// SwifterSwift: Int. 17| 3| var int: Int { 18| 3| return Int(self) 19| 3| } 20| | 21| | /// SwifterSwift: Double. 22| 3| var double: Double { 23| 3| return Double(self) 24| 3| } 25| | 26| | #if canImport(CoreGraphics) 27| | /// SwifterSwift: CGFloat. 28| 1| var cgFloat: CGFloat { 29| 1| return CGFloat(self) 30| 1| } 31| | #endif 32| |} 33| | 34| |// MARK: - Operators 35| | 36| |precedencegroup PowerPrecedence { higherThan: MultiplicationPrecedence } 37| |infix operator **: PowerPrecedence 38| |/// SwifterSwift: Value of exponentiation. 39| |/// 40| |/// - Parameters: 41| |/// - lhs: base float. 42| |/// - rhs: exponent float. 43| |/// - Returns: exponentiation result (4.4 ** 0.5 = 2.0976176963). 44| 1|func ** (lhs: Float, rhs: Float) -> Float { 45| 1| // http://nshipster.com/swift-operators/ 46| 1| return pow(lhs, rhs) 47| 1|} /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/FloatingPointExtensions.swift: 1| |// FloatingPointExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(Foundation) 4| |import Foundation 5| |#endif 6| | 7| |// MARK: - Properties 8| | 9| |public extension FloatingPoint { 10| | /// SwifterSwift: Absolute value of number. 11| 2| var abs: Self { 12| 2| return Swift.abs(self) 13| 2| } 14| | 15| | /// SwifterSwift: Check if number is positive. 16| 6| var isPositive: Bool { 17| 6| return self > 0 18| 6| } 19| | 20| | /// SwifterSwift: Check if number is negative. 21| 6| var isNegative: Bool { 22| 6| return self < 0 23| 6| } 24| | 25| | #if canImport(Foundation) 26| | /// SwifterSwift: Ceil of number. 27| 2| var ceil: Self { 28| 2| return Foundation.ceil(self) 29| 2| } 30| | #endif 31| | 32| | /// SwifterSwift: Radian value of degree input. 33| 2| var degreesToRadians: Self { 34| 2| return Self.pi * self / Self(180) 35| 2| } 36| | 37| | #if canImport(Foundation) 38| | /// SwifterSwift: Floor of number. 39| 2| var floor: Self { 40| 2| return Foundation.floor(self) 41| 2| } 42| | #endif 43| | 44| | /// SwifterSwift: Degree value of radian input. 45| 2| var radiansToDegrees: Self { 46| 2| return self * Self(180) / Self.pi 47| 2| } 48| |} 49| | 50| |// MARK: - Operators 51| | 52| |// swiftlint:disable identifier_name 53| |infix operator ± 54| |/// SwifterSwift: Tuple of plus-minus operation. 55| |/// 56| |/// - Parameters: 57| |/// - lhs: number 58| |/// - rhs: number 59| |/// - Returns: tuple of plus-minus operation ( 2.5 ± 1.5 -> (4, 1)). 60| 6|func ± (lhs: T, rhs: T) -> (T, T) { 61| 6| // http://nshipster.com/swift-operators/ 62| 6| return (lhs + rhs, lhs - rhs) 63| 6|} 64| | 65| |// swiftlint:enable identifier_name 66| | 67| |// swiftlint:disable identifier_name 68| |prefix operator ± 69| |/// SwifterSwift: Tuple of plus-minus operation. 70| |/// 71| |/// - Parameter int: number 72| |/// - Returns: tuple of plus-minus operation (± 2.5 -> (2.5, -2.5)). 73| 2|public prefix func ± (number: T) -> (T, T) { 74| 2| // http://nshipster.com/swift-operators/ 75| 2| return 0 ± number 76| 2|} 77| | 78| |// swiftlint:enable identifier_name 79| | 80| |// swiftlint:disable identifier_name 81| |prefix operator √ 82| |/// SwifterSwift: Square root of float. 83| |/// 84| |/// - Parameter float: float value to find square root for 85| |/// - Returns: square root of given float. 86| 2|public prefix func √ (float: T) -> T where T: FloatingPoint { 87| 2| // http://nshipster.com/swift-operators/ 88| 2| return sqrt(float) 89| 2|} 90| | 91| |// swiftlint:enable identifier_name /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/IntExtensions.swift: 1| |// IntExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(CoreGraphics) 4| |import CoreGraphics 5| |#endif 6| | 7| |#if os(macOS) || os(iOS) 8| |import Darwin 9| |#elseif os(Linux) 10| |import Glibc 11| |#endif 12| | 13| |// MARK: - Properties 14| | 15| |public extension Int { 16| | /// SwifterSwift: CountableRange 0.. { 18| 1| return 0..= 0 ? "" : "-" 57| 5| } 58| 8| let abs = Swift.abs(self) 59| 8| if abs == 0 { 60| 1| return "0k" 61| 7| } else if abs >= 0, abs < 1000 { 62| 2| return "0k" 63| 5| } else if abs >= 1000, abs < 1_000_000 { 64| 4| return String(format: "\(sign)%ik", abs / 1000) 65| 4| } 66| 1| return String(format: "\(sign)%ikk", abs / 100_000) 67| 5| } 68| | 69| | /// SwifterSwift: Array of digits of integer value. 70| 3| var digits: [Int] { 71| 3| guard self != 0 else { return [0] } 72| 2| var digits = [Int]() 73| 2| var number = abs 74| 2| 75| 8| while number != 0 { 76| 6| let xNumber = number % 10 77| 6| digits.append(xNumber) 78| 6| number /= 10 79| 6| } 80| 2| 81| 2| digits.reverse() 82| 2| return digits 83| 2| } 84| | 85| | /// SwifterSwift: Number of digits of integer value. 86| 4| var digitsCount: Int { 87| 4| guard self != 0 else { return 1 } 88| 3| let number = Double(abs) 89| 3| return Int(log10(number) + 1) 90| 4| } 91| |} 92| | 93| |// MARK: - Methods 94| | 95| |public extension Int { 96| | /// SwifterSwift: check if given integer prime or not. Warning: Using big numbers can be computationally expensive! 97| | /// - Returns: true or false depending on prime-ness 98| 14| func isPrime() -> Bool { 99| 14| // To improve speed on latter loop :) 100| 14| if self == 2 { return true } 101| 13| 102| 13| guard self > 1, self % 2 != 0 else { return false } 103| 9| 104| 9| // Explanation: It is enough to check numbers until 105| 9| // the square root of that number. If you go up from N by one, 106| 9| // other multiplier will go 1 down to get similar result 107| 9| // (integer-wise operation) such way increases speed of operation 108| 9| let base = Int(sqrt(Double(self))) 109| 88| for int in Swift.stride(from: 3, through: base, by: 2) where self % int == 0 { 110| 88| return false 111| 18.4E| } 112| 18.4E| return true 113| 9| } 114| | 115| | /// SwifterSwift: Roman numeral string from integer (if applicable). 116| | /// 117| | /// 10.romanNumeral() -> "X" 118| | /// 119| | /// - Returns: The roman numeral string. 120| 3| func romanNumeral() -> String? { 121| 3| // https://gist.github.com/kumo/a8e1cb1f4b7cff1548c7 122| 3| guard self > 0 else { // there is no roman numeral for 0 or negative numbers 123| 1| return nil 124| 2| } 125| 2| let romanValues = ["M", "CM", "D", "CD", "C", "XC", "L", "XL", "X", "IX", "V", "IV", "I"] 126| 2| let arabicValues = [1000, 900, 500, 400, 100, 90, 50, 40, 10, 9, 5, 4, 1] 127| 2| 128| 2| var romanValue = "" 129| 2| var startingValue = self 130| 2| 131| 26| for (index, romanChar) in romanValues.enumerated() { 132| 26| let arabicValue = arabicValues[index] 133| 26| let div = startingValue / arabicValue 134| 26| for _ in 0..
Int { 144| 3| return number == 0 ? self : Int(round(Double(self) / Double(number))) * number 145| 3| } 146| |} 147| | 148| |// MARK: - Operators 149| | 150| |precedencegroup PowerPrecedence { higherThan: MultiplicationPrecedence } 151| |infix operator **: PowerPrecedence 152| |/// SwifterSwift: Value of exponentiation. 153| |/// 154| |/// - Parameters: 155| |/// - lhs: base integer. 156| |/// - rhs: exponent integer. 157| |/// - Returns: exponentiation result (example: 2 ** 3 = 8). 158| 1|func ** (lhs: Int, rhs: Int) -> Double { 159| 1| // http://nshipster.com/swift-operators/ 160| 1| return pow(Double(lhs), Double(rhs)) 161| 1|} 162| | 163| |// swiftlint:disable identifier_name 164| |prefix operator √ 165| |/// SwifterSwift: Square root of integer. 166| |/// 167| |/// - Parameter int: integer value to find square root for 168| |/// - Returns: square root of given integer. 169| 1|public prefix func √ (int: Int) -> Double { 170| 1| // http://nshipster.com/swift-operators/ 171| 1| return sqrt(Double(int)) 172| 1|} 173| | 174| |// swiftlint:enable identifier_name 175| | 176| |// swiftlint:disable identifier_name 177| |infix operator ± 178| |/// SwifterSwift: Tuple of plus-minus operation. 179| |/// 180| |/// - Parameters: 181| |/// - lhs: integer number. 182| |/// - rhs: integer number. 183| |/// - Returns: tuple of plus-minus operation (example: 2 ± 3 -> (5, -1)). 184| 2|func ± (lhs: Int, rhs: Int) -> (Int, Int) { 185| 2| // http://nshipster.com/swift-operators/ 186| 2| return (lhs + rhs, lhs - rhs) 187| 2|} 188| | 189| |// swiftlint:enable identifier_name 190| | 191| |// swiftlint:disable identifier_name 192| |prefix operator ± 193| |/// SwifterSwift: Tuple of plus-minus operation. 194| |/// 195| |/// - Parameter int: integer number 196| |/// - Returns: tuple of plus-minus operation (example: ± 2 -> (2, -2)). 197| 1|public prefix func ± (int: Int) -> (Int, Int) { 198| 1| // http://nshipster.com/swift-operators/ 199| 1| return (int, -int) 200| 1|} 201| | 202| |// swiftlint:enable identifier_name /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/KeyedDecodingContainerExtensions.swift: 1| |// KeyedDecodingContainerExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(Foundation) 4| |import Foundation 5| |#endif 6| | 7| |public extension KeyedDecodingContainer { 8| | #if canImport(Foundation) 9| | /// SwifterSwift: Try to decode a Bool as Int then String before decoding as Bool. 10| | /// 11| | /// - Parameter key: Key. 12| | /// - Returns: Decoded Bool value. 13| | /// - Throws: Decoding error. 14| 5| func decodeBoolAsIntOrString(forKey key: Key) throws -> Bool { 15| 5| if let intValue = try? decode(Int.self, forKey: key) { 16| 2| return (intValue as NSNumber).boolValue 17| 3| } else if let stringValue = try? decode(String.self, forKey: key) { 18| 1| return (stringValue as NSString).boolValue 19| 2| } else { 20| 2| return try decode(Bool.self, forKey: key) 21| 2| } 22| 0| } 23| | #endif 24| | 25| | #if canImport(Foundation) 26| | /// SwifterSwift: Try to decode a Bool as Int then String before decoding as Bool if present. 27| | /// 28| | /// - Parameter key: Key. 29| | /// - Returns: Decoded Bool value. 30| | /// - Throws: Decoding error. 31| 4| func decodeBoolAsIntOrStringIfPresent(forKey key: Key) throws -> Bool? { 32| 4| if let intValue = try? decodeIfPresent(Int.self, forKey: key) { 33| 1| return (intValue as NSNumber).boolValue 34| 3| } else if let stringValue = try? decodeIfPresent(String.self, forKey: key) { 35| 1| return (stringValue as NSString).boolValue 36| 2| } else { 37| 2| return try decodeIfPresent(Bool.self, forKey: key) 38| 2| } 39| 0| } 40| | #endif 41| |} /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/MutableCollectionExtensions.swift: 1| |// MutableCollectionExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |public extension MutableCollection where Self: RandomAccessCollection { 4| | /// SwifterSwift: Sort the collection based on a keypath and a compare function. 5| | /// 6| | /// - Parameter keyPath: Key path to sort by. The key path type must be Comparable. 7| | /// - Parameter compare: Comparation function that will determine the ordering. 8| 3| mutating func sort(by keyPath: KeyPath, with compare: (T, T) -> Bool) { 9| 9| sort { compare($0[keyPath: keyPath], $1[keyPath: keyPath]) } 10| 3| } 11| | 12| | /// SwifterSwift: Sort the collection based on a keypath. 13| | /// 14| | /// - Parameter keyPath: Key path to sort by. The key path type must be Comparable. 15| 1| mutating func sort(by keyPath: KeyPath) { 16| 3| sort { $0[keyPath: keyPath] < $1[keyPath: keyPath] } 17| 1| } 18| | 19| | /// SwifterSwift: Sort the collection based on two key paths. The second one will be used in case the values of the first one match. 20| | /// 21| | /// - Parameters: 22| | /// - keyPath1: Key path to sort by. Must be Comparable. 23| | /// - keyPath2: Key path to sort by in case the values of `keyPath1` match. Must be Comparable. 24| | mutating func sort(by keyPath1: KeyPath, 25| 1| and keyPath2: KeyPath) { 26| 3| sort { 27| 3| if $0[keyPath: keyPath1] != $1[keyPath: keyPath1] { 28| 2| return $0[keyPath: keyPath1] < $1[keyPath: keyPath1] 29| 2| } 30| 1| return $0[keyPath: keyPath2] < $1[keyPath: keyPath2] 31| 3| } 32| 1| } 33| | 34| | /// SwifterSwift: Sort the collection based on three key paths. Whenever the values of one key path match, the next one will be used. 35| | /// 36| | /// - Parameters: 37| | /// - keyPath1: Key path to sort by. Must be Comparable. 38| | /// - keyPath2: Key path to sort by in case the values of `keyPath1` match. Must be Comparable. 39| | /// - keyPath3: Key path to sort by in case the values of `keyPath1` and `keyPath2` match. Must be Comparable. 40| | mutating func sort(by keyPath1: KeyPath, 41| | and keyPath2: KeyPath, 42| 1| and keyPath3: KeyPath) { 43| 4| sort { 44| 4| if $0[keyPath: keyPath1] != $1[keyPath: keyPath1] { 45| 2| return $0[keyPath: keyPath1] < $1[keyPath: keyPath1] 46| 2| } 47| 2| if $0[keyPath: keyPath2] != $1[keyPath: keyPath2] { 48| 1| return $0[keyPath: keyPath2] < $1[keyPath: keyPath2] 49| 1| } 50| 1| return $0[keyPath: keyPath3] < $1[keyPath: keyPath3] 51| 2| } 52| 1| } 53| |} 54| | 55| |public extension MutableCollection { 56| | /// SwifterSwift: Assign a given value to a field `keyPath` of all elements in the collection. 57| | /// 58| | /// - Parameter value: The new value of the field 59| | /// - Parameter keyPath: The actual field of the element 60| 2| mutating func assignToAll(value: Value, by keyPath: WritableKeyPath) { 61| 5| for idx in indices { 62| 5| self[idx][keyPath: keyPath] = value 63| 5| } 64| 2| } 65| |} /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/OptionalExtensions.swift: 1| |// OptionalExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |// MARK: - Methods 4| | 5| |public extension Optional { 6| | /// SwifterSwift: Get self of default value (if self is nil). 7| | /// 8| | /// let foo: String? = nil 9| | /// print(foo.unwrapped(or: "bar")) -> "bar" 10| | /// 11| | /// let bar: String? = "bar" 12| | /// print(bar.unwrapped(or: "foo")) -> "bar" 13| | /// 14| | /// - Parameter defaultValue: default value to return if self is nil. 15| | /// - Returns: self if not nil or default value if nil. 16| 2| func unwrapped(or defaultValue: Wrapped) -> Wrapped { 17| 2| // http://www.russbishop.net/improving-optionals 18| 2| return self ?? defaultValue 19| 2| } 20| | 21| | /// SwifterSwift: Gets the wrapped value of an optional. If the optional is `nil`, throw a custom error. 22| | /// 23| | /// let foo: String? = nil 24| | /// try print(foo.unwrapped(or: MyError.notFound)) -> error: MyError.notFound 25| | /// 26| | /// let bar: String? = "bar" 27| | /// try print(bar.unwrapped(or: MyError.notFound)) -> "bar" 28| | /// 29| | /// - Parameter error: The error to throw if the optional is `nil`. 30| | /// - Returns: The value wrapped by the optional. 31| | /// - Throws: The error passed in. 32| 2| func unwrapped(or error: Error) throws -> Wrapped { 33| 2| guard let wrapped = self else { throw error } 34| 1| return wrapped 35| 2| } 36| | 37| | /// SwifterSwift: Runs a block to Wrapped if not nil 38| | /// 39| | /// let foo: String? = nil 40| | /// foo.run { unwrappedFoo in 41| | /// // block will never run sice foo is nill 42| | /// print(unwrappedFoo) 43| | /// } 44| | /// 45| | /// let bar: String? = "bar" 46| | /// bar.run { unwrappedBar in 47| | /// // block will run sice bar is not nill 48| | /// print(unwrappedBar) -> "bar" 49| | /// } 50| | /// 51| | /// - Parameter block: a block to run if self is not nil. 52| 2| func run(_ block: (Wrapped) -> Void) { 53| 2| // http://www.russbishop.net/improving-optionals 54| 2| _ = map(block) 55| 2| } 56| | 57| | /// SwifterSwift: Assign an optional value to a variable only if the value is not nil. 58| | /// 59| | /// let someParameter: String? = nil 60| | /// let parameters = [String: Any]() // Some parameters to be attached to a GET request 61| | /// parameters[someKey] ??= someParameter // It won't be added to the parameters dict 62| | /// 63| | /// - Parameters: 64| | /// - lhs: Any? 65| | /// - rhs: Any? 66| 2| static func ??= (lhs: inout Optional, rhs: Optional) { 67| 2| guard let rhs = rhs else { return } 68| 1| lhs = rhs 69| 1| } 70| | 71| | /// SwifterSwift: Assign an optional value to a variable only if the variable is nil. 72| | /// 73| | /// var someText: String? = nil 74| | /// let newText = "Foo" 75| | /// let defaultText = "Bar" 76| | /// someText ?= newText // someText is now "Foo" because it was nil before 77| | /// someText ?= defaultText // someText doesn't change its value because it's not nil 78| | /// 79| | /// - Parameters: 80| | /// - lhs: Any? 81| | /// - rhs: Any? 82| 4| static func ?= (lhs: inout Optional, rhs: @autoclosure () -> Optional) { 83| 4| if lhs == nil { 84| 2| lhs = rhs() 85| 4| } 86| 4| } 87| |} 88| | 89| |// MARK: - Methods (Collection) 90| | 91| |public extension Optional where Wrapped: Collection { 92| | /// SwifterSwift: Check if optional is nil or empty collection. 93| 7| var isNilOrEmpty: Bool { 94| 7| guard let collection = self else { return true } 95| 5| return collection.isEmpty 96| 7| } 97| | 98| | /// SwifterSwift: Returns the collection only if it is not nill and not empty. 99| 4| var nonEmpty: Wrapped? { 100| 4| guard let collection = self else { return nil } 101| 3| guard !collection.isEmpty else { return nil } 102| 2| return collection 103| 3| } 104| |} 105| | 106| |// MARK: - Methods (RawRepresentable, RawValue: Equatable) 107| | 108| |public extension Optional where Wrapped: RawRepresentable, Wrapped.RawValue: Equatable { 109| | // swiftlint:disable missing_swifterswift_prefix 110| | 111| | /// Returns a Boolean value indicating whether two values are equal. 112| | /// 113| | /// Equality is the inverse of inequality. For any values `a` and `b`, 114| | /// `a == b` implies that `a != b` is `false`. 115| | /// 116| | /// - Parameters: 117| | /// - lhs: A value to compare. 118| | /// - rhs: Another value to compare. 119| 9| @inlinable static func == (lhs: Optional, rhs: Wrapped.RawValue?) -> Bool { 120| 9| return lhs?.rawValue == rhs 121| 9| } 122| | 123| | /// Returns a Boolean value indicating whether two values are equal. 124| | /// 125| | /// Equality is the inverse of inequality. For any values `a` and `b`, 126| | /// `a == b` implies that `a != b` is `false`. 127| | /// 128| | /// - Parameters: 129| | /// - lhs: A value to compare. 130| | /// - rhs: Another value to compare. 131| 9| @inlinable static func == (lhs: Wrapped.RawValue?, rhs: Optional) -> Bool { 132| 9| return lhs == rhs?.rawValue 133| 9| } 134| | 135| | /// Returns a Boolean value indicating whether two values are not equal. 136| | /// 137| | /// Inequality is the inverse of equality. For any values `a` and `b`, 138| | /// `a != b` implies that `a == b` is `false`. 139| | /// 140| | /// - Parameters: 141| | /// - lhs: A value to compare. 142| | /// - rhs: Another value to compare. 143| 9| @inlinable static func != (lhs: Optional, rhs: Wrapped.RawValue?) -> Bool { 144| 9| return lhs?.rawValue != rhs 145| 9| } 146| | 147| | /// Returns a Boolean value indicating whether two values are not equal. 148| | /// 149| | /// Inequality is the inverse of equality. For any values `a` and `b`, 150| | /// `a != b` implies that `a == b` is `false`. 151| | /// 152| | /// - Parameters: 153| | /// - lhs: A value to compare. 154| | /// - rhs: Another value to compare. 155| 9| @inlinable static func != (lhs: Wrapped.RawValue?, rhs: Optional) -> Bool { 156| 9| return lhs != rhs?.rawValue 157| 9| } 158| | 159| | // swiftlint:enable missing_swifterswift_prefix 160| |} 161| | 162| |// MARK: - Operators 163| | 164| |infix operator ??=: AssignmentPrecedence 165| |infix operator ?=: AssignmentPrecedence /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/RangeReplaceableCollectionExtensions.swift: 1| |// RangeReplaceableCollectionExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |// MARK: - Initializers 4| | 5| |public extension RangeReplaceableCollection { 6| | /// 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. 7| | /// 8| | /// let values = Array(expression: "Value", count: 3) 9| | /// print(values) 10| | /// // Prints "["Value", "Value", "Value"]" 11| | /// 12| | /// - Parameters: 13| | /// - expression: The expression to execute for each position of the collection. 14| | /// - count: The count of the collection. 15| 2| init(expression: @autoclosure () throws -> Element, count: Int) rethrows { 16| 2| self.init() 17| 2| // swiftlint:disable:next empty_count 18| 2| if count > 0 { 19| 1| reserveCapacity(count) 20| 4| while self.count < count { 21| 3| append(try expression()) 22| 3| } 23| 2| } 24| 2| } 25| |} 26| | 27| |// MARK: - Methods 28| | 29| |public extension RangeReplaceableCollection { 30| | ///  SwifterSwift: Returns a new rotated collection by the given places. 31| | /// 32| | /// [1, 2, 3, 4].rotated(by: 1) -> [4,1,2,3] 33| | /// [1, 2, 3, 4].rotated(by: 3) -> [2,3,4,1] 34| | /// [1, 2, 3, 4].rotated(by: -1) -> [2,3,4,1] 35| | /// 36| | /// - 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. 37| | /// - Returns: The new rotated collection. 38| 8| func rotated(by places: Int) -> Self { 39| 8| // Inspired by: https://ruby-doc.org/core-2.2.0/Array.html#method-i-rotate 40| 8| var copy = self 41| 8| return copy.rotate(by: places) 42| 8| } 43| | 44| | ///  SwifterSwift: Rotate the collection by the given places. 45| | /// 46| | /// [1, 2, 3, 4].rotate(by: 1) -> [4,1,2,3] 47| | /// [1, 2, 3, 4].rotate(by: 3) -> [2,3,4,1] 48| | /// [1, 2, 3, 4].rotated(by: -1) -> [2,3,4,1] 49| | /// 50| | /// - 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. 51| | /// - Returns: self after rotating. 52| | @discardableResult 53| 11| mutating func rotate(by places: Int) -> Self { 54| 11| guard places != 0 else { return self } 55| 9| let placesToMove = places % count 56| 9| if placesToMove > 0 { 57| 4| let range = index(endIndex, offsetBy: -placesToMove)... 58| 4| let slice = self[range] 59| 4| removeSubrange(range) 60| 4| insert(contentsOf: slice, at: startIndex) 61| 9| } else { 62| 5| let range = startIndex.. [1, 2, 3, 4, 2, 5] 73| | /// ["h", "e", "l", "l", "o"].removeFirst { $0 == "e" } -> ["h", "l", "l", "o"] 74| | /// 75| | /// - 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. 76| | /// - Returns: The first element for which predicate returns true, after removing it. If no elements in the collection satisfy the given predicate, returns `nil`. 77| | @discardableResult 78| 4| mutating func removeFirst(where predicate: (Element) throws -> Bool) rethrows -> Element? { 79| 4| guard let index = try firstIndex(where: predicate) else { return nil } 80| 3| return remove(at: index) 81| 4| } 82| | 83| | /// SwifterSwift: Remove a random value from the collection. 84| | @discardableResult 85| 2| mutating func removeRandomElement() -> Element? { 86| 2| guard let randomIndex = indices.randomElement() else { return nil } 87| 1| return remove(at: randomIndex) 88| 2| } 89| | 90| | /// SwifterSwift: Keep elements of Array while condition is true. 91| | /// 92| | /// [0, 2, 4, 7].keep(while: { $0 % 2 == 0 }) -> [0, 2, 4] 93| | /// 94| | /// - Parameter condition: condition to evaluate each element against. 95| | /// - Returns: self after applying provided condition. 96| | /// - Throws: provided condition exception. 97| | @discardableResult 98| 2| mutating func keep(while condition: (Element) throws -> Bool) rethrows -> Self { 99| 5| if let idx = try firstIndex(where: { try !condition($0) }) { 100| 2| removeSubrange(idx...) 101| 2| } 102| 2| return self 103| 2| } 104| | 105| | /// SwifterSwift: Take element of Array while condition is true. 106| | /// 107| | /// [0, 2, 4, 7, 6, 8].take( where: {$0 % 2 == 0}) -> [0, 2, 4] 108| | /// 109| | /// - Parameter condition: condition to evaluate each element against. 110| | /// - Returns: All elements up until condition evaluates to false. 111| 3| func take(while condition: (Element) throws -> Bool) rethrows -> Self { 112| 3| return Self(try prefix(while: condition)) 113| 3| } 114| | 115| | /// SwifterSwift: Skip elements of Array while condition is true. 116| | /// 117| | /// [0, 2, 4, 7, 6, 8].skip( where: {$0 % 2 == 0}) -> [6, 8] 118| | /// 119| | /// - Parameter condition: condition to evaluate each element against. 120| | /// - Returns: All elements after the condition evaluates to false. 121| 3| func skip(while condition: (Element) throws -> Bool) rethrows -> Self { 122| 5| guard let idx = try firstIndex(where: { try !condition($0) }) else { return Self() } 123| 2| return Self(self[idx...]) 124| 3| } 125| | 126| | /// SwifterSwift: Remove all duplicate elements using KeyPath to compare. 127| | /// 128| | /// - Parameter path: Key path to compare, the value must be Equatable. 129| 1| mutating func removeDuplicates(keyPath path: KeyPath) { 130| 1| var items = [Element]() 131| 7| removeAll { element -> Bool in 132| 12| guard items.contains(where: { $0[keyPath: path] == element[keyPath: path] }) else { 133| 4| items.append(element) 134| 4| return false 135| 4| } 136| 3| return true 137| 7| } 138| 1| } 139| | 140| | /// SwifterSwift: Remove all duplicate elements using KeyPath to compare. 141| | /// 142| | /// - Parameter path: Key path to compare, the value must be Hashable. 143| 1| mutating func removeDuplicates(keyPath path: KeyPath) { 144| 1| var set = Set() 145| 7| removeAll { !set.insert($0[keyPath: path]).inserted } 146| 1| } 147| | 148| | /// SwifterSwift: Accesses the element at the specified position. 149| | /// 150| | /// - 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. 151| | subscript(offset: Int) -> Element { 152| 2| get { 153| 2| return self[index(startIndex, offsetBy: offset)] 154| 2| } 155| 2| set { 156| 2| let offsetIndex = index(startIndex, offsetBy: offset) 157| 2| replaceSubrange(offsetIndex..(range: R) -> SubSequence where R: RangeExpression, R.Bound == Int { 165| 9| get { 166| 9| let indexRange = range.relative(to: 0.. true 7| | /// [1,2, 2, 4].all(matching: {$0 % 2 == 0}) -> false 8| | /// 9| | /// - Parameter condition: condition to evaluate each element against. 10| | /// - Returns: true when all elements in the array match the specified condition. 11| 1| func all(matching condition: (Element) throws -> Bool) rethrows -> Bool { 12| 6| return try !contains { try !condition($0) } 13| 1| } 14| | 15| | /// SwifterSwift: Check if no elements in collection match a conditon. 16| | /// 17| | /// [2, 2, 4].none(matching: {$0 % 2 == 0}) -> false 18| | /// [1, 3, 5, 7].none(matching: {$0 % 2 == 0}) -> true 19| | /// 20| | /// - Parameter condition: condition to evaluate each element against. 21| | /// - Returns: true when no elements in the array match the specified condition. 22| 1| func none(matching condition: (Element) throws -> Bool) rethrows -> Bool { 23| 6| return try !contains { try condition($0) } 24| 1| } 25| | 26| | /// SwifterSwift: Check if any element in collection match a conditon. 27| | /// 28| | /// [2, 2, 4].any(matching: {$0 % 2 == 0}) -> false 29| | /// [1, 3, 5, 7].any(matching: {$0 % 2 == 0}) -> true 30| | /// 31| | /// - Parameter condition: condition to evaluate each element against. 32| | /// - Returns: true when no elements in the array match the specified condition. 33| 1| func any(matching condition: (Element) throws -> Bool) rethrows -> Bool { 34| 3| return try contains { try condition($0) } 35| 1| } 36| | 37| | /// SwifterSwift: Filter elements based on a rejection condition. 38| | /// 39| | /// [2, 2, 4, 7].reject(where: {$0 % 2 == 0}) -> [7] 40| | /// 41| | /// - Parameter condition: to evaluate the exclusion of an element from the array. 42| | /// - Returns: the array with rejected values filtered from it. 43| 1| func reject(where condition: (Element) throws -> Bool) rethrows -> [Element] { 44| 5| return try filter { return try !condition($0) } 45| 1| } 46| | 47| | /// SwifterSwift: Get element count based on condition. 48| | /// 49| | /// [2, 2, 4, 7].count(where: {$0 % 2 == 0}) -> 3 50| | /// 51| | /// - Parameter condition: condition to evaluate each element against. 52| | /// - Returns: number of times the condition evaluated to true. 53| 1| func count(where condition: (Element) throws -> Bool) rethrows -> Int { 54| 1| var count = 0 55| 9| for element in self where try condition(element) { 56| 9| count += 1 57| 9| } 58| 1| return count 59| 1| } 60| | 61| | /// SwifterSwift: Iterate over a collection in reverse order. (right to left) 62| | /// 63| | /// [0, 2, 4, 7].forEachReversed({ print($0)}) -> // Order of print: 7,4,2,0 64| | /// 65| | /// - Parameter body: a closure that takes an element of the array as a parameter. 66| 1| func forEachReversed(_ body: (Element) throws -> Void) rethrows { 67| 1| try reversed().forEach(body) 68| 1| } 69| | 70| | /// SwifterSwift: Calls the given closure with each element where condition is true. 71| | /// 72| | /// [0, 2, 4, 7].forEach(where: {$0 % 2 == 0}, body: { print($0)}) -> // print: 0, 2, 4 73| | /// 74| | /// - Parameters: 75| | /// - condition: condition to evaluate each element against. 76| | /// - body: a closure that takes an element of the array as a parameter. 77| 1| func forEach(where condition: (Element) throws -> Bool, body: (Element) throws -> Void) rethrows { 78| 1| try lazy.filter(condition).forEach(body) 79| 1| } 80| | 81| | /// SwifterSwift: Reduces an array while returning each interim combination. 82| | /// 83| | /// [1, 2, 3].accumulate(initial: 0, next: +) -> [1, 3, 6] 84| | /// 85| | /// - Parameters: 86| | /// - initial: initial value. 87| | /// - next: closure that combines the accumulating value and next element of the array. 88| | /// - Returns: an array of the final accumulated value and each interim combination. 89| 1| func accumulate(initial: U, next: (U, Element) throws -> U) rethrows -> [U] { 90| 1| var runningTotal = initial 91| 3| return try map { element in 92| 3| runningTotal = try next(runningTotal, element) 93| 3| return runningTotal 94| 3| } 95| 1| } 96| | 97| | /// SwifterSwift: Filtered and map in a single operation. 98| | /// 99| | /// [1,2,3,4,5].filtered({ $0 % 2 == 0 }, map: { $0.string }) -> ["2", "4"] 100| | /// 101| | /// - Parameters: 102| | /// - isIncluded: condition of inclusion to evaluate each element against. 103| | /// - transform: transform element function to evaluate every element. 104| | /// - Returns: Return an filtered and mapped array. 105| 1| func filtered(_ isIncluded: (Element) throws -> Bool, map transform: (Element) throws -> T) rethrows -> [T] { 106| 1| return try lazy.filter(isIncluded).map(transform) 107| 1| } 108| | 109| | /// SwifterSwift: Get the only element based on a condition. 110| | /// 111| | /// [].single(where: {_ in true}) -> nil 112| | /// [4].single(where: {_ in true}) -> 4 113| | /// [1, 4, 7].single(where: {$0 % 2 == 0}) -> 4 114| | /// [2, 2, 4, 7].single(where: {$0 % 2 == 0}) -> nil 115| | /// 116| | /// - Parameter condition: condition to evaluate each element against. 117| | /// - Returns: The only element in the array matching the specified condition. If there are more matching elements, nil is returned. (optional) 118| 6| func single(where condition: (Element) throws -> Bool) rethrows -> Element? { 119| 6| var singleElement: Element? 120| 9| for element in self where try condition(element) { 121| 9| guard singleElement == nil else { 122| 2| singleElement = nil 123| 2| break 124| 7| } 125| 7| singleElement = element 126| 7| } 127| 6| return singleElement 128| 6| } 129| | 130| | /// SwifterSwift: Remove duplicate elements based on condition. 131| | /// 132| | /// [1, 2, 1, 3, 2].withoutDuplicates { $0 } -> [1, 2, 3] 133| | /// [(1, 4), (2, 2), (1, 3), (3, 2), (2, 1)].withoutDuplicates { $0.0 } -> [(1, 4), (2, 2), (3, 2)] 134| | /// 135| | /// - Parameter transform: A closure that should return the value to be evaluated for repeating elements. 136| | /// - Returns: Sequence without repeating elements 137| | /// - Complexity: O(*n*), where *n* is the length of the sequence. 138| 2| func withoutDuplicates(transform: (Element) throws -> T) rethrows -> [Element] { 139| 2| var set = Set() 140| 10| return try filter { set.insert(try transform($0)).inserted } 141| 2| } 142| | 143| | ///  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. 144| | /// 145| | /// let (even, odd) = [0, 1, 2, 3, 4, 5].divided { $0 % 2 == 0 } 146| | /// let (minors, adults) = people.divided { $0.age < 18 } 147| | /// 148| | /// - Parameter condition: condition to evaluate each element against. 149| | /// - Returns: A tuple of matched and non-matched items 150| 2| func divided(by condition: (Element) throws -> Bool) rethrows -> (matching: [Element], nonMatching: [Element]) { 151| 2| // Inspired by: http://ruby-doc.org/core-2.5.0/Enumerable.html#method-i-partition 152| 2| var matching = [Element]() 153| 2| var nonMatching = [Element]() 154| 2| 155| 12| for element in self { 156| 12| try condition(element) ? matching.append(element) : nonMatching.append(element) 157| 12| } 158| 2| return (matching, nonMatching) 159| 2| } 160| | 161| | /// SwifterSwift: Return a sorted array based on a key path and a compare function. 162| | /// 163| | /// - Parameter keyPath: Key path to sort by. 164| | /// - Parameter compare: Comparation function that will determine the ordering. 165| | /// - Returns: The sorted array. 166| 3| func sorted(by keyPath: KeyPath, with compare: (T, T) -> Bool) -> [Element] { 167| 9| return sorted { compare($0[keyPath: keyPath], $1[keyPath: keyPath]) } 168| 3| } 169| | 170| | /// SwifterSwift: Return a sorted array based on a key path. 171| | /// 172| | /// - Parameter keyPath: Key path to sort by. The key path type must be Comparable. 173| | /// - Returns: The sorted array. 174| 1| func sorted(by keyPath: KeyPath) -> [Element] { 175| 2| return sorted { $0[keyPath: keyPath] < $1[keyPath: keyPath] } 176| 1| } 177| | 178| | /// 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. 179| | /// 180| | /// - Parameters: 181| | /// - keyPath1: Key path to sort by. Must be Comparable. 182| | /// - keyPath2: Key path to sort by in case the values of `keyPath1` match. Must be Comparable. 183| | func sorted(by keyPath1: KeyPath, 184| 1| and keyPath2: KeyPath) -> [Element] { 185| 3| return sorted { 186| 3| if $0[keyPath: keyPath1] != $1[keyPath: keyPath1] { 187| 2| return $0[keyPath: keyPath1] < $1[keyPath: keyPath1] 188| 2| } 189| 1| return $0[keyPath: keyPath2] < $1[keyPath: keyPath2] 190| 3| } 191| 1| } 192| | 193| | /// SwifterSwift: Returns a sorted sequence based on three key paths. Whenever the values of one key path match, the next one will be used. 194| | /// 195| | /// - Parameters: 196| | /// - keyPath1: Key path to sort by. Must be Comparable. 197| | /// - keyPath2: Key path to sort by in case the values of `keyPath1` match. Must be Comparable. 198| | /// - keyPath3: Key path to sort by in case the values of `keyPath1` and `keyPath2` match. Must be Comparable. 199| | func sorted(by keyPath1: KeyPath, 200| | and keyPath2: KeyPath, 201| 1| and keyPath3: KeyPath) -> [Element] { 202| 4| return sorted { 203| 4| if $0[keyPath: keyPath1] != $1[keyPath: keyPath1] { 204| 2| return $0[keyPath: keyPath1] < $1[keyPath: keyPath1] 205| 2| } 206| 2| if $0[keyPath: keyPath2] != $1[keyPath: keyPath2] { 207| 1| return $0[keyPath: keyPath2] < $1[keyPath: keyPath2] 208| 1| } 209| 1| return $0[keyPath: keyPath3] < $1[keyPath: keyPath3] 210| 2| } 211| 1| } 212| | 213| | /// SwifterSwift: Sum of a `AdditiveArithmetic` property of each `Element` in a `Sequence`. 214| | /// 215| | /// ["James", "Wade", "Bryant"].sum(for: \.count) -> 15 216| | /// 217| | /// - Parameter keyPath: Key path of the `AdditiveArithmetic` property. 218| | /// - Returns: The sum of the `AdditiveArithmetic` propertys at `keyPath`. 219| 2| func sum(for keyPath: KeyPath) -> T { 220| 2| // Inspired by: https://swiftbysundell.com/articles/reducers-in-swift/ 221| 7| return reduce(.zero) { $0 + $1[keyPath: keyPath] } 222| 2| } 223| | 224| | /// SwifterSwift: Returns the first element of the sequence with having property by given key path equals to given `value`. 225| | /// 226| | /// - Parameters: 227| | /// - keyPath: The `KeyPath` of property for `Element` to compare. 228| | /// - value: The value to compare with `Element` property 229| | /// - 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. 230| 2| func first(where keyPath: KeyPath, equals value: T) -> Element? { 231| 4| return first { $0[keyPath: keyPath] == value } 232| 2| } 233| |} 234| | 235| |public extension Sequence where Element: Equatable { 236| | /// SwifterSwift: Check if array contains an array of elements. 237| | /// 238| | /// [1, 2, 3, 4, 5].contains([1, 2]) -> true 239| | /// [1.2, 2.3, 4.5, 3.4, 4.5].contains([2, 6]) -> false 240| | /// ["h", "e", "l", "l", "o"].contains(["l", "o"]) -> true 241| | /// 242| | /// - Parameter elements: array of elements to check. 243| | /// - Returns: true if array contains all given items. 244| | /// - Complexity: _O(m·n)_, where _m_ is the length of `elements` and _n_ is the length of this sequence. 245| 6| func contains(_ elements: [Element]) -> Bool { 246| 8| return elements.allSatisfy { contains($0) } 247| 6| } 248| |} 249| | 250| |public extension Sequence where Element: Hashable { 251| | /// SwifterSwift: Check if array contains an array of elements. 252| | /// 253| | /// [1, 2, 3, 4, 5].contains([1, 2]) -> true 254| | /// [1.2, 2.3, 4.5, 3.4, 4.5].contains([2, 6]) -> false 255| | /// ["h", "e", "l", "l", "o"].contains(["l", "o"]) -> true 256| | /// 257| | /// - Parameter elements: array of elements to check. 258| | /// - Returns: true if array contains all given items. 259| | /// - Complexity: _O(m + n)_, where _m_ is the length of `elements` and _n_ is the length of this sequence. 260| 6| func contains(_ elements: [Element]) -> Bool { 261| 6| let set = Set(self) 262| 8| return elements.allSatisfy { set.contains($0) } 263| 6| } 264| | 265| | /// SwifterSwift: Check whether a sequence contains duplicates. 266| | /// 267| | /// - Returns: true if the receiver contains duplicates. 268| 3| func containsDuplicates() -> Bool { 269| 3| var set = Set() 270| 7| for element in self { 271| 7| if !set.insert(element).inserted { 272| 1| return true 273| 6| } 274| 6| } 275| 2| return false 276| 3| } 277| | 278| | /// SwifterSwift: Getting the duplicated elements in a sequence. 279| | /// 280| | /// [1, 1, 2, 2, 3, 3, 3, 4, 5].duplicates().sorted() -> [1, 2, 3]) 281| | /// ["h", "e", "l", "l", "o"].duplicates().sorted() -> ["l"]) 282| | /// 283| | /// - Returns: An array of duplicated elements. 284| | /// 285| 2| func duplicates() -> [Element] { 286| 2| var set = Set() 287| 2| var duplicates = Set() 288| 14| forEach { 289| 14| if !set.insert($0).inserted { 290| 5| duplicates.insert($0) 291| 14| } 292| 14| } 293| 2| return Array(duplicates) 294| 2| } 295| |} 296| | 297| |// MARK: - Methods (AdditiveArithmetic) 298| | 299| |public extension Sequence where Element: AdditiveArithmetic { 300| | /// SwifterSwift: Sum of all elements in array. 301| | /// 302| | /// [1, 2, 3, 4, 5].sum() -> 15 303| | /// 304| | /// - Returns: sum of the array's elements. 305| 2| func sum() -> Element { 306| 2| return reduce(.zero, +) 307| 2| } 308| |} /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/SignedIntegerExtensions.swift: 1| |// SignedIntegerExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |// 4| |// SignedIntegerExtensions.swift 5| |// SwifterSwift 6| |// 7| |// Created by Omar Albeik on 8/15/17. 8| |// Copyright © 2017 SwifterSwift 9| |// 10| |#if canImport(Foundation) 11| |import Foundation 12| |#endif 13| | 14| |// MARK: - Properties 15| | 16| |public extension SignedInteger { 17| | /// SwifterSwift: Absolute value of integer number. 18| 7| var abs: Self { 19| 7| return Swift.abs(self) 20| 7| } 21| | 22| | /// SwifterSwift: Check if integer is positive. 23| 3| var isPositive: Bool { 24| 3| return self > 0 25| 3| } 26| | 27| | /// SwifterSwift: Check if integer is negative. 28| 3| var isNegative: Bool { 29| 3| return self < 0 30| 3| } 31| | 32| | /// SwifterSwift: Check if integer is even. 33| 2| var isEven: Bool { 34| 2| return (self % 2) == 0 35| 2| } 36| | 37| | /// SwifterSwift: Check if integer is odd. 38| 2| var isOdd: Bool { 39| 2| return (self % 2) != 0 40| 2| } 41| | 42| | /// SwifterSwift: String of format (XXh XXm) from seconds Int. 43| 5| var timeString: String { 44| 5| guard self > 0 else { 45| 1| return "0 sec" 46| 4| } 47| 4| if self < 60 { 48| 1| return "\(self) sec" 49| 3| } 50| 3| if self < 3600 { 51| 1| return "\(self / 60) min" 52| 2| } 53| 2| let hours = self / 3600 54| 2| let mins = (self % 3600) / 60 55| 2| 56| 2| if hours != 0, mins == 0 { 57| 1| return "\(hours)h" 58| 1| } 59| 1| return "\(hours)h \(mins)m" 60| 2| } 61| |} 62| | 63| |// MARK: - Methods 64| | 65| |public extension SignedInteger { 66| | /// SwifterSwift: Greatest common divisor of integer value and n. 67| | /// 68| | /// - Parameter number: integer value to find gcd with. 69| | /// - Returns: greatest common divisor of self and n. 70| 7| func gcd(of number: Self) -> Self { 71| 7| return number == 0 ? self : number.gcd(of: self % number) 72| 7| } 73| | 74| | /// SwifterSwift: Least common multiple of integer and n. 75| | /// 76| | /// - Parameter number: integer value to find lcm with. 77| | /// - Returns: least common multiple of self and n. 78| 1| func lcm(of number: Self) -> Self { 79| 1| return (self * number).abs / gcd(of: number) 80| 1| } 81| | 82| | #if canImport(Foundation) 83| | /// SwifterSwift: Ordinal representation of an integer. 84| | /// 85| | /// print((12).ordinalString()) // prints "12th" 86| | /// 87| | /// - Parameter locale: locale, default is .current. 88| | /// - Returns: string ordinal representation of number in specified locale language. E.g. input 92, output in "en": "92nd". 89| | @available(macOS 10.11, *) 90| 2| func ordinalString(locale: Locale = .current) -> String? { 91| 2| let formatter = NumberFormatter() 92| 2| formatter.locale = locale 93| 2| formatter.numberStyle = .ordinal 94| 2| guard let number = self as? NSNumber else { return nil } 95| 2| return formatter.string(from: number) 96| 2| } 97| | #endif 98| |} /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/SignedNumericExtensions.swift: 1| |// SignedNumericExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(Foundation) 4| |import Foundation 5| |#endif 6| | 7| |// MARK: - Properties 8| | 9| |public extension SignedNumeric { 10| | /// SwifterSwift: String. 11| 5| var string: String { 12| 5| return String(describing: self) 13| 5| } 14| | 15| | #if canImport(Foundation) 16| | /// SwifterSwift: String with number and current locale currency. 17| 9| var asLocaleCurrency: String? { 18| 9| let formatter = NumberFormatter() 19| 9| formatter.numberStyle = .currency 20| 9| formatter.locale = Locale.current 21| 9| // swiftlint:disable:next force_cast 22| 9| return formatter.string(from: self as! NSNumber) 23| 9| } 24| | #endif 25| |} 26| | 27| |// MARK: - Methods 28| | 29| |public extension SignedNumeric { 30| | #if canImport(Foundation) 31| | /// SwifterSwift: Spelled out representation of a number. 32| | /// 33| | /// print((12.32).spelledOutString()) // prints "twelve point three two" 34| | /// 35| | /// - Parameter locale: Locale, default is .current. 36| | /// - Returns: String representation of number spelled in specified locale language. E.g. input 92, output in "en": "ninety-two" 37| 2| func spelledOutString(locale: Locale = .current) -> String? { 38| 2| let formatter = NumberFormatter() 39| 2| formatter.locale = locale 40| 2| formatter.numberStyle = .spellOut 41| 2| 42| 2| guard let number = self as? NSNumber else { return nil } 43| 2| return formatter.string(from: number) 44| 2| } 45| | #endif 46| |} /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/StringExtensions.swift: 1| |// StringExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(Foundation) 4| |import Foundation 5| |#endif 6| | 7| |#if canImport(UIKit) 8| |import UIKit 9| |#endif 10| | 11| |#if canImport(AppKit) 12| |import AppKit 13| |#endif 14| | 15| |#if canImport(CoreGraphics) 16| |import CoreGraphics 17| |#endif 18| | 19| |// MARK: - Properties 20| | 21| |public extension String { 22| | #if canImport(Foundation) 23| | /// SwifterSwift: String decoded from base64 (if applicable). 24| | /// 25| | /// "SGVsbG8gV29ybGQh".base64Decoded = Optional("Hello World!") 26| | /// 27| 3| var base64Decoded: String? { 28| 3| let remainder = count % 4 29| 3| 30| 3| var padding = "" 31| 3| if remainder > 0 { 32| 1| padding = String(repeating: "=", count: 4 - remainder) 33| 3| } 34| 3| 35| 3| guard let data = Data(base64Encoded: self + padding, 36| 3| options: .ignoreUnknownCharacters) else { return nil } 37| 2| 38| 2| return String(data: data, encoding: .utf8) 39| 3| } 40| | #endif 41| | 42| | #if canImport(Foundation) 43| | /// SwifterSwift: String encoded in base64 (if applicable). 44| | /// 45| | /// "Hello World!".base64Encoded -> Optional("SGVsbG8gV29ybGQh") 46| | /// 47| 1| var base64Encoded: String? { 48| 1| // https://github.com/Reza-Rg/Base64-Swift-Extension/blob/master/Base64.swift 49| 1| let plainData = data(using: .utf8) 50| 1| return plainData?.base64EncodedString() 51| 1| } 52| | #endif 53| | 54| | /// SwifterSwift: Array of characters of a string. 55| 1| var charactersArray: [Character] { 56| 1| return Array(self) 57| 1| } 58| | 59| | #if canImport(Foundation) 60| | /// SwifterSwift: CamelCase of string. 61| | /// 62| | /// "sOme vAriable naMe".camelCased -> "someVariableName" 63| | /// 64| 2| var camelCased: String { 65| 2| let source = lowercased() 66| 2| let first = source[.. true 81| | /// 82| 2| var containEmoji: Bool { 83| 2| // http://stackoverflow.com/questions/30757193/find-out-if-character-in-string-is-emoji 84| 15| for scalar in unicodeScalars { 85| 15| switch scalar.value { 86| 15| case 0x1F600...0x1F64F, // Emoticons 87| 1| 0x1F300...0x1F5FF, // Misc Symbols and Pictographs 88| 1| 0x1F680...0x1F6FF, // Transport and Map 89| 1| 0x1F1E6...0x1F1FF, // Regional country flags 90| 1| 0x2600...0x26FF, // Misc symbols 91| 1| 0x2700...0x27BF, // Dingbats 92| 1| 0xE0020...0xE007F, // Tags 93| 1| 0xFE00...0xFE0F, // Variation Selectors 94| 1| 0x1F900...0x1F9FF, // Supplemental Symbols and Pictographs 95| 1| 127_000...127_600, // Various asian characters 96| 1| 65024...65039, // Variation selector 97| 1| 9100...9300, // Misc items 98| 1| 8400...8447: // Combining Diacritical Marks for Symbols 99| 1| return true 100| 15| default: 101| 14| continue 102| 15| } 103| 16| } 104| 16| return false 105| 2| } 106| | 107| | /// SwifterSwift: First character of string (if applicable). 108| | /// 109| | /// "Hello".firstCharacterAsString -> Optional("H") 110| | /// "".firstCharacterAsString -> nil 111| | /// 112| 7| var firstCharacterAsString: String? { 113| 7| guard let first = first else { return nil } 114| 6| return String(first) 115| 7| } 116| | 117| | /// SwifterSwift: Check if string contains one or more letters. 118| | /// 119| | /// "123abc".hasLetters -> true 120| | /// "123".hasLetters -> false 121| | /// 122| 3| var hasLetters: Bool { 123| 3| return rangeOfCharacter(from: .letters, options: .numeric, range: nil) != nil 124| 3| } 125| | 126| | /// SwifterSwift: Check if string contains one or more numbers. 127| | /// 128| | /// "abcd".hasNumbers -> false 129| | /// "123abc".hasNumbers -> true 130| | /// 131| 3| var hasNumbers: Bool { 132| 3| return rangeOfCharacter(from: .decimalDigits, options: .literal, range: nil) != nil 133| 3| } 134| | 135| | /// SwifterSwift: Check if string contains only letters. 136| | /// 137| | /// "abc".isAlphabetic -> true 138| | /// "123abc".isAlphabetic -> false 139| | /// 140| 3| var isAlphabetic: Bool { 141| 3| let hasLetters = rangeOfCharacter(from: .letters, options: .numeric, range: nil) != nil 142| 3| let hasNumbers = rangeOfCharacter(from: .decimalDigits, options: .literal, range: nil) != nil 143| 3| return hasLetters && !hasNumbers 144| 3| } 145| | 146| | /// SwifterSwift: Check if string contains at least one letter and one number. 147| | /// 148| | /// // useful for passwords 149| | /// "123abc".isAlphaNumeric -> true 150| | /// "abc".isAlphaNumeric -> false 151| | /// 152| 3| var isAlphaNumeric: Bool { 153| 3| let hasLetters = rangeOfCharacter(from: .letters, options: .numeric, range: nil) != nil 154| 3| let hasNumbers = rangeOfCharacter(from: .decimalDigits, options: .literal, range: nil) != nil 155| 3| let comps = components(separatedBy: .alphanumerics) 156| 3| return comps.joined(separator: "").count == 0 && hasLetters && hasNumbers 157| 3| } 158| | 159| | /// SwifterSwift: Check if string is palindrome. 160| | /// 161| | /// "abcdcba".isPalindrome -> true 162| | /// "Mom".isPalindrome -> true 163| | /// "A man a plan a canal, Panama!".isPalindrome -> true 164| | /// "Mama".isPalindrome -> false 165| | /// 166| 5| var isPalindrome: Bool { 167| 43| let letters = filter { $0.isLetter } 168| 5| guard !letters.isEmpty else { return false } 169| 4| let midIndex = letters.index(letters.startIndex, offsetBy: letters.count / 2) 170| 4| let firstHalf = letters[letters.startIndex.. true 181| | /// 182| 30| var isValidEmail: Bool { 183| 30| // http://emailregex.com/ 184| 30| let regex = 185| 30| "^(?:[\\p{L}0-9!#$%\\&'*+/=?\\^_`{|}~-]+(?:\\.[\\p{L}0-9!#$%\\&'*+/=?\\^_`{|}~-]+)*|\"(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21\\x23-\\x5b\\x5d-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])*\")@(?:(?:[\\p{L}0-9](?:[a-z0-9-]*[\\p{L}0-9])?\\.)+[\\p{L}0-9](?:[\\p{L}0-9-]*[\\p{L}0-9])?|\\[(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.){3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?|[\\p{L}0-9-]*[\\p{L}0-9]:(?:[\\x01-\\x08\\x0b\\x0c\\x0e-\\x1f\\x21-\\x5a\\x53-\\x7f]|\\\\[\\x01-\\x09\\x0b\\x0c\\x0e-\\x7f])+)\\])$" 186| 30| return range(of: regex, options: .regularExpression, range: nil, locale: nil) != nil 187| 30| } 188| | #endif 189| | 190| | #if canImport(Foundation) 191| | /// SwifterSwift: Check if string is a valid URL. 192| | /// 193| | /// "https://google.com".isValidUrl -> true 194| | /// 195| 3| var isValidUrl: Bool { 196| 3| return URL(string: self) != nil 197| 3| } 198| | #endif 199| | 200| | #if canImport(Foundation) 201| | /// SwifterSwift: Check if string is a valid schemed URL. 202| | /// 203| | /// "https://google.com".isValidSchemedUrl -> true 204| | /// "google.com".isValidSchemedUrl -> false 205| | /// 206| 4| var isValidSchemedUrl: Bool { 207| 4| guard let url = URL(string: self) else { return false } 208| 3| return url.scheme != nil 209| 4| } 210| | #endif 211| | 212| | #if canImport(Foundation) 213| | /// SwifterSwift: Check if string is a valid https URL. 214| | /// 215| | /// "https://google.com".isValidHttpsUrl -> true 216| | /// 217| 4| var isValidHttpsUrl: Bool { 218| 4| guard let url = URL(string: self) else { return false } 219| 3| return url.scheme == "https" 220| 4| } 221| | #endif 222| | 223| | #if canImport(Foundation) 224| | /// SwifterSwift: Check if string is a valid http URL. 225| | /// 226| | /// "http://google.com".isValidHttpUrl -> true 227| | /// 228| 3| var isValidHttpUrl: Bool { 229| 3| guard let url = URL(string: self) else { return false } 230| 2| return url.scheme == "http" 231| 3| } 232| | #endif 233| | 234| | #if canImport(Foundation) 235| | /// SwifterSwift: Check if string is a valid file URL. 236| | /// 237| | /// "file://Documents/file.txt".isValidFileUrl -> true 238| | /// 239| 3| var isValidFileUrl: Bool { 240| 3| return URL(string: self)?.isFileURL ?? false 241| 3| } 242| | #endif 243| | 244| | #if canImport(Foundation) 245| | /// 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, 246| | /// 247| | /// "123".isNumeric -> true 248| | /// "1.3".isNumeric -> true (en_US) 249| | /// "1,3".isNumeric -> true (fr_FR) 250| | /// "abc".isNumeric -> false 251| | /// 252| 8| var isNumeric: Bool { 253| 8| let scanner = Scanner(string: self) 254| 8| scanner.locale = NSLocale.current 255| 8| #if os(Linux) || targetEnvironment(macCatalyst) 256| 8| return scanner.scanDecimal() != nil && scanner.isAtEnd 257| 8| #else 258| 8| return scanner.scanDecimal(nil) && scanner.isAtEnd 259| 8| #endif 260| 8| } 261| | #endif 262| | 263| | #if canImport(Foundation) 264| | /// SwifterSwift: Check if string only contains digits. 265| | /// 266| | /// "123".isDigits -> true 267| | /// "1.3".isDigits -> false 268| | /// "abc".isDigits -> false 269| | /// 270| 5| var isDigits: Bool { 271| 5| return CharacterSet.decimalDigits.isSuperset(of: CharacterSet(charactersIn: self)) 272| 5| } 273| | #endif 274| | 275| | /// SwifterSwift: Last character of string (if applicable). 276| | /// 277| | /// "Hello".lastCharacterAsString -> Optional("o") 278| | /// "".lastCharacterAsString -> nil 279| | /// 280| 9| var lastCharacterAsString: String? { 281| 9| guard let last = last else { return nil } 282| 8| return String(last) 283| 9| } 284| | 285| | #if canImport(Foundation) 286| | /// SwifterSwift: Latinized string. 287| | /// 288| | /// "Hèllö Wórld!".latinized -> "Hello World!" 289| | /// 290| 1| var latinized: String { 291| 1| return folding(options: .diacriticInsensitive, locale: Locale.current) 292| 1| } 293| | #endif 294| | 295| | #if canImport(Foundation) 296| | /// SwifterSwift: Bool value from string (if applicable). 297| | /// 298| | /// "1".bool -> true 299| | /// "False".bool -> false 300| | /// "Hello".bool = nil 301| | /// 302| 5| var bool: Bool? { 303| 5| let selfLowercased = trimmingCharacters(in: .whitespacesAndNewlines).lowercased() 304| 5| switch selfLowercased { 305| 5| case "true", "yes", "1": 306| 2| return true 307| 5| case "false", "no", "0": 308| 2| return false 309| 5| default: 310| 1| return nil 311| 5| } 312| 5| } 313| | #endif 314| | 315| | #if canImport(Foundation) 316| | /// SwifterSwift: Date object from "yyyy-MM-dd" formatted string. 317| | /// 318| | /// "2007-06-29".date -> Optional(Date) 319| | /// 320| 1| var date: Date? { 321| 1| let selfLowercased = trimmingCharacters(in: .whitespacesAndNewlines).lowercased() 322| 1| let formatter = DateFormatter() 323| 1| formatter.timeZone = TimeZone.current 324| 1| formatter.dateFormat = "yyyy-MM-dd" 325| 1| return formatter.date(from: selfLowercased) 326| 1| } 327| | #endif 328| | 329| | #if canImport(Foundation) 330| | /// SwifterSwift: Date object from "yyyy-MM-dd HH:mm:ss" formatted string. 331| | /// 332| | /// "2007-06-29 14:23:09".dateTime -> Optional(Date) 333| | /// 334| 1| var dateTime: Date? { 335| 1| let selfLowercased = trimmingCharacters(in: .whitespacesAndNewlines).lowercased() 336| 1| let formatter = DateFormatter() 337| 1| formatter.timeZone = TimeZone.current 338| 1| formatter.dateFormat = "yyyy-MM-dd HH:mm:ss" 339| 1| return formatter.date(from: selfLowercased) 340| 1| } 341| | #endif 342| | 343| | /// SwifterSwift: Integer value from string (if applicable). 344| | /// 345| | /// "101".int -> 101 346| | /// 347| 3| var int: Int? { 348| 3| return Int(self) 349| 3| } 350| | 351| | /// SwifterSwift: Lorem ipsum string of given length. 352| | /// 353| | /// - Parameter length: number of characters to limit lorem ipsum to (default is 445 - full lorem ipsum). 354| | /// - Returns: Lorem ipsum dolor sit amet... string. 355| 3| static func loremIpsum(ofLength length: Int = 445) -> String { 356| 3| guard length > 0 else { return "" } 357| 2| 358| 2| // https://www.lipsum.com/ 359| 2| let loremIpsum = """ 360| 2| 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. 361| 2| """ 362| 2| if loremIpsum.count > length { 363| 1| return String(loremIpsum[loremIpsum.startIndex.. URL(string: "https://google.com") 372| | /// "not url".url -> nil 373| | /// 374| 2| var url: URL? { 375| 2| return URL(string: self) 376| 2| } 377| | #endif 378| | 379| | #if canImport(Foundation) 380| | /// SwifterSwift: String with no spaces or new lines in beginning and end. 381| | /// 382| | /// " hello \n".trimmed -> "hello" 383| | /// 384| 1| var trimmed: String { 385| 1| return trimmingCharacters(in: .whitespacesAndNewlines) 386| 1| } 387| | #endif 388| | 389| | #if canImport(Foundation) 390| | /// SwifterSwift: Readable string from a URL string. 391| | /// 392| | /// "it's%20easy%20to%20decode%20strings".urlDecoded -> "it's easy to decode strings" 393| | /// 394| 2| var urlDecoded: String { 395| 2| return removingPercentEncoding ?? self 396| 2| } 397| | #endif 398| | 399| | #if canImport(Foundation) 400| | /// SwifterSwift: URL escaped string. 401| | /// 402| | /// "it's easy to encode strings".urlEncoded -> "it's%20easy%20to%20encode%20strings" 403| | /// 404| 1| var urlEncoded: String { 405| 1| return addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)! 406| 1| } 407| | #endif 408| | 409| | #if canImport(Foundation) 410| | /// SwifterSwift: Escaped string for inclusion in a regular expression pattern 411| | /// 412| | /// "hello ^$ there" -> "hello \\^\\$ there" 413| | /// 414| 1| var regexEscaped: String { 415| 1| return NSRegularExpression.escapedPattern(for: self) 416| 1| } 417| | #endif 418| | 419| | #if canImport(Foundation) 420| | /// SwifterSwift: String without spaces and new lines. 421| | /// 422| | /// " \n Swifter \n Swift ".withoutSpacesAndNewLines -> "SwifterSwift" 423| | /// 424| 3| var withoutSpacesAndNewLines: String { 425| 3| return replacingOccurrences(of: " ", with: "").replacingOccurrences(of: "\n", with: "") 426| 3| } 427| | #endif 428| | 429| | #if canImport(Foundation) 430| | /// SwifterSwift: Check if the given string contains only white spaces 431| 3| var isWhitespace: Bool { 432| 3| return trimmingCharacters(in: .whitespacesAndNewlines).isEmpty 433| 3| } 434| | #endif 435| | 436| | #if os(iOS) || os(tvOS) 437| | /// SwifterSwift: Check if the given string spelled correctly 438| 2| var isSpelledCorrectly: Bool { 439| 2| let checker = UITextChecker() 440| 2| let range = NSRange(startIndex.. Float? { 462| 5| let formatter = NumberFormatter() 463| 5| formatter.locale = locale 464| 5| formatter.allowsFloats = true 465| 5| return formatter.number(from: self)?.floatValue 466| 5| } 467| | #endif 468| | 469| | #if canImport(Foundation) 470| | /// SwifterSwift: Double value from string (if applicable). 471| | /// 472| | /// - Parameter locale: Locale (default is Locale.current) 473| | /// - Returns: Optional Double value from given string. 474| 5| func double(locale: Locale = .current) -> Double? { 475| 5| let formatter = NumberFormatter() 476| 5| formatter.locale = locale 477| 5| formatter.allowsFloats = true 478| 5| return formatter.number(from: self)?.doubleValue 479| 5| } 480| | #endif 481| | 482| | #if canImport(CoreGraphics) && canImport(Foundation) 483| | /// SwifterSwift: CGFloat value from string (if applicable). 484| | /// 485| | /// - Parameter locale: Locale (default is Locale.current) 486| | /// - Returns: Optional CGFloat value from given string. 487| 5| func cgFloat(locale: Locale = .current) -> CGFloat? { 488| 5| let formatter = NumberFormatter() 489| 5| formatter.locale = locale 490| 5| formatter.allowsFloats = true 491| 5| return formatter.number(from: self) as? CGFloat 492| 5| } 493| | #endif 494| | 495| | #if canImport(Foundation) 496| | /// SwifterSwift: Array of strings separated by new lines. 497| | /// 498| | /// "Hello\ntest".lines() -> ["Hello", "test"] 499| | /// 500| | /// - Returns: Strings separated by new lines. 501| 1| func lines() -> [String] { 502| 1| var result = [String]() 503| 2| enumerateLines { line, _ in 504| 2| result.append(line) 505| 2| } 506| 1| return result 507| 1| } 508| | #endif 509| | 510| | #if canImport(Foundation) 511| | /// SwifterSwift: Returns a localized string, with an optional comment for translators. 512| | /// 513| | /// "Hello world".localized -> Hallo Welt 514| | /// 515| 2| func localized(comment: String = "") -> String { 516| 2| return NSLocalizedString(self, comment: comment) 517| 2| } 518| | #endif 519| | 520| | /// SwifterSwift: The most common character in string. 521| | /// 522| | /// "This is a test, since e is appearing everywhere e should be the common character".mostCommonCharacter() -> "e" 523| | /// 524| | /// - Returns: The most common character. 525| 2| func mostCommonCharacter() -> Character? { 526| 66| let mostCommon = withoutSpacesAndNewLines.reduce(into: [Character: Int]()) { 527| 66| let count = $0[$1] ?? 0 528| 66| $0[$1] = count + 1 529| 66| }.max { $0.1 < $1.1 }?.key 530| 2| 531| 2| return mostCommon 532| 2| } 533| | 534| | /// SwifterSwift: Array with unicodes for all characters in a string. 535| | /// 536| | /// "SwifterSwift".unicodeArray() -> [83, 119, 105, 102, 116, 101, 114, 83, 119, 105, 102, 116] 537| | /// 538| | /// - Returns: The unicodes for all characters in a string. 539| 1| func unicodeArray() -> [Int] { 540| 5| return unicodeScalars.map { Int($0.value) } 541| 1| } 542| | 543| | #if canImport(Foundation) 544| | /// SwifterSwift: an array of all words in a string 545| | /// 546| | /// "Swift is amazing".words() -> ["Swift", "is", "amazing"] 547| | /// 548| | /// - Returns: The words contained in a string. 549| 1| func words() -> [String] { 550| 1| // https://stackoverflow.com/questions/42822838 551| 1| let chararacterSet = CharacterSet.whitespacesAndNewlines.union(.punctuationCharacters) 552| 1| let comps = components(separatedBy: chararacterSet) 553| 3| return comps.filter { !$0.isEmpty } 554| 1| } 555| | #endif 556| | 557| | #if canImport(Foundation) 558| | /// SwifterSwift: Count of words in a string. 559| | /// 560| | /// "Swift is amazing".wordsCount() -> 3 561| | /// 562| | /// - Returns: The count of words contained in a string. 563| 1| func wordCount() -> Int { 564| 1| // https://stackoverflow.com/questions/42822838 565| 1| let chararacterSet = CharacterSet.whitespacesAndNewlines.union(.punctuationCharacters) 566| 1| let comps = components(separatedBy: chararacterSet) 567| 3| let words = comps.filter { !$0.isEmpty } 568| 1| return words.count 569| 1| } 570| | #endif 571| | 572| | #if canImport(Foundation) 573| | /// SwifterSwift: Transforms the string into a slug string. 574| | /// 575| | /// "Swift is amazing".toSlug() -> "swift-is-amazing" 576| | /// 577| | /// - Returns: The string in slug format. 578| 2| func toSlug() -> String { 579| 2| let lowercased = self.lowercased() 580| 2| let latinized = lowercased.folding(options: .diacriticInsensitive, locale: Locale.current) 581| 2| let withDashes = latinized.replacingOccurrences(of: " ", with: "-") 582| 2| 583| 2| let alphanumerics = NSCharacterSet.alphanumerics 584| 40| var filtered = withDashes.filter { 585| 40| guard String($0) != "-" else { return true } 586| 27| guard String($0) != "&" else { return true } 587| 26| return String($0).rangeOfCharacter(from: alphanumerics) != nil 588| 27| } 589| 2| 590| 6| while filtered.lastCharacterAsString == "-" { 591| 4| filtered = String(filtered.dropLast()) 592| 4| } 593| 2| 594| 4| while filtered.firstCharacterAsString == "-" { 595| 2| filtered = String(filtered.dropFirst()) 596| 2| } 597| 2| 598| 2| return filtered.replacingOccurrences(of: "--", with: "-") 599| 2| } 600| | #endif 601| | 602| | /// SwifterSwift: Safely subscript string with index. 603| | /// 604| | /// "Hello World!"[safe: 3] -> "l" 605| | /// "Hello World!"[safe: 20] -> nil 606| | /// 607| | /// - Parameter index: index. 608| 2| subscript(safe index: Int) -> Character? { 609| 2| guard index >= 0, index < count else { return nil } 610| 1| return self[self.index(startIndex, offsetBy: index)] 611| 2| } 612| | 613| | /// SwifterSwift: Safely subscript string within a given range. 614| | /// 615| | /// "Hello World!"[safe: 6..<11] -> "World" 616| | /// "Hello World!"[safe: 21..<110] -> nil 617| | /// 618| | /// "Hello World!"[safe: 6...11] -> "World!" 619| | /// "Hello World!"[safe: 21...110] -> nil 620| | /// 621| | /// - Parameter range: Range expression. 622| 41| subscript(safe range: R) -> String? where R: RangeExpression, R.Bound == Int { 623| 41| let range = range.relative(to: Int.min..= 0, 625| 41| let lowerIndex = index(startIndex, offsetBy: range.lowerBound, limitedBy: endIndex), 626| 41| let upperIndex = index(startIndex, offsetBy: range.upperBound, limitedBy: endIndex) else { 627| 20| return nil 628| 21| } 629| 21| 630| 21| return String(self[lowerIndex.. String { 656| 2| let source = lowercased() 657| 2| let first = source[.. "Hello world" 674| | /// "".firstCharacterUppercased() -> "" 675| | /// 676| 3| mutating func firstCharacterUppercased() { 677| 3| guard let first = first else { return } 678| 2| self = String(first).uppercased() + dropFirst() 679| 2| } 680| | 681| | /// SwifterSwift: Check if string contains only unique characters. 682| | /// 683| 3| func hasUniqueCharacters() -> Bool { 684| 3| guard count > 0 else { return false } 685| 2| var uniqueChars = Set() 686| 11| for char in self { 687| 11| if uniqueChars.contains(String(char)) { return false } 688| 10| uniqueChars.insert(String(char)) 689| 10| } 690| 1| return true 691| 2| } 692| | 693| | #if canImport(Foundation) 694| | /// SwifterSwift: Check if string contains one or more instance of substring. 695| | /// 696| | /// "Hello World!".contain("O") -> false 697| | /// "Hello World!".contain("o", caseSensitive: false) -> true 698| | /// 699| | /// - Parameters: 700| | /// - string: substring to search for. 701| | /// - caseSensitive: set true for case sensitive search (default is true). 702| | /// - Returns: true if string contains one or more instance of substring. 703| 16| func contains(_ string: String, caseSensitive: Bool = true) -> Bool { 704| 16| if !caseSensitive { 705| 1| return range(of: string, options: .caseInsensitive) != nil 706| 15| } 707| 15| return range(of: string) != nil 708| 16| } 709| | #endif 710| | 711| | #if canImport(Foundation) 712| | /// SwifterSwift: Count of substring in string. 713| | /// 714| | /// "Hello World!".count(of: "o") -> 2 715| | /// "Hello World!".count(of: "L", caseSensitive: false) -> 3 716| | /// 717| | /// - Parameters: 718| | /// - string: substring to search for. 719| | /// - caseSensitive: set true for case sensitive search (default is true). 720| | /// - Returns: count of appearance of substring in string. 721| 4| func count(of string: String, caseSensitive: Bool = true) -> Int { 722| 4| if !caseSensitive { 723| 2| return lowercased().components(separatedBy: string.lowercased()).count - 1 724| 2| } 725| 2| return components(separatedBy: string).count - 1 726| 4| } 727| | #endif 728| | 729| | /// SwifterSwift: Check if string ends with substring. 730| | /// 731| | /// "Hello World!".ends(with: "!") -> true 732| | /// "Hello World!".ends(with: "WoRld!", caseSensitive: false) -> true 733| | /// 734| | /// - Parameters: 735| | /// - suffix: substring to search if string ends with. 736| | /// - caseSensitive: set true for case sensitive search (default is true). 737| | /// - Returns: true if string ends with substring. 738| 2| func ends(with suffix: String, caseSensitive: Bool = true) -> Bool { 739| 2| if !caseSensitive { 740| 1| return lowercased().hasSuffix(suffix.lowercased()) 741| 1| } 742| 1| return hasSuffix(suffix) 743| 2| } 744| | 745| | #if canImport(Foundation) 746| | /// SwifterSwift: Latinize string. 747| | /// 748| | /// var str = "Hèllö Wórld!" 749| | /// str.latinize() 750| | /// print(str) // prints "Hello World!" 751| | /// 752| | @discardableResult 753| 1| mutating func latinize() -> String { 754| 1| self = folding(options: .diacriticInsensitive, locale: Locale.current) 755| 1| return self 756| 1| } 757| | #endif 758| | 759| | /// SwifterSwift: Random string of given length. 760| | /// 761| | /// String.random(ofLength: 18) -> "u7MMZYvGo9obcOcPj8" 762| | /// 763| | /// - Parameter length: number of characters in string. 764| | /// - Returns: random string of given length. 765| 3| static func random(ofLength length: Int) -> String { 766| 3| guard length > 0 else { return "" } 767| 2| let base = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" 768| 2| var randomString = "" 769| 20| for _ in 1...length { 770| 20| randomString.append(base.randomElement()!) 771| 20| } 772| 2| return randomString 773| 2| } 774| | 775| | /// SwifterSwift: Reverse string. 776| | @discardableResult 777| 1| mutating func reverse() -> String { 778| 1| let chars: [Character] = reversed() 779| 1| self = String(chars) 780| 1| return self 781| 1| } 782| | 783| | /// SwifterSwift: Sliced string from a start index with length. 784| | /// 785| | /// "Hello World".slicing(from: 6, length: 5) -> "World" 786| | /// 787| | /// - Parameters: 788| | /// - index: string index the slicing should start from. 789| | /// - length: amount of characters to be sliced after given index. 790| | /// - Returns: sliced substring of length number of characters (if applicable) (example: "Hello World".slicing(from: 6, length: 5) -> "World") 791| 8| func slicing(from index: Int, length: Int) -> String? { 792| 8| guard length >= 0, index >= 0, index < count else { return nil } 793| 6| guard index.advanced(by: length) <= count else { 794| 2| return self[safe: index.. 0 else { return "" } 797| 2| return self[safe: index.. String { 811| 4| if let str = slicing(from: index, length: length) { 812| 3| self = String(str) 813| 4| } 814| 4| return self 815| 4| } 816| | 817| | /// SwifterSwift: Slice given string from a start index to an end index (if applicable). 818| | /// 819| | /// var str = "Hello World" 820| | /// str.slice(from: 6, to: 11) 821| | /// print(str) // prints "World" 822| | /// 823| | /// - Parameters: 824| | /// - start: string index the slicing should start from. 825| | /// - end: string index the slicing should end at. 826| | @discardableResult 827| 2| mutating func slice(from start: Int, to end: Int) -> String { 828| 2| guard end >= start else { return self } 829| 1| if let str = self[safe: start.. String { 844| 2| guard index < count else { return self } 845| 1| if let str = self[safe: index.. true 854| | /// "hello World".starts(with: "H", caseSensitive: false) -> true 855| | /// 856| | /// - Parameters: 857| | /// - suffix: substring to search if string starts with. 858| | /// - caseSensitive: set true for case sensitive search (default is true). 859| | /// - Returns: true if string starts with substring. 860| 2| func starts(with prefix: String, caseSensitive: Bool = true) -> Bool { 861| 2| if !caseSensitive { 862| 1| return lowercased().hasPrefix(prefix.lowercased()) 863| 1| } 864| 1| return hasPrefix(prefix) 865| 2| } 866| | 867| | #if canImport(Foundation) 868| | /// SwifterSwift: Date object from string of date format. 869| | /// 870| | /// "2017-01-15".date(withFormat: "yyyy-MM-dd") -> Date set to Jan 15, 2017 871| | /// "not date string".date(withFormat: "yyyy-MM-dd") -> nil 872| | /// 873| | /// - Parameter format: date format. 874| | /// - Returns: Date object from string (if applicable). 875| 2| func date(withFormat format: String) -> Date? { 876| 2| let dateFormatter = DateFormatter() 877| 2| dateFormatter.dateFormat = format 878| 2| return dateFormatter.date(from: self) 879| 2| } 880| | #endif 881| | 882| | #if canImport(Foundation) 883| | /// SwifterSwift: Removes spaces and new lines in beginning and end of string. 884| | /// 885| | /// var str = " \n Hello World \n\n\n" 886| | /// str.trim() 887| | /// print(str) // prints "Hello World" 888| | /// 889| | @discardableResult 890| 1| mutating func trim() -> String { 891| 1| self = trimmingCharacters(in: CharacterSet.whitespacesAndNewlines) 892| 1| return self 893| 1| } 894| | #endif 895| | 896| | /// SwifterSwift: Truncate string (cut it to a given number of characters). 897| | /// 898| | /// var str = "This is a very long sentence" 899| | /// str.truncate(toLength: 14) 900| | /// print(str) // prints "This is a very..." 901| | /// 902| | /// - Parameters: 903| | /// - toLength: maximum number of characters before cutting. 904| | /// - trailing: string to add at the end of truncated string (default is "..."). 905| | @discardableResult 906| 4| mutating func truncate(toLength length: Int, trailing: String? = "...") -> String { 907| 4| guard length > 0 else { return self } 908| 3| if count > length { 909| 2| self = self[startIndex.. "This is a very..." 917| | /// "Short sentence".truncated(toLength: 14) -> "Short sentence" 918| | /// 919| | /// - Parameters: 920| | /// - toLength: maximum number of characters before cutting. 921| | /// - trailing: string to add at the end of truncated string. 922| | /// - Returns: truncated string (this is an extr...). 923| 5| func truncated(toLength length: Int, trailing: String? = "...") -> String { 924| 5| guard 0.. String { 937| 1| if let decoded = removingPercentEncoding { 938| 1| self = decoded 939| 1| } 940| 1| return self 941| 1| } 942| | #endif 943| | 944| | #if canImport(Foundation) 945| | /// SwifterSwift: Escape string. 946| | /// 947| | /// var str = "it's easy to encode strings" 948| | /// str.urlEncode() 949| | /// print(str) // prints "it's%20easy%20to%20encode%20strings" 950| | /// 951| | @discardableResult 952| 1| mutating func urlEncode() -> String { 953| 1| if let encoded = addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) { 954| 1| self = encoded 955| 1| } 956| 1| return self 957| 1| } 958| | #endif 959| | 960| | #if canImport(Foundation) 961| | /// SwifterSwift: Verify if string matches the regex pattern. 962| | /// 963| | /// - Parameter pattern: Pattern to verify. 964| | /// - Returns: `true` if string matches the pattern. 965| 4| func matches(pattern: String) -> Bool { 966| 4| return range(of: pattern, options: .regularExpression, range: nil, locale: nil) != nil 967| 4| } 968| | #endif 969| | 970| | #if canImport(Foundation) 971| | /// SwifterSwift: Verify if string matches the regex. 972| | /// 973| | /// - Parameter regex: Regex to verify. 974| | /// - Parameter options: The matching options to use. 975| | /// - Returns: `true` if string matches the regex. 976| 4| func matches(regex: NSRegularExpression, options: NSRegularExpression.MatchingOptions = []) -> Bool { 977| 4| let range = NSRange(startIndex.. Bool { 989| 28| return lhs.range(of: rhs, options: .regularExpression) != nil 990| 28| } 991| | #endif 992| | 993| | #if canImport(Foundation) 994| | /// SwifterSwift: Overload Swift's 'contains' operator for matching regex 995| | /// 996| | /// - Parameter lhs: String to check on regex. 997| | /// - Parameter rhs: Regex to match against. 998| | /// - Returns: `true` if there is at least one match for the regex in the string. 999| 2| static func ~= (lhs: String, rhs: NSRegularExpression) -> Bool { 1000| 2| let range = NSRange(lhs.startIndex..? = nil) -> String { 1017| 6| let range = NSRange(searchRange ?? startIndex.. " hue" 1025| | /// "hue".padStart(10, with: "br") -> "brbrbrbhue" 1026| | /// 1027| | /// - Parameter length: The target length to pad. 1028| | /// - Parameter string: Pad string. Default is " ". 1029| | @discardableResult 1030| 6| mutating func padStart(_ length: Int, with string: String = " ") -> String { 1031| 6| self = paddingStart(length, with: string) 1032| 6| return self 1033| 6| } 1034| | 1035| | /// SwifterSwift: Returns a string by padding to fit the length parameter size with another string in the start. 1036| | /// 1037| | /// "hue".paddingStart(10) -> " hue" 1038| | /// "hue".paddingStart(10, with: "br") -> "brbrbrbhue" 1039| | /// 1040| | /// - Parameter length: The target length to pad. 1041| | /// - Parameter string: Pad string. Default is " ". 1042| | /// - Returns: The string with the padding on the start. 1043| 12| func paddingStart(_ length: Int, with string: String = " ") -> String { 1044| 12| guard count < length else { return self } 1045| 10| 1046| 10| let padLength = length - count 1047| 10| if padLength < string.count { 1048| 2| return string[string.startIndex.. "hue " 1061| | /// "hue".padEnd(10, with: "br") -> "huebrbrbrb" 1062| | /// 1063| | /// - Parameter length: The target length to pad. 1064| | /// - Parameter string: Pad string. Default is " ". 1065| | @discardableResult 1066| 6| mutating func padEnd(_ length: Int, with string: String = " ") -> String { 1067| 6| self = paddingEnd(length, with: string) 1068| 6| return self 1069| 6| } 1070| | 1071| | /// SwifterSwift: Returns a string by padding to fit the length parameter size with another string in the end. 1072| | /// 1073| | /// "hue".paddingEnd(10) -> "hue " 1074| | /// "hue".paddingEnd(10, with: "br") -> "huebrbrbrb" 1075| | /// 1076| | /// - Parameter length: The target length to pad. 1077| | /// - Parameter string: Pad string. Default is " ". 1078| | /// - Returns: The string with the padding on the end. 1079| 12| func paddingEnd(_ length: Int, with string: String = " ") -> String { 1080| 12| guard count < length else { return self } 1081| 10| 1082| 10| let padLength = length - count 1083| 10| if padLength < string.count { 1084| 2| return self + string[string.startIndex.. "World!" 1097| | /// 1098| | /// - Parameter prefix: Prefix to remove from the string. 1099| | /// - Returns: The string after prefix removing. 1100| 1| func removingPrefix(_ prefix: String) -> String { 1101| 1| guard hasPrefix(prefix) else { return self } 1102| 1| return String(dropFirst(prefix.count)) 1103| 1| } 1104| | 1105| | /// SwifterSwift: Removes given suffix from the string. 1106| | /// 1107| | /// "Hello, World!".removingSuffix(", World!") -> "Hello" 1108| | /// 1109| | /// - Parameter suffix: Suffix to remove from the string. 1110| | /// - Returns: The string after suffix removing. 1111| 1| func removingSuffix(_ suffix: String) -> String { 1112| 1| guard hasSuffix(suffix) else { return self } 1113| 1| return String(dropLast(suffix.count)) 1114| 1| } 1115| | 1116| | /// SwifterSwift: Adds prefix to the string. 1117| | /// 1118| | /// "www.apple.com".withPrefix("https://") -> "https://www.apple.com" 1119| | /// 1120| | /// - Parameter prefix: Prefix to add to the string. 1121| | /// - Returns: The string with the prefix prepended. 1122| 2| func withPrefix(_ prefix: String) -> String { 1123| 2| // https://www.hackingwithswift.com/articles/141/8-useful-swift-extensions 1124| 2| guard !hasPrefix(prefix) else { return self } 1125| 1| return prefix + self 1126| 2| } 1127| |} 1128| | 1129| |// MARK: - Initializers 1130| | 1131| |public extension String { 1132| | #if canImport(Foundation) 1133| | /// SwifterSwift: Create a new string from a base64 string (if applicable). 1134| | /// 1135| | /// String(base64: "SGVsbG8gV29ybGQh") = "Hello World!" 1136| | /// String(base64: "hello") = nil 1137| | /// 1138| | /// - Parameter base64: base64 string. 1139| 3| init?(base64: String) { 1140| 3| guard let decodedData = Data(base64Encoded: base64) else { return nil } 1141| 2| guard let str = String(data: decodedData, encoding: .utf8) else { return nil } 1142| 2| self.init(str) 1143| 2| } 1144| | #endif 1145| | 1146| | /// SwifterSwift: Create a new random string of given length. 1147| | /// 1148| | /// String(randomOfLength: 10) -> "gY8r3MHvlQ" 1149| | /// 1150| | /// - Parameter length: number of characters in string. 1151| 3| init(randomOfLength length: Int) { 1152| 3| guard length > 0 else { 1153| 1| self.init() 1154| 1| return 1155| 2| } 1156| 2| 1157| 2| let base = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789" 1158| 2| var randomString = "" 1159| 20| for _ in 1...length { 1160| 20| randomString.append(base.randomElement()!) 1161| 20| } 1162| 2| self = randomString 1163| 2| } 1164| |} 1165| | 1166| |#if !os(Linux) 1167| | 1168| |// MARK: - NSAttributedString 1169| | 1170| |public extension String { 1171| | #if os(iOS) || os(macOS) 1172| | /// SwifterSwift: Bold string. 1173| | var bold: NSAttributedString { 1174| | return NSMutableAttributedString( 1175| | string: self, 1176| | attributes: [.font: Font.boldSystemFont(ofSize: Font.systemFontSize)]) 1177| | } 1178| | #endif 1179| | 1180| | #if canImport(Foundation) 1181| | /// SwifterSwift: Underlined string 1182| 1| var underline: NSAttributedString { 1183| 1| return NSAttributedString(string: self, attributes: [.underlineStyle: NSUnderlineStyle.single.rawValue]) 1184| 1| } 1185| | #endif 1186| | 1187| | #if canImport(Foundation) 1188| | /// SwifterSwift: Strikethrough string. 1189| 1| var strikethrough: NSAttributedString { 1190| 1| return NSAttributedString( 1191| 1| string: self, 1192| 1| attributes: [.strikethroughStyle: NSNumber(value: NSUnderlineStyle.single.rawValue as Int)]) 1193| 1| } 1194| | #endif 1195| | 1196| | #if os(iOS) 1197| | /// SwifterSwift: Italic string. 1198| | var italic: NSAttributedString { 1199| | return NSMutableAttributedString( 1200| | string: self, 1201| | attributes: [.font: UIFont.italicSystemFont(ofSize: UIFont.systemFontSize)]) 1202| | } 1203| | #endif 1204| | 1205| | #if canImport(AppKit) || canImport(UIKit) 1206| | /// SwifterSwift: Add color to string. 1207| | /// 1208| | /// - Parameter color: text color. 1209| | /// - Returns: a NSAttributedString versions of string colored with given color. 1210| 1| func colored(with color: Color) -> NSAttributedString { 1211| 1| return NSMutableAttributedString(string: self, attributes: [.foregroundColor: color]) 1212| 1| } 1213| | #endif 1214| |} 1215| | 1216| |#endif 1217| | 1218| |// MARK: - Operators 1219| | 1220| |public extension String { 1221| | /// SwifterSwift: Repeat string multiple times. 1222| | /// 1223| | /// 'bar' * 3 -> "barbarbar" 1224| | /// 1225| | /// - Parameters: 1226| | /// - lhs: string to repeat. 1227| | /// - rhs: number of times to repeat character. 1228| | /// - Returns: new string with given string repeated n times. 1229| 3| static func * (lhs: String, rhs: Int) -> String { 1230| 3| guard rhs > 0 else { return "" } 1231| 1| return String(repeating: lhs, count: rhs) 1232| 3| } 1233| | 1234| | /// SwifterSwift: Repeat string multiple times. 1235| | /// 1236| | /// 3 * 'bar' -> "barbarbar" 1237| | /// 1238| | /// - Parameters: 1239| | /// - lhs: number of times to repeat character. 1240| | /// - rhs: string to repeat. 1241| | /// - Returns: new string with given string repeated n times. 1242| 3| static func * (lhs: Int, rhs: String) -> String { 1243| 3| guard lhs > 0 else { return "" } 1244| 1| return String(repeating: rhs, count: lhs) 1245| 3| } 1246| |} 1247| | 1248| |#if canImport(Foundation) 1249| | 1250| |// MARK: - NSString extensions 1251| | 1252| |public extension String { 1253| | /// SwifterSwift: NSString from a string. 1254| 1| var nsString: NSString { 1255| 1| return NSString(string: self) 1256| 1| } 1257| | 1258| | /// SwifterSwift: The full `NSRange` of the string. 1259| 3| var fullNSRange: NSRange { NSRange(startIndex..`. 1287| | /// - Parameter nsRange: The `NSRange` within the receiver. 1288| | /// - Returns: The equivalent `Range` of `nsRange` found within the receiving string. 1289| 4| func range(from nsRange: NSRange) -> Range { 1290| 4| guard let range = Range(nsRange, in: self) else { fatalError("Failed to find range \(nsRange) in \(self)") } 1291| 4| return range 1292| 4| } 1293| | 1294| | /// SwifterSwift: Convert a `Range` into `NSRange`. 1295| | /// - Parameter range: The `Range` within the receiver. 1296| | /// - Returns: The equivalent `NSRange` of `range` found within the receiving string. 1297| 4| func nsRange(from range: Range) -> NSRange { 1298| 4| return NSRange(range, in: self) 1299| 4| } 1300| | 1301| | /// SwifterSwift: NSString appendingPathComponent(str: String) 1302| | /// 1303| | /// - Note: This method only works with file paths (not, for example, string representations of URLs. 1304| | /// See NSString [appendingPathComponent(_:)](https://developer.apple.com/documentation/foundation/nsstring/1417069-appendingpathcomponent) 1305| | /// - Parameter str: the path component to append to the receiver. 1306| | /// - Returns: a new string made by appending aString to the receiver, preceded if necessary by a path separator. 1307| 1| func appendingPathComponent(_ str: String) -> String { 1308| 1| return (self as NSString).appendingPathComponent(str) 1309| 1| } 1310| | 1311| | /// SwifterSwift: NSString appendingPathExtension(str: String) 1312| | /// 1313| | /// - Parameter str: The extension to append to the receiver. 1314| | /// - Returns: a new string made by appending to the receiver an extension separator followed by ext (if applicable). 1315| 1| func appendingPathExtension(_ str: String) -> String? { 1316| 1| return (self as NSString).appendingPathExtension(str) 1317| 1| } 1318| | 1319| | /// SwifterSwift: Accesses a contiguous subrange of the collection’s elements. 1320| | /// - Parameter nsRange: A range of the collection’s indices. The bounds of the range must be valid indices of the collection. 1321| | /// - Returns: A slice of the receiving string. 1322| 4| subscript(bounds: NSRange) -> Substring { 1323| 4| guard let range = Range(bounds, in: self) else { fatalError("Failed to find range \(bounds) in \(self)") } 1324| 4| return self[range] 1325| 4| } 1326| |} 1327| | 1328| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/SwiftStdlib/StringProtocolExtensions.swift: 1| |// StringProtocolExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |import Foundation 4| | 5| |public extension StringProtocol { 6| | /// SwifterSwift: The longest common suffix. 7| | /// 8| | /// "Hello world!".commonSuffix(with: "It's cold!") = "ld!" 9| | /// 10| | /// - Parameters: 11| | /// - Parameter aString: The string with which to compare the receiver. 12| | /// - Parameter options: Options for the comparison. 13| | /// - Returns: The longest common suffix of the receiver and the given String 14| 15| func commonSuffix(with aString: T, options: String.CompareOptions = []) -> String { 15| 15| return String(zip(reversed(), aString.reversed()) 16| 15| .lazy 17| 50| .prefix(while: { (lhs: Character, rhs: Character) in 18| 50| String(lhs).compare(String(rhs), options: options) == .orderedSame 19| 50| }) 20| 41| .map { (lhs: Character, _: Character) in lhs } 21| 15| .reversed()) 22| 15| } 23| | 24| | #if canImport(Foundation) 25| | /// 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. 26| | /// - Parameter ofPattern: Regex pattern to replace. 27| | /// - Parameter template: The regex template to replace the pattern. 28| | /// - Parameter options: Options to use when matching the regex. Only .regularExpression, .anchored .and caseInsensitive are supported. 29| | /// - Parameter searchRange: The range in the receiver in which to search. 30| | /// - Returns: A new string in which all occurrences of regex pattern in searchRange of the receiver are replaced by template. 31| | func replacingOccurrences( 32| | ofPattern pattern: Target, 33| | withTemplate template: Replacement, 34| | options: String.CompareOptions = [.regularExpression], 35| | range searchRange: Range? = nil) -> String where Target: StringProtocol, 36| 6| Replacement: StringProtocol { 37| 6| assert( 38| 6| options.isStrictSubset(of: [.regularExpression, .anchored, .caseInsensitive]), 39| 6| "Invalid options for regular expression replacement") 40| 6| return replacingOccurrences( 41| 6| of: pattern, 42| 6| with: template, 43| 6| options: options.union(.regularExpression), 44| 6| range: searchRange) 45| 6| } 46| | #endif 47| |} /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/UIKit/UIAlertControllerExtensions.swift: 1| |// UIAlertControllerExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(UIKit) && !os(watchOS) 4| |import UIKit 5| | 6| |#if canImport(AudioToolbox) 7| |import AudioToolbox 8| |#endif 9| | 10| |// MARK: - Methods 11| | 12| |public extension UIAlertController { 13| | /// SwifterSwift: Present alert view controller in the current view controller. 14| | /// 15| | /// - Parameters: 16| | /// - animated: set true to animate presentation of alert controller (default is true). 17| | /// - vibrate: set true to vibrate the device while presenting the alert (default is false). 18| | /// - completion: an optional completion handler to be called after presenting alert controller (default is nil). 19| | @available(iOSApplicationExtension, unavailable) 20| 0| func show(animated: Bool = true, vibrate: Bool = false, completion: (() -> Void)? = nil) { 21| 0| #if targetEnvironment(macCatalyst) 22| 0| let window = UIApplication.shared.windows.last 23| 0| #else 24| 0| let window = UIApplication.shared.keyWindow 25| 0| #endif 26| 0| window?.rootViewController?.present(self, animated: animated, completion: completion) 27| 0| if vibrate { 28| 0| #if canImport(AudioToolbox) 29| 0| AudioServicesPlayAlertSound(kSystemSoundID_Vibrate) 30| 0| #endif 31| 0| } 32| 0| } 33| | 34| | /// SwifterSwift: Add an action to Alert 35| | /// 36| | /// - Parameters: 37| | /// - title: action title 38| | /// - style: action style (default is UIAlertActionStyle.default) 39| | /// - isEnabled: isEnabled status for action (default is true) 40| | /// - handler: optional action handler to be called when button is tapped (default is nil) 41| | /// - Returns: action created by this method 42| | @discardableResult 43| | func addAction( 44| | title: String, 45| | style: UIAlertAction.Style = .default, 46| | isEnabled: Bool = true, 47| 1| handler: ((UIAlertAction) -> Void)? = nil) -> UIAlertAction { 48| 1| let action = UIAlertAction(title: title, style: style, handler: handler) 49| 1| action.isEnabled = isEnabled 50| 1| addAction(action) 51| 1| return action 52| 1| } 53| | 54| | /// SwifterSwift: Add a text field to Alert 55| | /// 56| | /// - Parameters: 57| | /// - text: text field text (default is nil) 58| | /// - placeholder: text field placeholder text (default is nil) 59| | /// - editingChangedTarget: an optional target for text field's editingChanged 60| | /// - editingChangedSelector: an optional selector for text field's editingChanged 61| | func addTextField( 62| | text: String? = nil, 63| | placeholder: String? = nil, 64| | editingChangedTarget: Any?, 65| 1| editingChangedSelector: Selector?) { 66| 1| addTextField { textField in 67| 1| textField.text = text 68| 1| textField.placeholder = placeholder 69| 1| if let target = editingChangedTarget, let selector = editingChangedSelector { 70| 1| textField.addTarget(target, action: selector, for: .editingChanged) 71| 1| } 72| 1| } 73| 1| } 74| |} 75| | 76| |// MARK: - Initializers 77| | 78| |public extension UIAlertController { 79| | /// SwifterSwift: Create new alert view controller with default OK action. 80| | /// 81| | /// - Parameters: 82| | /// - title: alert controller's title. 83| | /// - message: alert controller's message (default is nil). 84| | /// - defaultActionButtonTitle: default action button title (default is "OK") 85| | /// - tintColor: alert controller's tint color (default is nil) 86| | convenience init( 87| | title: String, 88| | message: String? = nil, 89| | defaultActionButtonTitle: String = "OK", 90| 1| tintColor: UIColor? = nil) { 91| 1| self.init(title: title, message: message, preferredStyle: .alert) 92| 1| let defaultAction = UIAlertAction(title: defaultActionButtonTitle, style: .default, handler: nil) 93| 1| addAction(defaultAction) 94| 1| if let color = tintColor { 95| 1| view.tintColor = color 96| 1| } 97| 1| } 98| | 99| | /// SwifterSwift: Create new error alert view controller from Error with default OK action. 100| | /// 101| | /// - Parameters: 102| | /// - title: alert controller's title (default is "Error"). 103| | /// - error: error to set alert controller's message to it's localizedDescription. 104| | /// - defaultActionButtonTitle: default action button title (default is "OK") 105| | /// - tintColor: alert controller's tint color (default is nil) 106| | convenience init( 107| | title: String = "Error", 108| | error: Error, 109| | defaultActionButtonTitle: String = "OK", 110| | preferredStyle: UIAlertController.Style = .alert, 111| 1| tintColor: UIColor? = nil) { 112| 1| self.init(title: title, message: error.localizedDescription, preferredStyle: preferredStyle) 113| 1| let defaultAction = UIAlertAction(title: defaultActionButtonTitle, style: .default, handler: nil) 114| 1| addAction(defaultAction) 115| 1| if let color = tintColor { 116| 1| view.tintColor = color 117| 1| } 118| 1| } 119| |} 120| | 121| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/UIKit/UIApplicationExtensions.swift: 1| |// UIApplicationExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(UIKit) 4| |import UIKit 5| | 6| |#if os(iOS) || os(tvOS) 7| | 8| |public extension UIApplication { 9| | /// SwifterSwift: Application running environment. 10| | /// 11| | /// - debug: Application is running in debug mode. 12| | /// - testFlight: Application is installed from Test Flight. 13| | /// - appStore: Application is installed from the App Store. 14| | enum Environment { 15| | /// SwifterSwift: Application is running in debug mode. 16| | case debug 17| | /// SwifterSwift: Application is installed from Test Flight. 18| | case testFlight 19| | /// SwifterSwift: Application is installed from the App Store. 20| | case appStore 21| | } 22| | 23| | /// SwifterSwift: Current inferred app environment. 24| 0| var inferredEnvironment: Environment { 25| 0| #if DEBUG 26| 0| return .debug 27| 0| 28| 0| #elseif targetEnvironment(simulator) 29| 0| return .debug 30| 0| 31| 0| #else 32| 0| if Bundle.main.path(forResource: "embedded", ofType: "mobileprovision") != nil { 33| 0| return .testFlight 34| 0| } 35| 0| 36| 0| guard let appStoreReceiptUrl = Bundle.main.appStoreReceiptURL else { 37| 0| return .debug 38| 0| } 39| 0| 40| 0| if appStoreReceiptUrl.lastPathComponent.lowercased() == "sandboxreceipt" { 41| 0| return .testFlight 42| 0| } 43| 0| 44| 0| if appStoreReceiptUrl.path.lowercased().contains("simulator") { 45| 0| return .debug 46| 0| } 47| 0| 48| 0| return .appStore 49| 0| #endif 50| 0| } 51| | 52| | /// SwifterSwift: Application name (if applicable). 53| 0| var displayName: String? { 54| 0| return Bundle.main.object(forInfoDictionaryKey: "CFBundleDisplayName") as? String 55| 0| } 56| | 57| | /// SwifterSwift: App current build number (if applicable). 58| 0| var buildNumber: String? { 59| 0| return Bundle.main.object(forInfoDictionaryKey: kCFBundleVersionKey as String) as? String 60| 0| } 61| | 62| | /// SwifterSwift: App's current version number (if applicable). 63| 0| var version: String? { 64| 0| return Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String 65| 0| } 66| |} 67| | 68| |#endif 69| | 70| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/UIKit/UIBarButtonItemExtensions.swift: 1| |// UIBarButtonItemExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(UIKit) && !os(watchOS) 4| |import UIKit 5| | 6| |// MARK: - Properties 7| | 8| |public extension UIBarButtonItem { 9| | /// SwifterSwift: Creates a flexible space UIBarButtonItem 10| 2| static var flexibleSpace: UIBarButtonItem { 11| 2| return UIBarButtonItem(barButtonSystemItem: .flexibleSpace, target: nil, action: nil) 12| 2| } 13| |} 14| | 15| |// MARK: - Methods 16| | 17| |public extension UIBarButtonItem { 18| | /// SwifterSwift: Add Target to UIBarButtonItem 19| | /// 20| | /// - Parameters: 21| | /// - target: target. 22| | /// - action: selector to run when button is tapped. 23| 1| func addTargetForAction(_ target: AnyObject, action: Selector) { 24| 1| self.target = target 25| 1| self.action = action 26| 1| } 27| | 28| | /// SwifterSwift: Creates a fixed space UIBarButtonItem with a specific width 29| | /// 30| | /// - Parameter width: Width of the UIBarButtonItem 31| 1| static func fixedSpace(width: CGFloat) -> UIBarButtonItem { 32| 1| let barButtonItem = UIBarButtonItem(barButtonSystemItem: .fixedSpace, target: nil, action: nil) 33| 1| barButtonItem.width = width 34| 1| return barButtonItem 35| 1| } 36| |} 37| | 38| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/UIKit/UIBezierPathExtensions.swift: 1| |// UIBezierPathExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(UIKit) 4| |import UIKit 5| | 6| |// MARK: - Initializers 7| | 8| |public extension UIBezierPath { 9| | /// SwifterSwift: Initializes a UIBezierPath with a line from a CGPoint to another CGPoint. 10| | /// 11| | /// - Parameters: 12| | /// - from: The point from which to path should start. 13| | /// - to: The point where the path should end. 14| 1| convenience init(from: CGPoint, to otherPoint: CGPoint) { 15| 1| self.init() 16| 1| move(to: from) 17| 1| addLine(to: otherPoint) 18| 1| } 19| | 20| | /// SwifterSwift: Initializes a UIBezierPath connecting the given CGPoints with straight lines. 21| | /// 22| | /// - Parameter points: The points of which the path should consist. 23| 2| convenience init(points: [CGPoint]) { 24| 2| self.init() 25| 2| if !points.isEmpty { 26| 1| move(to: points[0]) 27| 2| for point in points[1...] { 28| 2| addLine(to: point) 29| 2| } 30| 2| } 31| 2| } 32| | 33| | /// SwifterSwift: Initializes a polygonal UIBezierPath with the given CGPoints. At least 3 points must be given. 34| | /// 35| | /// - Parameter points: The points of the polygon which the path should form. 36| 2| convenience init?(polygonWithPoints points: [CGPoint]) { 37| 2| guard points.count > 2 else { return nil } 38| 1| self.init() 39| 1| move(to: points[0]) 40| 3| for point in points[1...] { 41| 3| addLine(to: point) 42| 3| } 43| 1| close() 44| 1| } 45| | 46| | /// SwifterSwift: Initializes a UIBezierPath with an oval path of given size. 47| | /// 48| | /// - Parameters: 49| | /// - size: The width and height of the oval. 50| | /// - centered: Whether the oval should be centered in its coordinate space. 51| 2| convenience init(ovalOf size: CGSize, centered: Bool) { 52| 2| let origin = centered ? CGPoint(x: -size.width / 2, y: -size.height / 2) : .zero 53| 2| self.init(ovalIn: CGRect(origin: origin, size: size)) 54| 2| } 55| | 56| | /// SwifterSwift: Initializes a UIBezierPath with a rectangular path of given size. 57| | /// 58| | /// - Parameters: 59| | /// - size: The width and height of the rect. 60| | /// - centered: Whether the oval should be centered in its coordinate space. 61| 2| convenience init(rectOf size: CGSize, centered: Bool) { 62| 2| let origin = centered ? CGPoint(x: -size.width / 2, y: -size.height / 2) : .zero 63| 2| self.init(rect: CGRect(origin: origin, size: size)) 64| 2| } 65| |} 66| | 67| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/UIKit/UIButtonExtensions.swift: 1| |// UIButtonExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(UIKit) && !os(watchOS) 4| |import UIKit 5| | 6| |// MARK: - Properties 7| | 8| |public extension UIButton { 9| | /// SwifterSwift: Image of disabled state for button; also inspectable from Storyboard. 10| | @IBInspectable 11| | var imageForDisabled: UIImage? { 12| 3| get { 13| 3| return image(for: .disabled) 14| 3| } 15| 1| set { 16| 1| setImage(newValue, for: .disabled) 17| 1| } 18| | } 19| | 20| | /// SwifterSwift: Image of highlighted state for button; also inspectable from Storyboard. 21| | @IBInspectable 22| | var imageForHighlighted: UIImage? { 23| 3| get { 24| 3| return image(for: .highlighted) 25| 3| } 26| 1| set { 27| 1| setImage(newValue, for: .highlighted) 28| 1| } 29| | } 30| | 31| | /// SwifterSwift: Image of normal state for button; also inspectable from Storyboard. 32| | @IBInspectable 33| | var imageForNormal: UIImage? { 34| 3| get { 35| 3| return image(for: .normal) 36| 3| } 37| 1| set { 38| 1| setImage(newValue, for: .normal) 39| 1| } 40| | } 41| | 42| | /// SwifterSwift: Image of selected state for button; also inspectable from Storyboard. 43| | @IBInspectable 44| | var imageForSelected: UIImage? { 45| 3| get { 46| 3| return image(for: .selected) 47| 3| } 48| 1| set { 49| 1| setImage(newValue, for: .selected) 50| 1| } 51| | } 52| | 53| | /// SwifterSwift: Title color of disabled state for button; also inspectable from Storyboard. 54| | @IBInspectable 55| | var titleColorForDisabled: UIColor? { 56| 3| get { 57| 3| return titleColor(for: .disabled) 58| 3| } 59| 1| set { 60| 1| setTitleColor(newValue, for: .disabled) 61| 1| } 62| | } 63| | 64| | /// SwifterSwift: Title color of highlighted state for button; also inspectable from Storyboard. 65| | @IBInspectable 66| | var titleColorForHighlighted: UIColor? { 67| 3| get { 68| 3| return titleColor(for: .highlighted) 69| 3| } 70| 1| set { 71| 1| setTitleColor(newValue, for: .highlighted) 72| 1| } 73| | } 74| | 75| | /// SwifterSwift: Title color of normal state for button; also inspectable from Storyboard. 76| | @IBInspectable 77| | var titleColorForNormal: UIColor? { 78| 3| get { 79| 3| return titleColor(for: .normal) 80| 3| } 81| 1| set { 82| 1| setTitleColor(newValue, for: .normal) 83| 1| } 84| | } 85| | 86| | /// SwifterSwift: Title color of selected state for button; also inspectable from Storyboard. 87| | @IBInspectable 88| | var titleColorForSelected: UIColor? { 89| 3| get { 90| 3| return titleColor(for: .selected) 91| 3| } 92| 1| set { 93| 1| setTitleColor(newValue, for: .selected) 94| 1| } 95| | } 96| | 97| | /// SwifterSwift: Title of disabled state for button; also inspectable from Storyboard. 98| | @IBInspectable 99| | var titleForDisabled: String? { 100| 3| get { 101| 3| return title(for: .disabled) 102| 3| } 103| 1| set { 104| 1| setTitle(newValue, for: .disabled) 105| 1| } 106| | } 107| | 108| | /// SwifterSwift: Title of highlighted state for button; also inspectable from Storyboard. 109| | @IBInspectable 110| | var titleForHighlighted: String? { 111| 3| get { 112| 3| return title(for: .highlighted) 113| 3| } 114| 1| set { 115| 1| setTitle(newValue, for: .highlighted) 116| 1| } 117| | } 118| | 119| | /// SwifterSwift: Title of normal state for button; also inspectable from Storyboard. 120| | @IBInspectable 121| | var titleForNormal: String? { 122| 3| get { 123| 3| return title(for: .normal) 124| 3| } 125| 1| set { 126| 1| setTitle(newValue, for: .normal) 127| 1| } 128| | } 129| | 130| | /// SwifterSwift: Title of selected state for button; also inspectable from Storyboard. 131| | @IBInspectable 132| | var titleForSelected: String? { 133| 3| get { 134| 3| return title(for: .selected) 135| 3| } 136| 1| set { 137| 1| setTitle(newValue, for: .selected) 138| 1| } 139| | } 140| |} 141| | 142| |// MARK: - Methods 143| | 144| |public extension UIButton { 145| 5| private var states: [UIControl.State] { 146| 5| return [.normal, .selected, .highlighted, .disabled] 147| 5| } 148| | 149| | /// SwifterSwift: Set image for all states. 150| | /// 151| | /// - Parameter image: UIImage. 152| 2| func setImageForAllStates(_ image: UIImage) { 153| 8| states.forEach { setImage(image, for: $0) } 154| 2| } 155| | 156| | /// SwifterSwift: Set title color for all states. 157| | /// 158| | /// - Parameter color: UIColor. 159| 1| func setTitleColorForAllStates(_ color: UIColor) { 160| 4| states.forEach { setTitleColor(color, for: $0) } 161| 1| } 162| | 163| | /// SwifterSwift: Set title for all states. 164| | /// 165| | /// - Parameter title: title string. 166| 2| func setTitleForAllStates(_ title: String) { 167| 8| states.forEach { setTitle(title, for: $0) } 168| 2| } 169| | 170| | /// SwifterSwift: Center align title text and image 171| | /// - Parameters: 172| | /// - imageAboveText: set true to make image above title text, default is false, image on left of text 173| | /// - spacing: spacing between title text and image. 174| 2| func centerTextAndImage(imageAboveText: Bool = false, spacing: CGFloat) { 175| 2| if imageAboveText { 176| 1| // https://stackoverflow.com/questions/2451223/#7199529 177| 1| guard 178| 1| let imageSize = imageView?.image?.size, 179| 1| let text = titleLabel?.text, 180| 1| let font = titleLabel?.font else { return } 181| 1| 182| 1| let titleSize = text.size(withAttributes: [.font: font]) 183| 1| 184| 1| let titleOffset = -(imageSize.height + spacing) 185| 1| titleEdgeInsets = UIEdgeInsets(top: 0.0, left: -imageSize.width, bottom: titleOffset, right: 0.0) 186| 1| 187| 1| let imageOffset = -(titleSize.height + spacing) 188| 1| imageEdgeInsets = UIEdgeInsets(top: imageOffset, left: 0.0, bottom: 0.0, right: -titleSize.width) 189| 1| 190| 1| let edgeOffset = abs(titleSize.height - imageSize.height) / 2.0 191| 1| contentEdgeInsets = UIEdgeInsets(top: edgeOffset, left: 0.0, bottom: edgeOffset, right: 0.0) 192| 2| } else { 193| 1| let insetAmount = spacing / 2 194| 1| imageEdgeInsets = UIEdgeInsets(top: 0, left: -insetAmount, bottom: 0, right: insetAmount) 195| 1| titleEdgeInsets = UIEdgeInsets(top: 0, left: insetAmount, bottom: 0, right: -insetAmount) 196| 1| contentEdgeInsets = UIEdgeInsets(top: 0, left: insetAmount, bottom: 0, right: insetAmount) 197| 2| } 198| 2| } 199| |} 200| | 201| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/UIKit/UICollectionViewExtensions.swift: 1| |// UICollectionViewExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(UIKit) && !os(watchOS) 4| |import UIKit 5| | 6| |// MARK: - Properties 7| | 8| |public extension UICollectionView { 9| | /// SwifterSwift: Index path of last item in collectionView. 10| 1| var indexPathForLastItem: IndexPath? { 11| 1| return indexPathForLastItem(inSection: lastSection) 12| 1| } 13| | 14| | /// SwifterSwift: Index of last section in collectionView. 15| 3| var lastSection: Int { 16| 3| return numberOfSections > 0 ? numberOfSections - 1 : 0 17| 3| } 18| |} 19| | 20| |// MARK: - Methods 21| | 22| |public extension UICollectionView { 23| | /// SwifterSwift: Number of all items in all sections of collectionView. 24| | /// 25| | /// - Returns: The count of all rows in the collectionView. 26| 2| func numberOfItems() -> Int { 27| 2| var section = 0 28| 2| var itemsCount = 0 29| 4| while section < numberOfSections { 30| 2| itemsCount += numberOfItems(inSection: section) 31| 2| section += 1 32| 2| } 33| 2| return itemsCount 34| 2| } 35| | 36| | /// SwifterSwift: IndexPath for last item in section. 37| | /// 38| | /// - Parameter section: section to get last item in. 39| | /// - Returns: optional last indexPath for last item in section (if applicable). 40| 4| func indexPathForLastItem(inSection section: Int) -> IndexPath? { 41| 4| guard section >= 0 else { 42| 1| return nil 43| 3| } 44| 3| guard section < numberOfSections else { 45| 1| return nil 46| 2| } 47| 2| guard numberOfItems(inSection: section) > 0 else { 48| 1| return IndexPath(item: 0, section: section) 49| 1| } 50| 1| return IndexPath(item: numberOfItems(inSection: section) - 1, section: section) 51| 2| } 52| | 53| | /// SwifterSwift: Reload data with a completion handler. 54| | /// 55| | /// - Parameter completion: completion handler to run after reloadData finishes. 56| 1| func reloadData(_ completion: @escaping () -> Void) { 57| 1| UIView.animate(withDuration: 0, animations: { 58| 1| self.reloadData() 59| 1| }, completion: { _ in 60| 1| completion() 61| 1| }) 62| 1| } 63| | 64| | /// SwifterSwift: Dequeue reusable UICollectionViewCell using class name. 65| | /// 66| | /// - Parameters: 67| | /// - name: UICollectionViewCell type. 68| | /// - indexPath: location of cell in collectionView. 69| | /// - Returns: UICollectionViewCell object with associated class name. 70| 0| func dequeueReusableCell(withClass name: T.Type, for indexPath: IndexPath) -> T { 71| 0| guard let cell = dequeueReusableCell(withReuseIdentifier: String(describing: name), for: indexPath) as? T else { 72| 0| fatalError( 73| 0| "Couldn't find UICollectionViewCell for \(String(describing: name)), make sure the cell is registered with collection view") 74| 0| } 75| 0| return cell 76| 0| } 77| | 78| | /// SwifterSwift: Dequeue reusable UICollectionReusableView using class name. 79| | /// 80| | /// - Parameters: 81| | /// - kind: the kind of supplementary view to retrieve. This value is defined by the layout object. 82| | /// - name: UICollectionReusableView type. 83| | /// - indexPath: location of cell in collectionView. 84| | /// - Returns: UICollectionReusableView object with associated class name. 85| | func dequeueReusableSupplementaryView(ofKind kind: String, withClass name: T.Type, 86| 0| for indexPath: IndexPath) -> T { 87| 0| guard let cell = dequeueReusableSupplementaryView( 88| 0| ofKind: kind, 89| 0| withReuseIdentifier: String(describing: name), 90| 0| for: indexPath) as? T else { 91| 0| fatalError( 92| 0| "Couldn't find UICollectionReusableView for \(String(describing: name)), make sure the view is registered with collection view") 93| 0| } 94| 0| return cell 95| 0| } 96| | 97| | /// SwifterSwift: Register UICollectionReusableView using class name. 98| | /// 99| | /// - Parameters: 100| | /// - kind: the kind of supplementary view to retrieve. This value is defined by the layout object. 101| | /// - name: UICollectionReusableView type. 102| 0| func register(supplementaryViewOfKind kind: String, withClass name: T.Type) { 103| 0| register(T.self, forSupplementaryViewOfKind: kind, withReuseIdentifier: String(describing: name)) 104| 0| } 105| | 106| | /// SwifterSwift: Register UICollectionViewCell using class name. 107| | /// 108| | /// - Parameters: 109| | /// - nib: Nib file used to create the collectionView cell. 110| | /// - name: UICollectionViewCell type. 111| 0| func register(nib: UINib?, forCellWithClass name: T.Type) { 112| 0| register(nib, forCellWithReuseIdentifier: String(describing: name)) 113| 0| } 114| | 115| | /// SwifterSwift: Register UICollectionViewCell using class name. 116| | /// 117| | /// - Parameter name: UICollectionViewCell type. 118| 0| func register(cellWithClass name: T.Type) { 119| 0| register(T.self, forCellWithReuseIdentifier: String(describing: name)) 120| 0| } 121| | 122| | /// SwifterSwift: Register UICollectionReusableView using class name. 123| | /// 124| | /// - Parameters: 125| | /// - nib: Nib file used to create the reusable view. 126| | /// - kind: the kind of supplementary view to retrieve. This value is defined by the layout object. 127| | /// - name: UICollectionReusableView type. 128| | func register(nib: UINib?, forSupplementaryViewOfKind kind: String, 129| 0| withClass name: T.Type) { 130| 0| register(nib, forSupplementaryViewOfKind: kind, withReuseIdentifier: String(describing: name)) 131| 0| } 132| | 133| | /// SwifterSwift: Register UICollectionViewCell with .xib file using only its corresponding class. 134| | /// Assumes that the .xib filename and cell class has the same name. 135| | /// 136| | /// - Parameters: 137| | /// - name: UICollectionViewCell type. 138| | /// - bundleClass: Class in which the Bundle instance will be based on. 139| 0| func register(nibWithCellClass name: T.Type, at bundleClass: AnyClass? = nil) { 140| 0| let identifier = String(describing: name) 141| 0| var bundle: Bundle? 142| 0| 143| 0| if let bundleName = bundleClass { 144| 0| bundle = Bundle(for: bundleName) 145| 0| } 146| 0| 147| 0| register(UINib(nibName: identifier, bundle: bundle), forCellWithReuseIdentifier: identifier) 148| 0| } 149| | 150| | /// SwifterSwift: Safely scroll to possibly invalid IndexPath 151| | /// 152| | /// - Parameters: 153| | /// - indexPath: Target IndexPath to scroll to 154| | /// - scrollPosition: Scroll position 155| | /// - animated: Whether to animate or not 156| 4| func safeScrollToItem(at indexPath: IndexPath, at scrollPosition: UICollectionView.ScrollPosition, animated: Bool) { 157| 4| guard indexPath.item >= 0, 158| 4| indexPath.section >= 0, 159| 4| indexPath.section < numberOfSections, 160| 4| indexPath.item < numberOfItems(inSection: indexPath.section) else { 161| 2| return 162| 2| } 163| 2| scrollToItem(at: indexPath, at: scrollPosition, animated: animated) 164| 2| } 165| | 166| | /// SwifterSwift: Check whether IndexPath is valid within the CollectionView 167| | /// 168| | /// - Parameter indexPath: An IndexPath to check 169| | /// - Returns: Boolean value for valid or invalid IndexPath 170| 5| func isValidIndexPath(_ indexPath: IndexPath) -> Bool { 171| 5| return indexPath.section >= 0 && 172| 5| indexPath.item >= 0 && 173| 5| indexPath.section < numberOfSections && 174| 5| indexPath.item < numberOfItems(inSection: indexPath.section) 175| 5| } 176| |} 177| | 178| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/UIKit/UIColorExtensions.swift: 1| |// UIColorExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(UIKit) 4| |import UIKit 5| | 6| |public extension UIColor { 7| | #if !os(watchOS) 8| | /// SwifterSwift: Create a UIColor with different colors for light and dark mode. 9| | /// 10| | /// - Parameters: 11| | /// - light: Color to use in light/unspecified mode. 12| | /// - dark: Color to use in dark mode. 13| 1| convenience init(light: UIColor, dark: UIColor) { 14| 1| if #available(iOS 13.0, tvOS 13.0, *) { 15| 2| self.init(dynamicProvider: { $0.userInterfaceStyle == .dark ? dark : light }) 16| 1| } else { 17| 0| self.init(cgColor: light.cgColor) 18| 1| } 19| 1| } 20| | #endif 21| |} 22| | 23| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/UIKit/UIFontExtensions.swift: 1| |// UIFontExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(UIKit) 4| |import UIKit 5| | 6| |// MARK: - Properties 7| | 8| |public extension UIFont { 9| | /// SwifterSwift: Font as bold font 10| 1| var bold: UIFont { 11| 1| return UIFont(descriptor: fontDescriptor.withSymbolicTraits(.traitBold)!, size: 0) 12| 1| } 13| | 14| | /// SwifterSwift: Font as italic font 15| 1| var italic: UIFont { 16| 1| return UIFont(descriptor: fontDescriptor.withSymbolicTraits(.traitItalic)!, size: 0) 17| 1| } 18| | 19| | /// SwifterSwift: Font as monospaced font 20| | /// 21| | /// UIFont.preferredFont(forTextStyle: .body).monospaced 22| | /// 23| 1| var monospaced: UIFont { 24| 1| let settings = [[ 25| 1| UIFontDescriptor.FeatureKey.featureIdentifier: kNumberSpacingType, 26| 1| UIFontDescriptor.FeatureKey.typeIdentifier: kMonospacedNumbersSelector 27| 1| ]] 28| 1| 29| 1| let attributes = [UIFontDescriptor.AttributeName.featureSettings: settings] 30| 1| let newDescriptor = fontDescriptor.addingAttributes(attributes) 31| 1| return UIFont(descriptor: newDescriptor, size: 0) 32| 1| } 33| |} 34| | 35| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/UIKit/UIImageExtensions.swift: 1| |// UIImageExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(UIKit) 4| |import UIKit 5| | 6| |// MARK: - Properties 7| | 8| |public extension UIImage { 9| | /// SwifterSwift: Size in bytes of UIImage 10| 10| var bytesSize: Int { 11| 10| return jpegData(compressionQuality: 1)?.count ?? 0 12| 10| } 13| | 14| | /// SwifterSwift: Size in kilo bytes of UIImage 15| 3| var kilobytesSize: Int { 16| 3| return (jpegData(compressionQuality: 1)?.count ?? 0) / 1024 17| 3| } 18| | 19| | /// SwifterSwift: UIImage with .alwaysOriginal rendering mode. 20| 1| var original: UIImage { 21| 1| return withRenderingMode(.alwaysOriginal) 22| 1| } 23| | 24| | /// SwifterSwift: UIImage with .alwaysTemplate rendering mode. 25| 1| var template: UIImage { 26| 1| return withRenderingMode(.alwaysTemplate) 27| 1| } 28| | 29| | #if canImport(CoreImage) 30| | /// SwifterSwift: Average color for this image 31| 3| func averageColor() -> UIColor? { 32| 3| // https://stackoverflow.com/questions/26330924 33| 3| guard let ciImage = ciImage ?? CIImage(image: self) else { return nil } 34| 3| 35| 3| // CIAreaAverage returns a single-pixel image that contains the average color for a given region of an image. 36| 3| let parameters = [kCIInputImageKey: ciImage, kCIInputExtentKey: CIVector(cgRect: ciImage.extent)] 37| 3| guard let outputImage = CIFilter(name: "CIAreaAverage", parameters: parameters)?.outputImage else { 38| 0| return nil 39| 3| } 40| 3| 41| 3| // After getting the single-pixel image from the filter extract pixel's RGBA8 data 42| 3| var bitmap = [UInt8](repeating: 0, count: 4) 43| 3| let workingColorSpace: Any = cgImage?.colorSpace ?? NSNull() 44| 3| let context = CIContext(options: [.workingColorSpace: workingColorSpace]) 45| 3| context.render(outputImage, 46| 3| toBitmap: &bitmap, 47| 3| rowBytes: 4, 48| 3| bounds: CGRect(x: 0, y: 0, width: 1, height: 1), 49| 3| format: .RGBA8, 50| 3| colorSpace: nil) 51| 3| 52| 3| // Convert pixel data to UIColor 53| 3| return UIColor(red: CGFloat(bitmap[0]) / 255.0, 54| 3| green: CGFloat(bitmap[1]) / 255.0, 55| 3| blue: CGFloat(bitmap[2]) / 255.0, 56| 3| alpha: CGFloat(bitmap[3]) / 255.0) 57| 3| } 58| | #endif 59| |} 60| | 61| |// MARK: - Methods 62| | 63| |public extension UIImage { 64| | /// SwifterSwift: Compressed UIImage from original UIImage. 65| | /// 66| | /// - 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). 67| | /// - Returns: optional UIImage (if applicable). 68| 2| func compressed(quality: CGFloat = 0.5) -> UIImage? { 69| 2| guard let data = jpegData(compressionQuality: quality) else { return nil } 70| 1| return UIImage(data: data) 71| 2| } 72| | 73| | /// SwifterSwift: Compressed UIImage data from original UIImage. 74| | /// 75| | /// - 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). 76| | /// - Returns: optional Data (if applicable). 77| 2| func compressedData(quality: CGFloat = 0.5) -> Data? { 78| 2| return jpegData(compressionQuality: quality) 79| 2| } 80| | 81| | /// SwifterSwift: UIImage Cropped to CGRect. 82| | /// 83| | /// - Parameter rect: CGRect to crop UIImage to. 84| | /// - Returns: cropped UIImage 85| 6| func cropped(to rect: CGRect) -> UIImage { 86| 6| guard rect.size.width <= size.width, rect.size.height <= size.height else { return self } 87| 4| let scaledRect = rect.applying(CGAffineTransform(scaleX: scale, y: scale)) 88| 4| guard let image = cgImage?.cropping(to: scaledRect) else { return self } 89| 4| return UIImage(cgImage: image, scale: scale, orientation: imageOrientation) 90| 4| } 91| | 92| | /// SwifterSwift: UIImage scaled to height with respect to aspect ratio. 93| | /// 94| | /// - Parameters: 95| | /// - toHeight: new height. 96| | /// - opaque: flag indicating whether the bitmap is opaque. 97| | /// - Returns: optional scaled UIImage (if applicable). 98| 1| func scaled(toHeight: CGFloat, opaque: Bool = false) -> UIImage? { 99| 1| let scale = toHeight / size.height 100| 1| let newWidth = size.width * scale 101| 1| UIGraphicsBeginImageContextWithOptions(CGSize(width: newWidth, height: toHeight), opaque, self.scale) 102| 1| draw(in: CGRect(x: 0, y: 0, width: newWidth, height: toHeight)) 103| 1| let newImage = UIGraphicsGetImageFromCurrentImageContext() 104| 1| UIGraphicsEndImageContext() 105| 1| return newImage 106| 1| } 107| | 108| | /// SwifterSwift: UIImage scaled to width with respect to aspect ratio. 109| | /// 110| | /// - Parameters: 111| | /// - toWidth: new width. 112| | /// - opaque: flag indicating whether the bitmap is opaque. 113| | /// - Returns: optional scaled UIImage (if applicable). 114| 1| func scaled(toWidth: CGFloat, opaque: Bool = false) -> UIImage? { 115| 1| let scale = toWidth / size.width 116| 1| let newHeight = size.height * scale 117| 1| UIGraphicsBeginImageContextWithOptions(CGSize(width: toWidth, height: newHeight), opaque, self.scale) 118| 1| draw(in: CGRect(x: 0, y: 0, width: toWidth, height: newHeight)) 119| 1| let newImage = UIGraphicsGetImageFromCurrentImageContext() 120| 1| UIGraphicsEndImageContext() 121| 1| return newImage 122| 1| } 123| | 124| | /// SwifterSwift: Creates a copy of the receiver rotated by the given angle. 125| | /// 126| | /// // Rotate the image by 180° 127| | /// image.rotated(by: Measurement(value: 180, unit: .degrees)) 128| | /// 129| | /// - Parameter angle: The angle measurement by which to rotate the image. 130| | /// - Returns: A new image rotated by the given angle. 131| | @available(tvOS 10.0, watchOS 3.0, *) 132| 2| func rotated(by angle: Measurement) -> UIImage? { 133| 2| let radians = CGFloat(angle.converted(to: .radians).value) 134| 2| 135| 2| let destRect = CGRect(origin: .zero, size: size) 136| 2| .applying(CGAffineTransform(rotationAngle: radians)) 137| 2| let roundedDestRect = CGRect(x: destRect.origin.x.rounded(), 138| 2| y: destRect.origin.y.rounded(), 139| 2| width: destRect.width.rounded(), 140| 2| height: destRect.height.rounded()) 141| 2| 142| 2| UIGraphicsBeginImageContextWithOptions(roundedDestRect.size, false, scale) 143| 2| guard let contextRef = UIGraphicsGetCurrentContext() else { return nil } 144| 2| 145| 2| contextRef.translateBy(x: roundedDestRect.width / 2, y: roundedDestRect.height / 2) 146| 2| contextRef.rotate(by: radians) 147| 2| 148| 2| draw(in: CGRect(origin: CGPoint(x: -size.width / 2, 149| 2| y: -size.height / 2), 150| 2| size: size)) 151| 2| 152| 2| let newImage = UIGraphicsGetImageFromCurrentImageContext() 153| 2| UIGraphicsEndImageContext() 154| 2| return newImage 155| 2| } 156| | 157| | /// SwifterSwift: Creates a copy of the receiver rotated by the given angle (in radians). 158| | /// 159| | /// // Rotate the image by 180° 160| | /// image.rotated(by: .pi) 161| | /// 162| | /// - Parameter radians: The angle, in radians, by which to rotate the image. 163| | /// - Returns: A new image rotated by the given angle. 164| 2| func rotated(by radians: CGFloat) -> UIImage? { 165| 2| let destRect = CGRect(origin: .zero, size: size) 166| 2| .applying(CGAffineTransform(rotationAngle: radians)) 167| 2| let roundedDestRect = CGRect(x: destRect.origin.x.rounded(), 168| 2| y: destRect.origin.y.rounded(), 169| 2| width: destRect.width.rounded(), 170| 2| height: destRect.height.rounded()) 171| 2| 172| 2| UIGraphicsBeginImageContextWithOptions(roundedDestRect.size, false, scale) 173| 2| guard let contextRef = UIGraphicsGetCurrentContext() else { return nil } 174| 2| 175| 2| contextRef.translateBy(x: roundedDestRect.width / 2, y: roundedDestRect.height / 2) 176| 2| contextRef.rotate(by: radians) 177| 2| 178| 2| draw(in: CGRect(origin: CGPoint(x: -size.width / 2, 179| 2| y: -size.height / 2), 180| 2| size: size)) 181| 2| 182| 2| let newImage = UIGraphicsGetImageFromCurrentImageContext() 183| 2| UIGraphicsEndImageContext() 184| 2| return newImage 185| 2| } 186| | 187| | /// SwifterSwift: UIImage filled with color 188| | /// 189| | /// - Parameter color: color to fill image with. 190| | /// - Returns: UIImage filled with given color. 191| 3| func filled(withColor color: UIColor) -> UIImage { 192| 3| #if !os(watchOS) 193| 3| if #available(tvOS 10.0, *) { 194| 3| let format = UIGraphicsImageRendererFormat() 195| 3| format.scale = scale 196| 3| let renderer = UIGraphicsImageRenderer(size: size, format: format) 197| 3| return renderer.image { context in 198| 1| color.setFill() 199| 1| context.fill(CGRect(origin: .zero, size: size)) 200| 1| } 201| 3| } 202| 0| #endif 203| 0| 204| 0| UIGraphicsBeginImageContextWithOptions(size, false, scale) 205| 0| color.setFill() 206| 0| guard let context = UIGraphicsGetCurrentContext() else { return self } 207| 0| 208| 0| context.translateBy(x: 0, y: size.height) 209| 0| context.scaleBy(x: 1.0, y: -1.0) 210| 0| context.setBlendMode(CGBlendMode.normal) 211| 0| 212| 0| let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height) 213| 0| guard let mask = cgImage else { return self } 214| 0| context.clip(to: rect, mask: mask) 215| 0| context.fill(rect) 216| 0| 217| 0| let newImage = UIGraphicsGetImageFromCurrentImageContext()! 218| 0| UIGraphicsEndImageContext() 219| 0| return newImage 220| 0| } 221| | 222| | /// SwifterSwift: UIImage tinted with color 223| | /// 224| | /// - Parameters: 225| | /// - color: color to tint image with. 226| | /// - blendMode: how to blend the tint 227| | /// - Returns: UIImage tinted with given color. 228| 1| func tint(_ color: UIColor, blendMode: CGBlendMode, alpha: CGFloat = 1.0) -> UIImage { 229| 1| let drawRect = CGRect(origin: .zero, size: size) 230| 1| 231| 1| #if !os(watchOS) 232| 1| if #available(tvOS 10.0, *) { 233| 1| let format = UIGraphicsImageRendererFormat() 234| 1| format.scale = scale 235| 1| return UIGraphicsImageRenderer(size: size, format: format).image { context in 236| 1| color.setFill() 237| 1| context.fill(drawRect) 238| 1| draw(in: drawRect, blendMode: blendMode, alpha: alpha) 239| 1| } 240| 1| } 241| 0| #endif 242| 0| 243| 0| UIGraphicsBeginImageContextWithOptions(size, false, scale) 244| 0| defer { 245| 0| UIGraphicsEndImageContext() 246| 0| } 247| 0| let context = UIGraphicsGetCurrentContext() 248| 0| color.setFill() 249| 0| context?.fill(drawRect) 250| 0| draw(in: drawRect, blendMode: blendMode, alpha: alpha) 251| 0| return UIGraphicsGetImageFromCurrentImageContext()! 252| 1| } 253| | 254| | /// SwifterSwift: UImage with background color 255| | /// 256| | /// - Parameters: 257| | /// - backgroundColor: Color to use as background color 258| | /// - Returns: UIImage with a background color that is visible where alpha < 1 259| 1| func withBackgroundColor(_ backgroundColor: UIColor) -> UIImage { 260| 1| #if !os(watchOS) 261| 1| if #available(tvOS 10.0, *) { 262| 1| let format = UIGraphicsImageRendererFormat() 263| 1| format.scale = scale 264| 1| return UIGraphicsImageRenderer(size: size, format: format).image { context in 265| 1| backgroundColor.setFill() 266| 1| context.fill(context.format.bounds) 267| 1| draw(at: .zero) 268| 1| } 269| 1| } 270| 0| #endif 271| 0| 272| 0| UIGraphicsBeginImageContextWithOptions(size, false, scale) 273| 0| defer { UIGraphicsEndImageContext() } 274| 0| 275| 0| backgroundColor.setFill() 276| 0| UIRectFill(CGRect(origin: .zero, size: size)) 277| 0| draw(at: .zero) 278| 0| 279| 0| return UIGraphicsGetImageFromCurrentImageContext()! 280| 1| } 281| | 282| | /// SwifterSwift: UIImage with rounded corners 283| | /// 284| | /// - Parameters: 285| | /// - radius: corner radius (optional), resulting image will be round if unspecified 286| | /// - Returns: UIImage with all corners rounded 287| 4| func withRoundedCorners(radius: CGFloat? = nil) -> UIImage? { 288| 4| let maxRadius = min(size.width, size.height) / 2 289| 4| let cornerRadius: CGFloat 290| 4| if let radius = radius, radius > 0, radius <= maxRadius { 291| 1| cornerRadius = radius 292| 4| } else { 293| 3| cornerRadius = maxRadius 294| 4| } 295| 4| 296| 4| UIGraphicsBeginImageContextWithOptions(size, false, scale) 297| 4| 298| 4| let rect = CGRect(origin: .zero, size: size) 299| 4| UIBezierPath(roundedRect: rect, cornerRadius: cornerRadius).addClip() 300| 4| draw(in: rect) 301| 4| 302| 4| let image = UIGraphicsGetImageFromCurrentImageContext() 303| 4| UIGraphicsEndImageContext() 304| 4| return image 305| 4| } 306| | 307| | /// SwifterSwift: Base 64 encoded PNG data of the image. 308| | /// 309| | /// - returns: Base 64 encoded PNG data of the image as a String. 310| 1| func pngBase64String() -> String? { 311| 1| return pngData()?.base64EncodedString() 312| 1| } 313| | 314| | /// SwifterSwift: Base 64 encoded JPEG data of the image. 315| | /// 316| | /// - 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). 317| | /// - returns: Base 64 encoded JPEG data of the image as a String. 318| 1| func jpegBase64String(compressionQuality: CGFloat) -> String? { 319| 1| return jpegData(compressionQuality: compressionQuality)?.base64EncodedString() 320| 1| } 321| |} 322| | 323| |// MARK: - Initializers 324| | 325| |public extension UIImage { 326| | /// SwifterSwift: Create UIImage from color and size. 327| | /// 328| | /// - Parameters: 329| | /// - color: image fill color. 330| | /// - size: image size. 331| 17| convenience init(color: UIColor, size: CGSize) { 332| 17| UIGraphicsBeginImageContextWithOptions(size, false, 1) 333| 17| 334| 17| defer { 335| 17| UIGraphicsEndImageContext() 336| 17| } 337| 17| 338| 17| color.setFill() 339| 17| UIRectFill(CGRect(origin: .zero, size: size)) 340| 17| 341| 17| guard let aCgImage = UIGraphicsGetImageFromCurrentImageContext()?.cgImage else { 342| 1| self.init() 343| 1| return 344| 16| } 345| 16| 346| 16| self.init(cgImage: aCgImage) 347| 16| } 348| | 349| | /// SwifterSwift: Create a new image from a base 64 string. 350| | /// 351| | /// - Parameters: 352| | /// - base64String: a base-64 `String`, representing the image 353| | /// - 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. 354| 2| convenience init?(base64String: String, scale: CGFloat = 1.0) { 355| 2| guard let data = Data(base64Encoded: base64String) else { return nil } 356| 2| self.init(data: data, scale: scale) 357| 2| } 358| | 359| | /// SwifterSwift: Create a new image from a URL 360| | /// 361| | /// - Important: 362| | /// Use this method to convert data:// URLs to UIImage objects. 363| | /// 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. 364| | /// 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. 365| | /// - Parameters: 366| | /// - url: a `URL`, representing the image location 367| | /// - 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. 368| 3| convenience init?(url: URL, scale: CGFloat = 1.0) throws { 369| 3| let data = try Data(contentsOf: url) 370| 3| self.init(data: data, scale: scale) 371| 3| } 372| |} 373| | 374| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/UIKit/UIImageViewExtensions.swift: 1| |// UIImageViewExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(UIKit) && !os(watchOS) 4| |import UIKit 5| | 6| |// MARK: - Methods 7| | 8| |public extension UIImageView { 9| | /// SwifterSwift: Set image from a URL. 10| | /// 11| | /// - Parameters: 12| | /// - url: URL of image. 13| | /// - contentMode: imageView content mode (default is .scaleAspectFit). 14| | /// - placeHolder: optional placeholder image 15| | /// - completionHandler: optional completion handler to run when download finishs (default is nil). 16| | func download( 17| | from url: URL, 18| | contentMode: UIView.ContentMode = .scaleAspectFit, 19| | placeholder: UIImage? = nil, 20| 2| completionHandler: ((UIImage?) -> Void)? = nil) { 21| 2| image = placeholder 22| 2| self.contentMode = contentMode 23| 2| URLSession.shared.dataTask(with: url) { data, response, _ in 24| 2| guard 25| 2| let httpURLResponse = response as? HTTPURLResponse, httpURLResponse.statusCode == 200, 26| 2| let mimeType = response?.mimeType, mimeType.hasPrefix("image"), 27| 2| let data = data, 28| 2| let image = UIImage(data: data) else { 29| 1| completionHandler?(nil) 30| 1| return 31| 1| } 32| 1| DispatchQueue.main.async { [unowned self] in 33| 1| self.image = image 34| 1| completionHandler?(image) 35| 1| } 36| 1| }.resume() 37| 2| } 38| | 39| | /// SwifterSwift: Make image view blurry 40| | /// 41| | /// - Parameter style: UIBlurEffectStyle (default is .light). 42| 2| func blur(withStyle style: UIBlurEffect.Style = .light) { 43| 2| let blurEffect = UIBlurEffect(style: style) 44| 2| let blurEffectView = UIVisualEffectView(effect: blurEffect) 45| 2| blurEffectView.frame = bounds 46| 2| blurEffectView.autoresizingMask = [.flexibleWidth, .flexibleHeight] // for supporting device rotation 47| 2| addSubview(blurEffectView) 48| 2| clipsToBounds = true 49| 2| } 50| | 51| | /// SwifterSwift: Blurred version of an image view 52| | /// 53| | /// - Parameter style: UIBlurEffectStyle (default is .light). 54| | /// - Returns: blurred version of self. 55| 1| func blurred(withStyle style: UIBlurEffect.Style = .light) -> UIImageView { 56| 1| let imgView = self 57| 1| imgView.blur(withStyle: style) 58| 1| return imgView 59| 1| } 60| |} 61| | 62| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/UIKit/UILabelExtensions.swift: 1| |// UILabelExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(UIKit) && !os(watchOS) 4| |import UIKit 5| | 6| |// MARK: - Methods 7| | 8| |public extension UILabel { 9| | /// SwifterSwift: Initialize a UILabel with text 10| 1| convenience init(text: String?) { 11| 1| self.init() 12| 1| self.text = text 13| 1| } 14| | 15| | /// SwifterSwift: Initialize a UILabel with a text and font style. 16| | /// 17| | /// - Parameters: 18| | /// - text: the label's text. 19| | /// - style: the text style of the label, used to determine which font should be used. 20| 1| convenience init(text: String, style: UIFont.TextStyle) { 21| 1| self.init() 22| 1| font = UIFont.preferredFont(forTextStyle: style) 23| 1| self.text = text 24| 1| } 25| | 26| | /// SwifterSwift: Required height for a label 27| 2| var requiredHeight: CGFloat { 28| 2| let label = UILabel(frame: CGRect(x: 0, y: 0, width: frame.width, height: CGFloat.greatestFiniteMagnitude)) 29| 2| label.numberOfLines = 0 30| 2| label.lineBreakMode = NSLineBreakMode.byWordWrapping 31| 2| label.font = font 32| 2| label.text = text 33| 2| label.attributedText = attributedText 34| 2| label.sizeToFit() 35| 2| return label.frame.height 36| 2| } 37| |} 38| | 39| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/UIKit/UILayoutPriorityExtensions.swift: 1| |// UILayoutPriorityExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if os(iOS) || os(tvOS) 4| |import UIKit 5| | 6| |extension UILayoutPriority: ExpressibleByFloatLiteral, ExpressibleByIntegerLiteral { 7| | // MARK: - Initializers 8| | 9| | /// SwifterSwift: Initialize `UILayoutPriority` with a float literal 10| | /// 11| | /// constraint.priority = 0.5 12| | /// 13| | /// - Parameter value: The float value of the constraint 14| 3| public init(floatLiteral value: Float) { 15| 3| self.init(rawValue: value) 16| 3| } 17| | 18| | /// SwifterSwift: Initialize `UILayoutPriority` with an integer literal 19| | /// 20| | /// constraint.priority = 5 21| | /// 22| | /// - Parameter value: The integer value of the constraint 23| 3| public init(integerLiteral value: Int) { 24| 3| self.init(rawValue: Float(value)) 25| 3| } 26| |} 27| | 28| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/UIKit/UINavigationBarExtensions.swift: 1| |// UINavigationBarExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(UIKit) && !os(watchOS) 4| |import UIKit 5| | 6| |// MARK: - Methods 7| | 8| |public extension UINavigationBar { 9| | /// SwifterSwift: Set Navigation Bar title, title color and font. 10| | /// 11| | /// - Parameters: 12| | /// - font: title font 13| | /// - color: title text color (default is .black). 14| 2| func setTitleFont(_ font: UIFont, color: UIColor = .black) { 15| 2| var attrs = [NSAttributedString.Key: Any]() 16| 2| attrs[.font] = font 17| 2| attrs[.foregroundColor] = color 18| 2| titleTextAttributes = attrs 19| 2| } 20| | 21| | /// SwifterSwift: Make navigation bar transparent. 22| | /// 23| | /// - Parameter tint: tint color (default is .white). 24| 2| func makeTransparent(withTint tint: UIColor = .white) { 25| 2| isTranslucent = true 26| 2| backgroundColor = .clear 27| 2| barTintColor = .clear 28| 2| setBackgroundImage(UIImage(), for: .default) 29| 2| tintColor = tint 30| 2| titleTextAttributes = [.foregroundColor: tint] 31| 2| shadowImage = UIImage() 32| 2| } 33| | 34| | /// SwifterSwift: Set navigationBar background and text colors 35| | /// 36| | /// - Parameters: 37| | /// - background: backgound color 38| | /// - text: text color 39| 1| func setColors(background: UIColor, text: UIColor) { 40| 1| isTranslucent = false 41| 1| backgroundColor = background 42| 1| barTintColor = background 43| 1| setBackgroundImage(UIImage(), for: .default) 44| 1| tintColor = text 45| 1| titleTextAttributes = [.foregroundColor: text] 46| 1| } 47| |} 48| | 49| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/UIKit/UINavigationControllerExtensions.swift: 1| |// UINavigationControllerExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(UIKit) && !os(watchOS) 4| |import UIKit 5| | 6| |// MARK: - Methods 7| | 8| |public extension UINavigationController { 9| | /// SwifterSwift: Pop ViewController with completion handler. 10| | /// 11| | /// - Parameters: 12| | /// - animated: Set this value to true to animate the transition (default is true). 13| | /// - completion: optional completion handler (default is nil). 14| 1| func popViewController(animated: Bool = true, _ completion: (() -> Void)? = nil) { 15| 1| // https://github.com/cotkjaer/UserInterface/blob/master/UserInterface/UIViewController.swift 16| 1| CATransaction.begin() 17| 1| CATransaction.setCompletionBlock(completion) 18| 1| popViewController(animated: animated) 19| 1| CATransaction.commit() 20| 1| } 21| | 22| | /// SwifterSwift: Push ViewController with completion handler. 23| | /// 24| | /// - Parameters: 25| | /// - viewController: viewController to push. 26| | /// - completion: optional completion handler (default is nil). 27| 1| func pushViewController(_ viewController: UIViewController, completion: (() -> Void)? = nil) { 28| 1| // https://github.com/cotkjaer/UserInterface/blob/master/UserInterface/UIViewController.swift 29| 1| CATransaction.begin() 30| 1| CATransaction.setCompletionBlock(completion) 31| 1| pushViewController(viewController, animated: true) 32| 1| CATransaction.commit() 33| 1| } 34| | 35| | /// SwifterSwift: Make navigation controller's navigation bar transparent. 36| | /// 37| | /// - Parameter tint: tint color (default is .white). 38| 1| func makeTransparent(withTint tint: UIColor = .white) { 39| 1| navigationBar.setBackgroundImage(UIImage(), for: .default) 40| 1| navigationBar.shadowImage = UIImage() 41| 1| navigationBar.isTranslucent = true 42| 1| navigationBar.tintColor = tint 43| 1| navigationBar.titleTextAttributes = [.foregroundColor: tint] 44| 1| } 45| |} 46| | 47| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/UIKit/UINavigationItemExtensions.swift: 1| |// UINavigationItemExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(UIKit) && !os(watchOS) 4| |import UIKit 5| | 6| |// MARK: - Methods 7| | 8| |public extension UINavigationItem { 9| | /// SwifterSwift: Replace title label with an image in navigation item. 10| | /// 11| | /// - Parameter image: UIImage to replace title with. 12| 1| func replaceTitle(with image: UIImage) { 13| 1| let logoImageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 100, height: 30)) 14| 1| logoImageView.contentMode = .scaleAspectFit 15| 1| logoImageView.image = image 16| 1| titleView = logoImageView 17| 1| } 18| |} 19| | 20| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/UIKit/UIScrollViewExtensions.swift: 1| |// UIScrollViewExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(UIKit) && !os(watchOS) 4| |import UIKit 5| | 6| |// MARK: - Methods 7| | 8| |public extension UIScrollView { 9| | /// SwifterSwift: Takes a snapshot of an entire ScrollView 10| | /// 11| | /// AnySubclassOfUIScroolView().snapshot 12| | /// UITableView().snapshot 13| | /// 14| | /// - Returns: Snapshot as UIimage for rendered ScrollView 15| 2| var snapshot: UIImage? { 16| 2| // Original Source: https://gist.github.com/thestoics/1204051 17| 2| UIGraphicsBeginImageContextWithOptions(contentSize, false, 0) 18| 2| defer { 19| 2| UIGraphicsEndImageContext() 20| 2| } 21| 2| guard let context = UIGraphicsGetCurrentContext() else { return nil } 22| 1| let previousFrame = frame 23| 1| frame = CGRect(origin: frame.origin, size: contentSize) 24| 1| layer.render(in: context) 25| 1| frame = previousFrame 26| 1| return UIGraphicsGetImageFromCurrentImageContext() 27| 2| } 28| | 29| | /// SwifterSwift: The currently visible region of the scroll view. 30| 3| var visibleRect: CGRect { 31| 3| let contentWidth = contentSize.width - contentOffset.x 32| 3| let contentHeight = contentSize.height - contentOffset.y 33| 3| return CGRect(origin: contentOffset, 34| 3| size: CGSize(width: min(min(bounds.size.width, contentSize.width), contentWidth), 35| 3| height: min(min(bounds.size.height, contentSize.height), contentHeight))) 36| 3| } 37| |} 38| | 39| |public extension UIScrollView { 40| | /// SwifterSwift: Scroll to the top-most content offset. 41| | /// - Parameter animated: `true` to animate the transition at a constant velocity to the new offset, `false` to make the transition immediate. 42| 1| func scrollToTop(animated: Bool = true) { 43| 1| setContentOffset(CGPoint(x: contentOffset.x, y: -contentInset.top), animated: animated) 44| 1| } 45| | 46| | /// SwifterSwift: Scroll to the left-most content offset. 47| | /// - Parameter animated: `true` to animate the transition at a constant velocity to the new offset, `false` to make the transition immediate. 48| 1| func scrollToLeft(animated: Bool = true) { 49| 1| setContentOffset(CGPoint(x: -contentInset.left, y: contentOffset.y), animated: animated) 50| 1| } 51| | 52| | /// SwifterSwift: Scroll to the bottom-most content offset. 53| | /// - Parameter animated: `true` to animate the transition at a constant velocity to the new offset, `false` to make the transition immediate. 54| 1| func scrollToBottom(animated: Bool = true) { 55| 1| setContentOffset( 56| 1| CGPoint(x: contentOffset.x, y: max(0, contentSize.height - bounds.height) + contentInset.bottom), 57| 1| animated: animated) 58| 1| } 59| | 60| | /// SwifterSwift: Scroll to the right-most content offset. 61| | /// - Parameter animated: `true` to animate the transition at a constant velocity to the new offset, `false` to make the transition immediate. 62| 1| func scrollToRight(animated: Bool = true) { 63| 1| setContentOffset( 64| 1| CGPoint(x: max(0, contentSize.width - bounds.width) + contentInset.right, y: contentOffset.y), 65| 1| animated: animated) 66| 1| } 67| | 68| | /// SwifterSwift: Scroll up one page of the scroll view. 69| | /// If `isPagingEnabled` is `true`, the previous page location is used. 70| | /// - Parameter animated: `true` to animate the transition at a constant velocity to the new offset, `false` to make the transition immediate. 71| 4| func scrollUp(animated: Bool = true) { 72| 4| let minY = -contentInset.top 73| 4| var y = max(minY, contentOffset.y - bounds.height) 74| 4| #if !os(tvOS) 75| 4| if isPagingEnabled, 76| 4| bounds.height != 0 { 77| 4| let page = max(0, ((y + contentInset.top) / bounds.height).rounded(.down)) 78| 4| y = max(minY, page * bounds.height - contentInset.top) 79| 4| } 80| 4| #endif 81| 4| setContentOffset(CGPoint(x: contentOffset.x, y: y), animated: animated) 82| 4| } 83| | 84| | /// SwifterSwift: Scroll left one page of the scroll view. 85| | /// If `isPagingEnabled` is `true`, the previous page location is used. 86| | /// - Parameter animated: `true` to animate the transition at a constant velocity to the new offset, `false` to make the transition immediate. 87| 4| func scrollLeft(animated: Bool = true) { 88| 4| let minX = -contentInset.left 89| 4| var x = max(minX, contentOffset.x - bounds.width) 90| 4| #if !os(tvOS) 91| 4| if isPagingEnabled, 92| 4| bounds.width != 0 { 93| 4| let page = ((x + contentInset.left) / bounds.width).rounded(.down) 94| 4| x = max(minX, page * bounds.width - contentInset.left) 95| 4| } 96| 4| #endif 97| 4| setContentOffset(CGPoint(x: x, y: contentOffset.y), animated: animated) 98| 4| } 99| | 100| | /// SwifterSwift: Scroll down one page of the scroll view. 101| | /// If `isPagingEnabled` is `true`, the next page location is used. 102| | /// - Parameter animated: `true` to animate the transition at a constant velocity to the new offset, `false` to make the transition immediate. 103| 3| func scrollDown(animated: Bool = true) { 104| 3| let maxY = max(0, contentSize.height - bounds.height) + contentInset.bottom 105| 3| var y = min(maxY, contentOffset.y + bounds.height) 106| 3| #if !os(tvOS) 107| 3| if isPagingEnabled, 108| 3| bounds.height != 0 { 109| 3| let page = ((y + contentInset.top) / bounds.height).rounded(.down) 110| 3| y = min(maxY, page * bounds.height - contentInset.top) 111| 3| } 112| 3| #endif 113| 3| setContentOffset(CGPoint(x: contentOffset.x, y: y), animated: animated) 114| 3| } 115| | 116| | /// SwifterSwift: Scroll right one page of the scroll view. 117| | /// If `isPagingEnabled` is `true`, the next page location is used. 118| | /// - Parameter animated: `true` to animate the transition at a constant velocity to the new offset, `false` to make the transition immediate. 119| 3| func scrollRight(animated: Bool = true) { 120| 3| let maxX = max(0, contentSize.width - bounds.width) + contentInset.right 121| 3| var x = min(maxX, contentOffset.x + bounds.width) 122| 3| #if !os(tvOS) 123| 3| if isPagingEnabled, 124| 3| bounds.width != 0 { 125| 3| let page = ((x + contentInset.left) / bounds.width).rounded(.down) 126| 3| x = min(maxX, page * bounds.width - contentInset.left) 127| 3| } 128| 3| #endif 129| 3| setContentOffset(CGPoint(x: x, y: contentOffset.y), animated: animated) 130| 3| } 131| |} 132| | 133| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/UIKit/UISegmentedControlExtensions.swift: 1| |// UISegmentedControlExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(UIKit) && !os(watchOS) 4| |import UIKit 5| | 6| |// MARK: - Properties 7| | 8| |public extension UISegmentedControl { 9| | /// SwifterSwift: Segments titles. 10| | var segmentTitles: [String] { 11| 2| get { 12| 2| let range = 0..(withClass name: T.Type) -> T? { 21| 0| return instantiateViewController(withIdentifier: String(describing: name)) as? T 22| 0| } 23| |} 24| | 25| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/UIKit/UITabBarExtensions.swift: 1| |// UITabBarExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(UIKit) && !os(watchOS) 4| |import UIKit 5| | 6| |// MARK: - Methods 7| | 8| |public extension UITabBar { 9| | /// SwifterSwift: Set tabBar colors. 10| | /// 11| | /// - Parameters: 12| | /// - background: background color. 13| | /// - selectedBackground: background color for selected tab. 14| | /// - item: icon tint color for items. 15| | /// - selectedItem: icon tint color for item. 16| | func setColors( 17| | background: UIColor? = nil, 18| | selectedBackground: UIColor? = nil, 19| | item: UIColor? = nil, 20| 3| selectedItem: UIColor? = nil) { 21| 3| // background 22| 3| barTintColor = background ?? barTintColor 23| 3| 24| 3| // selectedItem 25| 3| tintColor = selectedItem ?? tintColor 26| 3| // shadowImage = UIImage() 27| 3| backgroundImage = UIImage() 28| 3| isTranslucent = false 29| 3| 30| 3| // selectedBackgoundColor 31| 3| guard let barItems = items else { 32| 2| return 33| 2| } 34| 1| 35| 1| if let selectedbg = selectedBackground { 36| 1| let rect = CGSize(width: frame.width / CGFloat(barItems.count), height: frame.height) 37| 1| selectionIndicatorImage = { (color: UIColor, size: CGSize) -> UIImage in 38| 1| UIGraphicsBeginImageContextWithOptions(size, false, 1) 39| 1| color.setFill() 40| 1| UIRectFill(CGRect(x: 0, y: 0, width: size.width, height: size.height)) 41| 1| guard let image = UIGraphicsGetImageFromCurrentImageContext() else { return UIImage() } 42| 1| UIGraphicsEndImageContext() 43| 1| guard let aCgImage = image.cgImage else { return UIImage() } 44| 1| return UIImage(cgImage: aCgImage) 45| 1| }(selectedbg, rect) 46| 1| } 47| 1| 48| 1| if let itemColor = item { 49| 2| for barItem in barItems as [UITabBarItem] { 50| 2| // item 51| 2| guard let image = barItem.image else { continue } 52| 1| 53| 1| barItem.image = { (image: UIImage, color: UIColor) -> UIImage in 54| 1| UIGraphicsBeginImageContextWithOptions(image.size, false, image.scale) 55| 1| color.setFill() 56| 1| guard let context = UIGraphicsGetCurrentContext() else { 57| 0| return image 58| 1| } 59| 1| 60| 1| context.translateBy(x: 0, y: image.size.height) 61| 1| context.scaleBy(x: 1.0, y: -1.0) 62| 1| context.setBlendMode(CGBlendMode.normal) 63| 1| 64| 1| let rect = CGRect(x: 0, y: 0, width: image.size.width, height: image.size.height) 65| 1| guard let mask = image.cgImage else { return image } 66| 1| context.clip(to: rect, mask: mask) 67| 1| context.fill(rect) 68| 1| 69| 1| let newImage = UIGraphicsGetImageFromCurrentImageContext()! 70| 1| UIGraphicsEndImageContext() 71| 1| return newImage 72| 1| }(image, itemColor).withRenderingMode(.alwaysOriginal) 73| 1| 74| 1| barItem.setTitleTextAttributes([.foregroundColor: itemColor], for: .normal) 75| 1| if let selected = selectedItem { 76| 1| barItem.setTitleTextAttributes([.foregroundColor: selected], for: .selected) 77| 1| } 78| 1| } 79| 1| } 80| 1| } 81| |} 82| | 83| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/UIKit/UITableViewExtensions.swift: 1| |// UITableViewExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(UIKit) && !os(watchOS) 4| |import UIKit 5| | 6| |// MARK: - Properties 7| | 8| |public extension UITableView { 9| | /// SwifterSwift: Index path of last row in tableView. 10| 4| var indexPathForLastRow: IndexPath? { 11| 4| guard let lastSection = lastSection else { return nil } 12| 3| return indexPathForLastRow(inSection: lastSection) 13| 4| } 14| | 15| | /// SwifterSwift: Index of last section in tableView. 16| 6| var lastSection: Int? { 17| 6| return numberOfSections > 0 ? numberOfSections - 1 : nil 18| 6| } 19| |} 20| | 21| |// MARK: - Methods 22| | 23| |public extension UITableView { 24| | /// SwifterSwift: Number of all rows in all sections of tableView. 25| | /// 26| | /// - Returns: The count of all rows in the tableView. 27| 2| func numberOfRows() -> Int { 28| 2| var section = 0 29| 2| var rowCount = 0 30| 4| while section < numberOfSections { 31| 2| rowCount += numberOfRows(inSection: section) 32| 2| section += 1 33| 2| } 34| 2| return rowCount 35| 2| } 36| | 37| | /// SwifterSwift: IndexPath for last row in section. 38| | /// 39| | /// - Parameter section: section to get last row in. 40| | /// - Returns: optional last indexPath for last row in section (if applicable). 41| 8| func indexPathForLastRow(inSection section: Int) -> IndexPath? { 42| 8| guard numberOfSections > 0, section >= 0 else { return nil } 43| 5| guard numberOfRows(inSection: section) > 0 else { 44| 1| return IndexPath(row: 0, section: section) 45| 4| } 46| 4| return IndexPath(row: numberOfRows(inSection: section) - 1, section: section) 47| 5| } 48| | 49| | /// SwifterSwift: Reload data with a completion handler. 50| | /// 51| | /// - Parameter completion: completion handler to run after reloadData finishes. 52| 1| func reloadData(_ completion: @escaping () -> Void) { 53| 1| UIView.animate(withDuration: 0, animations: { 54| 1| self.reloadData() 55| 1| }, completion: { _ in 56| 1| completion() 57| 1| }) 58| 1| } 59| | 60| | /// SwifterSwift: Remove TableFooterView. 61| 1| func removeTableFooterView() { 62| 1| tableFooterView = nil 63| 1| } 64| | 65| | /// SwifterSwift: Remove TableHeaderView. 66| 1| func removeTableHeaderView() { 67| 1| tableHeaderView = nil 68| 1| } 69| | 70| | /// SwifterSwift: Dequeue reusable UITableViewCell using class name 71| | /// 72| | /// - Parameter name: UITableViewCell type 73| | /// - Returns: UITableViewCell object with associated class name. 74| 2| func dequeueReusableCell(withClass name: T.Type) -> T { 75| 2| guard let cell = dequeueReusableCell(withIdentifier: String(describing: name)) as? T else { 76| 0| fatalError( 77| 0| "Couldn't find UITableViewCell for \(String(describing: name)), make sure the cell is registered with table view") 78| 2| } 79| 2| return cell 80| 2| } 81| | 82| | /// SwifterSwift: Dequeue reusable UITableViewCell using class name for indexPath 83| | /// 84| | /// - Parameters: 85| | /// - name: UITableViewCell type. 86| | /// - indexPath: location of cell in tableView. 87| | /// - Returns: UITableViewCell object with associated class name. 88| 1| func dequeueReusableCell(withClass name: T.Type, for indexPath: IndexPath) -> T { 89| 1| guard let cell = dequeueReusableCell(withIdentifier: String(describing: name), for: indexPath) as? T else { 90| 0| fatalError( 91| 0| "Couldn't find UITableViewCell for \(String(describing: name)), make sure the cell is registered with table view") 92| 1| } 93| 1| return cell 94| 1| } 95| | 96| | /// SwifterSwift: Dequeue reusable UITableViewHeaderFooterView using class name 97| | /// 98| | /// - Parameter name: UITableViewHeaderFooterView type 99| | /// - Returns: UITableViewHeaderFooterView object with associated class name. 100| 2| func dequeueReusableHeaderFooterView(withClass name: T.Type) -> T { 101| 2| guard let headerFooterView = dequeueReusableHeaderFooterView(withIdentifier: String(describing: name)) as? T else { 102| 0| fatalError( 103| 0| "Couldn't find UITableViewHeaderFooterView for \(String(describing: name)), make sure the view is registered with table view") 104| 2| } 105| 2| return headerFooterView 106| 2| } 107| | 108| | /// SwifterSwift: Register UITableViewHeaderFooterView using class name 109| | /// 110| | /// - Parameters: 111| | /// - nib: Nib file used to create the header or footer view. 112| | /// - name: UITableViewHeaderFooterView type. 113| 0| func register(nib: UINib?, withHeaderFooterViewClass name: T.Type) { 114| 0| register(nib, forHeaderFooterViewReuseIdentifier: String(describing: name)) 115| 0| } 116| | 117| | /// SwifterSwift: Register UITableViewHeaderFooterView using class name 118| | /// 119| | /// - Parameter name: UITableViewHeaderFooterView type 120| 1| func register(headerFooterViewClassWith name: T.Type) { 121| 1| register(T.self, forHeaderFooterViewReuseIdentifier: String(describing: name)) 122| 1| } 123| | 124| | /// SwifterSwift: Register UITableViewCell using class name 125| | /// 126| | /// - Parameter name: UITableViewCell type 127| 1| func register(cellWithClass name: T.Type) { 128| 1| register(T.self, forCellReuseIdentifier: String(describing: name)) 129| 1| } 130| | 131| | /// SwifterSwift: Register UITableViewCell using class name 132| | /// 133| | /// - Parameters: 134| | /// - nib: Nib file used to create the tableView cell. 135| | /// - name: UITableViewCell type. 136| 0| func register(nib: UINib?, withCellClass name: T.Type) { 137| 0| register(nib, forCellReuseIdentifier: String(describing: name)) 138| 0| } 139| | 140| | /// SwifterSwift: Register UITableViewCell with .xib file using only its corresponding class. 141| | /// Assumes that the .xib filename and cell class has the same name. 142| | /// 143| | /// - Parameters: 144| | /// - name: UITableViewCell type. 145| | /// - bundleClass: Class in which the Bundle instance will be based on. 146| 0| func register(nibWithCellClass name: T.Type, at bundleClass: AnyClass? = nil) { 147| 0| let identifier = String(describing: name) 148| 0| var bundle: Bundle? 149| 0| 150| 0| if let bundleName = bundleClass { 151| 0| bundle = Bundle(for: bundleName) 152| 0| } 153| 0| 154| 0| register(UINib(nibName: identifier, bundle: bundle), forCellReuseIdentifier: identifier) 155| 0| } 156| | 157| | /// SwifterSwift: Check whether IndexPath is valid within the tableView 158| | /// 159| | /// - Parameter indexPath: An IndexPath to check 160| | /// - Returns: Boolean value for valid or invalid IndexPath 161| 3| func isValidIndexPath(_ indexPath: IndexPath) -> Bool { 162| 3| return indexPath.section >= 0 && 163| 3| indexPath.row >= 0 && 164| 3| indexPath.section < numberOfSections && 165| 3| indexPath.row < numberOfRows(inSection: indexPath.section) 166| 3| } 167| | 168| | /// SwifterSwift: Safely scroll to possibly invalid IndexPath 169| | /// 170| | /// - Parameters: 171| | /// - indexPath: Target IndexPath to scroll to 172| | /// - scrollPosition: Scroll position 173| | /// - animated: Whether to animate or not 174| 3| func safeScrollToRow(at indexPath: IndexPath, at scrollPosition: UITableView.ScrollPosition, animated: Bool) { 175| 3| guard indexPath.section < numberOfSections else { return } 176| 2| guard indexPath.row < numberOfRows(inSection: indexPath.section) else { return } 177| 2| scrollToRow(at: indexPath, at: scrollPosition, animated: animated) 178| 2| } 179| |} 180| | 181| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/UIKit/UITextFieldExtensions.swift: 1| |// UITextFieldExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(UIKit) && !os(watchOS) 4| |import UIKit 5| | 6| |// MARK: - Enums 7| | 8| |public extension UITextField { 9| | /// SwifterSwift: UITextField text type. 10| | /// 11| | /// - emailAddress: UITextField is used to enter email addresses. 12| | /// - password: UITextField is used to enter passwords. 13| | /// - generic: UITextField is used to enter generic text. 14| | enum TextType { 15| | /// SwifterSwift: UITextField is used to enter email addresses. 16| | case emailAddress 17| | 18| | /// SwifterSwift: UITextField is used to enter passwords. 19| | case password 20| | 21| | /// SwifterSwift: UITextField is used to enter generic text. 22| | case generic 23| | } 24| |} 25| | 26| |// MARK: - Properties 27| | 28| |public extension UITextField { 29| | /// SwifterSwift: Set textField for common text types. 30| | var textType: TextType { 31| 3| get { 32| 3| if keyboardType == .emailAddress { 33| 1| return .emailAddress 34| 2| } else if isSecureTextEntry { 35| 1| return .password 36| 1| } 37| 1| return .generic 38| 2| } 39| 3| set { 40| 3| switch newValue { 41| 3| case .emailAddress: 42| 1| keyboardType = .emailAddress 43| 1| autocorrectionType = .no 44| 1| autocapitalizationType = .none 45| 1| isSecureTextEntry = false 46| 1| placeholder = "Email Address" 47| 3| 48| 3| case .password: 49| 1| keyboardType = .asciiCapable 50| 1| autocorrectionType = .no 51| 1| autocapitalizationType = .none 52| 1| isSecureTextEntry = true 53| 1| placeholder = "Password" 54| 3| 55| 3| case .generic: 56| 1| isSecureTextEntry = false 57| 3| } 58| 3| } 59| | } 60| | 61| | /// SwifterSwift: Check if text field is empty. 62| 3| var isEmpty: Bool { 63| 3| return text?.isEmpty == true 64| 3| } 65| | 66| | /// SwifterSwift: Return text with no spaces or new lines in beginning and end. 67| 2| var trimmedText: String? { 68| 2| return text?.trimmingCharacters(in: .whitespacesAndNewlines) 69| 2| } 70| | 71| | /// SwifterSwift: Check if textFields text is a valid email format. 72| | /// 73| | /// textField.text = "john@doe.com" 74| | /// textField.hasValidEmail -> true 75| | /// 76| | /// textField.text = "swifterswift" 77| | /// textField.hasValidEmail -> false 78| | /// 79| 3| var hasValidEmail: Bool { 80| 3| // http://stackoverflow.com/questions/25471114/how-to-validate-an-e-mail-address-in-swift 81| 3| return text!.range(of: "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}", 82| 3| options: String.CompareOptions.regularExpression, 83| 3| range: nil, locale: nil) != nil 84| 3| } 85| | 86| | /// SwifterSwift: Left view tint color. 87| | @IBInspectable var leftViewTintColor: UIColor? { 88| 4| get { 89| 4| guard let iconView = leftView as? UIImageView else { return nil } 90| 2| return iconView.tintColor 91| 4| } 92| 2| set { 93| 2| guard let iconView = leftView as? UIImageView else { return } 94| 1| iconView.image = iconView.image?.withRenderingMode(.alwaysTemplate) 95| 1| iconView.tintColor = newValue 96| 1| } 97| | } 98| | 99| | /// SwifterSwift: Right view tint color. 100| | @IBInspectable var rightViewTintColor: UIColor? { 101| 4| get { 102| 4| guard let iconView = rightView as? UIImageView else { return nil } 103| 2| return iconView.tintColor 104| 4| } 105| 2| set { 106| 2| guard let iconView = rightView as? UIImageView else { return } 107| 1| iconView.image = iconView.image?.withRenderingMode(.alwaysTemplate) 108| 1| iconView.tintColor = newValue 109| 1| } 110| | } 111| |} 112| | 113| |// MARK: - Methods 114| | 115| |public extension UITextField { 116| | /// SwifterSwift: Clear text. 117| 1| func clear() { 118| 1| text = "" 119| 1| attributedText = NSAttributedString(string: "") 120| 1| } 121| | 122| | /// SwifterSwift: Set placeholder text color. 123| | /// 124| | /// - Parameter color: placeholder text color. 125| 2| func setPlaceHolderTextColor(_ color: UIColor) { 126| 2| guard let holder = placeholder, !holder.isEmpty else { return } 127| 1| attributedPlaceholder = NSAttributedString(string: holder, attributes: [.foregroundColor: color]) 128| 1| } 129| | 130| | /// SwifterSwift: Add padding to the left of the textfield rect. 131| | /// 132| | /// - Parameter padding: amount of padding to apply to the left of the textfield rect. 133| 1| func addPaddingLeft(_ padding: CGFloat) { 134| 1| leftView = UIView(frame: CGRect(x: 0, y: 0, width: padding, height: frame.height)) 135| 1| leftViewMode = .always 136| 1| } 137| | 138| | /// SwifterSwift: Add padding to the right of the textfield rect. 139| | /// 140| | /// - Parameter padding: amount of padding to apply to the right of the textfield rect. 141| 1| func addPaddingRight(_ padding: CGFloat) { 142| 1| rightView = UIView(frame: CGRect(x: 0, y: 0, width: padding, height: frame.height)) 143| 1| rightViewMode = .always 144| 1| } 145| | 146| | /// SwifterSwift: Add padding to the left of the textfield rect. 147| | /// 148| | /// - Parameters: 149| | /// - image: left image 150| | /// - padding: amount of padding between icon and the left of textfield 151| 1| func addPaddingLeftIcon(_ image: UIImage, padding: CGFloat) { 152| 1| let iconView = UIView(frame: CGRect(x: 0, y: 0, width: image.size.width + padding, height: image.size.height)) 153| 1| let imageView = UIImageView(image: image) 154| 1| imageView.frame = iconView.bounds 155| 1| imageView.contentMode = .center 156| 1| iconView.addSubview(imageView) 157| 1| leftView = iconView 158| 1| leftViewMode = .always 159| 1| } 160| | 161| | /// SwifterSwift: Add padding to the right of the textfield rect. 162| | /// 163| | /// - Parameters: 164| | /// - image: right image 165| | /// - padding: amount of padding between icon and the right of textfield 166| 1| func addPaddingRightIcon(_ image: UIImage, padding: CGFloat) { 167| 1| let iconView = UIView(frame: CGRect(x: 0, y: 0, width: image.size.width + padding, height: image.size.height)) 168| 1| let imageView = UIImageView(image: image) 169| 1| imageView.frame = iconView.bounds 170| 1| imageView.contentMode = .center 171| 1| iconView.addSubview(imageView) 172| 1| rightView = iconView 173| 1| rightViewMode = .always 174| 1| } 175| |} 176| | 177| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/UIKit/UITextViewExtensions.swift: 1| |// UITextViewExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(UIKit) && !os(watchOS) 4| |import UIKit 5| | 6| |// MARK: - Methods 7| | 8| |public extension UITextView { 9| | /// SwifterSwift: Clear text. 10| 1| func clear() { 11| 1| text = "" 12| 1| attributedText = NSAttributedString(string: "") 13| 1| } 14| | 15| | /// SwifterSwift: Scroll to the bottom of text view 16| 1| func scrollToBottom() { 17| 1| let range = NSRange(location: (text as NSString).length - 1, length: 1) 18| 1| scrollRangeToVisible(range) 19| 1| } 20| | 21| | /// SwifterSwift: Scroll to the top of text view 22| 1| func scrollToTop() { 23| 1| let range = NSRange(location: 0, length: 1) 24| 1| scrollRangeToVisible(range) 25| 1| } 26| | 27| | /// SwifterSwift: Wrap to the content (Text / Attributed Text). 28| 1| func wrapToContent() { 29| 1| contentInset = .zero 30| 1| scrollIndicatorInsets = .zero 31| 1| contentOffset = .zero 32| 1| textContainerInset = .zero 33| 1| textContainer.lineFragmentPadding = 0 34| 1| sizeToFit() 35| 1| } 36| |} 37| | 38| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/UIKit/UIViewControllerExtensions.swift: 1| |// UIViewControllerExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(UIKit) && !os(watchOS) 4| |import UIKit 5| | 6| |// MARK: - Properties 7| | 8| |public extension UIViewController { 9| | /// SwifterSwift: Check if ViewController is onscreen and not hidden. 10| 1| var isVisible: Bool { 11| 1| // http://stackoverflow.com/questions/2777438/how-to-tell-if-uiviewcontrollers-view-is-visible 12| 1| return isViewLoaded && view.window != nil 13| 1| } 14| |} 15| | 16| |// MARK: - Methods 17| | 18| |public extension UIViewController { 19| | /// SwifterSwift: Instantiate UIViewController from storyboard 20| | /// 21| | /// - Parameters: 22| | /// - storyboard: Name of the storyboard where the UIViewController is located 23| | /// - bundle: Bundle in which storyboard is located 24| | /// - identifier: UIViewController's storyboard identifier 25| | /// - Returns: Custom UIViewController instantiated from storyboard 26| 2| class func instantiate(from storyboard: String = "Main", bundle: Bundle? = nil, identifier: String? = nil) -> Self { 27| 2| let viewControllerIdentifier = identifier ?? String(describing: self) 28| 2| let storyboard = UIStoryboard(name: storyboard, bundle: bundle) 29| 2| guard let viewController = storyboard 30| 2| .instantiateViewController(withIdentifier: viewControllerIdentifier) as? Self else { 31| 0| preconditionFailure( 32| 0| "Unable to instantiate view controller with identifier \(viewControllerIdentifier) as type \(type(of: self))") 33| 2| } 34| 2| return viewController 35| 2| } 36| | 37| | /// SwifterSwift: Assign as listener to notification. 38| | /// 39| | /// - Parameters: 40| | /// - name: notification name. 41| | /// - selector: selector to run with notified. 42| 3| func addNotificationObserver(name: Notification.Name, selector: Selector) { 43| 3| NotificationCenter.default.addObserver(self, selector: selector, name: name, object: nil) 44| 3| } 45| | 46| | /// SwifterSwift: Unassign as listener to notification. 47| | /// 48| | /// - Parameter name: notification name. 49| 1| func removeNotificationObserver(name: Notification.Name) { 50| 1| NotificationCenter.default.removeObserver(self, name: name, object: nil) 51| 1| } 52| | 53| | /// SwifterSwift: Unassign as listener from all notifications. 54| 1| func removeNotificationsObserver() { 55| 1| NotificationCenter.default.removeObserver(self) 56| 1| } 57| | 58| | /// SwifterSwift: Helper method to display an alert on any UIViewController subclass. Uses UIAlertController to show an alert 59| | /// 60| | /// - Parameters: 61| | /// - title: title of the alert 62| | /// - message: message/body of the alert 63| | /// - buttonTitles: (Optional)list of button titles for the alert. Default button i.e "OK" will be shown if this paramter is nil 64| | /// - highlightedButtonIndex: (Optional) index of the button from buttonTitles that should be highlighted. If this parameter is nil no button will be highlighted 65| | /// - 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 66| | /// - Returns: UIAlertController object (discardable). 67| | @discardableResult 68| | func showAlert( 69| | title: String?, 70| | message: String?, 71| | buttonTitles: [String]? = nil, 72| | highlightedButtonIndex: Int? = nil, 73| 1| completion: ((Int) -> Void)? = nil) -> UIAlertController { 74| 1| let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert) 75| 1| var allButtons = buttonTitles ?? [String]() 76| 1| if allButtons.count == 0 { 77| 0| allButtons.append("OK") 78| 1| } 79| 1| 80| 2| for index in 0.. Void)? = nil) { 132| | popoverContent.modalPresentationStyle = .popover 133| | 134| | if let size = size { 135| | popoverContent.preferredContentSize = size 136| | } 137| | 138| | if let popoverPresentationVC = popoverContent.popoverPresentationController { 139| | popoverPresentationVC.sourceView = view 140| | popoverPresentationVC.sourceRect = CGRect(origin: sourcePoint, size: .zero) 141| | popoverPresentationVC.delegate = delegate 142| | } 143| | 144| | present(popoverContent, animated: animated, completion: completion) 145| | } 146| | #endif 147| |} 148| | 149| |#endif /Users/runner/work/SwifterSwift/SwifterSwift/Sources/SwifterSwift/UIKit/UIViewExtensions.swift: 1| |// UIViewExtensions.swift - Copyright 2020 SwifterSwift 2| | 3| |#if canImport(UIKit) && !os(watchOS) 4| |import UIKit 5| | 6| |// MARK: - enums 7| | 8| |public extension UIView { 9| | /// SwifterSwift: Shake directions of a view. 10| | /// 11| | /// - horizontal: Shake left and right. 12| | /// - vertical: Shake up and down. 13| | enum ShakeDirection { 14| | /// SwifterSwift: Shake left and right. 15| | case horizontal 16| | 17| | /// SwifterSwift: Shake up and down. 18| | case vertical 19| | } 20| | 21| | /// SwifterSwift: Angle units. 22| | /// 23| | /// - degrees: degrees. 24| | /// - radians: radians. 25| | enum AngleUnit { 26| | /// SwifterSwift: degrees. 27| | case degrees 28| | 29| | /// SwifterSwift: radians. 30| | case radians 31| | } 32| | 33| | /// SwifterSwift: Shake animations types. 34| | /// 35| | /// - linear: linear animation. 36| | /// - easeIn: easeIn animation. 37| | /// - easeOut: easeOut animation. 38| | /// - easeInOut: easeInOut animation. 39| | enum ShakeAnimationType { 40| | /// SwifterSwift: linear animation. 41| | case linear 42| | 43| | /// SwifterSwift: easeIn animation. 44| | case easeIn 45| | 46| | /// SwifterSwift: easeOut animation. 47| | case easeOut 48| | 49| | /// SwifterSwift: easeInOut animation. 50| | case easeInOut 51| | } 52| |} 53| | 54| |// MARK: - Properties 55| | 56| |public extension UIView { 57| | /// SwifterSwift: Border color of view; also inspectable from Storyboard. 58| | @IBInspectable var borderColor: UIColor? { 59| 2| get { 60| 2| guard let color = layer.borderColor else { return nil } 61| 1| return UIColor(cgColor: color) 62| 2| } 63| 2| set { 64| 2| guard let color = newValue else { 65| 1| layer.borderColor = nil 66| 1| return 67| 1| } 68| 1| // Fix React-Native conflict issue 69| 1| guard String(describing: type(of: color)) != "__NSCFType" else { return } 70| 1| layer.borderColor = color.cgColor 71| 1| } 72| | } 73| | 74| | /// SwifterSwift: Border width of view; also inspectable from Storyboard. 75| | @IBInspectable var borderWidth: CGFloat { 76| 1| get { 77| 1| return layer.borderWidth 78| 1| } 79| 2| set { 80| 2| layer.borderWidth = newValue 81| 2| } 82| | } 83| | 84| | /// SwifterSwift: Corner radius of view; also inspectable from Storyboard. 85| | @IBInspectable var cornerRadius: CGFloat { 86| 1| get { 87| 1| return layer.cornerRadius 88| 1| } 89| 1| set { 90| 1| layer.masksToBounds = true 91| 1| layer.cornerRadius = abs(CGFloat(Int(newValue * 100)) / 100) 92| 1| } 93| | } 94| | 95| | /// SwifterSwift: Height of view. 96| | var height: CGFloat { 97| 1| get { 98| 1| return frame.size.height 99| 1| } 100| 2| set { 101| 2| frame.size.height = newValue 102| 2| } 103| | } 104| | 105| | /// SwifterSwift: Check if view is in RTL format. 106| 1| var isRightToLeft: Bool { 107| 1| if #available(iOS 10.0, macCatalyst 13.0, tvOS 10.0, *) { 108| 1| return effectiveUserInterfaceLayoutDirection == .rightToLeft 109| 1| } else { 110| 0| return false 111| 0| } 112| 0| } 113| | 114| | /// SwifterSwift: Take screenshot of view (if applicable). 115| 2| var screenshot: UIImage? { 116| 2| UIGraphicsBeginImageContextWithOptions(layer.frame.size, false, 0) 117| 2| defer { 118| 2| UIGraphicsEndImageContext() 119| 2| } 120| 2| guard let context = UIGraphicsGetCurrentContext() else { return nil } 121| 1| layer.render(in: context) 122| 1| return UIGraphicsGetImageFromCurrentImageContext() 123| 2| } 124| | 125| | /// SwifterSwift: Shadow color of view; also inspectable from Storyboard. 126| | @IBInspectable var layerShadowColor: UIColor? { 127| 3| get { 128| 3| guard let color = layer.shadowColor else { return nil } 129| 2| return UIColor(cgColor: color) 130| 3| } 131| 1| set { 132| 1| layer.shadowColor = newValue?.cgColor 133| 1| } 134| | } 135| | 136| | /// SwifterSwift: Shadow offset of view; also inspectable from Storyboard. 137| | @IBInspectable var layerShadowOffset: CGSize { 138| 2| get { 139| 2| return layer.shadowOffset 140| 2| } 141| 1| set { 142| 1| layer.shadowOffset = newValue 143| 1| } 144| | } 145| | 146| | /// SwifterSwift: Shadow opacity of view; also inspectable from Storyboard. 147| | @IBInspectable var layerShadowOpacity: Float { 148| 2| get { 149| 2| return layer.shadowOpacity 150| 2| } 151| 1| set { 152| 1| layer.shadowOpacity = newValue 153| 1| } 154| | } 155| | 156| | /// SwifterSwift: Shadow radius of view; also inspectable from Storyboard. 157| | @IBInspectable var layerShadowRadius: CGFloat { 158| 2| get { 159| 2| return layer.shadowRadius 160| 2| } 161| 1| set { 162| 1| layer.shadowRadius = newValue 163| 1| } 164| | } 165| | 166| | /// SwifterSwift: Masks to bounds of view; also inspectable from Storyboard. 167| | @IBInspectable var masksToBounds: Bool { 168| 2| get { 169| 2| return layer.masksToBounds 170| 2| } 171| 1| set { 172| 1| layer.masksToBounds = newValue 173| 1| } 174| | } 175| | 176| | /// SwifterSwift: Size of view. 177| | var size: CGSize { 178| 1| get { 179| 1| return frame.size 180| 1| } 181| 1| set { 182| 1| width = newValue.width 183| 1| height = newValue.height 184| 1| } 185| | } 186| | 187| | /// SwifterSwift: Get view's parent view controller 188| 3| var parentViewController: UIViewController? { 189| 3| weak var parentResponder: UIResponder? = self 190| 4| while parentResponder != nil { 191| 3| parentResponder = parentResponder!.next 192| 3| if let viewController = parentResponder as? UIViewController { 193| 2| return viewController 194| 2| } 195| 1| } 196| 1| return nil 197| 3| } 198| | 199| | /// SwifterSwift: Width of view. 200| | var width: CGFloat { 201| 1| get { 202| 1| return frame.size.width 203| 1| } 204| 2| set { 205| 2| frame.size.width = newValue 206| 2| } 207| | } 208| | 209| | /// SwifterSwift: x origin of view. 210| | var x: CGFloat { 211| 1| get { 212| 1| return frame.origin.x 213| 1| } 214| 1| set { 215| 1| frame.origin.x = newValue 216| 1| } 217| | } 218| | 219| | /// SwifterSwift: y origin of view. 220| | var y: CGFloat { 221| 1| get { 222| 1| return frame.origin.y 223| 1| } 224| 1| set { 225| 1| frame.origin.y = newValue 226| 1| } 227| | } 228| |} 229| | 230| |// MARK: - Methods 231| | 232| |public extension UIView { 233| | /// SwifterSwift: Recursively find the first responder. 234| 4| func firstResponder() -> UIView? { 235| 4| var views = [UIView](arrayLiteral: self) 236| 4| var index = 0 237| 9| repeat { 238| 9| let view = views[index] 239| 9| if view.isFirstResponder { 240| 3| return view 241| 6| } 242| 6| views.append(contentsOf: view.subviews) 243| 6| index += 1 244| 6| } while index < views.count 245| 1| return nil 246| 4| } 247| | 248| | /// SwifterSwift: Set some or all corners radiuses of view. 249| | /// 250| | /// - Parameters: 251| | /// - corners: array of corners to change (example: [.bottomLeft, .topRight]). 252| | /// - radius: radius for selected corners. 253| 1| func roundCorners(_ corners: UIRectCorner, radius: CGFloat) { 254| 1| let maskPath = UIBezierPath( 255| 1| roundedRect: bounds, 256| 1| byRoundingCorners: corners, 257| 1| cornerRadii: CGSize(width: radius, height: radius)) 258| 1| 259| 1| let shape = CAShapeLayer() 260| 1| shape.path = maskPath.cgPath 261| 1| layer.mask = shape 262| 1| } 263| | 264| | /// SwifterSwift: Add shadow to view. 265| | /// 266| | /// - Note: This method only works with non-clear background color, or if the view has a `shadowPath` set. 267| | /// See parameter `opacity` for detail. 268| | /// 269| | /// - Parameters: 270| | /// - color: shadow color (default is #137992). 271| | /// - radius: shadow radius (default is 3). 272| | /// - offset: shadow offset (default is .zero). 273| | /// - opacity: shadow opacity (default is 0.5). It will also be affected by the `alpha` of `backgroundColor` 274| | func addShadow( 275| | ofColor color: UIColor = UIColor(red: 0.07, green: 0.47, blue: 0.57, alpha: 1.0), 276| | radius: CGFloat = 3, 277| | offset: CGSize = .zero, 278| 1| opacity: Float = 0.5) { 279| 1| layer.shadowColor = color.cgColor 280| 1| layer.shadowOffset = offset 281| 1| layer.shadowRadius = radius 282| 1| layer.shadowOpacity = opacity 283| 1| layer.masksToBounds = false 284| 1| } 285| | 286| | /// SwifterSwift: Add array of subviews to view. 287| | /// 288| | /// - Parameter subviews: array of subviews to add to self. 289| 2| func addSubviews(_ subviews: [UIView]) { 290| 4| subviews.forEach { addSubview($0) } 291| 2| } 292| | 293| | /// SwifterSwift: Fade in view. 294| | /// 295| | /// - Parameters: 296| | /// - duration: animation duration in seconds (default is 1 second). 297| | /// - completion: optional completion handler to run with animation finishes (default is nil) 298| 2| func fadeIn(duration: TimeInterval = 1, completion: ((Bool) -> Void)? = nil) { 299| 2| if isHidden { 300| 1| isHidden = false 301| 2| } 302| 2| UIView.animate(withDuration: duration, animations: { 303| 2| self.alpha = 1 304| 2| }, completion: completion) 305| 2| } 306| | 307| | /// SwifterSwift: Fade out view. 308| | /// 309| | /// - Parameters: 310| | /// - duration: animation duration in seconds (default is 1 second). 311| | /// - completion: optional completion handler to run with animation finishes (default is nil) 312| 2| func fadeOut(duration: TimeInterval = 1, completion: ((Bool) -> Void)? = nil) { 313| 2| if isHidden { 314| 1| isHidden = false 315| 2| } 316| 2| UIView.animate(withDuration: duration, animations: { 317| 2| self.alpha = 0 318| 2| }, completion: completion) 319| 2| } 320| | 321| | /// SwifterSwift: Load view from nib. 322| | /// 323| | /// - Parameters: 324| | /// - name: nib name. 325| | /// - bundle: bundle of nib (default is nil). 326| | /// - Returns: optional UIView (if applicable). 327| 1| class func loadFromNib(named name: String, bundle: Bundle? = nil) -> UIView? { 328| 1| return UINib(nibName: name, bundle: bundle).instantiate(withOwner: nil, options: nil)[0] as? UIView 329| 1| } 330| | 331| | /// SwifterSwift: Load view of a certain type from nib 332| | /// 333| | /// - Parameters: 334| | /// - withClass: UIView type. 335| | /// - bundle: bundle of nib (default is nil). 336| | /// - Returns: UIView 337| 1| class func loadFromNib(withClass name: T.Type, bundle: Bundle? = nil) -> T { 338| 1| let named = String(describing: name) 339| 1| guard let view = UINib(nibName: named, bundle: bundle).instantiate(withOwner: nil, options: nil)[0] as? T else { 340| 0| fatalError("First element in xib file \(named) is not of type \(named)") 341| 1| } 342| 1| return view 343| 1| } 344| | 345| | /// SwifterSwift: Remove all subviews in view. 346| 1| func removeSubviews() { 347| 2| subviews.forEach { $0.removeFromSuperview() } 348| 1| } 349| | 350| | /// SwifterSwift: Remove all gesture recognizers from view. 351| 2| func removeGestureRecognizers() { 352| 2| gestureRecognizers?.forEach(removeGestureRecognizer) 353| 2| } 354| | 355| | /// 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. 356| | /// 357| | /// - Parameter gestureRecognizers: The array of gesture recognizers to be added to the view. 358| 2| func addGestureRecognizers(_ gestureRecognizers: [UIGestureRecognizer]) { 359| 6| for recognizer in gestureRecognizers { 360| 6| addGestureRecognizer(recognizer) 361| 6| } 362| 2| } 363| | 364| | /// SwifterSwift: Detaches gesture recognizers from the receiving view. This method releases gestureRecognizers in addition to detaching them from the view. 365| | /// 366| | /// - Parameter gestureRecognizers: The array of gesture recognizers to be removed from the view. 367| 1| func removeGestureRecognizers(_ gestureRecognizers: [UIGestureRecognizer]) { 368| 3| for recognizer in gestureRecognizers { 369| 3| removeGestureRecognizer(recognizer) 370| 3| } 371| 1| } 372| | 373| | /// SwifterSwift: Rotate view by angle on relative axis. 374| | /// 375| | /// - Parameters: 376| | /// - angle: angle to rotate view by. 377| | /// - type: type of the rotation angle. 378| | /// - animated: set true to animate rotation (default is true). 379| | /// - duration: animation duration in seconds (default is 1 second). 380| | /// - completion: optional completion handler to run with animation finishes (default is nil). 381| | func rotate( 382| | byAngle angle: CGFloat, 383| | ofType type: AngleUnit, 384| | animated: Bool = false, 385| | duration: TimeInterval = 1, 386| 3| completion: ((Bool) -> Void)? = nil) { 387| 3| let angleWithType = (type == .degrees) ? .pi * angle / 180.0 : angle 388| 3| let aDuration = animated ? duration : 0 389| 3| UIView.animate(withDuration: aDuration, delay: 0, options: .curveLinear, animations: { () -> Void in 390| 3| self.transform = self.transform.rotated(by: angleWithType) 391| 3| }, completion: completion) 392| 3| } 393| | 394| | /// SwifterSwift: Rotate view to angle on fixed axis. 395| | /// 396| | /// - Parameters: 397| | /// - angle: angle to rotate view to. 398| | /// - type: type of the rotation angle. 399| | /// - animated: set true to animate rotation (default is false). 400| | /// - duration: animation duration in seconds (default is 1 second). 401| | /// - completion: optional completion handler to run with animation finishes (default is nil). 402| | func rotate( 403| | toAngle angle: CGFloat, 404| | ofType type: AngleUnit, 405| | animated: Bool = false, 406| | duration: TimeInterval = 1, 407| 3| completion: ((Bool) -> Void)? = nil) { 408| 3| let angleWithType = (type == .degrees) ? .pi * angle / 180.0 : angle 409| 3| let aDuration = animated ? duration : 0 410| 3| UIView.animate(withDuration: aDuration, animations: { 411| 3| self.transform = self.transform.concatenating(CGAffineTransform(rotationAngle: angleWithType)) 412| 3| }, completion: completion) 413| 3| } 414| | 415| | /// SwifterSwift: Scale view by offset. 416| | /// 417| | /// - Parameters: 418| | /// - offset: scale offset 419| | /// - animated: set true to animate scaling (default is false). 420| | /// - duration: animation duration in seconds (default is 1 second). 421| | /// - completion: optional completion handler to run with animation finishes (default is nil). 422| | func scale( 423| | by offset: CGPoint, 424| | animated: Bool = false, 425| | duration: TimeInterval = 1, 426| 2| completion: ((Bool) -> Void)? = nil) { 427| 2| if animated { 428| 1| UIView.animate(withDuration: duration, delay: 0, options: .curveLinear, animations: { () -> Void in 429| 1| self.transform = self.transform.scaledBy(x: offset.x, y: offset.y) 430| 1| }, completion: completion) 431| 2| } else { 432| 1| transform = transform.scaledBy(x: offset.x, y: offset.y) 433| 1| completion?(true) 434| 2| } 435| 2| } 436| | 437| | /// SwifterSwift: Shake view. 438| | /// 439| | /// - Parameters: 440| | /// - direction: shake direction (horizontal or vertical), (default is .horizontal) 441| | /// - duration: animation duration in seconds (default is 1 second). 442| | /// - animationType: shake animation type (default is .easeOut). 443| | /// - completion: optional completion handler to run with animation finishes (default is nil). 444| | func shake( 445| | direction: ShakeDirection = .horizontal, 446| | duration: TimeInterval = 1, 447| | animationType: ShakeAnimationType = .easeOut, 448| 0| completion: (() -> Void)? = nil) { 449| 0| CATransaction.begin() 450| 0| let animation: CAKeyframeAnimation 451| 0| switch direction { 452| 0| case .horizontal: 453| 0| animation = CAKeyframeAnimation(keyPath: "transform.translation.x") 454| 0| case .vertical: 455| 0| animation = CAKeyframeAnimation(keyPath: "transform.translation.y") 456| 0| } 457| 0| switch animationType { 458| 0| case .linear: 459| 0| animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear) 460| 0| case .easeIn: 461| 0| animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeIn) 462| 0| case .easeOut: 463| 0| animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeOut) 464| 0| case .easeInOut: 465| 0| animation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut) 466| 0| } 467| 0| CATransaction.setCompletionBlock(completion) 468| 0| animation.duration = duration 469| 0| animation.values = [-20.0, 20.0, -20.0, 20.0, -10.0, 10.0, -5.0, 5.0, 0.0] 470| 0| layer.add(animation, forKey: "shake") 471| 0| CATransaction.commit() 472| 0| } 473| | 474| | /// SwifterSwift: Add Visual Format constraints. 475| | /// 476| | /// - Parameters: 477| | /// - withFormat: visual Format language 478| | /// - views: array of views which will be accessed starting with index 0 (example: [v0], [v1], [v2]..) 479| 0| @available(iOS 9, *) func addConstraints(withFormat: String, views: UIView...) { 480| 0| // https://videos.letsbuildthatapp.com/ 481| 0| var viewsDictionary: [String: UIView] = [:] 482| 0| for (index, view) in views.enumerated() { 483| 0| let key = "v\(index)" 484| 0| view.translatesAutoresizingMaskIntoConstraints = false 485| 0| viewsDictionary[key] = view 486| 0| } 487| 0| addConstraints(NSLayoutConstraint.constraints( 488| 0| withVisualFormat: withFormat, 489| 0| options: NSLayoutConstraint.FormatOptions(), 490| 0| metrics: nil, 491| 0| views: viewsDictionary)) 492| 0| } 493| | 494| | /// SwifterSwift: Anchor all sides of the view into it's superview. 495| | @available(iOS 9, *) 496| 0| func fillToSuperview() { 497| 0| // https://videos.letsbuildthatapp.com/ 498| 0| translatesAutoresizingMaskIntoConstraints = false 499| 0| if let superview = superview { 500| 0| let left = leftAnchor.constraint(equalTo: superview.leftAnchor) 501| 0| let right = rightAnchor.constraint(equalTo: superview.rightAnchor) 502| 0| let top = topAnchor.constraint(equalTo: superview.topAnchor) 503| 0| let bottom = bottomAnchor.constraint(equalTo: superview.bottomAnchor) 504| 0| NSLayoutConstraint.activate([left, right, top, bottom]) 505| 0| } 506| 0| } 507| | 508| | /// SwifterSwift: Add anchors from any side of the current view into the specified anchors and returns the newly added constraints. 509| | /// 510| | /// - Parameters: 511| | /// - top: current view's top anchor will be anchored into the specified anchor 512| | /// - left: current view's left anchor will be anchored into the specified anchor 513| | /// - bottom: current view's bottom anchor will be anchored into the specified anchor 514| | /// - right: current view's right anchor will be anchored into the specified anchor 515| | /// - topConstant: current view's top anchor margin 516| | /// - leftConstant: current view's left anchor margin 517| | /// - bottomConstant: current view's bottom anchor margin 518| | /// - rightConstant: current view's right anchor margin 519| | /// - widthConstant: current view's width 520| | /// - heightConstant: current view's height 521| | /// - Returns: array of newly added constraints (if applicable). 522| | @available(iOS 9, *) 523| | @discardableResult 524| | func anchor( 525| | top: NSLayoutYAxisAnchor? = nil, 526| | left: NSLayoutXAxisAnchor? = nil, 527| | bottom: NSLayoutYAxisAnchor? = nil, 528| | right: NSLayoutXAxisAnchor? = nil, 529| | topConstant: CGFloat = 0, 530| | leftConstant: CGFloat = 0, 531| | bottomConstant: CGFloat = 0, 532| | rightConstant: CGFloat = 0, 533| | widthConstant: CGFloat = 0, 534| 6| heightConstant: CGFloat = 0) -> [NSLayoutConstraint] { 535| 6| // https://videos.letsbuildthatapp.com/ 536| 6| translatesAutoresizingMaskIntoConstraints = false 537| 6| 538| 6| var anchors = [NSLayoutConstraint]() 539| 6| 540| 6| if let top = top { 541| 1| anchors.append(topAnchor.constraint(equalTo: top, constant: topConstant)) 542| 6| } 543| 6| 544| 6| if let left = left { 545| 1| anchors.append(leftAnchor.constraint(equalTo: left, constant: leftConstant)) 546| 6| } 547| 6| 548| 6| if let bottom = bottom { 549| 1| anchors.append(bottomAnchor.constraint(equalTo: bottom, constant: -bottomConstant)) 550| 6| } 551| 6| 552| 6| if let right = right { 553| 1| anchors.append(rightAnchor.constraint(equalTo: right, constant: -rightConstant)) 554| 6| } 555| 6| 556| 6| if widthConstant > 0 { 557| 1| anchors.append(widthAnchor.constraint(equalToConstant: widthConstant)) 558| 6| } 559| 6| 560| 6| if heightConstant > 0 { 561| 1| anchors.append(heightAnchor.constraint(equalToConstant: heightConstant)) 562| 6| } 563| 6| 564| 6| anchors.forEach { $0.isActive = true } 565| 6| 566| 6| return anchors 567| 6| } 568| | 569| | /// SwifterSwift: Anchor center X into current view's superview with a constant margin value. 570| | /// 571| | /// - Parameter constant: constant of the anchor constraint (default is 0). 572| | @available(iOS 9, *) 573| 5| func anchorCenterXToSuperview(constant: CGFloat = 0) { 574| 5| // https://videos.letsbuildthatapp.com/ 575| 5| translatesAutoresizingMaskIntoConstraints = false 576| 5| if let anchor = superview?.centerXAnchor { 577| 5| centerXAnchor.constraint(equalTo: anchor, constant: constant).isActive = true 578| 5| } 579| 5| } 580| | 581| | /// SwifterSwift: Anchor center Y into current view's superview with a constant margin value. 582| | /// 583| | /// - Parameter withConstant: constant of the anchor constraint (default is 0). 584| | @available(iOS 9, *) 585| 3| func anchorCenterYToSuperview(constant: CGFloat = 0) { 586| 3| // https://videos.letsbuildthatapp.com/ 587| 3| translatesAutoresizingMaskIntoConstraints = false 588| 3| if let anchor = superview?.centerYAnchor { 589| 3| centerYAnchor.constraint(equalTo: anchor, constant: constant).isActive = true 590| 3| } 591| 3| } 592| | 593| | /// SwifterSwift: Anchor center X and Y into current view's superview 594| | @available(iOS 9, *) 595| 2| func anchorCenterSuperview() { 596| 2| // https://videos.letsbuildthatapp.com/ 597| 2| anchorCenterXToSuperview() 598| 2| anchorCenterYToSuperview() 599| 2| } 600| | 601| | /// SwifterSwift: Search all superviews until a view with the condition is found. 602| | /// 603| | /// - Parameter predicate: predicate to evaluate on superviews. 604| 23| func ancestorView(where predicate: (UIView?) -> Bool) -> UIView? { 605| 23| if predicate(superview) { 606| 8| return superview 607| 15| } 608| 15| return superview?.ancestorView(where: predicate) 609| 23| } 610| | 611| | /// SwifterSwift: Search all superviews until a view with this class is found. 612| | /// 613| | /// - Parameter name: class of the view to search. 614| 6| func ancestorView(withClass _: T.Type) -> T? { 615| 12| return ancestorView(where: { $0 is T }) as? T 616| 6| } 617| |} 618| | 619| |// MARK: - Constraints 620| | 621| |public extension UIView { 622| | /// SwifterSwift: Search constraints until we find one for the given view 623| | /// and attribute. This will enumerate ancestors since constraints are 624| | /// always added to the common ancestor. 625| | /// 626| | /// - Parameter attribute: the attribute to find 627| | /// - Parameter at: the view to find 628| | /// - Returns: matching constraint 629| 16| func findConstraint(attribute: NSLayoutConstraint.Attribute, for view: UIView) -> NSLayoutConstraint? { 630| 30| let constraint = constraints.first { 631| 30| ($0.firstAttribute == attribute && $0.firstItem as? UIView == view) || 632| 30| ($0.secondAttribute == attribute && $0.secondItem as? UIView == view) 633| 30| } 634| 16| return constraint ?? superview?.findConstraint(attribute: attribute, for: view) 635| 16| } 636| | 637| | /// SwifterSwift: First width constraint for this view 638| 2| var widthConstraint: NSLayoutConstraint? { 639| 2| findConstraint(attribute: .width, for: self) 640| 2| } 641| | 642| | /// SwifterSwift: First height constraint for this view 643| 1| var heightConstraint: NSLayoutConstraint? { 644| 1| findConstraint(attribute: .height, for: self) 645| 1| } 646| | 647| | /// SwifterSwift: First leading constraint for this view 648| 1| var leadingConstraint: NSLayoutConstraint? { 649| 1| findConstraint(attribute: .leading, for: self) 650| 1| } 651| | 652| | /// SwifterSwift: First trailing constraint for this view 653| 1| var trailingConstraint: NSLayoutConstraint? { 654| 1| findConstraint(attribute: .trailing, for: self) 655| 1| } 656| | 657| | /// SwifterSwift: First top constraint for this view 658| 1| var topConstraint: NSLayoutConstraint? { 659| 1| findConstraint(attribute: .top, for: self) 660| 1| } 661| | 662| | /// SwifterSwift: First bottom constraint for this view 663| 1| var bottomConstraint: NSLayoutConstraint? { 664| 1| findConstraint(attribute: .bottom, for: self) 665| 1| } 666| |} 667| | 668| |#endif <<<<<< EOF # path=fixes ./Tests/HealthKitTests/HKActivitySummaryExtensionsTests.swift:2,5,8,14,17,20,23,24,29,32,35,38,39,44,47,50,53,54,55 ./Tests/UIKitTests/UILayoutPriorityExtensionsTests.swift:2,4,7,12,15,18,19,23,26,29,30,31 ./Tests/UIKitTests/UIStackViewExtensionsTests.swift:2,5,8,11,16,20,25,29,32,34,37,42,43,50,51,60,61,62 ./Tests/UIKitTests/UIColorExtensionsTests.swift:2,5,8,15,21,22,24,25 ./Tests/UIKitTests/UIViewControllerExtensionsTests.swift:2,5,8,12,15,16,17,19,26,27,31,32,40,41,49,50,57,62,69,70,83,90,92,93,97,100,102,106,107,111,115,119,121,124,125,129,131,133,134,139,146,151,155,156,161,168,173,178,179,185,186,187,191,198,203,207,211,213,214 ./Tests/UIKitTests/UILabelExtensionsTests.swift:2,5,8,13,14,19,23,24,29,37,38,39 ./Tests/UIKitTests/UINavigationBarExtensionTests.swift:2,5,8,18,22,23,33,37,38,49,50,51 ./Tests/UIKitTests/UICollectionViewExtensionsTests.swift:2,5,8,10,25,29,32,35,38,41,42,45,46,50,51,55,56,61,62,68,69,70,77,79,86,88,91,94,97,99,104,107,109,111,114,117,119,122,123,129,131,136,137,138,142,143,147,148,151,152,153 ./Tests/UIKitTests/UIAlertControllerExtensionsTests.swift:2,5,8,17,19,21,23,27,28,30,33,35,41,43,45,50,51,58,60,64,66,68,71,72,74,77,83,85,89,91,93,96,97,98 ./Tests/UIKitTests/UIImageViewExtensionsTests.swift:2,5,8,19,22,32,34,38,39,43,50,51,56,57,58 ./Tests/UIKitTests/UISegmentedControlExtensionsTests.swift:2,5,8,16,17,24,25,26 ./Tests/UIKitTests/UITableViewExtensionsTests.swift:2,5,8,12,19,20,26,27,31,32,36,37,43,44,50,52,53,59,60,66,67,72,73,79,80,86,87,91,94,97,98,101,104,107,110,113,120,123,126,127,134,136,141,142,147,148,155,157,163,165,166,170,171,177,178,179,182,183,184 ./Tests/UIKitTests/UIWindowExtensionsTests.swift:2,5,8,13,16,19,23,25,30,31,33,34,35 ./Tests/UIKitTests/UIScrollViewExtensionsTests.swift:2,5,8,11,14,17,18,24,25,28,31,35,36,41,42,47,48,53,54,59,60,69,73,79,84,85,94,98,104,109,110,118,122,128,133,134,142,146,152,157,158,159 ./Tests/UIKitTests/UIFontExtensionsTests.swift:2,5,8,14,15,20,21,25,31,32,36,37,41,42,48,49,50 ./Tests/UIKitTests/UIBezierPathExtensionsTests.swift:2,5,8,15,16,20,28,29,36,47,48,53,58,61,62,67,72,75,76,77,90,92,93,95,96,97 ./Tests/UIKitTests/UINavigationControllerExtensionsTests.swift:2,5,8,13,15,17,22,24,25,32,38,40,41,49,55,56 ./Tests/UIKitTests/UINavigationItemExtensionsTests.swift:2,5,8,14,17,20,23,24,25 ./Tests/UIKitTests/UITextFieldExtensionsTests.swift:2,5,8,17,18,25,26,36,45,50,51,60,61,65,68,71,74,77,80,81,85,88,91,94,97,100,101,108,109,116,124,125,131,132,138,139,143,148,149,153,158,159,160 ./Tests/UIKitTests/UISearchBarExtensionsTests.swift:2,5,8,13,19,20,27,28,35,36,37 ./Tests/UIKitTests/UIGestureRecognizerExtensionsTests.swift:2,5,8,13,17,19,23,27,29,34,35,36 ./Tests/UIKitTests/UIStoryboardExtensionsTests.swift:2,5,8,12,13,18,19,20 ./Tests/UIKitTests/UIButtonExtensionsTests.swift:2,5,8,13,17,18,22,26,27,31,35,36,40,44,45,49,52,53,57,60,61,65,68,69,73,76,77,81,85,86,90,94,95,99,103,104,108,112,113,118,123,124,129,134,135,140,145,146,152,155,157,162,166,171,175,176,177 ./Tests/UIKitTests/UITabBarExtensionsTests.swift:2,5,8,14,16,20,23,29,31,32,33 ./Tests/UIKitTests/UIRefreshControlExntesionsTests.swift:2,5,8,16,19,24,27,28,29 ./Tests/UIKitTests/UIDatePickerExtensionsTests.swift:2,5,8,15,16,19,22,25,27,28 ./Tests/UIKitTests/UITextViewExtensionsTests.swift:2,5,8,11,14,15,21,22,29,32,33,37,42,51,54,57,63,68,69,70 ./Tests/UIKitTests/UIImageExtensionsTests.swift:2,5,8,12,16,27,28,30,31,37,38,43,44,48,49,53,54,63,64,73,74,82,85,88,92,93,97,100,103,104,108,112,113,117,121,122,127,131,136,137,141,145,150,151,156,160,164,165,171,174,176,179,182,183,189,192,195,199,203,204,206,207,213,214,222,223,230,231,237,238,244,245,246 ./Tests/UIKitTests/UIViewExtensionsTests.swift:2,5,8,20,21,27,30,31,36,39,40,44,46,52,60,63,64,71,72,76,77,82,90,91,100,101,108,111,112,121,122,130,131,139,140,149,150,157,158,163,167,168,173,177,178,185,186,193,194,201,202,207,210,211,216,220,222,226,229,230,233,234,238,242,244,247,250,253,254,260,265,267,270,273,276,277,283,288,290,293,296,299,300,307,309,312,315,316,321,322,328,329,335,341,342,345,347,351,353,356,357,360,362,366,368,371,373,376,377,384,387,390,393,396,400,401,411,412,422,423,434,435,446,452,453,462,469,470,481,484,485,490,500,508,511,512,513 ./Tests/UIKitTests/UISliderExtensionsTests.swift:2,5,8,18,20,21,27,31,34,35,44,47,48,49 ./Tests/UIKitTests/UISwitchExtensionsTests.swift:2,5,8,16,17,18 ./Tests/UIKitTests/UIBarButtonExtensionsTests.swift:2,5,8,15,16,18,22,24,26,29,30,35,36,37 ./Tests/XCTest/XCTestExtensions.swift:2,5,11,17,45,47 ./Tests/MapKitTests/MKPolylineTests.swift:2,5,8,10,19,22,25,29,30,31,34,36,40,41,42,43 ./Tests/MapKitTests/MKMapViewTests.swift:2,5,9,16,20,21,26,32,33,40,42,43,50,53,54,63,66,67,68,69,71 ./Tests/XCTestTests/XCTestExtensionsTests.swift:2,5,10,24,26 ./Tests/FoundationTests/CalendarExtensionTest.swift:2,5,8,26,27,28 ./Tests/FoundationTests/NSPredicateExtensionsTests.swift:2,5,8,14,20,22,23,29,35,37,38,44,50,52,53,58,64,66,67,73,79,81,82,88,94,96,97,107,111,112,113 ./Tests/FoundationTests/LocaleExtensionsTests.swift:2,5,8,13,14,18,21,22,30,33,34,35,36 ./Tests/FoundationTests/URLExtensionsTests.swift:2,5,8,13,19,20,25,26,30,34,41,42,45,46,50,51,54,58,62,63,68,69,74,75,79,84,86,99,103,104,105,106 ./Tests/FoundationTests/NSRegularExpressionExtensionsTests.swift:2,5,8,15,25,27,38,40,41,49,50,51,57,58,66,67,76,79,80,87,88,97,98,99 ./Tests/FoundationTests/NotificationCenterExtensionsTests.swift:2,5,9,19,21,30,37,44,51,52 ./Tests/FoundationTests/DateExtensionsTests.swift:2,5,8,14,15,53,54,55,59,60,65,68,71,75,76,80,83,86,87,91,94,97,98,102,111,112,116,120,124,128,132,133,137,145,146,149,153,157,161,165,166,170,177,178,182,186,190,194,198,199,203,204,208,214,215,219,223,227,231,232,236,241,242,246,250,254,258,259,263,267,268,272,276,280,284,285,289,292,296,297,301,304,308,312,313,317,320,321,325,328,329,336,337,344,345,352,353,357,358,362,363,369,370,376,377,383,384,388,389,393,397,400,403,404,408,411,414,417,418,422,425,428,431,432,436,439,442,445,446,450,453,456,457,461,464,465,468,473,478,483,488,493,498,503,508,509,513,519,522,528,531,537,540,546,549,555,558,560,565,568,569,572,576,581,586,591,596,601,606,610,611,615,618,621,624,628,634,639,644,647,648,651,655,659,666,671,677,679,680,685,688,691,694,697,699,702,705,708,711,712,716,720,724,728,732,733,737,740,743,746,749,752,755,758,760,761,766,769,772,775,778,779,785,786,792,793,799,800,806,807,813,814,820,821,826,831,832,836,842,848,854,857,858,873,875,888,889,895,896,901,902,905,907,910,911,915,916,921,925,929,933,934,939,943,947,951,955,956,963,964,971,977,978,984,985,991,992,993 ./Tests/FoundationTests/UserDefaultsExtensionsTests.swift:2,5,8,12,13,20,26,27,36,37,46,47,56,57,66,67,68 ./Tests/FoundationTests/DataExtensionsTests.swift:2,5,8,15,16,22,23,28,32,37,42,43,44 ./Tests/FoundationTests/URLRequestExtensionsTests.swift:2,5,8,12,20,23,24,32,35,38,44,48,51,54,60,64,65,66 ./Tests/FoundationTests/NSAttributedStringExtensionsTests.swift:2,5,8,12,16,23,27,28,34,35,40,44,45,51,52,54,61,72,73,81,82,84,90,91,99,105,106,113,130,133,147,150,152,161,164,169,172,176,189,190,191,193,194,196,204,208,216,220,232,233,234,235,238,239,244,246,253,257,261,262,264,265,267,272,273,276,277,283,287,289,302,303,304,307,308,310,317,321,322,323 ./Tests/FoundationTests/FileManagerExtensionsTests.swift:2,5,8,15,19,20,22,24,32,35,38,40,41,47,49,52,54,62,65,68,70,71,80,81,87,92,97,101,102,103,104 ./Tests/CoreAnimationTests/CAGradientLayerExtensionsTests.swift:2,5,7,15,22,28,29,30 ./Tests/CoreAnimationTests/CATransform3DExtensionsTests.swift:2,5,7,11,17,21,27,28,31,36,40,58,59,62,63,66,67,70,71,77,78,81,82,85,86,89,90,96,97,102,103,108,109,114,115,120,121,126,130,134,138,139,144,148,152,153,155,159,160,166,167,169,170 ./Tests/SceneKitTests/SCNPlaneExtensionsTests.swift:2,5,8,13,14,19,20,25,26,32,33,39,40,41 ./Tests/SceneKitTests/SCNMaterialExtensionsTests.swift:2,5,8,14,15,16 ./Tests/SceneKitTests/SCNBoxExtensionsTests.swift:2,5,8,13,14,19,20,25,26,30,31,37,38,44,45,46 ./Tests/SceneKitTests/SCNShapeExtensionsTests.swift:2,5,8,12,15,21,22,27,29,30 ./Tests/SceneKitTests/SCNConeExtensionsTests.swift:2,5,8,13,14,19,20,25,26,32,33,39,40,41 ./Tests/SceneKitTests/SCNGeometryExtensionsTests.swift:2,5,8,13,14,15 ./Tests/SceneKitTests/SCNCapsuleExtensionsTests.swift:2,5,8,13,14,19,20,25,26,32,33,39,40,41 ./Tests/SceneKitTests/SCNVector3ExtensionsTests.swift:2,5,8,12,15,16,20,21,25,31,32,36,37,42,43,47,48,53,54,58,59,64,65,69,70,74,75,80,81,82,86,87,88 ./Tests/SceneKitTests/SCNCylinderExtensionsTests.swift:2,5,8,13,14,19,20,25,26,32,33,39,40,41 ./Tests/SceneKitTests/SCNSphereExtensionsTests.swift:2,5,8,13,14,19,20,25,26,32,33,39,40,41 ./Tests/SpriteKitTests/SKNodeExtensionTests.swift:2,5,8,19,20,26,31,32,38,43,44,50,55,56,62,67,68,74,79,80,81 ./Tests/WebKitTests/WKWebViewExtensionsTests.swift:2,5,8,11,13,16,17,20,23,25,27,28,31,34,36,38,39,43,45,46,49,52,54,56,57,58,63,64,67,68,69,74,75,78,79,82,83,84 ./Tests/CoreGraphicsTests/CGFloatExtensionsTests.swift:2,5,8,12,13,16,17,20,21,26,27,32,33,36,37,40,41,44,45,48,49,52,53,54 ./Tests/CoreGraphicsTests/CGPointExtensionsTests.swift:2,5,8,12,16,17,21,22,27,28,34,35,40,41,47,48,53,57,58,64,65,66 ./Tests/CoreGraphicsTests/CGVectorExtensionsTests.swift:2,5,8,19,28,29,36,42,43,46,52,54,56,58,61,62,65,67,68,74,76,79,82,85,88,89,90 ./Tests/CoreGraphicsTests/CGSizeExtensionsTests.swift:2,5,8,16,17,24,25,29,32,33,37,40,41,45,48,49,56,57,63,64,71,72,78,79,86,87,93,94,101,102,108,109,116,117,123,124,130,131,138,139,145,146,147 ./Tests/CoreGraphicsTests/CGRectExtensionsTests.swift:2,5,8,15,16,23,24,29,38,44,51,58,65,66,67 ./Tests/CoreGraphicsTests/CGColorExtensionsTests.swift:2,5,8,14,21,23,29,31,32 ./Tests/CoreGraphicsTests/CGAffineTransformExtensionsTests.swift:2,5,8,12,17,21,25,27,28 ./Tests/ResourcesTests/MyViewController.swift:2,5,8,9 ./Tests/SwiftStdlibTests/BinaryFloatingPointExtensionsTests.swift:2,5,12,17,18 ./Tests/SwiftStdlibTests/ArrayExtensionsTests.swift:2,12,18,19,25,29,33,38,39,44,47,59,60,68,69,79,80,85,86,90,91,117,118 ./Tests/SwiftStdlibTests/DecodableExtensionsTests.swift:2,5,11,12,14,19,20,24,25,30,31,37,38,41,42 ./Tests/SwiftStdlibTests/DoubleExtensionsTests.swift:2,5,11,12,17,18,23,24,27,28 ./Tests/SwiftStdlibTests/CharacterExtensionsTests.swift:2,10,13,18,19,24,25,28,29,32,33,36,37,44,46,47,52,55,58,59 ./Tests/SwiftStdlibTests/DictionaryExtensionsTests.swift:2,5,8,12,13,20,21,25,31,32,35,38,41,44,47,48,53,57,60,61,68,69,74,75,78,84,85,92,93,100,101,108,109,116,117,120,123,124,126,127,134,135,145,146,148,149,154,165,166,177,183,186,190,191 ./Tests/SwiftStdlibTests/OptionalExtensionsTests.swift:2,5,8,9,14,17,18,22,25,26,32,39,40,41,45,48,50,53,57,58,63,67,71,75,76,80,83,86,89,92,95,98,99,103,106,110,111,116,124,132,140,141,146,154,162,170,171 ./Tests/SwiftStdlibTests/SignedIntegerExtensionsTests.swift:2,5,9,10,15,16,21,22,26,27,31,32,39,40,43,44,47,48,51,52 ./Tests/SwiftStdlibTests/RangeReplaceableCollectionTests.swift:2,5,14,15,26,27,36,37,48,50,51,55,61,62,67,71,72,77,81,83,84,89,93,95,96,105,109,112,113,122,127,130,131,136,139,142,144,146,148,153,158,163,166,169,172,173 ./Tests/SwiftStdlibTests/StringExtensionsTests.swift:2,5,10,14,15,20,21,24,25,30,31,35,36,40,41,46,47,52,53,58,59,64,65,70,71,78,79,82,96,114,115,120,121,127,128,134,135,140,141,146,147,157,158,165,166,171,172,175,176,180,184,185,192,193,203,204,208,210,211,217,221,222,225,228,229,232,233,237,238,241,242,245,246,249,250,254,257,260,261,265,268,274,275,279,282,288,289,294,297,300,301,306,307,311,312,318,319,322,323,326,327,330,331,336,337,342,345,352,360,367,373,380,386,387,394,400,401,406,410,411,416,420,424,425,430,431,435,436,442,443,447,448,453,454,458,461,463,465,466,471,472,478,482,487,491,495,499,503,507,511,512,516,517,523,524,529,530,535,539,543,546,547,552,555,556,561,562,567,568,570,576,577,583,584,595,597,602,603,608,612,616,620,624,628,629,637,638,643,647,651,655,659,663,664,672,673,677,679,683,684,688,689,693,694,698,699,704,705,709,712,714,716,717,726,731,737,741,742,754,757,758,770,773,774,786,789,790,799,803,806,807,810,811,816,817,822,823,828,829,834,835,840,841,846,847,851,854,857,860,861,866,870,872,874,875,880,881,886,887,890,893,896,899,900,905,906,912,913,917,919,926,930,934,935,941,942 ./Tests/SwiftStdlibTests/BoolExtensionsTests.swift:2,5,10,11,15,16 ./Tests/SwiftStdlibTests/KeyedDecodingContainerExtensionsTests.swift:2,4,6,10,14,15,20,21,22,30,31,38,39,46,47,54,55,60,61,64,65 ./Tests/SwiftStdlibTests/FloatExtensionsTests.swift:2,5,11,12,17,18,23,24,27,28 ./Tests/SwiftStdlibTests/FloatingPointExtensionsTests.swift:2,5,10,11,16,20,21,26,30,31,35,36,40,41,45,46,50,51,57,62,63 ./Tests/SwiftStdlibTests/IntExtensionsTests.swift:2,5,9,10,13,14,17,18,21,22,26,27,31,32,37,38,42,44,47,50,52,53,59,60,67,68,77,84,89,90,95,96,101,102,108,109 ./Tests/SwiftStdlibTests/TestHelpers.swift:2,8,9,17,18,24,30,31,32,35,36,40,43,44 ./Tests/SwiftStdlibTests/SignedNumericExtensionsTests.swift:2,5,10,13,14,18,23,26,31,34,35 ./Tests/SwiftStdlibTests/SequenceExtensionsTests.swift:2,5,8,9,12,14,15,20,21,25,26,30,31,36,37,42,43,49,50,56,57,62,63,69,70,78,79,83,84,90,97,98,106,107,115,116,121,122,126,127,131,132,136,137,142,145,150,151,154,155,168,169,184,185,192,194,196,198,200,201 ./Tests/SwiftStdlibTests/StringProtocolExtensionsTests.swift:2,5,9,12,22,26,28,29,32,37,40,43,44 ./Tests/SwiftStdlibTests/MutableCollectionTests.swift:2,4,6,14,18,23,24,28,29,43,44,60,61,67,73,74 ./Tests/SwiftStdlibTests/CollectionExtensionsTests.swift:2,5,8,12,13,16,25,26,27,30,31,32,37,38,46,47,54,56,65,67,69,80,82,84,91,93,95,101,103,104,110,116,122,128,129,135,136,140,143,144 ./Tests/SwiftStdlibTests/ComparableExtensionsTests.swift:2,4,6,13,16,17,23,24 ./Tests/SwiftStdlibTests/BidirectionalCollectionExtensionsTests.swift:2,5,12,13,20,22,24,26,28,29 ./Tests/SharedTests/ColorExtensionsTests.swift:2,5,7,11,15,20,24,28,32,36,38,39,41,46,50,54,58,62,64,65,69,72,75,78,79,86,88,90,96,101,106,111,116,121,126,127,131,134,137,140,143,146,149,152,155,156,160,163,166,169,172,173,177,180,183,186,187,191,194,197,200,201,211,217,223,224,228,230,231,233,237,242,245,251,261,271,274,275,280,284,288,289,294,298,302,303,305,312,318,324,330,336,345,349,350,354,357,360,363,366,369,372,373,383,392,401,410,411,415,418,421,424,425,431,436,442,443,444 ./Tests/SharedTests/EdgeInsetsExtensionsTests.swift:2,4,7,12,13,17,18,25,26,33,34,43,50,51,60,67,68,77,84,85,94,101,102,111,118,119,128,135,136,145,152,153,156,161,166,167,172,177,182,183,184 ./Tests/DispatchTests/DispatchQueueExtensionsTests.swift:2,5,8,13,16,19,20,23,24,26,27,32,35,39,40,43,44,46,47,51,54,55,57,58,62,66,67,70,71,74,75,77,80,81,82,83 ./Tests/AppKitTests/NSViewExtensionsTests.swift:2,4,7,14,19,20,26,29,30,35,38,39,46,47,56,57,66,67,75,76,84,85,90,94,95,102,103,108,111,112,118,119,125,129,130,131 ./Tests/AppKitTests/NSImageExtensionsTests.swift:2,4,7,13,18,19,20 ./Tests/AppKitTests/NSColorExtensionsTests.swift:2,5,8,15,17,21,25,26,27,28 ./Tests/StoreKitTests/SKProductTests.swift:2,5,8,20,21,22,29,31,36,38,43,45,50,52,53 ./Tests/CoreLocationTests/CLLocationArrayExtensionsTests.swift:2,5,8,21,25,26,27 ./Tests/CoreLocationTests/CLLocationExtensionsTests.swift:2,5,8,14,17,20,21,26,28,29,30 ./Tests/CoreLocationTests/CLVisitExtensionsTests.swift:2,5,8,13,16,17,18 ./Examples/Examples.playground/Pages/02-FoundationExtensions.xcplaygroundpage/Contents.swift:2,4,6,8,10,13,17,20,23,26,29,32,36,41,45 ./Examples/Examples.playground/Pages/04-CoreGraphicsExtensions.xcplaygroundpage/Contents.swift:2,4,6,8,10,13,16,18,21 ./Examples/Examples.playground/Pages/01-SwiftStdlibExtensions.xcplaygroundpage/Contents.swift:2,4,6,8,10,14,16,18,21,24,27,29,32,35,38,41,43,46,48,51,54,57,60,63,66,70,74,77,80,83,86,90,93,97,101,104,107,110 ./Examples/Examples.playground/Pages/05-TryYourself.xcplaygroundpage/Contents.swift:2,4,6,9 ./Examples/Examples.playground/Pages/03-UIKitExtensions.xcplaygroundpage/Contents.swift:2,4,7,9,11,13,18,20,23,26,29,32,35,38,41,44,46,48,51,55,58,61,63,65,74,76,80,82,86,88,90,92,94,97,101,104 ./Examples/Examples.playground/Pages/00-ToC.xcplaygroundpage/Contents.swift:3,5,7,13,15 ./Package.swift:3,5 ./Sources/SwifterSwift.h:8,11,19,20,23,26 ./Sources/SwifterSwift/StoreKit/SKProductExtensions.swift:2,5,13,19,20 ./Sources/SwifterSwift/WebKit/WKWebViewExtensions.swift:2,5,7,15,16,24,25,26 ./Sources/SwifterSwift/CoreGraphics/CGRectExtensions.swift:2,5,7,11,12,14,23,24,25,27,48,49,50 ./Sources/SwifterSwift/CoreGraphics/CGFloatExtensions.swift:2,5,9,11,16,17,22,24,28,29,34,36,40,41,45,46,50,51,55,56,60,61,65,66,67 ./Sources/SwifterSwift/CoreGraphics/CGVectorExtensions.swift:2,5,7,14,15,21,22,23,25,38,39,40,42,55,56,68,69,81,82,92,93,94 ./Sources/SwifterSwift/CoreGraphics/CGSizeExtensions.swift:2,5,7,13,14,18,19,23,24,25,27,41,42,57,58,59,61,76,77,90,91,105,106,119,120,134,135,148,149,163,164,177,178,192,193,206,207,220,221,235,236,249,250,251 ./Sources/SwifterSwift/CoreGraphics/CGAffineTransformExtensions.swift:2,5,7,9,11,16,17,19 ./Sources/SwifterSwift/CoreGraphics/CGPointExtensions.swift:2,5,7,20,21,36,37,38,40,55,56,70,71,85,86,100,101,114,115,129,130,143,144,145 ./Sources/SwifterSwift/CoreGraphics/CGColorExtensions.swift:2,5,9,13,15,21,23,28,30,31 ./Sources/SwifterSwift/HealthKit/HKActivitySummaryExtensions.swift:2,5,7,12,15,18,19 ./Sources/SwifterSwift/UIKit/UIBezierPathExtensions.swift:2,5,7,18,19,29,30,31,32,42,44,45,54,55,64,65,66 ./Sources/SwifterSwift/UIKit/UISearchBarExtensions.swift:2,5,7,13,17,19,20,24,25,26,28,33,34,35 ./Sources/SwifterSwift/UIKit/UITableViewExtensions.swift:2,5,7,13,14,18,19,20,22,33,35,36,45,47,48,58,59,63,64,68,69,78,80,81,92,94,95,104,106,107,115,116,122,123,129,130,138,139,149,152,153,155,156,166,167,178,179,180 ./Sources/SwifterSwift/UIKit/UIScrollViewExtensions.swift:2,5,7,20,27,28,36,37,38,44,45,50,51,58,59,66,67,79,82,83,95,98,99,111,114,115,127,130,131,132 ./Sources/SwifterSwift/UIKit/UITextFieldExtensions.swift:2,5,7,17,20,23,24,25,27,36,38,47,54,57,58,59,60,64,65,69,70,84,85,91,96,97,98,104,109,110,111,112,114,120,121,128,129,136,137,144,145,159,160,174,175,176 ./Sources/SwifterSwift/UIKit/UILayoutPriorityExtensions.swift:2,5,8,16,17,25,26,27 ./Sources/SwifterSwift/UIKit/UINavigationItemExtensions.swift:2,5,7,17,18,19 ./Sources/SwifterSwift/UIKit/UIStackViewExtensions.swift:2,5,7,30,31,38,39,40,45,46,47,48 ./Sources/SwifterSwift/UIKit/UICollectionViewExtensions.swift:2,5,7,12,13,17,18,19,21,32,34,35,43,46,49,51,52,62,63,74,76,77,93,95,96,104,105,113,114,120,121,131,132,142,145,146,148,149,162,164,165,175,176,177 ./Sources/SwifterSwift/UIKit/UITextViewExtensions.swift:2,5,7,13,14,19,20,25,26,35,36,37 ./Sources/SwifterSwift/UIKit/UIBarButtonItemExtensions.swift:2,5,7,12,13,14,16,26,27,35,36,37 ./Sources/SwifterSwift/UIKit/UIStoryboardExtensions.swift:2,5,7,14,15,22,23,24 ./Sources/SwifterSwift/UIKit/UITabBarExtensions.swift:2,5,7,23,29,33,34,46,47,52,58,59,63,68,73,77,78,79,80,81,82 ./Sources/SwifterSwift/UIKit/UISegmentedControlExtensions.swift:2,5,7,14,19,20,21,22,28,33,34,35,36,37 ./Sources/SwifterSwift/UIKit/UINavigationBarExtensions.swift:2,5,7,19,20,32,33,46,47,48 ./Sources/SwifterSwift/UIKit/UIActivityExtensions.swift:2,5,7,11,14,17,20,21 ./Sources/SwifterSwift/UIKit/UISliderExtensions.swift:2,5,7,26,27,28,29 ./Sources/SwifterSwift/UIKit/UIViewControllerExtensions.swift:2,5,7,13,14,15,17,33,35,36,44,45,51,52,56,57,78,79,89,90,93,94,104,105,109,113,114,133,136,137,142,143,145,147,148 ./Sources/SwifterSwift/UIKit/UIImageExtensions.swift:2,5,7,12,13,17,18,22,23,27,28,34,39,40,51,57,59,60,62,71,72,79,80,90,91,106,107,122,123,134,141,144,147,151,155,156,171,174,177,181,185,186,200,201,203,207,211,216,220,221,230,239,240,242,246,252,253,268,269,271,274,278,280,281,294,295,297,301,305,306,312,313,320,321,322,324,333,336,337,340,344,345,347,348,357,358,371,372,373 ./Sources/SwifterSwift/UIKit/UINavigationControllerExtensions.swift:2,5,7,20,21,33,34,44,45,46 ./Sources/SwifterSwift/UIKit/UIViewExtensions.swift:2,5,7,16,19,20,28,31,32,42,45,48,51,52,53,55,62,67,71,72,73,78,81,82,83,88,92,93,94,99,102,103,104,111,112,113,119,123,124,130,133,134,135,140,143,144,145,150,153,154,155,160,163,164,165,170,173,174,175,180,184,185,186,194,195,197,198,203,206,207,208,213,216,217,218,223,226,227,228,229,231,241,246,247,258,262,263,284,285,291,292,301,305,306,315,319,320,329,330,341,343,344,348,349,353,354,361,362,363,370,371,372,392,393,413,414,434,435,436,456,466,472,473,486,492,493,505,506,507,537,539,542,543,546,547,550,551,554,555,558,559,562,563,565,567,568,578,579,580,590,591,592,599,600,607,609,610,616,617,618,620,633,635,636,640,641,645,646,650,651,655,656,660,661,665,666,667 ./Sources/SwifterSwift/UIKit/UIGestureRecognizerExtensions.swift:2,5,7,12,13,14 ./Sources/SwifterSwift/UIKit/UIWindowExtensions.swift:2,5,7,27,28,37,38,39 ./Sources/SwifterSwift/UIKit/UILabelExtensions.swift:2,5,7,13,14,24,25,36,37,38 ./Sources/SwifterSwift/UIKit/UISwitchExtensions.swift:2,5,7,14,15,16 ./Sources/SwifterSwift/UIKit/UIButtonExtensions.swift:2,5,7,14,17,18,19,25,28,29,30,36,39,40,41,47,50,51,52,58,61,62,63,69,72,73,74,80,83,84,85,91,94,95,96,102,105,106,107,113,116,117,118,124,127,128,129,135,138,139,140,141,143,147,148,154,155,161,162,168,169,181,183,186,189,197,198,199,200 ./Sources/SwifterSwift/UIKit/UIApplicationExtensions.swift:2,5,7,21,22,27,30,34,35,38,39,42,43,46,47,50,51,55,56,60,61,65,66,67,69 ./Sources/SwifterSwift/UIKit/UIImageViewExtensions.swift:2,5,7,31,35,37,38,49,50,59,60,61 ./Sources/SwifterSwift/UIKit/UIRefreshControlExtensions.swift:2,5,7,18,22,25,26,27,28 ./Sources/SwifterSwift/UIKit/UIAlertControllerExtensions.swift:2,5,9,11,31,32,33,52,53,71,72,73,74,75,77,96,97,98,117,118,119,120 ./Sources/SwifterSwift/UIKit/UIFontExtensions.swift:2,5,7,12,13,17,18,28,32,33,34 ./Sources/SwifterSwift/UIKit/UIDatePickerExtensions.swift:2,5,7,14,17,18,20,21 ./Sources/SwifterSwift/UIKit/UIColorExtensions.swift:2,5,18,19,21,22 ./Sources/SwifterSwift/Dispatch/DispatchQueueExtensions.swift:2,5,7,17,19,20,21,23,31,34,36,37,50,51,62,63,64,65,66,67 ./Sources/SwifterSwift/SceneKit/SCNShapeExtensions.swift:2,5,9,11,14,24,25,35,36,38,39 ./Sources/SwifterSwift/SceneKit/SCNSphereExtensions.swift:2,5,7,14,15,24,25,33,34,43,44,52,53,54 ./Sources/SwifterSwift/SceneKit/SCNBoxExtensions.swift:2,5,7,17,18,26,27,44,45,55,56,68,69,79,80,81 ./Sources/SwifterSwift/SceneKit/SCNGeometryExtensions.swift:2,5,7,12,13,14 ./Sources/SwifterSwift/SceneKit/SCNVector3Extensions.swift:2,10,13,15,23,24,31,32,40,41,42,44,56,57,69,70,81,82,94,95,106,107,120,121,132,133,144,145,156,157,158 ./Sources/SwifterSwift/SceneKit/SCNCylinderExtensions.swift:2,5,7,17,18,28,29,39,40,50,51,61,62,63 ./Sources/SwifterSwift/SceneKit/SCNMaterialExtensions.swift:2,5,7,15,16,17 ./Sources/SwifterSwift/SceneKit/SCNPlaneExtensions.swift:2,5,7,14,15,25,26,35,36,46,47,56,57,58 ./Sources/SwifterSwift/SceneKit/SCNConeExtensions.swift:2,5,7,17,18,29,30,41,42,53,54,65,66,67 ./Sources/SwifterSwift/SceneKit/SCNCapsuleExtensions.swift:2,5,7,16,17,27,28,38,39,49,50,60,61,62 ./Sources/SwifterSwift/Foundation/NSPredicateExtensions.swift:2,5,7,12,13,14,16,24,25,32,33,34,36,43,44,53,54,63,64,73,74,75 ./Sources/SwifterSwift/Foundation/NSRegularExpressionExtensions.swift:2,5,36,37,38,52,53,54,56,70,71,85,86,100,101,116,117,134,135,156,157,158 ./Sources/SwifterSwift/Foundation/UserDefaultsExtensions.swift:2,5,7,15,18,19,20,27,28,35,36,47,48,58,59,60 ./Sources/SwifterSwift/Foundation/DataExtensions.swift:2,5,7,13,14,15,17,25,26,36,37,38 ./Sources/SwifterSwift/Foundation/URLRequestExtensions.swift:2,5,9,11,19,20,24,28,29,33,34,38,39,40,44,45,47,48,49 ./Sources/SwifterSwift/Foundation/FileManagerExtensions.swift:2,5,19,21,22,37,41,45,47,48,50,52,68,81,82,83 ./Sources/SwifterSwift/Foundation/NotificationCenterExtensions.swift:2,5,32,33,34,35 ./Sources/SwifterSwift/Foundation/URLExtensions.swift:2,5,10,12,18,20,23,24,26,27,28,30,39,40,41,43,58,59,70,71,83,84,95,97,98,107,108,109,118,119,121,124,125,126,128,147,150,152,154,155 ./Sources/SwifterSwift/Foundation/LocaleExtensions.swift:2,5,7,12,13,22,23,24,26,36,40,41,42,43 ./Sources/SwifterSwift/Foundation/DateExtensions.swift:2,5,11,13,23,26,29,30,39,42,45,46,47,49,55,56,63,64,75,77,84,85,92,93,104,111,112,113,114,125,129,134,135,136,137,148,152,157,158,159,160,167,168,179,183,188,189,190,191,202,206,211,212,213,214,225,229,234,235,236,237,248,257,260,263,264,265,266,277,287,290,291,292,293,300,301,308,309,316,317,324,325,332,333,337,338,342,343,347,348,352,353,357,358,369,371,372,391,392,411,412,431,432,451,452,465,468,470,471,479,480,488,489,496,497,498,500,516,517,532,533,534,561,568,575,582,589,596,602,605,606,607,610,623,624,629,632,635,638,641,644,647,648,649,652,654,673,680,687,693,700,707,714,717,718,719,721,731,732,745,746,761,762,777,778,793,794,814,815,818,819,839,840,843,844,851,852,859,860,867,868,875,876,887,889,890,902,903,913,914,924,925,937,938,951,952,953,955,994,997,998,1012,1013,1021,1022,1032,1033,1034 ./Sources/SwifterSwift/Foundation/NSAttributedStringExtensions.swift:2,5,9,13,15,21,31,33,35,40,42,47,57,59,61,66,68,73,74,75,77,85,89,90,98,100,112,115,118,119,121,122,132,134,135,136,138,149,150,161,162,170,171,180,181,182 ./Sources/SwifterSwift/Foundation/CalendarExtensions.swift:2,5,7,18,19,20 ./Sources/SwifterSwift/Shared/EdgeInsetsExtensions.swift:2,11,15,16,32,33,34,37,39,41,48,49,55,56,57,59,66,67,76,77,85,86,94,95,103,104,112,113,121,122,130,131,132,134,147,148,159,160,161 ./Sources/SwifterSwift/Shared/FontExtensions.swift:2,8 ./Sources/SwifterSwift/Shared/ColorExtensions.swift:2,4,10,16,20,22,30,31,49,50,52,70,71,73,81,84,85,87,96,97,104,105,118,119,123,124,129,131,139,144,146,147,160,161,162,165,169,171,172,173,175,188,192,195,201,207,210,213,216,219,224,226,227,243,244,260,261,262,264,277,281,283,284,294,299,300,314,315,320,321,323,327,332,333,348,349,350,353,358,359,360,362,367,370,373,376,379,382,385,388,391,394,397,400,403,406,409,412,415,418,421,424,427,430,433,436,439,442,445,448,449,450,452,458,461,464,467,470,473,476,479,482,485,488,491,494,497,500,503,506,509,512,515,518,521,524,527,530,533,536,539,542,545,548,551,554,557,560,563,566,569,572,575,578,581,584,587,590,593,596,599,602,605,608,611,614,617,620,623,626,629,632,635,638,641,644,647,650,653,656,659,662,665,668,671,674,677,680,683,686,689,692,695,698,701,704,707,710,713,716,719,722,725,728,731,734,737,740,743,746,749,752,755,758,761,764,767,770,773,776,779,782,785,788,791,794,797,800,803,806,809,812,815,818,821,824,827,830,833,836,839,842,845,848,851,854,857,860,863,866,869,872,875,878,881,884,887,890,893,896,899,902,905,908,911,914,917,920,923,926,929,932,935,938,941,944,947,950,953,956,959,962,965,968,971,974,977,980,983,986,989,992,995,998,1001,1004,1007,1010,1013,1016,1019,1022,1025,1028,1031,1034,1037,1040,1043,1046,1049,1052,1055,1058,1061,1064,1067,1070,1073,1076,1079,1082,1085,1088,1091,1094,1097,1100,1103,1106,1109,1112,1115,1118,1121,1124,1127,1130,1133,1136,1139,1142,1145,1148,1151,1154,1157,1160,1163,1166,1169,1172,1175,1178,1181,1184,1187,1190,1193,1196,1199,1202,1205,1208,1211,1214,1217,1220,1223,1226,1229,1232,1235,1238,1241,1244,1247,1250,1253,1256,1259,1262,1265,1268,1271,1274,1277,1280,1283,1284,1285,1287,1292,1295,1298,1301,1304,1307,1310,1313,1316,1319,1322,1325,1328,1331,1334,1337,1340,1343,1346,1349,1352,1355,1358,1361,1364,1367,1370,1373,1376,1379,1382,1385,1388,1391,1394,1397,1400,1403,1406,1409,1412,1415,1418,1421,1424,1427,1430,1433,1436,1439,1442,1445,1448,1451,1454,1457,1460,1463,1466,1469,1472,1475,1478,1481,1484,1487,1490,1493,1496,1499,1502,1505,1508,1511,1514,1517,1520,1523,1526,1529,1532,1535,1538,1541,1544,1547,1550,1553,1556,1559,1562,1565,1568,1571,1574,1577,1580,1583,1586,1589,1592,1595,1598,1601,1604,1607,1610,1613,1616,1619,1622,1625,1628,1631,1634,1637,1640,1643,1646,1649,1652,1655,1658,1661,1664,1667,1670,1673,1676,1679,1682,1685,1688,1691,1694,1697,1700,1703,1706,1709,1712,1715,1718,1721,1724,1727,1730,1733,1736,1737,1738,1740,1745,1748,1751,1754,1757,1760,1763,1766,1769,1772,1775,1778,1781,1784,1787,1790,1793,1796,1799,1802,1805,1806,1808 ./Sources/SwifterSwift/SpriteKit/SKNodeExtensions.swift:2,5,7,17,18,27,31,32,33,42,46,47,48,57,61,62,63,72,76,77,78,87,91,92,93,94 ./Sources/SwifterSwift/MapKit/MKMapViewExtensions.swift:2,5,16,17,24,25,39,40,42,43,53,63,64,65,66,68 ./Sources/SwifterSwift/MapKit/MKPolylineExtensions.swift:2,5,7,16,17,18,20,28,29,30 ./Sources/SwifterSwift/SwiftStdlib/SignedIntegerExtensions.swift:2,13,15,20,21,25,26,30,31,35,36,40,41,46,49,52,55,58,60,61,62,64,72,73,80,81,96,98 ./Sources/SwifterSwift/SwiftStdlib/BidirectionalCollectionExtensions.swift:2,4,16,17,26,27 ./Sources/SwifterSwift/SwiftStdlib/ComparableExtensions.swift:2,4,20,21,35,36 ./Sources/SwifterSwift/SwiftStdlib/ArrayExtensions.swift:2,4,14,15,29,30,46,47,48,49,51,64,65,78,79,92,93,95,96,109,110,111,112,121,122,123,124,132,133 ./Sources/SwifterSwift/SwiftStdlib/StringProtocolExtensions.swift:2,4,22,23,45,47 ./Sources/SwifterSwift/SwiftStdlib/BinaryFloatingPointExtensions.swift:2,5,7,26,28,29 ./Sources/SwifterSwift/SwiftStdlib/FloatExtensions.swift:2,6,12,14,19,20,24,25,30,32,33,35,47 ./Sources/SwifterSwift/SwiftStdlib/KeyedDecodingContainerExtensions.swift:2,6,21,22,24,38,39,41 ./Sources/SwifterSwift/SwiftStdlib/SequenceExtensions.swift:2,13,14,24,25,35,36,45,46,57,59,60,68,69,79,80,94,95,96,107,108,124,126,128,129,141,142,154,157,159,160,168,169,176,177,188,190,191,192,205,208,210,211,212,222,223,232,233,234,247,248,249,263,264,273,274,276,277,291,292,294,295,296,298,307,308 ./Sources/SwifterSwift/SwiftStdlib/MutableCollectionExtensions.swift:2,10,11,17,18,29,31,32,33,46,49,51,52,53,54,63,64,65 ./Sources/SwifterSwift/SwiftStdlib/SignedNumericExtensions.swift:2,6,8,13,14,23,25,26,28,41,44,46 ./Sources/SwifterSwift/SwiftStdlib/DictionaryExtensions.swift:2,6,8,17,18,29,30,42,43,49,50,59,63,65,96,98,104,105,112,113,128,129,130,131,133,146,147,148,150,163,164,165,166,167,169,193,194,196,201,205,206,207,208,209,210,212,230,231,233,247,248,265,266,280,281 ./Sources/SwifterSwift/SwiftStdlib/StringExtensions.swift:2,6,10,14,18,20,29,33,34,37,39,41,51,53,57,58,72,75,77,102,103,105,106,115,116,124,125,133,134,144,145,157,158,173,174,187,189,197,199,209,211,220,222,231,233,241,243,260,262,272,274,283,284,292,294,311,312,314,326,328,340,342,349,350,357,364,366,367,376,378,386,388,396,398,406,408,416,418,426,428,433,435,441,449,451,452,454,466,468,479,481,492,494,505,507,509,517,519,530,532,533,541,542,554,556,569,571,582,588,589,592,593,596,597,599,601,611,612,628,629,631,632,645,647,664,666,669,670,679,680,689,691,692,706,708,710,724,726,728,741,743,744,756,758,771,773,774,781,782,795,798,799,813,815,816,831,833,834,847,849,850,863,865,866,879,881,893,895,910,912,913,926,927,939,941,943,955,957,959,967,969,979,981,990,992,1002,1004,1019,1021,1033,1034,1045,1053,1055,1056,1057,1069,1070,1081,1089,1091,1092,1093,1103,1104,1114,1115,1126,1127,1128,1130,1143,1145,1155,1156,1161,1163,1164,1165,1167,1169,1177,1179,1184,1186,1193,1195,1202,1204,1212,1214,1215,1217,1219,1232,1233,1245,1246,1247,1249,1251,1256,1257,1260,1264,1265,1269,1270,1274,1275,1279,1280,1284,1285,1292,1293,1299,1300,1309,1310,1317,1318,1325,1326,1327 ./Sources/SwifterSwift/SwiftStdlib/DecodableExtensions.swift:2,6,16,18 ./Sources/SwifterSwift/SwiftStdlib/BoolExtensions.swift:2,4,13,14,22,23 ./Sources/SwifterSwift/SwiftStdlib/DoubleExtensions.swift:2,6,12,14,19,20,24,25,30,32,33,35,47 ./Sources/SwifterSwift/SwiftStdlib/CharacterExtensions.swift:2,4,30,31,32,40,41,48,49,56,57,64,65,66,68,77,78,79,81,94,95,107,108 ./Sources/SwifterSwift/SwiftStdlib/FloatingPointExtensions.swift:2,6,8,13,14,18,19,23,24,29,31,35,36,41,43,47,48,49,51,63,64,66,76,77,79,89,90 ./Sources/SwifterSwift/SwiftStdlib/IntExtensions.swift:2,6,12,14,19,20,24,25,29,30,34,35,39,40,44,45,50,52,57,65,67,68,74,79,80,83,84,90,91,92,94,101,103,111,113,114,124,127,130,136,138,140,141,145,146,147,149,161,162,172,173,175,187,188,190,200,201 ./Sources/SwifterSwift/SwiftStdlib/RangeReplaceableCollectionExtensions.swift:2,4,22,23,24,25,26,28,42,43,66,68,69,81,82,88,89,101,103,104,113,114,124,125,135,137,138,139,146,147,154,158,159,160,169,175,176,177 ./Sources/SwifterSwift/SwiftStdlib/OptionalExtensions.swift:2,4,19,20,35,36,55,56,69,70,85,86,87,88,90,96,97,103,104,105,107,110,121,122,133,134,145,146,157,158,160,161,163 ./Sources/SwifterSwift/SwiftStdlib/CollectionExtensions.swift:2,6,8,12,13,15,28,29,31,41,42,59,61,62,72,73,88,89,90,91,93,105,106,107,109,118,119,120,122,132,133 ./Sources/SwifterSwift/SwiftStdlib/Deprecated/StdlibDeprecated.swift:2,6,7,11,12,23,25,26,36,38,39,53,55,56,70,72,73,74,83,84,92,93,101,102,113,115,116 ./Sources/SwifterSwift/CoreLocation/CLLocationArrayExtensions.swift:2,5,7,17,21,23,24,25 ./Sources/SwifterSwift/CoreLocation/CLVisitExtensions.swift:2,5,7,14,15,16 ./Sources/SwifterSwift/CoreLocation/CLLocationExtensions.swift:2,5,7,20,27,32,34,35,42,43,55,58,63,65,66,67 ./Sources/SwifterSwift/AppKit/NSColorExtensions.swift:2,5,15,16,17 ./Sources/SwifterSwift/AppKit/NSViewExtensions.swift:2,5,7,15,19,20,21,27,31,32,33,39,44,45,46,51,54,55,56,63,67,68,69,75,79,80,81,87,91,92,93,99,103,104,105,114,115,119,120,121,126,130,131,132,137,140,141,142,143,145,152,153,157,158,159 ./Sources/SwifterSwift/AppKit/NSImageExtensions.swift:2,5,7,16,18,27,28,32,35,39,41,42,51,54,57,58,59 ./Sources/SwifterSwift/CoreAnimation/CAGradientLayerExtensions.swift:2,5,29,30,31 ./Sources/SwifterSwift/CoreAnimation/CATransform3DExtensions.swift:2,4,6,8,10,13,25,26,28,29,31,36,37,39,42,66,67,93,94,96,97,99,109,110,119,120,132,133,134,136,141,142,144,156,157,168,169,182,183,191,192,199,200,209,210,219,220,232,233,240,241,247,248,249,251,253,255,260,267,268,269,271 <<<<<< EOF