├── .gitignore ├── .swiftlint.yml ├── .travis.yml ├── Dangerfile ├── Example Watch Extension ├── Assets.xcassets │ └── README__ignoredByTemplate__ ├── ExtensionDelegate.swift ├── Info.plist ├── InterfaceController.swift ├── NotificationController.swift └── PushNotificationPayload.apns ├── Example Watch ├── Assets.xcassets │ └── AppIcon.appiconset │ │ └── Contents.json ├── Base.lproj │ └── Interface.storyboard └── Info.plist ├── Example ├── AppDelegate.swift ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Contents.json │ ├── first.imageset │ │ ├── Contents.json │ │ └── first.pdf │ ├── puppy.imageset │ │ ├── Contents.json │ │ ├── puppy.png │ │ └── puppy@2x.png │ └── second.imageset │ │ ├── Contents.json │ │ └── second.pdf ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── FirstViewController.swift ├── Info.plist └── SecondViewController.swift ├── Gemfile ├── Gemfile.lock ├── Images └── puppy.png ├── LICENSE ├── Package.swift ├── Package@swift-4.swift ├── README.md ├── Sources ├── Info-iOS.plist ├── Info-tvOS.plist ├── Info-watchOS.plist ├── Info.plist ├── ViaSwiftUtils.h └── ViaSwiftUtils │ ├── Foundation │ ├── CGPoint+Operators.swift │ ├── CGRect+AspectRatio.swift │ ├── CountableRange+Random.swift │ ├── Date+ComponentAccessors.swift │ ├── Date+TimesBetweenDates.swift │ ├── Dictionary+MapValues.swift │ ├── ForceUnwrappingOperators.swift │ ├── MutableCollection+Shuffle.swift │ ├── Observable.swift │ ├── ObserverType.swift │ ├── RandomAccessCollection+SafeAccess.swift │ ├── Sequence+Helpers.swift │ ├── SignedInteger+Random.swift │ ├── TimeInterval+Converter.swift │ └── TimeInterval+Intervals.swift │ └── UIKit │ ├── Bundle+ApplicationInfo.swift │ ├── CGVector+Operators.swift │ ├── NibView.swift │ ├── String+Localized.swift │ ├── String+Words.swift │ ├── UIButton+States.swift │ ├── UIControl+States.swift │ ├── UIImage+Color.swift │ ├── UIImage+Rounding.swift │ ├── UIImageView+Rotation.swift │ └── UIView+Xib.swift ├── Tests ├── LinuxMain.swift └── ViaSwiftUtilsTests │ ├── BetterForceUnwrappingTests.swift │ ├── CGPoint+OperatorsTest.swift │ ├── CGRectTests.swift │ ├── CollectionShuffledTest.swift │ ├── Date_ComparableTest.swift │ ├── Date_ComponentAccessorsTest.swift │ ├── Date_TimesBetween.swift │ ├── Dictionary+MapValuesTests.swift │ ├── RandomAccessCollection+SafeAccessTests.swift │ └── SequenceTypeHelperTests.swift ├── ViaSwiftUtils.playground ├── Contents.swift ├── Resources │ └── puppy.png ├── contents.xcplayground ├── playground.xcworkspace │ └── contents.xcworkspacedata └── timeline.xctimeline ├── ViaSwiftUtils.podspec ├── ViaSwiftUtils.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcshareddata │ └── xcschemes │ ├── ViaSwiftUtils_iOS.xcscheme │ ├── ViaSwiftUtils_tvOS.xcscheme │ └── ViaSwiftUtils_watchOS.xcscheme ├── ViaSwiftUtils.xcworkspace ├── contents.xcworkspacedata ├── xcshareddata │ ├── IDEWorkspaceChecks.plist │ └── ViaSwiftUtils.xcscmblueprint └── xcuserdata │ └── feilek.xcuserdatad │ └── xcdebugger │ └── Breakpoints_v2.xcbkptlist ├── iOSTests ├── CGVectorOperatorsTest.swift ├── Info.plist ├── NSLocale+Swizzling.swift ├── NibViewTest.swift ├── StringWordsTest.swift ├── TestStoryboard.storyboard ├── TestViewWithOwner.xib ├── TestViewWithoutOwner.xib ├── TimeIntervalConverterArabicTest.swift ├── TimeIntervalConverterTest.swift ├── UIButtonStateTests.swift ├── UIImageColorTest.swift ├── UIImageViewRotationTests.swift └── UIViewXibTest.swift ├── install_swiftlint.sh └── tvOSTests ├── Info.plist └── ViaSwiftUtilsTests_tvOSTests.swift /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | build/ 4 | *.pbxuser 5 | !default.pbxuser 6 | *.mode1v3 7 | !default.mode1v3 8 | *.mode2v3 9 | !default.mode2v3 10 | *.perspectivev3 11 | !default.perspectivev3 12 | */xcuserdata/* 13 | *.xccheckout 14 | *.moved-aside 15 | DerivedData 16 | *.hmap 17 | *.ipa 18 | *.xcuserstate 19 | 20 | .DS_Store 21 | 22 | # CocoaPods 23 | # 24 | # We recommend against adding the Pods directory to your .gitignore. However 25 | # you should judge for yourself, the pros and cons are mentioned at: 26 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 27 | # 28 | Pods/ 29 | #*.lock 30 | 31 | # SPM 32 | 33 | .build 34 | /.previous-build 35 | 36 | # Carthage 37 | 38 | Carthage/* 39 | 40 | # Build 41 | 42 | *.log 43 | *.csv 44 | *.dat 45 | *.out 46 | *.pid 47 | *.rdb 48 | *.pyc 49 | 50 | Rakefile 51 | 52 | # fastlane specific 53 | fastlane/report.xml 54 | 55 | # deliver temporary files 56 | fastlane/Preview.html 57 | 58 | # snapshot generated screenshots 59 | fastlane/screenshots/**/*.png 60 | fastlane/screenshots/screenshots.html 61 | 62 | # scan temporary files 63 | fastlane/test_output 64 | 65 | # Local Fastlane Settings 66 | fastlane/.env.local 67 | 68 | # Node.js 69 | Sketch/node_modules/ 70 | .jshintrc 71 | /tmp 72 | 73 | # Sigh generated files 74 | *.mobileprovision 75 | *.dSYM.zip 76 | -------------------------------------------------------------------------------- /.swiftlint.yml: -------------------------------------------------------------------------------- 1 | included: 2 | - Sources 3 | - iOSTests 4 | - Example 5 | 6 | disabled_rules: # rule identifiers to exclude from running 7 | - trailing_whitespace 8 | 9 | opt_in_rules: 10 | - anyobject_protocol 11 | - array_init 12 | - attributes 13 | - closure_body_length 14 | - closure_end_indentation 15 | - closure_spacing 16 | - collection_alignment 17 | - contains_over_filter_count 18 | - contains_over_filter_is_empty 19 | - contains_over_first_not_nil 20 | - contains_over_range_nil_comparison 21 | - convenience_type 22 | - discouraged_optional_boolean 23 | - discouraged_optional_collection 24 | - empty_collection_literal 25 | - empty_count 26 | - empty_enum_arguments 27 | - empty_string 28 | - empty_xctest_method 29 | - expiring_todo 30 | - explicit_init 31 | - extension_access_modifier 32 | - fallthrough 33 | - fatal_error_message 34 | - file_header 35 | - file_name 36 | - first_where 37 | - flatmap_over_map_reduce 38 | - force_unwrapping 39 | - function_default_parameter_at_end 40 | - identical_operands 41 | - implicit_return 42 | - implicitly_unwrapped_optional 43 | - joined_default_parameter 44 | - last_where 45 | - legacy_multiple 46 | - legacy_random 47 | - let_var_whitespace 48 | - literal_expression_end_indentation 49 | - lower_acl_than_parent 50 | - modifier_order 51 | - multiline_arguments 52 | - multiline_function_chains 53 | - multiline_literal_brackets 54 | - multiline_parameters 55 | - multiline_parameters_brackets 56 | - nimble_operator 57 | - no_grouping_extension 58 | - nslocalizedstring_require_bundle 59 | - number_separator 60 | - object_literal 61 | - operator_usage_whitespace 62 | - overridden_super_call 63 | - override_in_extension 64 | - pattern_matching_keywords 65 | - prefixed_toplevel_constant 66 | - private_action 67 | - private_outlet 68 | - prohibited_super_call 69 | - quick_discouraged_call 70 | - quick_discouraged_focused_test 71 | - quick_discouraged_pending_test 72 | - raw_value_for_camel_cased_codable_enum 73 | - reduce_into 74 | - redundant_nil_coalescing 75 | - required_enum_case 76 | - single_test_class 77 | - sorted_first_last 78 | - sorted_imports 79 | - strict_fileprivate 80 | - switch_case_on_newline 81 | - toggle_bool 82 | - trailing_closure 83 | - unavailable_function 84 | - unneeded_parentheses_in_closure_argument 85 | - unowned_variable_capture 86 | - untyped_error_in_catch 87 | - vertical_parameter_alignment_on_call 88 | - xct_specific_matcher 89 | - yoda_condition 90 | 91 | excluded: # paths to ignore during linting. overridden by `included`. 92 | - Carthage 93 | - Submodules 94 | 95 | closure_body_length: 96 | warning: 30 97 | error: 40 98 | 99 | number_separator: 100 | minimum_length: 5 101 | 102 | modifier_order: 103 | preferred_modifier_order: 104 | - acl 105 | - override 106 | - final 107 | 108 | line_length: 109 | warning: 160 110 | error: 200 111 | ignores_comments: true 112 | 113 | type_body_length: 114 | warning: 300 115 | error: 400 116 | 117 | function_body_length: 118 | warning: 60 119 | error: 120 120 | 121 | vertical_whitespace: 122 | max_empty_lines: 2 123 | 124 | file_header: 125 | required_pattern: | 126 | \/\/ 127 | \/\/ .*?\.swift 128 | \/\/ ViaSwiftUtils 129 | \/\/ 130 | \/\/ Copyright \d{4} Viacom, Inc. 131 | \/\/ 132 | \/\/ Licensed under the Apache License, Version 2\.0 \(the "License"\); 133 | \/\/ you may not use this file except in compliance with the License\. 134 | \/\/ You may obtain a copy of the License at 135 | \/\/ 136 | \/\/ http:\/\/www.apache.org\/licenses\/LICENSE-2\.0 137 | \/\/ 138 | \/\/ Unless required by applicable law or agreed to in writing, software 139 | \/\/ distributed under the License is distributed on an "AS IS" BASIS, 140 | \/\/ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied\. 141 | \/\/ See the License for the specific language governing permissions and 142 | \/\/ limitations under the License\. 143 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | # references: 2 | # * http://www.objc.io/issue-6/travis-ci.html 3 | 4 | jobs: 5 | cache: bundler 6 | include: 7 | # Linux build 8 | - os: linux 9 | dist: xenial 10 | before_install: 11 | - git clone https://github.com/IBM-Swift/Package-Builder.git 12 | script: ./Package-Builder/build-package.sh -projectDir $TRAVIS_BUILD_DIR 13 | 14 | # Mac OS latest Xcode build and test 15 | - os: osx 16 | osx_image: xcode11.1 17 | language: objective-c 18 | xcode_workspace: ViaSwiftUtils.xcworkspace 19 | xcode_scheme: ViaSwiftUtils_iOS 20 | xcode_destination: platform=iOS Simulator,OS=13.1,name=iPhone 11 Pro 21 | install: 22 | - ./install_swiftlint.sh 23 | - bundle install 24 | script: 25 | - bundle exec danger 26 | - set -o pipefail && xcodebuild test -workspace ViaSwiftUtils.xcworkspace -scheme ViaSwiftUtils_iOS -destination "platform=iOS Simulator,OS=13.1,name=iPhone 11 Pro" | xcpretty 27 | 28 | # Mac OS older Xcode build 29 | - os: osx 30 | osx_image: xcode10.3 31 | language: objective-c 32 | xcode_workspace: ViaSwiftUtils.xcworkspace 33 | xcode_scheme: ViaSwiftUtils_iOS 34 | xcode_destination: platform=iOS Simulator,OS=12.4,name=iPhone 8 35 | install: 36 | - ./install_swiftlint.sh 37 | - bundle install 38 | -------------------------------------------------------------------------------- /Dangerfile: -------------------------------------------------------------------------------- 1 | # Sometimes it's a README fix, or something like that - which isn't relevant for 2 | # including in a project's CHANGELOG for example 3 | declared_trivial = github.pr_title.include? "#trivial" 4 | 5 | # Run swiftlint and output results inline with the code changes 6 | swiftlint.config_file = '.swiftlint.yml' 7 | swiftlint.lint_files inline_mode: true 8 | 9 | # Make it more obvious that a PR is a work in progress and shouldn't be merged yet 10 | warn("PR is classed as Work in Progress") if github.pr_title.include? "[WIP]" 11 | 12 | # Warn when there is a big PR 13 | warn("Big PR") if git.lines_of_code > 500 14 | 15 | # Don't let testing shortcuts get into master by accident 16 | fail("fdescribe left in tests") if `grep -r fdescribe specs/ `.length > 1 17 | fail("fit left in tests") if `grep -r fit specs/ `.length > 1 18 | 19 | # Mainly to encourage writing up some reasoning about the PR, rather than just leaving a title. 20 | if github.pr_body.length < 3 && git.lines_of_code > 10 21 | warn("Please provide a summary in the Pull Request description") 22 | end 23 | 24 | ## Let's check if there are any changes in the project folder 25 | has_app_changes = !git.modified_files.grep(/Sources/).empty? 26 | ## Then, we should check if tests are updated 27 | has_test_changes = !git.modified_files.grep(/iOSTests/).empty? 28 | ## Finally, let's combine them and put extra condition 29 | ## for changed number of lines of code 30 | if has_app_changes && !(has_test_changes) && git.lines_of_code > 20 31 | warn("Tests were not updated", sticky: false) 32 | end 33 | -------------------------------------------------------------------------------- /Example Watch Extension/Assets.xcassets/README__ignoredByTemplate__: -------------------------------------------------------------------------------- 1 | Did you know that git does not support storing empty directories? 2 | -------------------------------------------------------------------------------- /Example Watch Extension/ExtensionDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExtensionDelegate.swift 3 | // 4 | // Copyright 2017 Viacom, Inc. 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | import WatchKit 19 | 20 | class ExtensionDelegate: NSObject, WKExtensionDelegate { 21 | 22 | func applicationDidFinishLaunching() { 23 | // Perform any final initialization of your application. 24 | } 25 | 26 | func applicationDidBecomeActive() { 27 | } 28 | 29 | func applicationWillResignActive() { 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /Example Watch Extension/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ViaSwiftUtils Example Watch Extension 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | XPC! 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | NSExtension 26 | 27 | NSExtensionAttributes 28 | 29 | WKAppBundleIdentifier 30 | com.vimn.ViaSwiftUtils-Example.watchkitapp 31 | 32 | NSExtensionPointIdentifier 33 | com.apple.watchkit 34 | 35 | WKExtensionDelegateClassName 36 | $(PRODUCT_MODULE_NAME).ExtensionDelegate 37 | 38 | 39 | -------------------------------------------------------------------------------- /Example Watch Extension/InterfaceController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // InterfaceController.swift 3 | // ViaSwiftUtils Example Watch Extension 4 | // 5 | // Copyright 2017 Viacom, Inc. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | import Foundation 20 | import WatchKit 21 | 22 | class InterfaceController: WKInterfaceController { 23 | 24 | override func awake(withContext context: Any?) { 25 | super.awake(withContext: context) 26 | 27 | // Configure interface objects here. 28 | } 29 | 30 | override func willActivate() { 31 | // This method is called when watch view controller is about to be visible to user 32 | super.willActivate() 33 | } 34 | 35 | override func didDeactivate() { 36 | // This method is called when watch view controller is no longer visible 37 | super.didDeactivate() 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /Example Watch Extension/NotificationController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NotificationController.swift 3 | // 4 | // Copyright 2017 Viacom, Inc. 5 | // 6 | // Licensed under the Apache License, Version 2.0 (the "License"); 7 | // you may not use this file except in compliance with the License. 8 | // You may obtain a copy of the License at 9 | // 10 | // http://www.apache.org/licenses/LICENSE-2.0 11 | // 12 | // Unless required by applicable law or agreed to in writing, software 13 | // distributed under the License is distributed on an "AS IS" BASIS, 14 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 | // See the License for the specific language governing permissions and 16 | // limitations under the License. 17 | 18 | import Foundation 19 | import WatchKit 20 | 21 | class NotificationController: WKUserNotificationInterfaceController { 22 | 23 | override init() { 24 | // Initialize variables here. 25 | super.init() 26 | 27 | // Configure interface objects here. 28 | } 29 | 30 | override func willActivate() { 31 | // This method is called when watch view controller is about to be visible to user 32 | super.willActivate() 33 | } 34 | 35 | override func didDeactivate() { 36 | // This method is called when watch view controller is no longer visible 37 | super.didDeactivate() 38 | } 39 | 40 | /* 41 | override func didReceiveLocalNotification(localNotification: UILocalNotification, 42 | withCompletion completionHandler: ((WKUserNotificationInterfaceType) -> Void)) { 43 | // This method is called when a local notification needs to be presented. 44 | // Implement it if you use a dynamic notification interface. 45 | // Populate your dynamic notification interface as quickly as possible. 46 | // 47 | // After populating your dynamic notification interface call the completion block. 48 | completionHandler(.Custom) 49 | } 50 | */ 51 | 52 | /* 53 | override func didReceiveRemoteNotification(remoteNotification: [NSObject : AnyObject], 54 | withCompletion completionHandler: ((WKUserNotificationInterfaceType) -> Void)) { 55 | // This method is called when a remote notification needs to be presented. 56 | // Implement it if you use a dynamic notification interface. 57 | // Populate your dynamic notification interface as quickly as possible. 58 | // 59 | // After populating your dynamic notification interface call the completion block. 60 | completionHandler(.Custom) 61 | } 62 | */ 63 | } 64 | -------------------------------------------------------------------------------- /Example Watch Extension/PushNotificationPayload.apns: -------------------------------------------------------------------------------- 1 | { 2 | "aps": { 3 | "alert": { 4 | "body": "Test message", 5 | "title": "Optional title" 6 | }, 7 | "category": "myCategory" 8 | }, 9 | 10 | "WatchKit Simulator Actions": [ 11 | { 12 | "title": "First Button", 13 | "identifier": "firstButtonAction" 14 | } 15 | ], 16 | 17 | "customKey": "Use this file to define a testing payload for your notifications. The aps dictionary specifies the category, alert text and title. The WatchKit Simulator Actions array can provide info for one or more action buttons in addition to the standard Dismiss button. Any other top level keys are custom payload. If you have multiple such JSON files in your project, you'll be able to select them when choosing to debug the notification interface of your Watch App." 18 | } 19 | -------------------------------------------------------------------------------- /Example Watch/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "size" : "24x24", 5 | "idiom" : "watch", 6 | "scale" : "2x", 7 | "role" : "notificationCenter", 8 | "subtype" : "38mm" 9 | }, 10 | { 11 | "size" : "27.5x27.5", 12 | "idiom" : "watch", 13 | "scale" : "2x", 14 | "role" : "notificationCenter", 15 | "subtype" : "42mm" 16 | }, 17 | { 18 | "size" : "29x29", 19 | "idiom" : "watch", 20 | "role" : "companionSettings", 21 | "scale" : "2x" 22 | }, 23 | { 24 | "size" : "29x29", 25 | "idiom" : "watch", 26 | "role" : "companionSettings", 27 | "scale" : "3x" 28 | }, 29 | { 30 | "size" : "40x40", 31 | "idiom" : "watch", 32 | "scale" : "2x", 33 | "role" : "appLauncher", 34 | "subtype" : "38mm" 35 | }, 36 | { 37 | "size" : "86x86", 38 | "idiom" : "watch", 39 | "scale" : "2x", 40 | "role" : "quickLook", 41 | "subtype" : "38mm" 42 | }, 43 | { 44 | "size" : "98x98", 45 | "idiom" : "watch", 46 | "scale" : "2x", 47 | "role" : "quickLook", 48 | "subtype" : "42mm" 49 | } 50 | ], 51 | "info" : { 52 | "version" : 1, 53 | "author" : "xcode" 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Example Watch/Base.lproj/Interface.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /Example Watch/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleDisplayName 8 | ViaSwiftUtils Example 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | APPL 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleSignature 22 | ???? 23 | CFBundleVersion 24 | 1 25 | UISupportedInterfaceOrientations 26 | 27 | UIInterfaceOrientationPortrait 28 | UIInterfaceOrientationPortraitUpsideDown 29 | 30 | WKCompanionAppBundleIdentifier 31 | com.vimn.ViaSwiftUtils-Example 32 | WKWatchKitApp 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /Example/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // ViaSwiftUtils 4 | // 5 | // Copyright 2017 Viacom, Inc. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | import UIKit 20 | 21 | @UIApplicationMain 22 | class AppDelegate: UIResponder, UIApplicationDelegate { 23 | 24 | var window: UIWindow? 25 | 26 | //swiftlint:disable:next discouraged_optional_collection 27 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool { 28 | // Override point for customization after application launch. 29 | return true 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Example/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/first.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "first.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/first.imageset/first.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ViacomInc/ViaSwiftUtils/28414e8385a86102121b726dafb99daa9cfe6a1b/Example/Assets.xcassets/first.imageset/first.pdf -------------------------------------------------------------------------------- /Example/Assets.xcassets/puppy.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "puppy.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "puppy@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "version" : 1, 20 | "author" : "xcode" 21 | } 22 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/puppy.imageset/puppy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ViacomInc/ViaSwiftUtils/28414e8385a86102121b726dafb99daa9cfe6a1b/Example/Assets.xcassets/puppy.imageset/puppy.png -------------------------------------------------------------------------------- /Example/Assets.xcassets/puppy.imageset/puppy@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ViacomInc/ViaSwiftUtils/28414e8385a86102121b726dafb99daa9cfe6a1b/Example/Assets.xcassets/puppy.imageset/puppy@2x.png -------------------------------------------------------------------------------- /Example/Assets.xcassets/second.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "second.pdf" 6 | } 7 | ], 8 | "info" : { 9 | "version" : 1, 10 | "author" : "xcode" 11 | } 12 | } -------------------------------------------------------------------------------- /Example/Assets.xcassets/second.imageset/second.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ViacomInc/ViaSwiftUtils/28414e8385a86102121b726dafb99daa9cfe6a1b/Example/Assets.xcassets/second.imageset/second.pdf -------------------------------------------------------------------------------- /Example/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /Example/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | Helvetica 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 37 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 89 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | -------------------------------------------------------------------------------- /Example/FirstViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FirstViewController.swift 3 | // ViaSwiftUtils 4 | // 5 | // Copyright 2017 Viacom, Inc. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | import UIKit 20 | import ViaSwiftUtils 21 | 22 | class FirstViewController: UIViewController { 23 | 24 | @IBOutlet private weak var imageView: UIImageView! 25 | 26 | override func viewDidLoad() { 27 | super.viewDidLoad() 28 | 29 | let rect = CGRect(x: 0, y: 0, width: 10, height: 5) 30 | print("aspect ratio: \(rect.aspectRatio)") 31 | 32 | var mutableNumberList = [1, 2, 3, 4, 5, 6] 33 | mutableNumberList.shuffleInPlace() 34 | print("shuffled numbers: \( mutableNumberList )") 35 | 36 | imageView.image = #imageLiteral(resourceName: "puppy").cornersRounded(usingRadius: 30) 37 | } 38 | 39 | override func didReceiveMemoryWarning() { 40 | super.didReceiveMemoryWarning() 41 | // Dispose of any resources that can be recreated. 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /Example/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | LSRequiresIPhoneOS 24 | 25 | UILaunchStoryboardName 26 | LaunchScreen 27 | UIMainStoryboardFile 28 | Main 29 | UIRequiredDeviceCapabilities 30 | 31 | armv7 32 | 33 | UIStatusBarTintParameters 34 | 35 | UINavigationBar 36 | 37 | Style 38 | UIBarStyleDefault 39 | Translucent 40 | 41 | 42 | 43 | UISupportedInterfaceOrientations 44 | 45 | UIInterfaceOrientationPortrait 46 | UIInterfaceOrientationLandscapeLeft 47 | UIInterfaceOrientationLandscapeRight 48 | 49 | UISupportedInterfaceOrientations~ipad 50 | 51 | UIInterfaceOrientationPortrait 52 | UIInterfaceOrientationPortraitUpsideDown 53 | UIInterfaceOrientationLandscapeLeft 54 | UIInterfaceOrientationLandscapeRight 55 | 56 | 57 | 58 | -------------------------------------------------------------------------------- /Example/SecondViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SecondViewController.swift 3 | // ViaSwiftUtils 4 | // 5 | // Copyright 2017 Viacom, Inc. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | import UIKit 20 | 21 | class SecondViewController: UIViewController { 22 | 23 | override func viewDidLoad() { 24 | super.viewDidLoad() 25 | // Do any additional setup after loading the view, typically from a nib. 26 | } 27 | 28 | override func didReceiveMemoryWarning() { 29 | super.didReceiveMemoryWarning() 30 | // Dispose of any resources that can be recreated. 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem "danger" 4 | gem "danger-swiftlint" 5 | gem "unicode-display_width" 6 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | addressable (2.7.0) 5 | public_suffix (>= 2.0.2, < 5.0) 6 | claide (1.0.3) 7 | claide-plugins (0.9.2) 8 | cork 9 | nap 10 | open4 (~> 1.3) 11 | colored2 (3.1.2) 12 | cork (0.3.0) 13 | colored2 (~> 3.1) 14 | danger (6.0.9) 15 | claide (~> 1.0) 16 | claide-plugins (>= 0.9.2) 17 | colored2 (~> 3.1) 18 | cork (~> 0.1) 19 | faraday (~> 0.9) 20 | faraday-http-cache (~> 2.0) 21 | git (~> 1.5) 22 | kramdown (~> 2.0) 23 | kramdown-parser-gfm (~> 1.0) 24 | no_proxy_fix 25 | octokit (~> 4.7) 26 | terminal-table (~> 1) 27 | danger-swiftlint (0.23.0) 28 | danger 29 | rake (> 10) 30 | thor (~> 0.19) 31 | faraday (0.15.4) 32 | multipart-post (>= 1.2, < 3) 33 | faraday-http-cache (2.0.0) 34 | faraday (~> 0.8) 35 | git (1.5.0) 36 | kramdown (2.1.0) 37 | kramdown-parser-gfm (1.1.0) 38 | kramdown (~> 2.0) 39 | multipart-post (2.1.1) 40 | nap (1.1.0) 41 | no_proxy_fix (0.1.2) 42 | octokit (4.14.0) 43 | sawyer (~> 0.8.0, >= 0.5.3) 44 | open4 (1.3.4) 45 | public_suffix (4.0.1) 46 | rake (12.3.3) 47 | sawyer (0.8.2) 48 | addressable (>= 2.3.5) 49 | faraday (> 0.8, < 2.0) 50 | terminal-table (1.8.0) 51 | unicode-display_width (~> 1.1, >= 1.1.1) 52 | thor (0.20.3) 53 | unicode-display_width (1.6.0) 54 | 55 | PLATFORMS 56 | ruby 57 | 58 | DEPENDENCIES 59 | danger 60 | danger-swiftlint 61 | unicode-display_width 62 | 63 | BUNDLED WITH 64 | 1.17.3 65 | -------------------------------------------------------------------------------- /Images/puppy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ViacomInc/ViaSwiftUtils/28414e8385a86102121b726dafb99daa9cfe6a1b/Images/puppy.png -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2017 Viacom, Inc. 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:3.1 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "ViaSwiftUtils", 7 | exclude: ["iOSTests", "Example", "Carthage", "Images", "Sources/ViaSwiftUtils/UIKit"] 8 | ) 9 | -------------------------------------------------------------------------------- /Package@swift-4.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:4.0 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "ViaSwiftUtils", 7 | // exclude: ["iOSTests", "Example","Carthage","Images","Sources/ViaSwiftUtils/UIKit"], 8 | products: [ 9 | .library( 10 | name: "ViaSwiftUtils", 11 | targets: ["ViaSwiftUtils"] 12 | ) 13 | ], 14 | dependencies: [], 15 | targets: [ 16 | .target( 17 | name: "ViaSwiftUtils", 18 | dependencies: [], 19 | path: "Sources/ViaSwiftUtils/Foundation" 20 | ), 21 | .testTarget( 22 | name: "ViaSwiftUtilsTests", 23 | dependencies: ["ViaSwiftUtils"] 24 | ) 25 | ] 26 | ) 27 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ViaSwiftUtils 2 | 3 | [![Swift 5.0](https://img.shields.io/badge/Swift-5.0-orange.svg?style=flat)](https://swift.org/) 4 | [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage) 5 | [![Version](https://img.shields.io/cocoapods/v/ViaSwiftUtils.svg?style=flat)](http://cocoapods.org/pods/ViaSwiftUtils) 6 | [![Platform](https://img.shields.io/cocoapods/p/ViaSwiftUtils.svg?style=flat)](http://cocoapods.org/pods/ViaSwiftUtils) 7 | [![Build Status](https://travis-ci.org/ViacomInc/ViaSwiftUtils.svg?branch=master)](https://travis-ci.org/ViacomInc/ViaSwiftUtils) 8 | [![License](https://img.shields.io/cocoapods/l/ViaSwiftUtils.svg?style=flat)](http://cocoapods.org/pods/ViaSwiftUtils) 9 | 10 | What can `ViaSwiftUtils` do for you? Here are some examples we compiled: 11 | 12 | 13 | Tired of spelling out `NSLocalizedString` every time you localize a string? 14 | ```swift 15 | title = "HomePage.title".localized 16 | print(title) // 'Main', '頭版', 'صفحه نخست', 'Startseite' 17 | ``` 18 | 19 | -------- 20 | 21 | Want to find the longest word in a string? 22 | ```swift 23 | print(germanText.longestWord()) 24 | // 'Verkehrsinfrastrukturfinanzierungsgesellschaft' 25 | ``` 26 | 27 | -------- 28 | 29 | Need an array (or any other `MutableCollection`) shuffled? [^3] 30 | ```swift 31 | var numbers = [1, 2, 3, 4, 5, 6] 32 | numbers.shuffleInPlace() //e.g. [4, 1, 5, 2, 6, 3] 33 | ``` 34 | 35 | -------- 36 | 37 | Want all the unique entries in a `Sequence` with `Hashable` elements? 38 | ```swift 39 | let emojis = ["😀", "👀", "😱", "😡", "👀", "😀", "👀", "😱"] 40 | let uniqueEmojis = emojis.unique() // ["😀", "👀", "😱", "😡"] 41 | ``` 42 | 43 | -------- 44 | 45 | Is dealing with `TimeInterval` inconvenient? 46 | ```swift 47 | let twelveDays = 12 * TimeInterval.day 48 | let minutes = twelveDays / TimeInterval.minute // 17280 49 | ``` 50 | 51 | -------- 52 | 53 | Need to create an image with rounded corners from an existing image? 54 | 55 | ![Rounded image](/Images/puppy.png) 56 | 57 | -------- 58 | 59 | Does one of the views in your app need to rotate? 60 | ```swift 61 | someView.startRotating() 62 | // ... 63 | someView.stopRotating() 64 | ``` 65 | 66 | -------- 67 | 68 | And more date helper methods & variables: 69 | ```swift 70 | let components = DateComponents(calendar: gregorian, era: 0, 71 | year: 44, month: 3, day: 15, hour: 13) 72 | let idesOfMarch = gregorian.date(from: components)! 73 | let weekDay = idesOfMarch.dayOfWeek // 6 74 | let daysSince = idesOfMarch.days(to: Date()) // 752533 75 | ``` 76 | 77 |   78 | 79 | These are just a few examples. We are expanding the library continuously and we accept Pull-Requests ☺️ 80 | 81 |   82 | 83 | 84 | ## Requirements 85 | 86 | #### Versions support 87 | 88 | | **Swift** | **Xcode** | **ViaSwiftUtils** | 89 | | -- | -- | -- | 90 | | 4.2 - 5.0 | 10.X, 11.X | 2.1.0 | 91 | | 4.0 | 10.X | 2.0.5 | 92 | | 3.2 - 4.1 | 9.X | 2.0.5 | 93 | 94 |   95 | 96 | ## How to install [ViaSwiftUtils](https://github.com/ViacomInc/ViaSwiftUtils) 97 | 98 | #### Via Cocoapods 99 | Add the following line to your `Podfile`. 100 | Remember you'll need to enable `use_frameworks!`. 101 | ``` 102 | pod 'ViaSwiftUtils', '2.1.0' 103 | ``` 104 | 105 | 106 | #### Via Carthage 107 | Add the following line to your `Cartfile` 108 | ``` 109 | github "ViacomInc/ViaSwiftUtils" == 2.1.0 110 | ``` 111 | 112 | #### Via Swift package manager 113 | 114 | Add the following to your `Package.swift` 115 | ```swift 116 | dependencies: [ 117 | .package(url: "https://github.com/ViacomInc/ViaSwiftUtils.git", .exact("2.1.0")), 118 | ] 119 | 120 | .target(name: <#yourTarget#>, dependencies: ["ViaSwiftUtils"]), 121 | ``` 122 | I should mention that only the `Foundation` functions that are part of `ViaSwiftUtils` work via SPM, as `UIKit` is not available on Linux or MacOS. 123 | 124 | 125 | #### Importing 126 | After you installed the framework via one of the 3 above methods, import it in your `.swift` file. 127 | ```swift 128 | import ViaSwiftUtils 129 | ``` 130 | 131 |   132 | 133 | 134 | ## License 135 | 136 | Licensed under the Apache License, Version 2.0 (the "License"); 137 | you may not use this file except in compliance with the License. 138 | You may obtain a copy of the License at 139 | 140 | http://www.apache.org/licenses/LICENSE-2.0 141 | 142 | Unless required by applicable law or agreed to in writing, software 143 | distributed under the License is distributed on an "AS IS" BASIS, 144 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 145 | See the License for the specific language governing permissions and 146 | limitations under the License. 147 | -------------------------------------------------------------------------------- /Sources/Info-iOS.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Sources/Info-tvOS.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Sources/Info-watchOS.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Sources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | FMWK 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Sources/ViaSwiftUtils.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViaSwiftUtils iOS.h 3 | // ViaSwiftUtils iOS 4 | // 5 | // Copyright 2017 Viacom, Inc. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | #import 20 | 21 | //! Project version number for ViaSwiftUtils iOS. 22 | FOUNDATION_EXPORT double ViaSwiftUtils_iOSVersionNumber; 23 | 24 | //! Project version string for ViaSwiftUtils iOS. 25 | FOUNDATION_EXPORT const unsigned char ViaSwiftUtils_iOSVersionString[]; 26 | 27 | // In this header, you should import all the public headers of your framework using statements like #import 28 | 29 | //#import 30 | -------------------------------------------------------------------------------- /Sources/ViaSwiftUtils/Foundation/CGPoint+Operators.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CGPoint+Operators.swift 3 | // ViaSwiftUtils 4 | // 5 | // Copyright 2017 Viacom, Inc. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | #if os(OSX) || os(iOS) || os(tvOS) || os(watchOS) 20 | import CoreGraphics 21 | #endif 22 | import Foundation 23 | 24 | public extension CGPoint { 25 | /// Overloads + operator for two CGPoints addition 26 | /// 27 | /// - parameter lhs: as lef hand side parameter - CGPoint type 28 | /// - parameter rhs: as right hand side parameter - CGPoint type 29 | /// 30 | /// - returns: new CGPoint calculated from adding two given points 31 | static func + (lhs: CGPoint, rhs: CGPoint) -> CGPoint { 32 | return self.init(x: lhs.x + rhs.x, y: lhs.y + rhs.y) 33 | } 34 | 35 | /// Overloads - operator for two CGPoints substraction 36 | /// 37 | /// - parameter lhs: as lef hand side parameter - CGPoint type 38 | /// - parameter rhs: as right hand side parameter - CGPoint type 39 | /// 40 | /// - returns: new CGPoint calculated from substractiong two given points 41 | static func - (lhs: CGPoint, rhs: CGPoint) -> CGPoint { 42 | return self.init(x: lhs.x - rhs.x, y: lhs.y - rhs.y) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Sources/ViaSwiftUtils/Foundation/CGRect+AspectRatio.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CGRect+AspectRatio.swift 3 | // ViaSwiftUtils 4 | // 5 | // Copyright 2017 Viacom, Inc. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | // swiftlint:disable identifier_name 20 | 21 | #if os(OSX) || os(iOS) || os(tvOS) || os(watchOS) 22 | import CoreGraphics 23 | #endif 24 | import Foundation 25 | 26 | public extension CGRect { 27 | 28 | ///aspect ratio of the rect, i.e. width/height. For a height of 0 the aspect ratio is 0 29 | var aspectRatio: CGFloat { 30 | guard self.size.height > 0 else { return 0 } 31 | return self.size.width / self.size.height 32 | } 33 | 34 | /// linear combination of the receiver with otherRect 35 | /// - parameter otherRect: The other rect to which linearly combine the receiver 36 | /// - parameter value: linear factor, clamped to [0,1] 37 | /// - returns: A new CGRect that represents the linear combination 38 | func linearCombined(with otherRect: CGRect, by value: CGFloat) -> CGRect { 39 | let minMaxValue = min(1.0, max(0.0, value)) 40 | let x = (1 - minMaxValue) * self.origin.x + minMaxValue * otherRect.origin.x 41 | let y = (1 - minMaxValue) * self.origin.y + minMaxValue * otherRect.origin.y 42 | let width = (1 - minMaxValue) * self.size.width + minMaxValue * otherRect.size.width 43 | let height = (1 - minMaxValue) * self.size.height + minMaxValue * otherRect.size.height 44 | 45 | return CGRect(origin: CGPoint(x: x, y: y), size: CGSize(width: width, height: height)) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Sources/ViaSwiftUtils/Foundation/CountableRange+Random.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Range+Random.swift 3 | // ViaSwiftUtils 4 | // 5 | // Copyright 2017 Viacom, Inc. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | import Foundation 20 | 21 | public extension CountableRange { 22 | 23 | var arc4random: Bound { 24 | return lowerBound.advanced(by: Bound.Stride.arc4random_uniform(numericCast(count))) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Sources/ViaSwiftUtils/Foundation/Date+ComponentAccessors.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Date+ComponentAccessors.swift 3 | // ViaSwiftUtils 4 | // 5 | // Copyright 2017 Viacom, Inc. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | import Foundation 20 | 21 | public extension Date { 22 | 23 | /// returns the hour of the receiver 'NSDate' 24 | var hourOfDay: Int { 25 | return Calendar.current.component(.hour, from: self) 26 | } 27 | 28 | /// returns the day of the week of the receiver 'NSDate' 29 | var dayOfWeek: Int { 30 | return Calendar.current.component(.weekday, from: self) 31 | } 32 | 33 | /// returns the year of the receiver 'NSDate' as Int anno domini 34 | var year: Int { 35 | return Calendar.current.component(.year, from: self) 36 | } 37 | 38 | /// returns the year of the receiver NSDate as localized String anno domini and a fixed dateFormat 39 | var localizedYear: String { 40 | let formatter = DateFormatter() 41 | formatter.dateFormat = "y" 42 | return formatter.string(from: self) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Sources/ViaSwiftUtils/Foundation/Date+TimesBetweenDates.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSDate+TimesBetweenDates.swift 3 | // ViaSwiftUtils 4 | // 5 | // Copyright 2017 Viacom, Inc. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | import Foundation 20 | 21 | public extension Date { 22 | 23 | /// returns the days from the receiver `Date` to the parameter 24 | /// - parameter date: the date to compare too 25 | /// - returns: the days between the two dates as Int 26 | func days(to date: Date) -> Int? { 27 | let calendar = Calendar.current 28 | let components = calendar.dateComponents([.day], from: self, to: date) 29 | return components.day 30 | } 31 | 32 | /// returns the minutes from the receiver `Date` to the parameter 33 | /// - parameter date: the date to compare too 34 | /// - returns: the minutes between the two dates as Int 35 | func minutes(to date: Date) -> Int? { 36 | let calendar = Calendar.current 37 | let components = calendar.dateComponents([.minute], from: self, to: date) 38 | return components.minute 39 | } 40 | 41 | /// returns the seconds from the receiver `Date` to the parameter 42 | /// - parameter date: the date to compare too 43 | /// - returns: the seconds between the two dates as Int 44 | func seconds(to date: Date) -> Int? { 45 | let calendar = Calendar.current 46 | let components = calendar.dateComponents([.second], from: self, to: date) 47 | return components.second 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Sources/ViaSwiftUtils/Foundation/Dictionary+MapValues.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Dictionary+MapValues.swift 3 | // ViaSwiftUtils 4 | // 5 | // Copyright 2017 Viacom, Inc. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | import Foundation 20 | 21 | public extension Dictionary { 22 | 23 | /// Merges the Dictionary with a Sequence of (Key, Value) 24 | /// - parameter other: The Sequence that is merged in 25 | mutating func merge(_ other: S) where S.Iterator.Element == Element { 26 | for (key, value) in other { 27 | self[key] = value 28 | } 29 | } 30 | 31 | /// Initializes a Dictionary with a Sequence of (Key, Value) 32 | /// - parameter sequence: The Sequence of elements that this Dictionary will contain after initialization 33 | init(_ sequence: S) where S.Iterator.Element == Element { 34 | self = [:] 35 | self.merge(sequence) 36 | } 37 | 38 | /// Maps the values of a dictionary into new values, creating a new dictionary with the same keys 39 | /// - parameter transform: The transformation the values will be going through 40 | /// - returns: new dictionary with the mapped values but the same keys 41 | func mapValues(_ transform: (Value) -> NewValue) -> [Key: NewValue] { 42 | return [Key: NewValue](map { key, value in 43 | (key, transform(value)) 44 | }) 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Sources/ViaSwiftUtils/Foundation/ForceUnwrappingOperators.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ForceUnwrappingOperators.swift 3 | // ViaSwiftUtils 4 | // 5 | // Copyright 2017 Viacom, Inc. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | import Foundation 20 | 21 | infix operator !! 22 | 23 | /// Use like this: 24 | /// 25 | /// `let s = "foo"` 26 | /// 27 | /// `i = Int(s) !! "Expecting integer, got \(s)"` 28 | /// 29 | /// - Parameters: 30 | /// - wrapped: The Optional(T) value that will be unwrapped 31 | /// - failureText: an autoclosure that will be printed in the case of failure 32 | /// - Returns: The Non-Optional value of Type T 33 | /// - Note: 34 | /// [chapter 'When to Force Unwrap' from Advanced Swift](https://www.objc.io/books/advanced-swift/) 35 | 36 | public func !! (wrapped: T?, failureText: @autoclosure () -> String) -> T { 37 | // swiftlint:disable:next identifier_name 38 | if let x = wrapped { return x } 39 | fatalError(failureText()) 40 | } 41 | 42 | infix operator !? 43 | 44 | /// Use like this: 45 | /// 46 | /// `let i = Int(s) !? "Expecting integer, got \(s)"` 47 | /// 48 | /// - Parameters: 49 | /// - wrapped: The Optional(T) value that will be unwrapped 50 | /// - failureText: an autoclosure that will be printed in the case of failure 51 | /// - Returns: The Non-Optional value of Type T 52 | /// - Note: 53 | /// [chapter 'When to Force Unwrap' from Advanced Swift](https://www.objc.io/books/advanced-swift/) 54 | public func !? (wrapped: T?, failureText: @autoclosure () -> String) -> T { 55 | assert(wrapped != nil, failureText()) 56 | return wrapped ?? 0 57 | } 58 | 59 | /// use like this: 60 | /// 61 | /// `let i = Array(s) !? "Expecting array, got \(s)"` 62 | /// 63 | /// - Parameters: 64 | /// - wrapped: The Optional(T) value that will be unwrapped 65 | /// - failureText: an autoclosure that will be printed in the case of failure 66 | /// - Returns: The Non-Optional value of Type T 67 | /// - Note: 68 | /// [chapter 'When to Force Unwrap' from Advanced Swift](https://www.objc.io/books/advanced-swift/) 69 | public func !? (wrapped: T?, failureText: @autoclosure () -> String) -> T { 70 | assert(wrapped != nil, failureText()) 71 | return wrapped ?? [] 72 | } 73 | 74 | /// use like this: 75 | /// 76 | /// `let i = String(s) !? "Expecting string, got \(s)"` 77 | /// 78 | /// - Parameters: 79 | /// - wrapped: The Optional(T) value that will be unwrapped 80 | /// - failureText: an autoclosure that will be printed in the case of failure 81 | /// - Returns: The Non-Optional value of Type T 82 | /// - Note: 83 | /// [chapter 'When to Force Unwrap' from Advanced Swift](https://www.objc.io/books/advanced-swift/) 84 | public func !? (wrapped: T?, failureText: @autoclosure () -> String) -> T { 85 | assert(wrapped != nil, failureText()) 86 | return wrapped ?? "" 87 | } 88 | 89 | /// for arbitraty types, with default value. Used in this way: 90 | /// 91 | /// `Int(s) !? (5, "Expected Integer")` 92 | /// 93 | /// - Parameters: 94 | /// - wrapped: The Optional(T) value that will be unwrapped 95 | /// - nilDefault: an tuple (value, text) containing a default value and an error Text 96 | /// - Returns: The Non-Optional value of Type T 97 | /// - Note: 98 | /// [chapter 'When to Force Unwrap' from Advanced Swift](https://www.objc.io/books/advanced-swift/) 99 | public func !? (wrapped: T?, nilDefault: @autoclosure () -> (value: T, text: String)) -> T { 100 | assert(wrapped != nil, nilDefault().text) 101 | return wrapped ?? nilDefault().value 102 | } 103 | 104 | /// for void methods optional chains, like in 105 | /// 106 | /// `var output: String? = nil` 107 | /// 108 | /// `output?.write("something") !? "Wasn't expecting chained nil here"` 109 | /// 110 | /// - Parameters: 111 | /// - wrapped: The Optional(T) value that will be unwrapped 112 | /// - failureText: an autoclosure that will be printed in the case of failure 113 | /// - Returns: The Non-Optional value of Type T 114 | /// - Note: 115 | /// [chapter 'When to Force Unwrap' from Advanced Swift](https://www.objc.io/books/advanced-swift/) 116 | public func !? (wrapped: ()?, failureText: @autoclosure () -> String) { 117 | assert(wrapped != nil, failureText()) 118 | } 119 | -------------------------------------------------------------------------------- /Sources/ViaSwiftUtils/Foundation/MutableCollection+Shuffle.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MutableCollection+Shuffle.swift 3 | // ViaSwiftUtils 4 | // 5 | // Copyright 2017 Viacom, Inc. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | import Foundation 20 | 21 | public extension MutableCollection where Index == Int { 22 | 23 | /// implements [FisherYates](https://en.wikipedia.org/wiki/Fisher–Yates_shuffle) to shuffle elements in place 24 | mutating func shuffleInPlace() { 25 | if count <= 1 { return } 26 | 27 | for index in indices.dropLast() { 28 | let randomIndex = (index..(_ observer: O) where O.Event == Event 24 | } 25 | -------------------------------------------------------------------------------- /Sources/ViaSwiftUtils/Foundation/ObserverType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ObserverType.swift 3 | // ViaSwiftUtils 4 | // 5 | // Copyright 2017 Viacom, Inc. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | import Foundation 20 | 21 | public protocol ObserverType { 22 | associatedtype Event 23 | 24 | func receive(_ event: Event) 25 | } 26 | -------------------------------------------------------------------------------- /Sources/ViaSwiftUtils/Foundation/RandomAccessCollection+SafeAccess.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Array+SafeAccess.swift 3 | // ViaSwiftUtils 4 | // 5 | // Copyright 2017 Viacom, Inc. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | import Foundation 20 | 21 | public extension RandomAccessCollection { 22 | 23 | /// Accesses a `RandomAccessCollection`, while being safe from out of bounds errors 24 | /// Important: Arbitrarily subscripting Arrays in Swift is discouraged, 25 | /// when practical, favor iteration like for-in, `map`, `first` etc 26 | /// - parameter index: The Index at which the access happens 27 | /// - returns: the element at `index`, if out of bounds returns nil 28 | subscript (safe index: Self.Index) -> Self.Iterator.Element? { 29 | return index >= self.startIndex && index < self.endIndex ? self[index] : nil 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /Sources/ViaSwiftUtils/Foundation/Sequence+Helpers.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Sequence+Helpers.swift 3 | // ViaSwiftUtils 4 | // 5 | // Copyright 2017 Viacom, Inc. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | import Foundation 20 | 21 | public extension Sequence where Iterator.Element: Hashable { 22 | 23 | /// Creates an array of unique elements from the elements of the collection 24 | /// - returns: an array containing the unique elements 25 | func unique() -> [Iterator.Element] { 26 | var seen: Set = [] 27 | return filter { element -> Bool in 28 | if seen.contains(element) { 29 | return false 30 | } 31 | 32 | seen.insert(element) 33 | return true 34 | } 35 | } 36 | 37 | } 38 | 39 | public extension Sequence { 40 | 41 | /// Logical method that returns true if at least one element fits the predicate 42 | /// - returns: a boolean indication whether at least one element fits the predicate 43 | func any(_ predicate: (Iterator.Element) throws -> Bool) rethrows -> Bool { 44 | for element in self where try predicate(element) { 45 | return true 46 | } 47 | return false 48 | } 49 | 50 | /// Logical method that returns true if all elements fit the predicate 51 | /// - returns: a boolean indication whether all elements fit the predicate 52 | func all(_ predicate: (Iterator.Element) throws -> Bool) rethrows -> Bool { 53 | for element in self where try !predicate(element) { 54 | return false 55 | } 56 | return true 57 | } 58 | 59 | /// Logical method that returns true if none of the elements fit the predicate 60 | /// - returns: a boolean indication whether none of the elements fit the predicate 61 | func none(_ predicate: (Iterator.Element) throws -> Bool) rethrows -> Bool { 62 | return try all { try !predicate($0) } 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /Sources/ViaSwiftUtils/Foundation/SignedInteger+Random.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SignedInteger+Random.swift 3 | // ViaSwiftUtils 4 | // 5 | // Copyright 2017 Viacom, Inc. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | import Foundation 20 | 21 | public extension SignedInteger { 22 | 23 | static func arc4random_uniform(_ upperBound: Self) -> Self { 24 | precondition(upperBound > 0 && Int64(upperBound) < UInt32.max, 25 | "arc4random_uniform only callable up to \(UInt32.max)") 26 | #if os(OSX) || os(iOS) || os(tvOS) || os(watchOS) 27 | return numericCast(Darwin.arc4random_uniform(numericCast(upperBound))) 28 | #elseif os(Linux) || CYGWIN 29 | srandom(UInt32(time(nil))) 30 | return numericCast(UInt32(random() % numericCast(upperBound))) 31 | #endif 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Sources/ViaSwiftUtils/Foundation/TimeInterval+Converter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TimeInterval+Converter.swift 3 | // ViaSwiftUtils 4 | // 5 | // Copyright 2017 Viacom, Inc. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | import Foundation 20 | 21 | extension TimeInterval { 22 | 23 | private static let formatter = DateFormatter() 24 | 25 | /// Returns a string representation of the time interval representing video or music playtime. 26 | /// In minutes and seconds, in `en_US` output is for example "4:36" for 4minutes and 36secs. 27 | /// uses same formatting as Apple Music app across all localizations 28 | public var asMediaDuration: String { 29 | let components = DateComponentsFormatter() 30 | components.zeroFormattingBehavior = .pad 31 | components.allowedUnits = [.minute, .second] 32 | components.unitsStyle = .positional 33 | components.calendar = Locale.current.calendar 34 | 35 | return components.string(from: self) ?? "?" 36 | } 37 | 38 | /// [Data Formatting Guide]: https://developer.apple.com/library/prerelease/content/documentation/Cocoa/Conceptual/DataFormatting/DataFormatting.html#//apple_ref/doc/uid/10000029i 39 | /// 40 | /// Convenience method returns a string representation of the time interval initialized by using a given format string as a template 41 | /// relative to 00:00:00 UTC on 1 January 1970. 42 | /// 43 | /// - Warning: For user facing strings use of `dateStyle`, `timeStyle` or `dateFormat(fromTemplate:options:locale:)` 44 | /// should be preferred over this method. 45 | /// 46 | /// See [Data Formatting Guide] for a list of the conversion specifiers permitted in date format strings. 47 | /// 48 | /// - Parameter format: The output format of string. 49 | /// - Parameter locale: (optional) locale to use for the string conversion. Defaults to en_US_POSIX. 50 | public func string(withFormat format: String, locale: Locale = Locale(identifier: "en_US_POSIX")) -> String { 51 | let date = Date(timeIntervalSince1970: self) 52 | TimeInterval.formatter.dateFormat = format 53 | TimeInterval.formatter.locale = locale 54 | 55 | return String(TimeInterval.formatter.string(from: date)) 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /Sources/ViaSwiftUtils/Foundation/TimeInterval+Intervals.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TimeInterval+Intervals.swift 3 | // ViaSwiftUtils 4 | // 5 | // Copyright 2017 Viacom, Inc. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | import Foundation 20 | 21 | public extension TimeInterval { 22 | static let second: TimeInterval = 1 23 | static let minute: TimeInterval = 60 * second 24 | static let hour: TimeInterval = 60 * minute 25 | static let day: TimeInterval = 24 * hour 26 | static let week: TimeInterval = 7 * day 27 | static let year: TimeInterval = 365 * day 28 | } 29 | -------------------------------------------------------------------------------- /Sources/ViaSwiftUtils/UIKit/Bundle+ApplicationInfo.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSBundle+Version.swift 3 | // ViaSwiftUtils 4 | // 5 | // Copyright 2017 Viacom, Inc. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | import Foundation 20 | 21 | public extension Bundle { 22 | 23 | /// fetches the CFBundleShortVersionString from the NSBundle.mainBundle 24 | /// - returns: the version as a String 25 | static var applicationVersion: String? { 26 | return Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as? String 27 | } 28 | 29 | /// fetches the CFBundleDisplayName from the NSBundle.mainBundle 30 | /// - returns: the version as a String 31 | static var applicationName: String? { 32 | return Bundle.main.object(forInfoDictionaryKey: "CFBundleDisplayName") as? String 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Sources/ViaSwiftUtils/UIKit/CGVector+Operators.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CGVector+Operators.swift 3 | // ViaSwiftUtils 4 | // 5 | // Copyright 2017 Viacom, Inc. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | import CoreGraphics 20 | import Foundation 21 | 22 | public extension CGVector { 23 | /// Overloads + operator for two CGVectors addition 24 | /// 25 | /// - parameter lhs: as lef hand side parameter - CGVector type 26 | /// - parameter rhs: as right hand side parameter - CGVector type 27 | /// 28 | /// - returns: new CGVector calculated from adding two given vectors 29 | static func + (lhs: CGVector, rhs: CGVector) -> CGVector { 30 | return self.init(dx: lhs.dx + rhs.dx, dy: lhs.dy + rhs.dy) 31 | } 32 | 33 | /// Overloads - operator for two CGVectors substraction 34 | /// 35 | /// - parameter lhs: as lef hand side parameter - CGVector type 36 | /// - parameter rhs: as right hand side parameter - CGVector type 37 | /// 38 | /// - returns: new CGVector calculated from substractiong two given vectors 39 | static func - (lhs: CGVector, rhs: CGVector) -> CGVector { 40 | return self.init(dx: lhs.dx - rhs.dx, dy: lhs.dy - rhs.dy) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Sources/ViaSwiftUtils/UIKit/NibView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NibView.swift 3 | // ViaSwiftUtils 4 | // 5 | // Copyright 2017 Viacom, Inc. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | import UIKit 20 | 21 | // MARK: - NibView Class 22 | 23 | /// This class is intended to be subclassed. If you provide a Xib-file inside the same Bundle the subclass 24 | /// lives in, this class will automatically find the layout file, load subviews from the xib and connect to 25 | /// the respective IBOutlets. The class is not intended to be used as is. 26 | open class NibView: UIView { 27 | 28 | private var didEncode: Bool = false 29 | private let didEncodeKey = "didEncodeKey" 30 | 31 | /// Designated initializer for UIView instance that use the nib file 32 | /// 33 | /// - parameter coder: The instance of class NSCoder 34 | /// 35 | public required init?(coder aDecoder: NSCoder) { 36 | super.init(coder: aDecoder) 37 | self.didEncode = aDecoder.decodeBool(forKey: didEncodeKey) 38 | if didEncode == false { 39 | self.loadAssociatedNibView() 40 | } 41 | } 42 | 43 | /// Designated initializer for UIView instance that uses the nib file 44 | /// 45 | /// - parameter frame: The frame of initialized instance 46 | /// 47 | override init(frame: CGRect) { 48 | super.init(frame: frame) 49 | self.loadAssociatedNibView() 50 | } 51 | 52 | open override func encode(with aCoder: NSCoder) { 53 | super.encode(with: aCoder) 54 | didEncode = true 55 | aCoder.encode(didEncode, forKey: didEncodeKey) 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /Sources/ViaSwiftUtils/UIKit/String+Localized.swift: -------------------------------------------------------------------------------- 1 | // 2 | // String+Localized.swift 3 | // ViaSwiftUtils 4 | // 5 | // Copyright 2017 Viacom, Inc. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | import Foundation 20 | 21 | public extension String { 22 | 23 | /// localized version of this string using it as a key in Localizable.strings in the main Bundle 24 | var localized: String { 25 | return NSLocalizedString(self, tableName: nil, bundle: Bundle.main, value: "", comment: "") 26 | } 27 | 28 | /// localized uppercase version of this string, with newline characters escaped 29 | var customLocalizedUppercaseString: String { 30 | return localizedUppercase.replacingOccurrences(of: "\\N", with: "\n") 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Sources/ViaSwiftUtils/UIKit/String+Words.swift: -------------------------------------------------------------------------------- 1 | // 2 | // String+Words.swift 3 | // ViaSwiftUtils 4 | // 5 | // Copyright 2017 Viacom, Inc. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | import Foundation 20 | 21 | public extension String { 22 | 23 | /// calculates the longest word in the sentence 24 | /// 25 | /// - returns: the longest word amongst source string sentence 26 | func longestWord() -> String { 27 | let range = startIndex.. newLongestWord.count ? word : newLongestWord 33 | } 34 | } 35 | 36 | return newLongestWord 37 | } 38 | 39 | /// counts all the words present in source sentence 40 | /// 41 | /// - returns: words count in a sentence 42 | func wordCount() -> Int { 43 | let range = startIndex.. Bool { 57 | return wordCount() == 1 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /Sources/ViaSwiftUtils/UIKit/UIButton+States.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIButton+States.swift 3 | // ViaSwiftUtils 4 | // 5 | // Copyright 2017 Viacom, Inc. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | import UIKit 20 | 21 | public extension UIButton { 22 | 23 | /// Convenience method to set a title for all possible states of the receiver 24 | /// - parameter title: The title being set 25 | func setTitleForAllStates(_ title: String?) { 26 | for controlstate in UIControl.State.allValues { 27 | setTitle(title, for: controlstate) 28 | } 29 | } 30 | 31 | /// Convenience method to set a background image for all possible states of the receiver 32 | /// - parameter image: The image being set for all states 33 | func setBackgroundImageForAllStates(_ image: UIImage?) { 34 | for controlstate in UIControl.State.allValues { 35 | setBackgroundImage(image, for: controlstate) 36 | } 37 | } 38 | 39 | /// Convenience method to set an image for all possible states of the receiver 40 | /// - parameter image: The image being set for all states 41 | func setImageForAllStates(_ image: UIImage?) { 42 | for controlstate in UIControl.State.allValues { 43 | setImage(image, for: controlstate) 44 | } 45 | } 46 | 47 | /// Convenience method to set a title color for all possible states of the receiver 48 | /// - parameter color: The color being set for all states 49 | func setTitleColorForAllStates(_ color: UIColor?) { 50 | for controlstate in UIControl.State.allValues { 51 | setTitleColor(color, for: controlstate) 52 | } 53 | } 54 | 55 | /// Convenience method to set images for all possible states of the receiver 56 | /// 57 | /// - parameter normal: The image for state .normal 58 | /// - parameter highlighted: The image for state .highlighted 59 | /// - parameter selected: The image for state .selected 60 | /// - parameter climax: The image for state [.selected, .highlighted] 61 | func setImagesForStates(normal: UIImage? = nil, highlighted: UIImage? = nil, selected: UIImage? = nil, climax: UIImage? = nil) { 62 | self.setImage(normal, for: .normal) 63 | self.setImage(highlighted, for: .highlighted) 64 | self.setImage(selected, for: .selected) 65 | self.setImage(climax, for: [.selected, .highlighted]) 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /Sources/ViaSwiftUtils/UIKit/UIControl+States.swift: -------------------------------------------------------------------------------- 1 | //swiftlint:disable:this file_name 2 | // 3 | // UIControl+States.swift 4 | // ViaSwiftUtils 5 | // 6 | // Copyright 2017 Viacom, Inc. 7 | // 8 | // Licensed under the Apache License, Version 2.0 (the "License"); 9 | // you may not use this file except in compliance with the License. 10 | // You may obtain a copy of the License at 11 | // 12 | // http://www.apache.org/licenses/LICENSE-2.0 13 | // 14 | // Unless required by applicable law or agreed to in writing, software 15 | // distributed under the License is distributed on an "AS IS" BASIS, 16 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 17 | // See the License for the specific language governing permissions and 18 | // limitations under the License. 19 | 20 | import UIKit 21 | 22 | public extension UIControl.State { 23 | 24 | /// array of all values of UIControlState 25 | static var allValues: [UIControl.State] { 26 | return [.normal, .highlighted, .selected, .disabled, .focused, .application, .reserved] 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Sources/ViaSwiftUtils/UIKit/UIImage+Color.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIImage+Color.swift 3 | // ViaSwiftUtils 4 | // 5 | // Copyright 2017 Viacom, Inc. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | import UIKit 20 | 21 | public extension UIImage { 22 | 23 | /// Convenience initializer to create an image with a uniform color 24 | /// 25 | /// - parameter color: The color of the image created 26 | /// - parameter size: The size of the image created 27 | convenience init?(color: UIColor?, size: CGSize = CGSize(width: 1.0, height: 1.0)) { 28 | defer { 29 | UIGraphicsEndImageContext() 30 | } 31 | let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height) 32 | UIGraphicsBeginImageContextWithOptions(size, false, 0) 33 | color?.setFill() 34 | UIRectFill(rect) 35 | UIGraphicsGetImageFromCurrentImageContext() 36 | guard let imageRef = UIGraphicsGetImageFromCurrentImageContext()?.cgImage else { 37 | return nil 38 | } 39 | self.init(cgImage: imageRef) 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /Sources/ViaSwiftUtils/UIKit/UIImage+Rounding.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIImageView+Rounding.swift 3 | // ViaSwiftUtils 4 | // 5 | // Copyright 2017 Viacom, Inc. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | import UIKit 20 | 21 | public extension UIImage { 22 | 23 | /// returns a rounded corner image, rerendered using the receiver 24 | /// - parameter radius: the radius of the rounded corners 25 | /// - returns: the image created by the method 26 | final func cornersRounded(usingRadius radius: CGFloat) -> UIImage? { 27 | UIGraphicsBeginImageContextWithOptions(size, false, 0) 28 | 29 | guard let context = UIGraphicsGetCurrentContext() else { 30 | print("WARNING: Failed to get current Graphics context") 31 | return nil 32 | } 33 | 34 | let rect = CGRect(x: 0, y: 0, width: size.width, height: size.height) 35 | 36 | context.addPath(UIBezierPath(roundedRect: rect, cornerRadius: radius).cgPath) 37 | context.clip() 38 | 39 | self.draw(in: rect) 40 | let output = UIGraphicsGetImageFromCurrentImageContext() 41 | 42 | UIGraphicsEndImageContext() 43 | 44 | return output 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /Sources/ViaSwiftUtils/UIKit/UIImageView+Rotation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIImageView+ActivityIndicator.swift 3 | // ViaSwiftUtils 4 | // 5 | // Copyright 2017 Viacom, Inc. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | import UIKit 20 | 21 | enum RotationAnimation { 22 | static let key = "ViaSwiftUtils.SpinAnimation" 23 | } 24 | 25 | public extension UIView { 26 | 27 | enum AnimationDuration { 28 | case slow 29 | case normal 30 | case fast 31 | case custom(time: TimeInterval) 32 | 33 | internal var duration: TimeInterval { 34 | switch self { 35 | case .slow: 36 | return 2.0 37 | case .normal: 38 | return 1.0 39 | case .fast: 40 | return 0.5 41 | case .custom(let time): 42 | return time 43 | } 44 | } 45 | } 46 | 47 | final var isRotating: Bool { 48 | return layer.animation(forKey: RotationAnimation.key) != nil 49 | } 50 | 51 | /// Starts animating the image like an activityIndicator. 52 | /// - parameter duration: an NSTimeInterval duration the animation should take 53 | final func startRotating(_ animationDuration: AnimationDuration = .normal, isRemovedOnCompletion: Bool = true) { 54 | layer.removeAnimation(forKey: RotationAnimation.key) 55 | isHidden = false 56 | let animation = CABasicAnimation(keyPath: "transform.rotation.z") 57 | animation.fromValue = CGFloat(0.0) 58 | animation.toValue = 2 * CGFloat.pi 59 | animation.duration = animationDuration.duration 60 | animation.repeatCount = HUGE 61 | animation.isRemovedOnCompletion = isRemovedOnCompletion 62 | layer.add(animation, forKey: RotationAnimation.key) 63 | } 64 | 65 | /// Stops rotating the image 66 | final func stopRotating() { 67 | layer.removeAnimation(forKey: RotationAnimation.key) 68 | isHidden = true 69 | } 70 | } 71 | 72 | public extension UIImageView { 73 | 74 | /// makes the image of UIImageView AlwaysTemplate and sets the tintColor provided 75 | /// - parameter color: color that is set as a tintColor of the templated image 76 | final func colorize(_ color: UIColor) { 77 | if let image = self.image { 78 | self.image = image.withRenderingMode(.alwaysTemplate) 79 | tintColor = color 80 | } 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /Sources/ViaSwiftUtils/UIKit/UIView+Xib.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIView+Xib.swift 3 | // ViaSwiftUtils 4 | // 5 | // Copyright 2017 Viacom, Inc. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | import UIKit 20 | 21 | public extension UIView { 22 | 23 | class func loadFromNib(named nibName: String, bundle: Bundle? = nil, owner: AnyObject? = nil) -> UIView? { 24 | let nib = UINib(nibName: nibName, bundle: bundle) 25 | return nib.instantiate(withOwner: owner, options: nil).first as? UIView 26 | } 27 | 28 | /// Find nib instance associated with view, name of UIView instance must be the same as name of UINib instance. The owner of nib's view is 'self'. 29 | internal func loadAssociatedNibView() { 30 | self.loadNibView(self) 31 | } 32 | 33 | /// Find nib instance associated with view, name of UIView instance must be the same as name of UINib instance. 34 | /// 35 | /// - parameter owner: The owner of nib's view 36 | internal func loadNibView(_ owner: AnyObject?) { 37 | let bundle = Bundle(for: type(of: self)) 38 | UIView.loadFromNib(named: String(describing: type(of: self)), bundle: bundle, owner: owner).map { 39 | $0.frame = self.bounds 40 | self.addSubview($0) 41 | } 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /Tests/LinuxMain.swift: -------------------------------------------------------------------------------- 1 | import XCTest 2 | @testable import ViaSwiftUtilsTests 3 | 4 | XCTMain([ 5 | testCase(BetterForceUnwrappingTests.allTests), 6 | testCase(CGPointOperatorsTest.allTests), 7 | testCase(CollectionShuffledTest.allTests), 8 | testCase(CGRectTests.allTests), 9 | testCase(SequenceTypeHelperTests.allTests), 10 | testCase(DictionaryMapValuesTests.allTests), 11 | testCase(DateComparableTest.allTests), 12 | testCase(RandomAccessCollectionSafeAccessTests.allTests), 13 | testCase(DateComponentAccessorsTest.allTests), 14 | testCase(DateTimesBetweenTests.allTests) 15 | ]) 16 | -------------------------------------------------------------------------------- /Tests/ViaSwiftUtilsTests/BetterForceUnwrappingTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BetterForceUnwrappingTests.swift 3 | // ViaSwiftUtils 4 | // 5 | // Copyright 2017 Viacom, Inc. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | @testable import ViaSwiftUtils 20 | import XCTest 21 | 22 | class BetterForceUnwrappingTests: XCTestCase { 23 | 24 | static var allTests = [ 25 | ("testForceUnwrapping", testForceUnwrapping), 26 | ("testIntegerConvertible", testIntegerConvertible), 27 | ("testStringConvertible", testStringConvertible), 28 | ("testGeneralConvertible", testGeneralConvertible) 29 | ] 30 | 31 | func testForceUnwrapping() { 32 | // Given 33 | let someURLString = "www.test.com/something" 34 | 35 | // When 36 | // swiftlint:disable:next force_unwrapping 37 | let url = URL(string: someURLString) !! "ERROR: \(someURLString) isn't url convertible" 38 | 39 | // Then 40 | XCTAssertEqual(url.path, someURLString) 41 | } 42 | 43 | func testIntegerConvertible() { 44 | // Given 45 | let aSampleString = "10" 46 | 47 | // When 48 | let anInt = Int(aSampleString) !? "WARNING: Couldn't convert \(aSampleString) to int" 49 | 50 | // Then 51 | XCTAssertEqual(anInt, 10) 52 | } 53 | 54 | func testStringConvertible() { 55 | // Given 56 | let anUTF16View = "abc".utf16 57 | 58 | // When 59 | let aString = String(anUTF16View) !? "WARNING: Couldn't convert \(anUTF16View) to String" 60 | 61 | // Then 62 | XCTAssertEqual(aString, "abc") 63 | } 64 | 65 | func testGeneralConvertible() { 66 | // Given 67 | let someURLString = "www.test.com/something" 68 | 69 | // When 70 | let url = URL(string: someURLString) !? (URL(fileURLWithPath: "\\"), "WARNING: \(someURLString) isn't url convertible") 71 | 72 | // Then 73 | XCTAssertEqual(url.path, someURLString) 74 | } 75 | 76 | } 77 | -------------------------------------------------------------------------------- /Tests/ViaSwiftUtilsTests/CGPoint+OperatorsTest.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CGPoint+OperatorsTest.swift 3 | // ViaSwiftUtils 4 | // 5 | // Copyright 2017 Viacom, Inc. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | @testable import ViaSwiftUtils 20 | import XCTest 21 | 22 | class CGPointOperatorsTest: XCTestCase { 23 | 24 | static var allTests = [ 25 | ("testPlusOperator", testPlusOperator), 26 | ("testMinusOperator", testMinusOperator) 27 | ] 28 | 29 | func testPlusOperator() { 30 | // Given: 31 | let point1 = CGPoint(x: 1.0, y: 1.0) 32 | let point2 = CGPoint(x: 1.0, y: 1.0) 33 | 34 | // When: 35 | let point3 = point1 + point2 36 | 37 | // Then: 38 | XCTAssertEqual(point3, CGPoint(x: 2.0, y: 2.0)) 39 | } 40 | 41 | func testMinusOperator() { 42 | // Given: 43 | let point1 = CGPoint(x: 2.0, y: 2.0) 44 | let point2 = CGPoint(x: 1.0, y: 1.0) 45 | 46 | // When: 47 | let point3 = point1 - point2 48 | 49 | // Then: 50 | XCTAssertEqual(point3, CGPoint(x: 1.0, y: 1.0)) 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Tests/ViaSwiftUtilsTests/CGRectTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViaSwiftUtils_iOSTests.swift 3 | // ViaSwiftUtils 4 | // 5 | // Copyright 2017 Viacom, Inc. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | @testable import ViaSwiftUtils 20 | import XCTest 21 | 22 | class CGRectTests: XCTestCase { 23 | 24 | static var allTests = [ 25 | ("testRectSize10x5", testRectSize10x5), 26 | ("testRectWithZeroWidth", testRectWithZeroWidth), 27 | ("testRectWithZeroHeight", testRectWithZeroHeight), 28 | ("testRectWithLinearCombineRectsHalf", testRectWithLinearCombineRectsHalf), 29 | ("testRectWithLinearCombineRectsIllegalRatio", testRectWithLinearCombineRectsIllegalRatio) 30 | ] 31 | 32 | func testRectSize10x5() { 33 | // Given a rect sized 10 x 5 34 | let rect = CGRect(x: 0.0, y: 0.0, width: 10.0, height: 5.0) 35 | 36 | // Then 37 | XCTAssertEqual(Double(rect.aspectRatio), 2.0, accuracy: Double.ulpOfOne, "Expected Aspect ratio of 2.0") 38 | } 39 | 40 | func testRectWithZeroWidth() { 41 | // Given a rect sized 0 x 5 42 | let rect = CGRect(x: 0.0, y: 0.0, width: 0.0, height: 5.0) 43 | 44 | // Then 45 | XCTAssertEqual(Double(rect.aspectRatio), 0.0, accuracy: Double.ulpOfOne, "Expected Aspect ratio of 0.0") 46 | } 47 | 48 | func testRectWithZeroHeight() { 49 | // Given a rect sized 10 x 0 50 | let rect = CGRect(x: 0.0, y: 0.0, width: 10.0, height: 0.0) 51 | 52 | // Then 53 | XCTAssertEqual(Double(rect.aspectRatio), 0.0, accuracy: Double.ulpOfOne, "Expected Aspect ratio of 0.0") 54 | } 55 | 56 | func testRectWithLinearCombineRectsHalf() { 57 | // Given two given rects 58 | let rect1 = CGRect(x: 4.0, y: -4.0, width: 20.0, height: 10.0) 59 | let rect2 = CGRect(x: 0.0, y: 0.0, width: 10.0, height: 20.0) 60 | 61 | // When combined 50:50 62 | let combinedRect = rect1.linearCombined(with: rect2, by: 0.5) 63 | 64 | // Then 65 | XCTAssertEqual(Double(combinedRect.origin.x), 2.0, 66 | accuracy: .ulpOfOne, "Expected x to be 2.0") 67 | XCTAssertEqual(Double(combinedRect.origin.y), -2.0, 68 | accuracy: .ulpOfOne, "Expected y to be -2.0") 69 | XCTAssertEqual(Double(combinedRect.size.width), 15, 70 | accuracy: .ulpOfOne, "Expected width to be 15.0") 71 | XCTAssertEqual(Double(combinedRect.size.height), 15.0, 72 | accuracy: .ulpOfOne, "Expected height to be 15.0") 73 | } 74 | 75 | func testRectWithLinearCombineRectsIllegalRatio() { 76 | // Given a two given rects 77 | let rect1 = CGRect(x: 4.0, y: -4.0, width: 20.0, height: 10.0) 78 | let rect2 = CGRect(x: 0.0, y: 0.0, width: 10.0, height: 20.0) 79 | 80 | // When combined with a value < 0 81 | let combinedRect = rect1.linearCombined(with: rect2, by: -1.5) 82 | 83 | // Then 84 | XCTAssertEqual(Double(combinedRect.origin.x), Double(rect1.origin.x), 85 | accuracy: .ulpOfOne, "Expected x to be equal to rect1.x") 86 | 87 | // When combined with a value > 1 88 | let combinedRect2 = rect1.linearCombined(with: rect2, by: 1.5) 89 | 90 | // Then 91 | XCTAssertEqual(Double(combinedRect2.origin.y), Double(rect2.origin.y), 92 | accuracy: .ulpOfOne, "Expected y to be equal to rect2.y") 93 | } 94 | 95 | } 96 | -------------------------------------------------------------------------------- /Tests/ViaSwiftUtilsTests/CollectionShuffledTest.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Shuffled.swift 3 | // ViaSwiftUtils 4 | // 5 | // Copyright 2017 Viacom, Inc. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | @testable import ViaSwiftUtils 20 | import XCTest 21 | 22 | class CollectionShuffledTest: XCTestCase { 23 | 24 | static var allTests = [ 25 | ("testShuffleDoesNotChangeCount", testShuffleDoesNotChangeCount), 26 | ("testShuffledArraysKeepElements", testShuffledArraysKeepElements) 27 | ] 28 | 29 | func testShuffleDoesNotChangeCount() { 30 | // Given a shuffled 31 | var mutableNumberList = [1, 2, 3, 4, 5, 6] 32 | let originalNumbers = mutableNumberList 33 | 34 | // When shuffled 35 | mutableNumberList.shuffleInPlace() 36 | 37 | // Then array should have the same amount of elements 38 | XCTAssertEqual(originalNumbers.count, mutableNumberList.count, 39 | "Expected equal count of shuffled Collection to Original") 40 | } 41 | 42 | func testShuffledArraysKeepElements() { 43 | // Given hundred random arrays 44 | for _ in 0..<100 { 45 | let array = (0..<100).map({ _ in return (100..<200).arc4random }) 46 | var shuffledArray = array 47 | 48 | // When shuffled 49 | shuffledArray.shuffleInPlace() 50 | 51 | // Then each element should occurs in equally often in both original and shuffled array 52 | for element in array.unique() { 53 | XCTAssertEqual(shuffledArray.filter({ element == $0 }).count, array.filter({ element == $0 }).count, 54 | "Expected the same elements in the shuffled array as in the original") 55 | } 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /Tests/ViaSwiftUtilsTests/Date_ComparableTest.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Date_Comparable.swift 3 | // ViaSwiftUtils 4 | // 5 | // Copyright 2017 Viacom, Inc. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | @testable import ViaSwiftUtils 20 | import XCTest 21 | 22 | class DateComparableTest: XCTestCase { 23 | 24 | static var allTests = [ 25 | ("testComparable", testComparable) 26 | ] 27 | 28 | func testComparable() { 29 | // Given, When 30 | let earlyDate = Date(timeIntervalSince1970: 0) 31 | let laterDate = Date(timeIntervalSince1970: 1000) 32 | let otherDate = Date(timeIntervalSince1970: 1000) 33 | 34 | // Then 35 | XCTAssertTrue(earlyDate < laterDate, "Expected earlyDate to be before laterDate") 36 | XCTAssertTrue(laterDate > earlyDate, "Expected earlyDate to be before laterDate") 37 | XCTAssertTrue(otherDate == laterDate, "Expected otherDate to be equal laterDate") 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Tests/ViaSwiftUtilsTests/Date_ComponentAccessorsTest.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSCalender+CurrentYear.swift 3 | // ViaSwiftUtils 4 | // 5 | // Copyright 2017 Viacom, Inc. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | @testable import ViaSwiftUtils 20 | import XCTest 21 | 22 | class DateComponentAccessorsTest: XCTestCase { 23 | 24 | static var allTests = [ 25 | ("testDateComponentAsInts", testDateComponentAsInts), 26 | ("testCurrentYear", testCurrentYear), 27 | ("testHistoricalYear", testHistoricalYear) 28 | ] 29 | 30 | func testDateComponentAsInts() { 31 | // Given 32 | let eightAM1970 = Date(timeIntervalSince1970: 8 * TimeInterval.hour + 0.5 * TimeInterval.minute) 33 | 34 | // When in UTC time zone then 35 | if let utcZone = TimeZone(abbreviation: "UTC") { NSTimeZone.default = utcZone } 36 | 37 | // Then 38 | XCTAssertEqual(eightAM1970.hourOfDay, 8, "Expected 8:00AM 1/1/1970 to be in the 8th hour of the day") 39 | XCTAssertEqual(eightAM1970.dayOfWeek, 5, "Expected 8:00AM 1/1/1970 to be a thursday") 40 | XCTAssertEqual(eightAM1970.year, 1970, "Expected 8:00AM 1/1/1970 to be year 1970") 41 | } 42 | 43 | func testCurrentYear() { 44 | // Given the current date 45 | let currentYear = Date().localizedYear 46 | 47 | // Then 48 | XCTAssertEqual(currentYear.count, 4, "Expected currentyear to have 4 characters") 49 | } 50 | 51 | func testHistoricalYear() { 52 | // Given the fall of the west roman empire 53 | let fallOfRome = Date(timeIntervalSince1970: -TimeInterval.year * (1970 - 476)) 54 | 55 | // When converting to localized string 56 | let localizedString = fallOfRome.localizedYear 57 | 58 | // Then 59 | XCTAssertEqual(localizedString.count, 3, "Expected year of the fall of west rome to have 3 characters") 60 | } 61 | 62 | } 63 | -------------------------------------------------------------------------------- /Tests/ViaSwiftUtilsTests/Date_TimesBetween.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Date_TimesBetween.swift 3 | // ViaSwiftUtils 4 | // 5 | // Copyright 2017 Viacom, Inc. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | @testable import ViaSwiftUtils 20 | import XCTest 21 | 22 | class DateTimesBetweenTests: XCTestCase { 23 | 24 | static var allTests = [ 25 | ("testTomorrow", testTomorrow), 26 | ("testNextMinute", testNextMinute), 27 | ("testNextSecond", testNextSecond) 28 | ] 29 | 30 | func testTomorrow() { 31 | // Given the current date 32 | let now = Date() 33 | 34 | // When comparing to tomorrow 35 | let tomorrow = Date(timeIntervalSinceNow: TimeInterval.day) 36 | // Then 37 | XCTAssertEqual(now.days(to: tomorrow), 1, "Expected today to be 1 day from tomorrow") 38 | } 39 | 40 | func testNextMinute() { 41 | // Given the current date 42 | let now = Date() 43 | // When comparing to next minute 44 | let nextMinute = Date(timeIntervalSinceNow: TimeInterval.minute) 45 | // Then 46 | XCTAssertEqual(now.minutes(to: nextMinute), 1, "Expected nextMinute to be 1 minute from now") 47 | } 48 | 49 | func testNextSecond() { 50 | // Given the current date 51 | let now = Date() 52 | // When comparing to next second 53 | let nextSecond = Date(timeIntervalSinceNow: TimeInterval.second) 54 | // Then 55 | XCTAssertEqual(now.seconds(to: nextSecond), 1, "Expected nextSecond to be 1 second from now") 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /Tests/ViaSwiftUtilsTests/Dictionary+MapValuesTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Dictionary+MapValuesTests.swift 3 | // ViaSwiftUtils 4 | // 5 | // Copyright 2017 Viacom, Inc. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | @testable import ViaSwiftUtils 20 | import XCTest 21 | 22 | class DictionaryMapValuesTests: XCTestCase { 23 | 24 | static var allTests = [ 25 | ("testMergeDictionariesOfEqualType", testMergeDictionariesOfEqualType), 26 | ("testMergeDictionariesWithOverlappingKeys", testMergeDictionariesWithOverlappingKeys), 27 | ("testMapValues", testMapValues) 28 | ] 29 | 30 | func testMergeDictionariesOfEqualType() { 31 | // Given two dictionarys of the same types 32 | var dict1 = ["One": 1, "Two": 2, "Three": 3] 33 | let dict2 = ["Four": 10, "Five": 2, "Six": 3] 34 | 35 | // When 36 | dict1.merge(dict2) 37 | 38 | // Then 39 | XCTAssertEqual(dict1.count, 6, "Expected count of the combined arrays to be 6") 40 | } 41 | 42 | func testMergeDictionariesWithOverlappingKeys() { 43 | // Given two dictionarys with overlapping keys 44 | var dict3 = ["One": 1, "Two": 2, "Three": 3] 45 | let dict4 = ["Four": 10, "Two": 2, "Six": 3] 46 | 47 | // When 48 | dict3.merge(dict4) 49 | 50 | // Then 51 | XCTAssertEqual(dict3.count, 5, "Expected count of the combined arrays to be 5") 52 | } 53 | 54 | func testMapValues() { 55 | // Given two dictionarys [String: Int] 56 | let dict = ["One": 1, "Two": 2, "Three": 3] 57 | 58 | // When 59 | let mappedDict = dict.mapValues { number -> String in 60 | let numberFormatter = NumberFormatter() 61 | return numberFormatter.string(from: NSNumber(value: number)) ?? "N.A." 62 | } 63 | 64 | // Then 65 | XCTAssertEqual(Array(dict.keys.sorted()), Array(mappedDict.keys.sorted()), "Expected both dicts to have the same keys") 66 | } 67 | 68 | } 69 | -------------------------------------------------------------------------------- /Tests/ViaSwiftUtilsTests/RandomAccessCollection+SafeAccessTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Date_Comparable.swift 3 | // ViaSwiftUtils 4 | // 5 | // Copyright 2017 Viacom, Inc. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | @testable import ViaSwiftUtils 20 | import XCTest 21 | 22 | class RandomAccessCollectionSafeAccessTests: XCTestCase { 23 | 24 | static var allTests = [ 25 | ("testOutOfBoundsIndex", testOutOfBoundsIndex), 26 | ("testValidIndex", testValidIndex), 27 | ("testNegativeIndex", testNegativeIndex), 28 | ("testArraySliceOutOfBounds", testArraySliceOutOfBounds), 29 | ("testArraySliceInBounds", testArraySliceInBounds), 30 | ("testContiguousArray", testContiguousArray) 31 | ] 32 | 33 | private let testArray = ["zero", "one", "two", "three", "four"] 34 | 35 | func testOutOfBoundsIndex() { 36 | // Given an index that is out of bounds 37 | let index = testArray.count 38 | // then the safe access should return nil 39 | XCTAssertNil(testArray[safe: index]) 40 | } 41 | 42 | func testValidIndex() { 43 | // Given an index that is inside the bounds 44 | let index = 3 45 | // then the correct value is returned 46 | XCTAssertEqual(testArray[safe: index], "three") 47 | } 48 | 49 | func testNegativeIndex() { 50 | // Given an index that is out of bounds 51 | let index = -1 52 | // then the safe access should return nil 53 | XCTAssertNil(testArray[safe: index]) 54 | } 55 | 56 | private var testSlice: ArraySlice { return testArray[1...3] } 57 | 58 | func testArraySliceOutOfBounds() { 59 | // Given an out of bounds index is requested 60 | let outOfBoundsIndex = 0 61 | // then the safe access should still return the correct value 62 | XCTAssertNil(testSlice[safe: outOfBoundsIndex]) 63 | } 64 | 65 | func testArraySliceInBounds() { 66 | // Given a value inside the subsclice is asked for 67 | let validIndex = 1 68 | // then the safe access should still return the correct value 69 | XCTAssertEqual(testSlice[safe: validIndex], "one") 70 | } 71 | 72 | func testContiguousArray() { 73 | // Given a (more efficient) ContiguousArray 74 | let array = ContiguousArray(testArray) 75 | // then the safe access should still return the correct value 76 | XCTAssertEqual(testArray[safe: 1], array[safe: 1]) 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /Tests/ViaSwiftUtilsTests/SequenceTypeHelperTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SequenceTypeHelperTests.swift 3 | // ViaSwiftUtils 4 | // 5 | // Copyright 2017 Viacom, Inc. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | @testable import ViaSwiftUtils 20 | import XCTest 21 | 22 | class SequenceTypeHelperTests: XCTestCase { 23 | 24 | static var allTests = [ 25 | ("testFindFirstExistentOfMultiple", testFindFirstExistentOfMultiple), 26 | ("testUniqueElements", testUniqueElements), 27 | ("testANYALLNONE", testANYALLNONE) 28 | ] 29 | 30 | private struct TestElement { 31 | let number: Int 32 | let value: Int 33 | } 34 | 35 | func testFindFirstExistentOfMultiple() { 36 | // given a set of items with multiple values over 10 37 | let elements = [TestElement(number: 0, value: 10), 38 | TestElement(number: 1, value: 10), 39 | TestElement(number: 2, value: 20), 40 | TestElement(number: 3, value: 10), 41 | TestElement(number: 4, value: 20), 42 | TestElement(number: 5, value: 10), 43 | TestElement(number: 6, value: 20), 44 | TestElement(number: 7, value: 10)] 45 | 46 | // Then 47 | XCTAssertEqual(elements.first(where: { $0.value > 10 })?.number, 2, "Expected first element to be larger then 10 to be 2") 48 | } 49 | 50 | func testUniqueElements() { 51 | // Given an already unique arrays of elements, When 52 | let elements = [1, 2, 3, 4, 5, 6] 53 | 54 | // Then 55 | XCTAssertEqual(elements.unique().count, elements.count, "Expected elements to be equal to unique elements") 56 | 57 | // Given a bigger array of elements, When 58 | let moreElements = [1, 2, 3, 4, 5, 6, 1, 2, 3, 8] 59 | 60 | // Then 61 | XCTAssertEqual(moreElements.unique().count, 7, "Expected elements to be equal to unique elements") 62 | } 63 | 64 | func testANYALLNONE() { 65 | // Given, When 66 | let elements = [1, 2, 3, 4, 5, 6] 67 | 68 | // Then 69 | XCTAssertTrue(elements.any { $0 > 3 }, "Expected 'any number larger 3' to be true") 70 | XCTAssertTrue(elements.all { $0 > 0 }, "Expected 'all to be bigger then 0' to be true") 71 | XCTAssertTrue(elements.none { $0 < 0 }, "Expected 'none to be negative' to be true") 72 | XCTAssertFalse(elements.any { $0 > 6 }, "Expected 'any larger then 6' to be false") 73 | XCTAssertFalse(elements.all { $0 > 2 }, "Expected all to be false") 74 | XCTAssertFalse(elements.all { $0 > 6 }, "Expected all to be false") 75 | XCTAssertFalse(elements.none { $0 > 1 }, "Expected 'none over 1' to be false") 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /ViaSwiftUtils.playground/Contents.swift: -------------------------------------------------------------------------------- 1 | //: Playground - noun: a place where people can play 2 | 3 | import UIKit 4 | import ViaSwiftUtils 5 | 6 | //CGRect 7 | let rect = CGRect(x: 0, y: 0, width: 10, height: 5) 8 | print("aspect ratio: \(rect.aspectRatio)") 9 | 10 | let otherRect = CGRect(x: 5, y: -5, width: 20, height: 5) 11 | print("combined: \(rect.linearCombined(with: otherRect, by: 0.5) )") 12 | 13 | var mutableNumberList = [1, 2, 3, 4, 5, 6] 14 | mutableNumberList.shuffleInPlace() 15 | 16 | let emojis = ["😀", "👀", "😱", "😡", "👀", "😀", "👀", "😱"] 17 | let uniqueEmojis = emojis.unique() 18 | 19 | let image = #imageLiteral(resourceName: "puppy").cornersRounded(usingRadius: 100) 20 | 21 | //Example type-safe Observer Pattern 22 | enum VideoEvent { 23 | case started 24 | case ended 25 | } 26 | 27 | struct VideoEventEventReceiver: ObserverType { 28 | let name: String 29 | 30 | func receive(_ event: VideoEvent) { 31 | print("\(name) received \(event)") 32 | } 33 | } 34 | 35 | struct Player: Observable { 36 | typealias Event = VideoEvent 37 | 38 | // var observes: [ObserverType] = [] would not work, because ObserverType has an associated type 39 | var observers: [(Event) -> Void] = [] 40 | 41 | mutating func register(_ observer: O) where O.Event == Event { 42 | observers.append({ observer.receive($0) }) 43 | } 44 | 45 | func fire(event: VideoEvent) { 46 | for observer in observers { observer(event) } 47 | } 48 | } 49 | 50 | var player = Player() 51 | let sko = VideoEventEventReceiver(name: "SKO") 52 | var gallup = VideoEventEventReceiver(name: "Gallup") 53 | player.register(sko) 54 | player.register(gallup) 55 | player.fire(event: .started) 56 | 57 | Date().year 58 | Date().dayOfWeek 59 | 60 | let formatter = DateFormatter() 61 | formatter.dateFormat = "y" 62 | let twelveDays = 12 * TimeInterval.day 63 | let minutes = twelveDays / TimeInterval.minute 64 | 65 | let gregorian = Calendar(identifier: .gregorian) 66 | let components = DateComponents(calendar: gregorian, 67 | era: 0, year: 44, month: 3, day: 15, hour: 13) 68 | let idesOfMarch = gregorian.date(from: components)! 69 | let weekDay = idesOfMarch.dayOfWeek 70 | let daysSince = idesOfMarch.days(to: Date()) 71 | 72 | formatter.string(from: idesOfMarch) 73 | formatter.string(from: Date()) 74 | 75 | formatter.locale = Locale(identifier: "hi_IN") 76 | 77 | formatter.string(from: idesOfMarch) 78 | formatter.string(from: Date()) 79 | 80 | var dict1 = ["One": 1, "Two": 2, "Three": 3] 81 | let dict2 = ["Four": 10, "Five": 2, "Six": 3] 82 | 83 | dict1.merge(dict2) 84 | 85 | print(dict1) 86 | 87 | let someString = "10" 88 | let someInt = Int(someString) !? "Couldn't convert \(someString) to int" 89 | -------------------------------------------------------------------------------- /ViaSwiftUtils.playground/Resources/puppy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ViacomInc/ViaSwiftUtils/28414e8385a86102121b726dafb99daa9cfe6a1b/ViaSwiftUtils.playground/Resources/puppy.png -------------------------------------------------------------------------------- /ViaSwiftUtils.playground/contents.xcplayground: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /ViaSwiftUtils.playground/playground.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ViaSwiftUtils.playground/timeline.xctimeline: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /ViaSwiftUtils.podspec: -------------------------------------------------------------------------------- 1 | # Copyright 2019 Viacom, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License.# 14 | # Be sure to run `pod lib lint KDTree.podspec' to ensure this is a 15 | # valid spec before submitting. 16 | # 17 | # Any lines starting with a # are optional, but their use is encouraged 18 | # To learn more about a Podspec see http://guides.cocoapods.org/syntax/podspec.html 19 | # 20 | 21 | Pod::Spec.new do |s| 22 | s.name = "ViaSwiftUtils" 23 | s.version = "2.1.0" 24 | s.summary = "Swift Utilities written with and for Viacom Inc." 25 | s.swift_versions = ['4.0', '4.2', '5.0'] 26 | 27 | # This description is used to generate tags and improve search results. 28 | s.description = "Goodie box of small helper functions/extensions used in many Swift Apps at Viacom" 29 | 30 | s.homepage = "https://github.com/ViacomInc/ViaSwiftUtils" 31 | s.license = { :type => 'Apache License, Version 2.0' } 32 | s.author = { "Konrad Feiler" => "konrad.feiler@vimn.com" } 33 | s.source = { :git => "https://github.com/ViacomInc/ViaSwiftUtils.git", :tag => s.version.to_s } 34 | 35 | s.ios.deployment_target = '9.0' 36 | s.watchos.deployment_target = '2.0' 37 | s.tvos.deployment_target = '9.0' 38 | s.requires_arc = true 39 | 40 | s.ios.source_files = 'Sources/ViaSwiftUtils/**/*' 41 | s.osx.source_files = 'Sources/ViaSwiftUtils/Foundation/*' 42 | s.watchos.source_files = 'Sources/ViaSwiftUtils/Foundation/*' 43 | s.tvos.source_files = 'Sources/ViaSwiftUtils/Foundation/*' 44 | 45 | # s.public_header_files = 'Pod/Classes/**/*.h' 46 | end 47 | -------------------------------------------------------------------------------- /ViaSwiftUtils.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /ViaSwiftUtils.xcodeproj/xcshareddata/xcschemes/ViaSwiftUtils_iOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 31 | 32 | 34 | 40 | 41 | 42 | 43 | 44 | 50 | 51 | 52 | 53 | 54 | 55 | 65 | 66 | 72 | 73 | 74 | 75 | 76 | 77 | 83 | 84 | 90 | 91 | 92 | 93 | 95 | 96 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /ViaSwiftUtils.xcodeproj/xcshareddata/xcschemes/ViaSwiftUtils_tvOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 49 | 50 | 51 | 52 | 53 | 54 | 64 | 65 | 71 | 72 | 73 | 74 | 75 | 76 | 82 | 83 | 89 | 90 | 91 | 92 | 94 | 95 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /ViaSwiftUtils.xcodeproj/xcshareddata/xcschemes/ViaSwiftUtils_watchOS.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 34 | 35 | 45 | 46 | 52 | 53 | 54 | 55 | 56 | 57 | 63 | 64 | 70 | 71 | 72 | 73 | 75 | 76 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /ViaSwiftUtils.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /ViaSwiftUtils.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /ViaSwiftUtils.xcworkspace/xcshareddata/ViaSwiftUtils.xcscmblueprint: -------------------------------------------------------------------------------- 1 | { 2 | "DVTSourceControlWorkspaceBlueprintPrimaryRemoteRepositoryKey" : "74667E66266B70EF860E327D2D4A8B3182F679FB", 3 | "DVTSourceControlWorkspaceBlueprintWorkingCopyRepositoryLocationsKey" : { 4 | 5 | }, 6 | "DVTSourceControlWorkspaceBlueprintWorkingCopyStatesKey" : { 7 | "74667E66266B70EF860E327D2D4A8B3182F679FB" : 0, 8 | "D676CDCA5771490D15D8EB193B74E257D80F2D2E" : 0 9 | }, 10 | "DVTSourceControlWorkspaceBlueprintIdentifierKey" : "87558D39-4567-4480-AA1A-2FCCCF0C21CC", 11 | "DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : { 12 | "74667E66266B70EF860E327D2D4A8B3182F679FB" : "ViaSwiftUtils\/", 13 | "D676CDCA5771490D15D8EB193B74E257D80F2D2E" : "..\/PlayPlex-iOS" 14 | }, 15 | "DVTSourceControlWorkspaceBlueprintNameKey" : "ViaSwiftUtils", 16 | "DVTSourceControlWorkspaceBlueprintVersion" : 204, 17 | "DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "ViaSwiftUtils.xcworkspace", 18 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [ 19 | { 20 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/vimn-north\/ViaSwiftUtils.git", 21 | "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", 22 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "74667E66266B70EF860E327D2D4A8B3182F679FB" 23 | }, 24 | { 25 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryURLKey" : "https:\/\/github.com\/vimn-north\/PlayPlex-iOS.git", 26 | "DVTSourceControlWorkspaceBlueprintRemoteRepositorySystemKey" : "com.apple.dt.Xcode.sourcecontrol.Git", 27 | "DVTSourceControlWorkspaceBlueprintRemoteRepositoryIdentifierKey" : "D676CDCA5771490D15D8EB193B74E257D80F2D2E" 28 | } 29 | ] 30 | } -------------------------------------------------------------------------------- /ViaSwiftUtils.xcworkspace/xcuserdata/feilek.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /iOSTests/CGVectorOperatorsTest.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CGVectorOperatorsTest.swift 3 | // ViaSwiftUtils 4 | // 5 | // Copyright 2017 Viacom, Inc. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | @testable import ViaSwiftUtils 20 | import XCTest 21 | 22 | class CGVectorOperatorsTest: XCTestCase { 23 | 24 | func testPlusOperator() { 25 | // Given: 26 | let vector1 = CGVector(dx: 1.0, dy: 1.0) 27 | let vector2 = CGVector(dx: 1.0, dy: 1.0) 28 | 29 | // When: 30 | let vector3 = vector1 + vector2 31 | 32 | // Then: 33 | XCTAssertEqual(vector3, CGVector(dx: 2.0, dy: 2.0)) 34 | } 35 | 36 | func testMinusOperator() { 37 | // Given: 38 | let vector1 = CGVector(dx: 2.0, dy: 2.0) 39 | let vector2 = CGVector(dx: 1.0, dy: 1.0) 40 | 41 | // When: 42 | let vector3 = vector1 - vector2 43 | 44 | // Then: 45 | XCTAssertEqual(vector3, CGVector(dx: 1.0, dy: 1.0)) 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /iOSTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /iOSTests/NSLocale+Swizzling.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSLocale+Swizzling.swift 3 | // ViaSwiftUtils 4 | // 5 | // Copyright 2017 Viacom, Inc. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | import Foundation 20 | @testable import ViaSwiftUtils 21 | 22 | // swiftlint:disable identifier_name 23 | 24 | extension NSLocale { 25 | @nonobjc static var ttt_locale = "en_US" 26 | @nonobjc static var ttt_swizzled = false 27 | 28 | @objc 29 | class func customizedLocale() -> NSLocale { 30 | return NSLocale(localeIdentifier: NSLocale.ttt_locale) 31 | } 32 | 33 | class func forceLocale(identifier: String) { 34 | NSLocale.ttt_locale = identifier 35 | 36 | print("Swizzling to locale \(identifier)") 37 | 38 | if !NSLocale.ttt_swizzled { 39 | NSLocale.ttt_swizzled = true 40 | let originalSelector = #selector(getter: NSLocale.current) 41 | let swizzledSelector = #selector(self.customizedLocale) 42 | 43 | let originalMethod = class_getClassMethod(self, originalSelector) !! "Expected NSLocale.current exists" 44 | let swizzledMethod = class_getClassMethod(self, swizzledSelector) !! "Expected customizedLocale exists" 45 | 46 | method_exchangeImplementations(originalMethod, swizzledMethod) 47 | } 48 | } 49 | 50 | class func undoForcing() { 51 | print("Undoing the swizzling") 52 | 53 | if NSLocale.ttt_swizzled { 54 | NSLocale.ttt_swizzled = false 55 | let swizzledSelector = #selector(self.customizedLocale) 56 | let originalSelector = #selector(getter: NSLocale.current) 57 | 58 | let originalMethod = class_getClassMethod(self, originalSelector) !! "Expected NSLocale.current exists" 59 | let swizzledMethod = class_getClassMethod(self, swizzledSelector) !! "Expected customizedLocale exists" 60 | 61 | method_exchangeImplementations(originalMethod, swizzledMethod) 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /iOSTests/NibViewTest.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NibViewTest.swift 3 | // ViaSwiftUtils 4 | // 5 | // Copyright 2017 Viacom, Inc. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | import Foundation 20 | @testable import ViaSwiftUtils 21 | import XCTest 22 | 23 | //swiftlint:disable strict_fileprivate 24 | class TestViewController: UIViewController { 25 | 26 | @IBOutlet fileprivate var horizontalTestViewConstraint: NSLayoutConstraint? 27 | @IBOutlet fileprivate var testView: TestViewWithOwner? 28 | 29 | } 30 | 31 | class TestViewWithOwner: NibView { 32 | 33 | @IBOutlet fileprivate var horizontalLabelConstraint: NSLayoutConstraint? 34 | @IBOutlet fileprivate var testLabel: UILabel? 35 | 36 | } 37 | //swiftlint:enable strict_fileprivate 38 | 39 | class NibViewTest: XCTestCase { 40 | 41 | func testInitializationWithinStoryboard() { 42 | // Given 43 | let storyboard = UIStoryboard(name: "TestStoryboard", bundle: Bundle(for: type(of: self))) 44 | 45 | // When 46 | let testViewController = storyboard.instantiateInitialViewController() as? TestViewController 47 | _ = testViewController?.view 48 | let testView = testViewController?.testView 49 | 50 | // Then 51 | XCTAssertNotNil(testViewController) 52 | XCTAssertNotNil(testViewController?.testView) 53 | XCTAssertNotNil(testViewController?.horizontalTestViewConstraint) 54 | XCTAssertNotNil(testView) 55 | XCTAssertNotNil(testView?.testLabel) 56 | XCTAssertNotNil(testView?.horizontalLabelConstraint) 57 | } 58 | 59 | func testInitializationWithFrame() { 60 | // Given 61 | let frame = CGRect(x: 0.0, y: 0.0, width: 10.0, height: 10.0) 62 | let expectedNumberOfSubviews = 1 63 | let expectedSubviewType = String(describing: XibView.self) 64 | 65 | // When 66 | let testView = TestViewWithOwner(frame: frame) 67 | 68 | // Then 69 | XCTAssertEqual(testView.subviews.count, expectedNumberOfSubviews, "Expected number of subviews to be \(expectedNumberOfSubviews)") 70 | 71 | let subviewType = String(describing: type(of: testView.subviews[0])) 72 | XCTAssertEqual(subviewType, expectedSubviewType, "Expected subview type to be \(expectedSubviewType)") 73 | } 74 | 75 | func testInitializationWithCoder() { 76 | // Given 77 | let frame = CGRect(x: 0.0, y: 0.0, width: 10.0, height: 10.0) 78 | let expectedNumberOfSubviews = 1 79 | let expectedSubviewType = String(describing: XibView.self) 80 | let archivePath = NSTemporaryDirectory().appending("testView") 81 | 82 | // When 83 | let testView = TestViewWithOwner(frame: frame) 84 | NSKeyedArchiver.archiveRootObject(testView, toFile: archivePath) 85 | let unarchivedTestView = NSKeyedUnarchiver.unarchiveObject(withFile: archivePath) as? TestViewWithOwner 86 | 87 | // Then 88 | XCTAssertNotNil(unarchivedTestView, "Expected unarchived view not to be nil") 89 | unarchivedTestView.map { 90 | XCTAssertEqual($0.subviews.count, expectedNumberOfSubviews, "Expected number of subviews to be \(expectedNumberOfSubviews)") 91 | 92 | let subviewType = String(describing: type(of: $0.subviews[0])) 93 | XCTAssertEqual(subviewType, expectedSubviewType, "Expected subview type to be \(expectedSubviewType)") 94 | } 95 | } 96 | 97 | } 98 | -------------------------------------------------------------------------------- /iOSTests/StringWordsTest.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StringWordsTest.swift 3 | // ViaSwiftUtils 4 | // 5 | // Copyright 2017 Viacom, Inc. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | @testable import ViaSwiftUtils 20 | import XCTest 21 | 22 | class StringWordsTest: XCTestCase { 23 | 24 | static var allTests = [ 25 | ("test_longestWordWithSuccessScenario", test_longestWordWithSuccessScenario), 26 | ("test_longestWordWithFailureScenario", test_longestWordWithFailureScenario), 27 | ("test_longestWordWithCommaSuccessScenario", test_longestWordWithCommaSuccessScenario), 28 | ("test_longestWordWithCommaFailureScenario", test_longestWordWithCommaFailureScenario), 29 | ("test_wordCountWithSuccessScenario", test_wordCountWithSuccessScenario), 30 | ("test_wordCountWithFailureScenario", test_wordCountWithFailureScenario), 31 | ("test_wordCountWithCommaSuccessScenario", test_wordCountWithCommaSuccessScenario), 32 | ("test_wordCountWithCommaFailureScenario", test_wordCountWithCommaFailureScenario), 33 | ("test_isOneWordWithSuccessScenario", test_isOneWordWithSuccessScenario), 34 | ("test_isOneWordWithFailureScenario", test_isOneWordWithFailureScenario), 35 | ("test_isOneWordWithCommaFailureScenario", test_isOneWordWithCommaFailureScenario), 36 | ("test_isOneWordWithFailureScenario_Japanese", test_isOneWordWithFailureScenario_Japanese) 37 | ] 38 | 39 | func test_longestWordWithSuccessScenario() { 40 | // Given: 41 | let source = "This is a test string" 42 | let longestWordToCompare = "string" 43 | 44 | // When: 45 | let longestWordCalculated = source.longestWord() 46 | 47 | // Then: 48 | XCTAssertEqual(longestWordToCompare, longestWordCalculated) 49 | } 50 | 51 | func test_longestWordWithFailureScenario() { 52 | // Given: 53 | let source = "This is a test string" 54 | let notTheLongestWord = "test" 55 | 56 | // When: 57 | let longestWordCalculated = source.longestWord() 58 | 59 | // Then: 60 | XCTAssertNotEqual(notTheLongestWord, longestWordCalculated) 61 | } 62 | 63 | func test_longestWordWithCommaSuccessScenario() { 64 | // Given: 65 | let source = "This is just, a test" 66 | let longestWordToCompare = "This" 67 | 68 | // When: 69 | let longestWordCalculated = source.longestWord() 70 | 71 | // Then: 72 | XCTAssertEqual(longestWordToCompare, longestWordCalculated) 73 | } 74 | 75 | func test_longestWordWithCommaFailureScenario() { 76 | // Given: 77 | let source = "This is just, a test" 78 | let notTheLongestWord = "just" 79 | 80 | // When: 81 | let longestWordCalculated = source.longestWord() 82 | 83 | // Then: 84 | XCTAssertNotEqual(notTheLongestWord, longestWordCalculated) 85 | } 86 | 87 | func test_wordCountWithSuccessScenario() { 88 | // Given: 89 | let source = "This is a test string" 90 | 91 | // When: 92 | let wordCount = source.wordCount() 93 | 94 | // Then: 95 | XCTAssertEqual(wordCount, 5) 96 | } 97 | 98 | func test_wordCountWithFailureScenario() { 99 | // Given: 100 | let source = "This is a test string" 101 | 102 | // When: 103 | let wordCount = source.wordCount() 104 | 105 | // Then: 106 | XCTAssertNotEqual(wordCount, 2) 107 | } 108 | 109 | func test_wordCountWithCommaSuccessScenario() { 110 | // Given: 111 | let source = "This is a test,string" 112 | 113 | // When: 114 | let wordCount = source.wordCount() 115 | 116 | // Then: 117 | XCTAssertEqual(wordCount, 5) 118 | } 119 | 120 | func test_wordCountWithCommaFailureScenario() { 121 | // Given: 122 | let source = "This is a test,string" 123 | 124 | // When: 125 | let wordCount = source.wordCount() 126 | 127 | // Then: 128 | XCTAssertNotEqual(wordCount, 4) 129 | } 130 | 131 | func test_isOneWordWithSuccessScenario() { 132 | // Given: 133 | let source = "Thisisateststring" 134 | 135 | // When: 136 | let isOneWord = source.isOneWord() 137 | 138 | // Then: 139 | XCTAssertTrue(isOneWord) 140 | } 141 | 142 | func test_isOneWordWithFailureScenario() { 143 | // Given: 144 | let source = "This is a test string" 145 | 146 | // When: 147 | let isOneWord = source.isOneWord() 148 | 149 | // Then: 150 | XCTAssertFalse(isOneWord) 151 | } 152 | 153 | func test_isOneWordWithCommaFailureScenario() { 154 | // Given: 155 | let source = "This,is,a,test,string" 156 | 157 | // When: 158 | let isOneWord = source.isOneWord() 159 | 160 | // Then: 161 | XCTAssertFalse(isOneWord) 162 | } 163 | 164 | func test_isOneWordWithFailureScenario_Japanese() { 165 | // Given: 166 | let source = "あなたはそれを行うべきではありません" 167 | 168 | // When: 169 | let isOneWord = source.isOneWord() 170 | 171 | // Then: 172 | XCTAssertFalse(isOneWord) 173 | } 174 | } 175 | -------------------------------------------------------------------------------- /iOSTests/TestStoryboard.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | -------------------------------------------------------------------------------- /iOSTests/TestViewWithOwner.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /iOSTests/TestViewWithoutOwner.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /iOSTests/TimeIntervalConverterArabicTest.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TimeIntervalConverterArabicTest.swift 3 | // ViaSwiftUtils 4 | // 5 | // Copyright 2017 Viacom, Inc. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | import Foundation 20 | @testable import ViaSwiftUtils 21 | import XCTest 22 | 23 | class TimeIntervalConverterArabicTest: XCTestCase { 24 | 25 | override func setUp() { 26 | super.setUp() 27 | 28 | NSLocale.forceLocale(identifier: "ar_SA") 29 | } 30 | 31 | override func tearDown() { 32 | super.tearDown() 33 | 34 | NSLocale.undoForcing() 35 | } 36 | 37 | func testMediaDuration() { 38 | // Given 39 | let interval: TimeInterval = 18 * 60 + 37 40 | 41 | // When 42 | let formattedString = interval.asMediaDuration 43 | 44 | // Then 45 | let expected = "١٨:٣٧" 46 | XCTAssertEqual(formattedString, expected, "Expected string \(expected)") 47 | } 48 | 49 | func testToStringMinutes() { 50 | // Given 51 | let format = "mm:ss" 52 | let interval = 600 53 | let expected = "10:00" 54 | let timeInterval = TimeInterval(interval) 55 | 56 | // When 57 | let formattedString = timeInterval.string(withFormat: format) 58 | 59 | // Then 60 | XCTAssertEqual(formattedString, expected, "Expected string \(expected) even when locale is \(Locale.current)") 61 | } 62 | 63 | func testSecondsToStringHours() { 64 | // Given 65 | let format = "mm:ss" 66 | let interval = 3603 67 | let expected = "00:03" 68 | let timeInterval = TimeInterval(interval) 69 | 70 | // When 71 | let formattedString = timeInterval.string(withFormat: format) 72 | 73 | // Then 74 | XCTAssertEqual(formattedString, expected, "Expected string \(expected) even when locale is \(Locale.current)") 75 | } 76 | 77 | func testSecondsToStringYears() { 78 | // Given 79 | let format = "yyyy, mm:ss" 80 | let interval = 360_045_003 81 | let expected = "1981, 30:03" 82 | let timeInterval = TimeInterval(interval) 83 | 84 | // When 85 | let formattedString = timeInterval.string(withFormat: format) 86 | 87 | // Then 88 | XCTAssertEqual(formattedString, expected, "Expected string \(expected) even when locale is \(Locale.current)") 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /iOSTests/TimeIntervalConverterTest.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TimeIntervalConverterTest.swift 3 | // ViaSwiftUtils 4 | // 5 | // Copyright 2017 Viacom, Inc. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | import Foundation 20 | @testable import ViaSwiftUtils 21 | import XCTest 22 | 23 | class TimeIntervalConverterTest: XCTestCase { 24 | 25 | override func setUp() { 26 | super.setUp() 27 | 28 | NSLocale.forceLocale(identifier: "en_US") 29 | } 30 | 31 | override func tearDown() { 32 | super.tearDown() 33 | 34 | NSLocale.undoForcing() 35 | } 36 | 37 | func testMediaDuration() { 38 | // Given 39 | let interval: TimeInterval = 18 * 60 + 37 40 | 41 | // When 42 | let formattedString = interval.asMediaDuration 43 | 44 | // Then 45 | let expected = "18:37" 46 | XCTAssertEqual(formattedString, expected, "Expected string \(expected)") 47 | } 48 | 49 | func testToStringMinutes() { 50 | // Given 51 | let format = "mm:ss" 52 | let interval = 600 53 | let expected = "10:00" 54 | let timeInterval = TimeInterval(interval) 55 | 56 | // When 57 | let formattedString = timeInterval.string(withFormat: format) 58 | 59 | // Then 60 | XCTAssertEqual(formattedString, expected, "Expected string \(expected)") 61 | } 62 | 63 | func testSecondsToStringHours() { 64 | // Given 65 | let format = "mm:ss" 66 | let interval = 3603 67 | let expected = "00:03" 68 | let timeInterval = TimeInterval(interval) 69 | 70 | // When 71 | let formattedString = timeInterval.string(withFormat: format) 72 | 73 | // Then 74 | XCTAssertEqual(formattedString, expected, "Expected string \(expected)") 75 | } 76 | 77 | func testSecondsToStringYears() { 78 | // Given 79 | let format = "yyyy, mm:ss" 80 | let interval = 360_045_003 81 | let expected = "1981, 30:03" 82 | let timeInterval = TimeInterval(interval) 83 | 84 | // When 85 | let formattedString = timeInterval.string(withFormat: format) 86 | 87 | // Then 88 | XCTAssertEqual(formattedString, expected, "Expected string \(expected)") 89 | } 90 | 91 | } 92 | -------------------------------------------------------------------------------- /iOSTests/UIButtonStateTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIButtonStateTests.swift 3 | // ViaSwiftUtils 4 | // 5 | // Copyright 2017 Viacom, Inc. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | @testable import ViaSwiftUtils 20 | import XCTest 21 | 22 | class UIButtonStateTests: XCTestCase { 23 | 24 | func testAllControlStates() { 25 | // Given 26 | for state in [UIControl.State.normal, .highlighted, .selected, .disabled, .application, .reserved] { 27 | 28 | // When, Then 29 | XCTAssertTrue(UIControl.State.allValues.contains(state), "Expected allValues to contain \(state)") 30 | } 31 | } 32 | 33 | func testFunctionsForAllControlStates() { 34 | // Given a button 35 | let button = UIButton(type: UIButton.ButtonType.custom) 36 | let title = "Click me!" 37 | let backgroundImage = UIImage() 38 | let frontImage = UIImage() 39 | let color = UIColor.brown 40 | 41 | // When 42 | button.setTitleForAllStates(title) 43 | button.setImageForAllStates(frontImage) 44 | button.setBackgroundImageForAllStates(backgroundImage) 45 | button.setTitleColorForAllStates(color) 46 | 47 | // Then 48 | for state in UIControl.State.allValues { 49 | //title is set for all states 50 | XCTAssertEqual(button.title(for: state), title, "Expected title for state \(state) to be \(title)") 51 | XCTAssertEqual(button.backgroundImage(for: state), backgroundImage, "Expected background image for state \(state) to be backgroundImage") 52 | XCTAssertEqual(button.image(for: state), frontImage, "Expected image for state \(state) to be frontImage") 53 | XCTAssertEqual(button.titleColor(for: state), color, "Expected color for state \(state) to be \(color)") 54 | } 55 | 56 | } 57 | 58 | func testImageForState() { 59 | // Given 60 | let button = UIButton(type: UIButton.ButtonType.custom) 61 | let frontImageNormal = UIImage() 62 | let frontImageHighlighted = UIImage() 63 | let frontImageSelected = UIImage() 64 | let frontImageClimax = UIImage() 65 | 66 | // When 67 | button.setImagesForStates(normal: frontImageNormal, highlighted: frontImageHighlighted, selected: frontImageSelected, climax: frontImageClimax ) 68 | 69 | // Then 70 | XCTAssertEqual(button.image(for: .normal), 71 | frontImageNormal, 72 | "Expected image for state \(UIControl.State.normal) to be frontImageNormal") 73 | XCTAssertEqual(button.image(for: .highlighted), 74 | frontImageHighlighted, 75 | "Expected image for state \(UIControl.State.highlighted) to be frontImageHighlighted") 76 | XCTAssertEqual(button.image(for: .selected), 77 | frontImageSelected, 78 | "Expected image for state \(UIControl.State.selected) to be frontImageSelected") 79 | XCTAssertEqual(button.image(for: [.selected, .highlighted]), 80 | frontImageClimax, 81 | "Expected image for state \([UIControl.State.selected, UIControl.State.highlighted]) to be frontImageClimax") 82 | } 83 | 84 | } 85 | -------------------------------------------------------------------------------- /iOSTests/UIImageColorTest.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIImageColorTest.swift 3 | // ViaSwiftUtils 4 | // 5 | // Copyright 2017 Viacom, Inc. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | @testable import ViaSwiftUtils 20 | import XCTest 21 | 22 | class UIImageColorTest: XCTestCase { 23 | 24 | func testConvenienceFailableInitializer() { 25 | // Given 26 | let black = UIColor.black 27 | let defaultSize = CGSize(width: 1.0 * UIScreen.main.scale, height: 1.0 * UIScreen.main.scale) 28 | 29 | // When 30 | let image = UIImage(color: black) 31 | 32 | // Then 33 | XCTAssertNotNil(image, "Expected image should not be nil") 34 | XCTAssertEqual(image?.size, defaultSize, "Expected image size to be \(defaultSize)") 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /iOSTests/UIImageViewRotationTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIImageViewRotationTests.swift 3 | // ViaSwiftUtils 4 | // 5 | // Copyright 2017 Viacom, Inc. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | @testable import ViaSwiftUtils 20 | import XCTest 21 | 22 | class UIImageViewRotationTests: XCTestCase { 23 | 24 | func testIsRotating() { 25 | // Given, When 26 | let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 100, height: 100)) 27 | 28 | // Then 29 | XCTAssertFalse(imageView.isRotating, "Expected imageView to not be rotating before starts") 30 | } 31 | 32 | func testStartRotating() { 33 | // Given, When 34 | let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 100, height: 100)) 35 | 36 | // When 37 | imageView.startRotating() 38 | 39 | // Then 40 | XCTAssertTrue(imageView.isRotating, "Expected imageView to be rotating after startRotating") 41 | } 42 | 43 | func testStopRotating() { 44 | // Given, When 45 | let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 100, height: 100)) 46 | 47 | // When 48 | imageView.startRotating() 49 | imageView.stopRotating() 50 | 51 | // Then 52 | XCTAssertFalse(imageView.isRotating, "Expected imageView to not be rotating after stopRotating") 53 | } 54 | 55 | func testSlowIsSlowerThanNormal() { 56 | // Given, When 57 | let slow = UIImageView.AnimationDuration.slow 58 | let normal = UIImageView.AnimationDuration.normal 59 | 60 | // Then 61 | XCTAssertGreaterThan(slow.duration, normal.duration, "Expected slow duration to be slower than normal duration") 62 | } 63 | 64 | func testNormalIsSlowerThanFast() { 65 | // Given, When 66 | let normal = UIImageView.AnimationDuration.normal 67 | let fast = UIImageView.AnimationDuration.fast 68 | 69 | // Then 70 | XCTAssertGreaterThan(normal.duration, fast.duration, "Expected normal duration to be slower than fast duration") 71 | } 72 | 73 | func testCustomIsWhatYouGiveIt() { 74 | // Given, When 75 | let custom = UIImageView.AnimationDuration.custom(time: 0.2) 76 | 77 | // Then 78 | XCTAssertEqual(custom.duration, 0.2, "Expected custom duration to be exactly what you give it") 79 | } 80 | 81 | func testIsRemovedOnCompletion() { 82 | // Given 83 | let imageView = UIImageView(frame: CGRect(x: 0, y: 0, width: 100, height: 100)) 84 | 85 | // When 86 | imageView.startRotating(isRemovedOnCompletion: false) 87 | 88 | // Then 89 | if let animation = imageView.layer.animation(forKey: RotationAnimation.key) { 90 | XCTAssertFalse(animation.isRemovedOnCompletion) 91 | } else { 92 | XCTFail("imageView is missing animation for \(RotationAnimation.key)") 93 | } 94 | } 95 | 96 | } 97 | -------------------------------------------------------------------------------- /iOSTests/UIViewXibTest.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIViewXibTest.swift 3 | // ViaSwiftUtils 4 | // 5 | // Copyright 2017 Viacom, Inc. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | import Foundation 20 | @testable import ViaSwiftUtils 21 | import XCTest 22 | 23 | class XibView: UIView { } 24 | class TestViewWithoutOwner: UIView { } 25 | 26 | class UIViewXibTest: XCTestCase { 27 | 28 | let expectedSubviewType = String(describing: XibView.self) 29 | 30 | func testLoadNibView() { 31 | // Given 32 | let frame = CGRect(x: 0.0, y: 0.0, width: 10.0, height: 10.0) 33 | let testView = TestViewWithoutOwner(frame: frame) 34 | let owner = testView 35 | let expectedNumberOfSubviews = 1 36 | 37 | // When 38 | testView.loadNibView(owner) 39 | 40 | // Then 41 | XCTAssertEqual(testView.subviews.count, expectedNumberOfSubviews, "Expected number of subviews to be \(expectedNumberOfSubviews)") 42 | 43 | let subviewType = String(describing: type(of: testView.subviews[0])) 44 | XCTAssertEqual(subviewType, expectedSubviewType, "Expected subview type to be \(expectedSubviewType)") 45 | } 46 | 47 | func testLoadFromNib() { 48 | // Given 49 | let nibName = String(describing: TestViewWithoutOwner.self) 50 | let bundle = Bundle(for: TestViewWithoutOwner.self) 51 | 52 | // When 53 | let view = UIView.loadFromNib(named: nibName, bundle: bundle) 54 | 55 | // Then 56 | XCTAssertNotNil(view, "Expected view not to be nil") 57 | view.map { 58 | let subviewType = String(describing: type(of: $0)) 59 | XCTAssertEqual(subviewType, expectedSubviewType, "Expected view type to be \(expectedSubviewType)") 60 | } 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /install_swiftlint.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Installs the SwiftLint package. 4 | # Tries to get the precompiled .pkg file from Github, but if that 5 | # fails just recompiles from source. 6 | 7 | set -e 8 | 9 | SWIFTLINT_PKG_PATH="/tmp/SwiftLint.pkg" 10 | SWIFTLINT_PKG_URL="https://github.com/realm/SwiftLint/releases/download/0.38.0/SwiftLint.pkg" 11 | 12 | wget --output-document=$SWIFTLINT_PKG_PATH $SWIFTLINT_PKG_URL 13 | 14 | if [ -f $SWIFTLINT_PKG_PATH ]; then 15 | echo "SwiftLint package exists! Installing it..." 16 | sudo installer -pkg $SWIFTLINT_PKG_PATH -target / 17 | else 18 | echo "SwiftLint package doesn't exist. Compiling from source..." && 19 | git clone https://github.com/realm/SwiftLint.git /tmp/SwiftLint && 20 | cd /tmp/SwiftLint && 21 | git submodule update --init --recursive && 22 | sudo make install 23 | fi 24 | -------------------------------------------------------------------------------- /tvOSTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | 1 23 | 24 | 25 | -------------------------------------------------------------------------------- /tvOSTests/ViaSwiftUtilsTests_tvOSTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViaSwiftUtilsTests_tvOSTests.swift 3 | // ViaSwiftUtilsTests tvOSTests 4 | // 5 | // Copyright 2017 Viacom, Inc. 6 | // 7 | // Licensed under the Apache License, Version 2.0 (the "License"); 8 | // you may not use this file except in compliance with the License. 9 | // You may obtain a copy of the License at 10 | // 11 | // http://www.apache.org/licenses/LICENSE-2.0 12 | // 13 | // Unless required by applicable law or agreed to in writing, software 14 | // distributed under the License is distributed on an "AS IS" BASIS, 15 | // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | // See the License for the specific language governing permissions and 17 | // limitations under the License. 18 | 19 | import XCTest 20 | 21 | class ViaSwiftUtilsTestsTVOSTests: XCTestCase { 22 | 23 | override func setUp() { 24 | super.setUp() 25 | // Put setup code here. This method is called before the invocation of each test method in the class. 26 | } 27 | 28 | override func tearDown() { 29 | // Put teardown code here. This method is called after the invocation of each test method in the class. 30 | super.tearDown() 31 | } 32 | 33 | func testExample() { 34 | // This is an example of a functional test case. 35 | // Use XCTAssert and related functions to verify your tests produce the correct results. 36 | } 37 | 38 | func testPerformanceExample() { 39 | // This is an example of a performance test case. 40 | self.measureBlock { 41 | // Put the code you want to measure the time of here. 42 | } 43 | } 44 | 45 | } 46 | --------------------------------------------------------------------------------