├── .gitignore ├── Cartfile ├── Cartfile.resolved ├── Carthage └── Checkouts │ └── VinceRP │ ├── .gitignore │ ├── .gitmodules │ ├── .travis.yml │ ├── CONTRIBUTING.md │ ├── Cartfile.private │ ├── Cartfile.resolved │ ├── LICENSE │ ├── README.md │ ├── VinceRP.podspec │ ├── examples │ ├── BasicExample.xcodeproj │ │ ├── project.pbxproj │ │ └── project.xcworkspace │ │ │ └── contents.xcworkspacedata │ ├── BasicExample │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ │ ├── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ │ ├── Contents.json │ │ │ ├── flickr-icon.imageset │ │ │ │ ├── Contents.json │ │ │ │ ├── flickr-icon-1.png │ │ │ │ ├── flickr-icon.png │ │ │ │ └── flickr-icon@3x.png │ │ │ └── login-icon.imageset │ │ │ │ ├── Contents.json │ │ │ │ ├── login-icon.png │ │ │ │ ├── login-icon@2x.png │ │ │ │ └── login-icon@3x.png │ │ ├── Base.lproj │ │ │ ├── LaunchScreen.storyboard │ │ │ └── Main.storyboard │ │ ├── FlickrPhotoCell.swift │ │ ├── FlickrService.swift │ │ ├── FlickrViewController.swift │ │ ├── Info.plist │ │ ├── LoginService.swift │ │ ├── LoginViewControler.swift │ │ └── UIImage+FromColor.swift │ ├── Cartfile │ └── Cartfile.resolved │ ├── logoTrimmed.png │ ├── performance │ ├── Cartfile │ ├── Cartfile.resolved │ ├── Performance.xcodeproj │ │ ├── project.pbxproj │ │ └── project.xcworkspace │ │ │ └── contents.xcworkspacedata │ ├── Performance │ │ ├── AppDelegate.swift │ │ ├── Assets.xcassets │ │ │ └── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ ├── Base.lproj │ │ │ └── MainMenu.xib │ │ └── Info.plist │ └── PerformanceTests │ │ ├── Info.plist │ │ └── PerformanceTests.swift │ ├── scripts │ ├── test.sh │ └── unstash.sh │ ├── vincerp.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ ├── VinceRP-Mac.xcscheme │ │ └── vincerp.xcscheme │ ├── vincerp.xcworkspace │ └── xcshareddata │ │ └── WorkspaceSettings.xcsettings │ ├── vincerp │ ├── Common │ │ ├── Api.swift │ │ ├── Convenient │ │ │ └── Addable.swift │ │ ├── Core │ │ │ ├── BatchUpdate.swift │ │ │ ├── ChangeObserver.swift │ │ │ ├── Dynamic.swift │ │ │ ├── ErrorObserver.swift │ │ │ ├── Hub+operators.swift │ │ │ ├── Hub.swift │ │ │ ├── Incrementing.swift │ │ │ ├── Mapper.swift │ │ │ ├── Node.swift │ │ │ ├── NodeTuple.swift │ │ │ ├── Propagator.swift │ │ │ ├── Reducer.swift │ │ │ ├── Source.swift │ │ │ ├── Throttle.swift │ │ │ └── Wrapper.swift │ │ ├── Threading │ │ │ ├── AtomicLong.swift │ │ │ ├── AtomicReference.swift │ │ │ ├── Dispatchable.swift │ │ │ ├── DynamicVariable.swift │ │ │ ├── Spinlock.swift │ │ │ └── ThreadLocal.swift │ │ ├── Timer.swift │ │ └── Util │ │ │ ├── CommonConstants.swift │ │ │ ├── ExceptionHandling.swift │ │ │ ├── Flattenable.swift │ │ │ ├── FunctionalArray.swift │ │ │ ├── FunctionalDictionary.swift │ │ │ ├── FunctionalSet.swift │ │ │ ├── ReactivePropertyGenerator.swift │ │ │ ├── Try.swift │ │ │ ├── VRPNSObjectHelper.h │ │ │ ├── VRPNSObjectHelper.m │ │ │ ├── WeakReference.swift │ │ │ └── WeakSet.swift │ ├── Extension │ │ ├── AppKit │ │ │ ├── NSResponder+VinceRP.swift │ │ │ ├── NSTextField+VinceRP.swift │ │ │ ├── NSTextField+didChange.swift │ │ │ └── NSView+VinceRP.swift │ │ ├── Common │ │ │ ├── Foundation │ │ │ │ └── String+vincerp.swift │ │ │ └── NSObject+vincerp.swift │ │ └── UIKit │ │ │ ├── UIActivityIndicatorView+vincerp.swift │ │ │ ├── UIButton+vincerp.swift │ │ │ ├── UIControl+vincerp.swift │ │ │ ├── UILabel+vincerp.swift │ │ │ ├── UIResponder+vincerp.swift │ │ │ ├── UISearchBar+didChange.swift │ │ │ ├── UISearchBar+vincerp.swift │ │ │ ├── UIStepper+vincerp.swift │ │ │ ├── UITextField+didChange.swift │ │ │ ├── UITextField+vincerp.swift │ │ │ └── UIView+vincerp.swift │ ├── Info.plist │ ├── UpdateState.swift │ └── vincerp.h │ └── vincerpTests │ ├── Common │ ├── AdvancedSpec.swift │ ├── BasicSpec.swift │ ├── Core │ │ ├── Hub+operatorsSpec.swift │ │ ├── MapperSpec.swift │ │ ├── ReducerSpec.swift │ │ └── ThrottleSpec.swift │ ├── SmokeSpec.swift │ ├── Threading │ │ └── DynamicVariableSpec.swift │ └── Util │ │ ├── FunctionalArraySpec.swift │ │ ├── FunctionalDictionarySpec.swift │ │ ├── FunctionalSetSpec.swift │ │ ├── TrySpec.swift │ │ └── WeakReferenceTests.swift │ ├── Info.plist │ └── TestUtil.swift ├── CopyPasta.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ └── contents.xcworkspacedata ├── xcshareddata │ └── xcschemes │ │ └── CopyPasta.xcscheme └── xcuserdata │ └── vasarhelyia.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ └── xcschememanagement.plist ├── CopyPasta ├── AppDelegate.swift ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ └── Contents.json │ ├── Contents.json │ └── pasta_icon.imageset │ │ ├── Contents.json │ │ └── pasta_icon.png ├── ImageItemCell.xib ├── Info.plist ├── MainMenu.xib ├── MainViewController.swift ├── MainViewController.xib ├── PastePopoverBackgroundView.swift ├── PasteViewModel.swift ├── PasteboardCollectionViewItem.swift ├── PasteboardItem.swift ├── PasteboardService.swift ├── PasteboardViewController.swift ├── PasteboardViewController.xib ├── TextItemCell.xib └── View │ └── Popover │ └── PastePopoverRootView.swift ├── CopyPastaTests ├── Info.plist ├── PasteViewModelTests.swift ├── PasteboardItemTests.swift ├── PasteboardPollingTests.swift └── PasteboardServiceTests.swift ├── LICENSE ├── README.md └── bitrise.yml /.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 | *.xccheckout 13 | *.moved-aside 14 | DerivedData 15 | *.hmap 16 | *.ipa 17 | *.xcuserstate 18 | 19 | # CocoaPods 20 | # 21 | # We recommend against adding the Pods directory to your .gitignore. However 22 | # you should judge for yourself, the pros and cons are mentioned at: 23 | # http://guides.cocoapods.org/using/using-cocoapods.html#should-i-ignore-the-pods-directory-in-source-control 24 | # 25 | # Pods/ 26 | 27 | # Carthage 28 | # 29 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 30 | # Carthage/Checkouts 31 | 32 | Carthage/Build 33 | -------------------------------------------------------------------------------- /Cartfile: -------------------------------------------------------------------------------- 1 | github "bvic23/VinceRP" "v0.2.5" 2 | -------------------------------------------------------------------------------- /Cartfile.resolved: -------------------------------------------------------------------------------- 1 | github "bvic23/VinceRP" "v0.2.5" 2 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | #------- 3 | .DS_Store 4 | 5 | # XCode 6 | #------- 7 | *.pbxuser 8 | *.mode1v3 9 | *.mode2v3 10 | *.perspectivev3 11 | xcuserdata 12 | */build/* 13 | profile 14 | *.moved-aside 15 | DerivedData 16 | Build 17 | *.hmap 18 | *.xccheckout 19 | !default.pbxuser 20 | !default.mode1v3 21 | !default.mode2v3 22 | !default.perspectivev3 23 | 24 | # AppCode 25 | #------- 26 | .idea 27 | 28 | # Carthage 29 | #--------- 30 | Carthage 31 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "Carthage/Checkouts/Nimble"] 2 | path = Carthage/Checkouts/Nimble 3 | url = https://github.com/Quick/Nimble.git 4 | [submodule "Carthage/Checkouts/Quick"] 5 | path = Carthage/Checkouts/Quick 6 | url = https://github.com/Quick/Quick.git 7 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/.travis.yml: -------------------------------------------------------------------------------- 1 | language: objective-c 2 | osx_image: xcode7.1 3 | before_install: 4 | - gem install xcpretty --no-rdoc --no-ri --no-document --quiet 5 | - gem install xcpretty-travis-formatter --no-rdoc --no-ri --no-document --quiet 6 | - ./scripts/unstash.sh 7 | install: 8 | - set -o pipefail && xcodebuild clean build test -project VinceRP.xcodeproj -scheme VinceRP-Mac -sdk macosx -enableCodeCoverage YES ONLY_ACTIVE_ARCH=NO GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=YES GCC_GENERATE_TEST_COVERAGE_FILES=YES CODE_SIGN_IDENTITY="" CODE_SIGNING_REQUIRED=NO | xcpretty -c -f `xcpretty-travis-formatter` 9 | env: 10 | global: 11 | - LANG=en_US.UTF-8 12 | - LC_ALL=en_US.UTF-8 13 | branches: 14 | only: 15 | - master 16 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contribution Guidelines 2 | 3 | ## General Guidelines 4 | 5 | - **Min iOS SDK**: 7.0 6 | - **Min OSX SDK**: 10.9 7 | - **Language**: Swift 2.0 8 | - **Tests**: Yes, please using [Quick](https://github.com/Quick/Quick) 9 | 10 | ## Style Guide 11 | 12 | #### Base style: 13 | 14 | Please add new code to this project based on the following style guideline: 15 | 16 | - [The official Swift style guide for raywenderlich.com.](https://github.com/raywenderlich/swift-style-guide) 17 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/Cartfile.private: -------------------------------------------------------------------------------- 1 | github "Quick/Quick" "HEAD" 2 | github "Quick/Nimble" "HEAD" 3 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/Cartfile.resolved: -------------------------------------------------------------------------------- 1 | github "Quick/Nimble" "c2aafa5da886f4e44ee29b4faaaa20fdac8a9369" 2 | github "Quick/Quick" "5fff1989f54b73f9d51725727e04aafb9557b41e" 3 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright © 2015 bvic23 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/VinceRP.podspec: -------------------------------------------------------------------------------- 1 | Pod::Spec.new do |s| 2 | 3 | s.name = 'VinceRP' 4 | s.version = '0.2.4' 5 | s.summary = 'Easy to use, easy to extend reactive framework for Swift.' 6 | 7 | s.description = <<-DESC 8 | Easy to use, easy to extend reactive framework for Swift. If you are bored of doing MVC over and over give VinceRP a try. 9 | DESC 10 | 11 | s.homepage = 'https://github.com/bvic23/VinceRP' 12 | s.license = { :type => 'MIT', :file => 'LICENSE' } 13 | 14 | s.author = { 'bvic23' => 'bvic23@gmail.com' } 15 | s.social_media_url = 'http://twitter.com/bvic23' 16 | s.platform = :ios, :osx 17 | 18 | s.source = { :git => 'https://github.com/bvic23/VinceRP.git', :tag => s.version.to_s } 19 | 20 | s.source_files = 'VinceRP/Common/**/*.{swift,h,m}', 'VinceRP/Extension/Common/**/*.{swift}' 21 | 22 | s.ios.deployment_target = '8.0' 23 | s.ios.source_files = 'VinceRP/Extension/UIKit/*.{swift}', 24 | 25 | s.osx.deployment_target = '10.10' 26 | s.osx.source_files = 'VinceRP/Extension/AppKit/*.{swift}' 27 | 28 | end 29 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/examples/BasicExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/examples/BasicExample/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 05/09/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | import UIKit 7 | 8 | @UIApplicationMain 9 | class AppDelegate: UIResponder, UIApplicationDelegate { 10 | 11 | var window: UIWindow? 12 | 13 | } 14 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/examples/BasicExample/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/examples/BasicExample/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/examples/BasicExample/Assets.xcassets/flickr-icon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "flickr-icon.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "flickr-icon-1.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "flickr-icon@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/examples/BasicExample/Assets.xcassets/flickr-icon.imageset/flickr-icon-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alltheflow/CopyPasta/79460ff452600945de8c7ef1560c948dfe7b6942/Carthage/Checkouts/VinceRP/examples/BasicExample/Assets.xcassets/flickr-icon.imageset/flickr-icon-1.png -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/examples/BasicExample/Assets.xcassets/flickr-icon.imageset/flickr-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alltheflow/CopyPasta/79460ff452600945de8c7ef1560c948dfe7b6942/Carthage/Checkouts/VinceRP/examples/BasicExample/Assets.xcassets/flickr-icon.imageset/flickr-icon.png -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/examples/BasicExample/Assets.xcassets/flickr-icon.imageset/flickr-icon@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alltheflow/CopyPasta/79460ff452600945de8c7ef1560c948dfe7b6942/Carthage/Checkouts/VinceRP/examples/BasicExample/Assets.xcassets/flickr-icon.imageset/flickr-icon@3x.png -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/examples/BasicExample/Assets.xcassets/login-icon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "filename" : "login-icon.png", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "filename" : "login-icon@2x.png", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "universal", 15 | "filename" : "login-icon@3x.png", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "version" : 1, 21 | "author" : "xcode" 22 | } 23 | } -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/examples/BasicExample/Assets.xcassets/login-icon.imageset/login-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alltheflow/CopyPasta/79460ff452600945de8c7ef1560c948dfe7b6942/Carthage/Checkouts/VinceRP/examples/BasicExample/Assets.xcassets/login-icon.imageset/login-icon.png -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/examples/BasicExample/Assets.xcassets/login-icon.imageset/login-icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alltheflow/CopyPasta/79460ff452600945de8c7ef1560c948dfe7b6942/Carthage/Checkouts/VinceRP/examples/BasicExample/Assets.xcassets/login-icon.imageset/login-icon@2x.png -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/examples/BasicExample/Assets.xcassets/login-icon.imageset/login-icon@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alltheflow/CopyPasta/79460ff452600945de8c7ef1560c948dfe7b6942/Carthage/Checkouts/VinceRP/examples/BasicExample/Assets.xcassets/login-icon.imageset/login-icon@3x.png -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/examples/BasicExample/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 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/examples/BasicExample/FlickrPhotoCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 05/09/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | import UIKit 7 | 8 | class FlickrPhotoCell: UICollectionViewCell { 9 | 10 | @IBOutlet weak var imageView: UIImageView! 11 | 12 | } 13 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/examples/BasicExample/FlickrService.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 07/09/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | import Alamofire 7 | import SwiftyJSON 8 | import UIKit 9 | import VinceRP 10 | 11 | class FlickrService { 12 | 13 | func searchFlickrForTerm(searchTerm: String) -> Hub<[UIImage]> { 14 | 15 | // Create a UIImage-array stream which represents the result of this search 16 | let result = reactive([UIImage]()) 17 | 18 | // Send the search term using the amazing Alamofire framework 19 | Alamofire.request(.GET, searchTerm.flickrSearchURL(), parameters:nil, encoding: .JSON) 20 | .responseData { response in 21 | 22 | // Process the JSON response in a background queue 23 | dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)) { 24 | 25 | switch response.result { 26 | case .Success(let jsonData): 27 | let json = JSON(data: jsonData) 28 | self.processResult(json, result: result) 29 | case .Failure(let error): 30 | 31 | // Send an error using the extension below 32 | result <- JSON.error(error.localizedDescription) 33 | } 34 | } 35 | } 36 | 37 | // Return with this stream variable as a future 38 | return result 39 | } 40 | 41 | private func processResult(json: JSON, result:Source<[UIImage]>) { 42 | if let error = json.error() { 43 | result <- error 44 | return 45 | } 46 | 47 | let photosContainer = json["photos"] 48 | let photosReceived = photosContainer["photo"].array! 49 | let flickrPhotos = photosReceived.map(UIImage.jsonToImageMap) 50 | 51 | result <- flickrPhotos 52 | } 53 | 54 | } 55 | 56 | extension UIImage { 57 | 58 | static func jsonToImageMap(photoDictionary: JSON) -> UIImage { 59 | 60 | // Parse the metadata for an image 61 | let photoID = photoDictionary["id"].string ?? "" 62 | let farm = photoDictionary["farm"].int ?? 0 63 | let server = photoDictionary["server"].string ?? "" 64 | let secret = photoDictionary["secret"].string ?? "" 65 | 66 | // Build the url of the image 67 | let flickrPhotoURL = NSURL(string: "https://farm\(farm).staticflickr.com/\(server)/\(photoID)_\(secret)_m.jpg")! 68 | 69 | // In a real world application this would be an asnyc call. 70 | let imageData = NSData(contentsOfURL: flickrPhotoURL) 71 | return UIImage(data: imageData!)! 72 | } 73 | 74 | } 75 | 76 | // Helper extension 77 | extension JSON { 78 | 79 | // Is there any error in the JSON answer 80 | func error() -> NSError? { 81 | if let state = self["stat"].string { 82 | switch (state) { 83 | case "ok": return nil 84 | case "fail": return JSON.error(self["message"].string!) 85 | default: return JSON.error("Unknown API error") 86 | } 87 | } 88 | return nil 89 | } 90 | 91 | static func error(reason: String) -> NSError { 92 | return NSError(domain: "FlickrSearch", code: 0, userInfo: [NSLocalizedFailureReasonErrorKey:reason]) 93 | } 94 | 95 | } 96 | 97 | extension String { 98 | 99 | // Build the full search URL with tokens and everything 100 | func flickrSearchURL() -> NSURL { 101 | 102 | // URL escape the search term 103 | let escapedTerm = self.stringByAddingPercentEncodingWithAllowedCharacters(NSCharacterSet.URLQueryAllowedCharacterSet())! 104 | 105 | // This is the full search URL with api key and paging 106 | let URLString = "https://api.flickr.com/services/rest/?method=flickr.photos.search&api_key=74c678b0fd9ff0887a104875925021bf&text=\(escapedTerm)&per_page=20&format=json&nojsoncallback=1" 107 | 108 | return NSURL(string: URLString)! 109 | } 110 | 111 | } 112 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/examples/BasicExample/FlickrViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 05/09/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | import UIKit 7 | import VinceRP 8 | 9 | class FlickrViewControler: UIViewController { 10 | 11 | // Renders the thumbnail images 12 | @IBOutlet weak var collectionView: UICollectionView! 13 | 14 | // Show it till the response arrives 15 | @IBOutlet weak var activityIndicator: UIActivityIndicatorView! 16 | 17 | @IBOutlet weak var searchBar: UISearchBar! 18 | 19 | // If there is no result show this 20 | @IBOutlet weak var noResultsLabel: UILabel! 21 | 22 | // Shows how much time we have left before throttle "timeouts" 23 | @IBOutlet weak var progressView: UIProgressView! 24 | 25 | // Counts how much time we have left before throttle "timeouts" 26 | private var throttleTimeout:Source = reactive(0.0) 27 | 28 | // This reactive variable serves as a future/promise: 29 | // - in the one hand we will "write" image arrays into it 30 | // - in the other hand we observing the changes and react on them 31 | private let searchResults = reactive([UIImage]()) 32 | 33 | override func viewDidLoad() { 34 | 35 | // If the results variable changes let's refresh the tableview 36 | // It acts like a future here 37 | self.searchResults.onChange { _ in 38 | self.collectionView.reloadData() 39 | }.dispatchOnMainQueue() 40 | 41 | // True if there is an ongoing search 42 | let searchIsOngoing = reactive(false) 43 | 44 | // True if there is any search results 45 | let hasResult = self.searchResults.map{ $0 != [] } 46 | 47 | let searchTerm = self.searchBar.reactiveText.dispatchOnMainQueue() 48 | 49 | // Hide activity indicator if we are not in a search 50 | self.activityIndicator.reactiveHidden = searchIsOngoing.not() 51 | 52 | // Hide the collection view during the search 53 | self.collectionView.reactiveHidden = searchIsOngoing 54 | 55 | // Update the label on new search 56 | self.noResultsLabel.reactiveText = searchTerm.map { 57 | "Search for '\($0)'" 58 | } 59 | 60 | // Update the progress on every tick of the timer 61 | throttleTimeout.onChange { 62 | self.progressView.setProgress(fminf($0, 1.0), animated: ($0 != 0.0)) 63 | }.dispatchOnMainQueue() 64 | 65 | // If we type let's reset the timeout variable and thus the progress 66 | searchTerm.onChange { _ in 67 | self.throttleTimeout <- 0.0 68 | } 69 | 70 | // If there is search result or search is happening let's hide this label 71 | self.noResultsLabel.reactiveHidden = definedAs { 72 | hasResult* || searchIsOngoing* 73 | } 74 | 75 | // If there is no result update the noResultLabel 76 | hasResult.not().onChange { _ in 77 | self.noResultsLabel.text = "No results for '\(self.searchBar.reactiveText*)'" 78 | }.dispatchOnMainQueue() 79 | 80 | // If no update (new keyboard press) within a 1 second time frame 81 | // and there is anything in the searchbar let's propagate the change 82 | searchTerm.throttle(1.0).ignore("").onChange { searchText in 83 | 84 | // Start the search -> it hides the collectionView (line 52) and shows the activity indicator (line 49) 85 | searchIsOngoing <- true 86 | 87 | // Let's start the search and get back the results on the main thread 88 | let searchResult = FlickrService().searchFlickrForTerm(searchText).dispatchOnMainQueue() 89 | 90 | searchResult.onChange { images in 91 | 92 | // Hide the activity indicator and show the collection view 93 | searchIsOngoing <- false 94 | 95 | // Trigger a reload on the collectionview 96 | // It acts like a promise here 97 | self.searchResults <- images 98 | } 99 | 100 | searchResult.onError { 101 | 102 | // Hide the activity indicator and show the collection view 103 | searchIsOngoing <- false 104 | 105 | // Show the error 106 | self.showError($0) 107 | } 108 | } 109 | 110 | // Set the default states 111 | self.activityIndicator.hidden = true 112 | self.noResultsLabel.hidden = true 113 | 114 | // Start the timer 115 | timer(0.1) { 116 | 117 | // Increment the on every tick 118 | // Note: this is an operator overloading (CMD + click on the "+=" to check it out) 119 | self.throttleTimeout += 0.1 120 | } 121 | 122 | } 123 | 124 | // Build and show an alert dialog 125 | private func showError(error: NSError) { 126 | 127 | let alertController = UIAlertController(title: "Flickr error", 128 | message: error.localizedDescription, 129 | preferredStyle: UIAlertControllerStyle.Alert) 130 | 131 | alertController.addAction(UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.Default, handler: nil)) 132 | 133 | self.presentViewController(alertController, animated: true, completion: nil) 134 | } 135 | } 136 | 137 | // Make this controller as a datasource of the CollectionView 138 | extension FlickrViewControler: UICollectionViewDataSource { 139 | 140 | func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { 141 | return searchResults*.count 142 | } 143 | 144 | func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell { 145 | let cell = collectionView.dequeueReusableCellWithReuseIdentifier("FlickrCell", forIndexPath: indexPath) as! FlickrPhotoCell 146 | let flickrPhoto = searchResults.value()[indexPath.row] 147 | cell.imageView.image = flickrPhoto 148 | return cell 149 | } 150 | 151 | } 152 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/examples/BasicExample/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 | UISupportedInterfaceOrientations 34 | 35 | UIInterfaceOrientationPortrait 36 | UIInterfaceOrientationLandscapeLeft 37 | UIInterfaceOrientationLandscapeRight 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/examples/BasicExample/LoginService.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 13/10/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | import Alamofire 7 | import SwiftyJSON 8 | import VinceRP 9 | 10 | // This example service uses a simple login/password service: 11 | // http://todolist.parseapp.com/ 12 | 13 | class LoginService { 14 | 15 | func login(username: String, password: String) -> Hub { 16 | 17 | // Let's define a stream of bool values which represents the result of the login 18 | // We'll return this variable as a promise at the end of this method 19 | let returnValue = reactive(false) 20 | 21 | // The necessary parameters for the login, username / password are the dynamic ones 22 | let params = [ 23 | "_ApplicationId": "0Oq3tTp9JMvd72LOrGN25PiEq9XgVHCxo57MQbpT", 24 | "_ClientVersion": "js1.1.15", 25 | "_InstallationId": "33bf814e-f1c0-8412-ee3e-76f976898599", 26 | "_JavaScriptKey": "vUFy2o7nFx3eeKVlZneYMPI2MBoxT5LhWNoIWPja", 27 | "_method": "GET", 28 | "username": username, 29 | "password": password, 30 | ] 31 | 32 | // Send the request 33 | Alamofire.request(.POST, "https://api.parse.com/1/login", parameters:params, encoding: .JSON) 34 | .responseData { response in 35 | 36 | // Parse the response 37 | switch response.result { 38 | case .Success(let jsonData): 39 | let json = JSON(data: jsonData) 40 | 41 | // If there is an error field... 42 | if let _ = json["error"].string { 43 | 44 | // ... we send a false here 45 | returnValue <- false 46 | } else { 47 | 48 | // ... we send a true otherwise 49 | returnValue <- true 50 | } 51 | 52 | // If it fails... 53 | case .Failure(let error): 54 | // ... we send an error 55 | returnValue <- self.error("\(error)") 56 | } 57 | } 58 | 59 | // We return with our "future like" reactive variable 60 | return returnValue 61 | } 62 | 63 | private func error(errorString: String) -> NSError { 64 | 65 | return NSError(domain: "VinceRP", code: -1, userInfo: [NSLocalizedFailureReasonErrorKey: errorString]) 66 | 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/examples/BasicExample/LoginViewControler.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 05/09/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | import Alamofire 7 | import UIKit 8 | import VinceRP 9 | 10 | private let emailRegEx = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}" 11 | 12 | // Let's add a handy extension helps us to validate the email and password field 13 | extension UITextField { 14 | 15 | var isValidEmail: Bool { 16 | return definedAs { 17 | // Remember * is just a shortcut for getting the current value 18 | let range = self.reactiveText*.rangeOfString(emailRegEx, options:.RegularExpressionSearch) 19 | return range != nil 20 | }* 21 | } 22 | 23 | var isValidPassword: Bool { 24 | return definedAs { 25 | self.reactiveText*.trim().length > 0 26 | }* 27 | } 28 | 29 | } 30 | 31 | class LoginViewControler: UIViewController { 32 | 33 | @IBOutlet weak var emailField: UITextField! 34 | @IBOutlet weak var passwordField: UITextField! 35 | @IBOutlet weak var loginButton: UIButton! 36 | @IBOutlet weak var activityIndicator: UIActivityIndicatorView! 37 | 38 | override func viewDidAppear(animated: Bool) { 39 | 40 | // Make this button blue when enabled and gray when disabled 41 | setupLoginButtonUI() 42 | 43 | // Let's defined reactive version of the enabled property 44 | self.loginButton.reactiveEnabled = definedAs { 45 | 46 | // Whenever email or password field receive's a new value (via typing or copy/paste) 47 | // this 'reactiveEnabled' will receive a new boolean value 48 | // which is true if both 'isValidEmail' and 'isValidPassword' emit a true, false otherwise 49 | self.emailField.isValidEmail && 50 | self.passwordField.isValidPassword 51 | } 52 | 53 | // Default values for username and password 54 | self.emailField.text = "bvic23@gmail.com" 55 | self.passwordField.text = "123456" 56 | 57 | // Hide the spinner login command is not executing 58 | self.activityIndicator.reactiveHidden = self.loginButton.executing.not() 59 | 60 | // Hide the button if login is in progress 61 | self.loginButton.reactiveHidden = self.loginButton.executing 62 | 63 | // Create a stream of string value start with an empty string 64 | let alert = reactive("") 65 | 66 | // Watch the changes of alert variable and skip the first value 67 | alert.onChange(skipInitial: true) { 68 | 69 | // Get the current value from the stream which is the message we should show in the alert dialog 70 | let message = $0 71 | 72 | // Create an alert controller 73 | let alertController = UIAlertController(title: "Login Result", 74 | message: message, 75 | preferredStyle: UIAlertControllerStyle.Alert) 76 | 77 | // Add a close button 78 | alertController.addAction(UIAlertAction(title: "Dismiss", style: UIAlertActionStyle.Default, handler: nil)) 79 | 80 | // Show it 81 | self.presentViewController(alertController, animated: true, completion: nil) 82 | }.dispatchOnMainQueue() 83 | 84 | // Add a clickhandler 85 | self.loginButton.clickHandler = definedAs { handler in // Because this block can contain any kind of threading 86 | // this handler is for marking this clickhandler "done". 87 | // Why this is necessary? Because I like the "executing" property of RACCommand and 88 | // the calling of "done" sets "executing" to false. 89 | // If you have any better idea how to do this please send a PR or create an issue 90 | 91 | 92 | // Close the keyboard 93 | self.emailField.resignFirstResponder() 94 | self.passwordField.resignFirstResponder() 95 | 96 | // Create a loginservice 97 | let loginService = LoginService() 98 | 99 | // Initiate the login process 100 | let login = loginService.login(self.emailField.text!, password: self.passwordField.text!) 101 | login.onChange { 102 | 103 | if $0 { 104 | // Send a greeting 105 | alert <- "Welcome!" 106 | } else { 107 | // Send a greeting 108 | alert <- "Login failed, username or password is invalid!" 109 | } 110 | 111 | // Mark this handler done 112 | handler.done() 113 | } 114 | login.onError { error in 115 | 116 | // Send an error 117 | alert <- "Login failed with error: \(error)" 118 | handler.done() 119 | } 120 | } 121 | 122 | } 123 | 124 | func setupLoginButtonUI() { 125 | self.loginButton.setBackgroundImage(UIImage.imageWithColor(UIColor.blueColor()), forState: .Normal) 126 | self.loginButton.setBackgroundImage(UIImage.imageWithColor(UIColor.grayColor()), forState: .Disabled) 127 | self.loginButton.layer.cornerRadius = 3.0 128 | self.loginButton.layer.masksToBounds = true 129 | } 130 | 131 | } 132 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/examples/BasicExample/UIImage+FromColor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 14/09/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | import UIKit 7 | 8 | public extension UIImage { 9 | 10 | // Create an image from a given color 11 | public class func imageWithColor(color: UIColor) -> UIImage { 12 | let rect = CGRectMake(0.0, 0.0, 1.0, 1.0) 13 | UIGraphicsBeginImageContext(rect.size) 14 | let context = UIGraphicsGetCurrentContext() 15 | 16 | CGContextSetFillColorWithColor(context, color.CGColor) 17 | CGContextFillRect(context, rect) 18 | 19 | let image = UIGraphicsGetImageFromCurrentImageContext() 20 | UIGraphicsEndImageContext() 21 | 22 | return image 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/examples/Cartfile: -------------------------------------------------------------------------------- 1 | github "Alamofire/Alamofire" 2 | github "SwiftyJSON/SwiftyJSON" "HEAD" 3 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/examples/Cartfile.resolved: -------------------------------------------------------------------------------- 1 | github "Alamofire/Alamofire" "3.1.0" 2 | github "SwiftyJSON/SwiftyJSON" "3b5d0d4e0c19173b1bf0dc823bde25ad870f70c0" 3 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/logoTrimmed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alltheflow/CopyPasta/79460ff452600945de8c7ef1560c948dfe7b6942/Carthage/Checkouts/VinceRP/logoTrimmed.png -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/performance/Cartfile: -------------------------------------------------------------------------------- 1 | github "JensRavens/Interstellar" "master" 2 | github "ReactiveX/RxSwift" "master" 3 | github "SwiftBond/Bond" "master" 4 | github "ReactiveCocoa/ReactiveCocoa" "v4.0.0-alpha.2" 5 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/performance/Cartfile.resolved: -------------------------------------------------------------------------------- 1 | github "SwiftBond/Bond" "41e557292ee940257216a8b862f0b2da5fd44cb0" 2 | github "JensRavens/Interstellar" "5133f2914830cef456d0cfff832a402d04d61ae3" 3 | github "antitypical/Result" "0.6.0-beta.3" 4 | github "ReactiveX/RxSwift" "c909f0f572d84f7b7578c55b7b16d853ebad5f8a" 5 | github "ReactiveCocoa/ReactiveCocoa" "v4.0.0-alpha.2" 6 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/performance/Performance.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/performance/Performance/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // Performance 4 | // 5 | // Created by Viktor Belényesi on 30/11/15. 6 | // Copyright © 2015 Viktor Belenyesi. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | @NSApplicationMain 12 | class AppDelegate: NSObject, NSApplicationDelegate { 13 | 14 | @IBOutlet weak var window: NSWindow! 15 | 16 | 17 | func applicationDidFinishLaunching(aNotification: NSNotification) { 18 | // Insert code here to initialize your application 19 | } 20 | 21 | func applicationWillTerminate(aNotification: NSNotification) { 22 | // Insert code here to tear down your application 23 | } 24 | 25 | 26 | } 27 | 28 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/performance/Performance/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "size" : "16x16", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "size" : "16x16", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "mac", 15 | "size" : "32x32", 16 | "scale" : "1x" 17 | }, 18 | { 19 | "idiom" : "mac", 20 | "size" : "32x32", 21 | "scale" : "2x" 22 | }, 23 | { 24 | "idiom" : "mac", 25 | "size" : "128x128", 26 | "scale" : "1x" 27 | }, 28 | { 29 | "idiom" : "mac", 30 | "size" : "128x128", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "idiom" : "mac", 35 | "size" : "256x256", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "mac", 40 | "size" : "256x256", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "mac", 45 | "size" : "512x512", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "mac", 50 | "size" : "512x512", 51 | "scale" : "2x" 52 | } 53 | ], 54 | "info" : { 55 | "version" : 1, 56 | "author" : "xcode" 57 | } 58 | } -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/performance/Performance/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 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 | LSMinimumSystemVersion 26 | $(MACOSX_DEPLOYMENT_TARGET) 27 | NSHumanReadableCopyright 28 | Copyright © 2015 Viktor Belenyesi. All rights reserved. 29 | NSMainNibFile 30 | MainMenu 31 | NSPrincipalClass 32 | NSApplication 33 | 34 | 35 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/performance/PerformanceTests/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 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/scripts/test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | set -o pipefail && xcodebuild clean build test \ 4 | -project VinceRP.xcodeproj \ 5 | -scheme VinceRP_mac \ 6 | -sdk macosx \ 7 | -enableCodeCoverage YES 8 | ONLY_ACTIVE_ARCH=NO \ 9 | GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=YES \ 10 | GCC_GENERATE_TEST_COVERAGE_FILES=YES \ 11 | CODE_SIGN_IDENTITY="" \ 12 | CODE_SIGNING_REQUIRED=NO 13 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/scripts/unstash.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | CARTHAGE_BINARY_DIR="Carthage/Build" 4 | CARTHAGE_ZIP="Carthage.zip" 5 | 6 | rm -rf "${CARTHAGE_BINARY_DIR}" 7 | mkdir -p "${CARTHAGE_BINARY_DIR}" 8 | curl -L https://www.googledrive.com/host/0B--nGoDU083MUGoyenBlajFIOUk > "${CARTHAGE_BINARY_DIR}/${CARTHAGE_ZIP}" 9 | cd "${CARTHAGE_BINARY_DIR}" 10 | unzip "${CARTHAGE_ZIP}" 11 | rm -f "${CARTHAGE_ZIP}" 12 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerp.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerp.xcodeproj/xcshareddata/xcschemes/VinceRP-Mac.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 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerp.xcodeproj/xcshareddata/xcschemes/vincerp.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 29 | 35 | 36 | 37 | 38 | 39 | 45 | 46 | 48 | 54 | 55 | 56 | 57 | 58 | 64 | 65 | 66 | 67 | 68 | 69 | 79 | 80 | 86 | 87 | 88 | 89 | 90 | 91 | 97 | 98 | 104 | 105 | 106 | 107 | 109 | 110 | 113 | 114 | 115 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerp.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEWorkspaceSharedSettings_AutocreateContextsIfNeeded 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerp/Common/Api.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 01/05/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | /* Operators */ 7 | 8 | postfix operator * {} 9 | 10 | public postfix func *(arg: Hub) -> T { 11 | return arg.value() 12 | } 13 | 14 | infix operator <- { associativity left precedence 160 } 15 | 16 | public func <-(left: Source, right: NSError) -> Source { 17 | left.error(right) 18 | return left 19 | } 20 | 21 | public func <-(left: Source, right: T) -> Source { 22 | left.update(right) 23 | return left 24 | } 25 | 26 | /* API functions */ 27 | 28 | public func definedAs(calc: () -> T) -> Dynamic { 29 | return Dynamic(calc: calc) 30 | } 31 | 32 | public func reactive(initValue: T) -> Source { 33 | return Source(initValue: initValue) 34 | } 35 | 36 | public func reactive() -> Source { 37 | return Source() 38 | } 39 | 40 | public func onChangeDo(source: Hub, callback: (T) -> ()) -> ChangeObserver { 41 | return onChangeDo(source, skipInitial: false, callback: callback) 42 | } 43 | 44 | public func onChangeDo(source: Hub, skipInitial: Bool, callback: (T) -> ()) -> ChangeObserver { 45 | return ChangeObserver(source: source, callback: ({ 46 | callback(source.value()) 47 | }), skipInitial: skipInitial) 48 | } 49 | 50 | public func onErrorDo(source: Hub, callback: (NSError) -> ()) -> ErrorObserver { 51 | return ErrorObserver(source: source, callback: callback) 52 | } 53 | 54 | public func timer(interval: NSTimeInterval, tick: () -> ()) -> Timer { 55 | return Timer.timer(interval, tick: tick) 56 | } 57 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerp/Common/Convenient/Addable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 18/09/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | public protocol Addable { 7 | func +(lhs: Self, rhs: Self) -> Self 8 | } 9 | 10 | extension Double: Addable {} 11 | extension Float: Addable {} 12 | 13 | public func +=(left: Source, right: T) -> Source { 14 | left <- (left* + right) 15 | return left 16 | } 17 | 18 | public func +=(left: Source, right: T) -> Source { 19 | left <- (left* + right) 20 | return left 21 | } 22 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerp/Common/Core/BatchUpdate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 27/04/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | protocol UpdateProtocol { 7 | func ancestors() -> [NodeTuple] 8 | } 9 | 10 | public struct BatchUpdate { 11 | var updates: [UpdateProtocol] 12 | 13 | init(_ v: Source, withValue t: T) { 14 | self.updates = [Update(v, t)] 15 | } 16 | 17 | init(updates: [UpdateProtocol], u: Update) { 18 | self.updates = updates.arrayByAppending(u) 19 | } 20 | 21 | func and(v: Source, withValue t: T) -> BatchUpdate { 22 | return BatchUpdate(updates: self.updates, u: Update(v, t)) 23 | } 24 | 25 | func now() { 26 | Propagator.dispatch { 27 | Propagator.propagate(self.updates.flatMap { 28 | $0.ancestors() 29 | }) 30 | } 31 | } 32 | 33 | } 34 | 35 | struct Update: UpdateProtocol { 36 | let v: Source 37 | let t: T 38 | 39 | init(_ v: Source, _ t: T) { 40 | self.v = v 41 | self.t = t 42 | v.updateSilent(t) 43 | } 44 | 45 | func ancestors() -> [NodeTuple] { 46 | return Array(self.v.children).map { 47 | NodeTuple($0, $0) 48 | } 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerp/Common/Core/ChangeObserver.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 13/04/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | public class ChangeObserver: Node { 7 | 8 | private static var changeObservers = Set() 9 | var dispatchQueue: dispatch_queue_t! 10 | 11 | let source: Node 12 | let callback: () -> () 13 | 14 | public init(source: Node, callback: () -> (), skipInitial: Bool = false) { 15 | self.source = source 16 | self.callback = callback 17 | 18 | super.init() 19 | 20 | source.linkChild(self) 21 | 22 | if (!skipInitial) { 23 | trigger() 24 | } 25 | 26 | ChangeObserver.changeObservers.insert(self) 27 | } 28 | 29 | override public var parents: Set { 30 | return toSet(source) 31 | } 32 | 33 | override var level: long { 34 | return long.max 35 | } 36 | 37 | override func ping(incoming: Set) -> Set { 38 | if (!parents.intersect(incoming).isEmpty && source.isSuccess()) { 39 | if let q = dispatchQueue { 40 | dispatch_async(q) { 41 | self.callback() 42 | } 43 | } else { 44 | callback() 45 | } 46 | } 47 | return Set() 48 | } 49 | 50 | private func trigger() { 51 | ping(parents) 52 | } 53 | 54 | override public func kill() { 55 | super.kill() 56 | ChangeObserver.changeObservers.remove(self) 57 | } 58 | 59 | } 60 | 61 | extension ChangeObserver: Dispatchable { 62 | 63 | public func dispatchOnQueue(dispatchQueue: dispatch_queue_t?) -> ChangeObserver { 64 | self.dispatchQueue = dispatchQueue 65 | return self 66 | } 67 | 68 | } -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerp/Common/Core/Dynamic.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 18/04/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | let globalDynamic: DynamicVariable<(Node, Array)> = DynamicVariable(nil) 7 | 8 | public class Dynamic: Incrementing { 9 | private let calc: () -> T 10 | 11 | public init(calc: () -> T) { 12 | self.calc = calc 13 | super.init() 14 | } 15 | 16 | override func makeState() -> UpdateState { 17 | let startID = nextID() 18 | 19 | let (newValue, deps): (Try, Array) = globalDynamic.withValue((self, [])) { 20 | let calcResult = self.probe(self.calc) 21 | guard let deps = globalDynamic.value?.1 else { 22 | return (calcResult, []) 23 | } 24 | return (calcResult, deps) 25 | } 26 | 27 | let levels = Set(deps.map { 28 | $0.level 29 | }) 30 | 31 | let level = levels.max(0) 32 | 33 | return UpdateState(Set(deps), level, startID, newValue) 34 | } 35 | 36 | func probe(calc: () -> T) ->Try { 37 | var result: Try? 38 | try2 { 39 | ({ 40 | result = Try(calc()) 41 | }, 42 | catch2: { e in 43 | if let ex = e { 44 | if let e = ex.userInfo!["error"] as? NSError { 45 | result = Try(e) 46 | } 47 | } 48 | }, 49 | finally: { 50 | 51 | }) 52 | } 53 | return result! 54 | } 55 | 56 | override func ping(incoming: Set) -> Set { 57 | guard !parents.intersect(incoming).isEmpty || incoming.contains(self) else { 58 | return Set() 59 | } 60 | return super.ping(incoming) 61 | } 62 | 63 | override public func toTry() -> Try { 64 | return self.state.value 65 | } 66 | 67 | override func isSuccess() -> Bool { 68 | return self.state.value.isSuccess() 69 | } 70 | 71 | override public var parents: Set { 72 | return state.parents 73 | } 74 | 75 | override var level: long { 76 | return state.level 77 | } 78 | 79 | } 80 | 81 | 82 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerp/Common/Core/ErrorObserver.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 27/09/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | import Foundation 7 | 8 | public class ErrorObserver: ChangeObserver { 9 | 10 | private static var errorObservers = Set() 11 | private var errorCallback: (NSError) -> () 12 | 13 | public init(source: Node, callback: (NSError) -> (), name: String = "") { 14 | self.errorCallback = callback 15 | super.init(source:source, callback:({}), skipInitial:true) 16 | ErrorObserver.errorObservers.insert(self) 17 | } 18 | 19 | override func ping(incoming: Set) -> Set { 20 | if (!parents.intersect(incoming).isEmpty && !source.isSuccess()) { 21 | if let q = dispatchQueue { 22 | dispatch_async(q) { 23 | self.errorCallback(self.source.error()) 24 | } 25 | } else { 26 | self.errorCallback(source.error()) 27 | } 28 | } 29 | return Set() 30 | } 31 | 32 | override public func kill() { 33 | super.kill() 34 | ErrorObserver.errorObservers.remove(self) 35 | } 36 | 37 | } 38 | 39 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerp/Common/Core/Hub+operators.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 27/09/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | public extension Hub where T: BooleanType { 7 | 8 | public func not() -> Hub { 9 | return self.map(!) 10 | } 11 | 12 | } 13 | 14 | public extension Hub where T: Equatable { 15 | 16 | public func ignore(ignorabeValues: T) -> Hub { 17 | return self.filter { $0 != ignorabeValues } 18 | } 19 | 20 | } 21 | 22 | 23 | public extension Hub { 24 | 25 | public func foreach(skipInitial: Bool = false, callback: T -> ()) -> ChangeObserver { 26 | let obs = ChangeObserver(source:self, callback:{callback(self.value())}, skipInitial:skipInitial) 27 | return obs 28 | } 29 | 30 | public func skipErrors() -> Hub { 31 | return filterAll { $0.isSuccess() } 32 | } 33 | 34 | } 35 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerp/Common/Core/Hub.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 27/09/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | public class Hub: Node { 7 | 8 | var dispatchQueue: dispatch_queue_t! 9 | 10 | func currentValue() -> T { 11 | switch (toTry()) { 12 | case .Success(let value): return value 13 | case .Failure(let error): NSException(name:"name", reason:"domain", userInfo:["error":error]).raise() 14 | } 15 | fatalError(UNREACHABLE_CODE) 16 | } 17 | 18 | override public func error() -> NSError { 19 | switch (toTry()) { 20 | case .Failure(let error): return error 21 | case .Success(_): fatalError(UNREACHABLE_CODE) 22 | } 23 | fatalError(UNREACHABLE_CODE) 24 | } 25 | 26 | public func value() -> T { 27 | if let (e, d) = globalDynamic.value { 28 | linkChild(e) 29 | globalDynamic.value = (e, d.arrayByPrepending(self)) 30 | } else { 31 | globalDynamic.value = nil 32 | } 33 | return currentValue() 34 | } 35 | 36 | func propagate() { 37 | Propagator.dispatch { 38 | let mappedTargets = self.children.map { 39 | NodeTuple(self, $0) 40 | } 41 | Propagator.propagate(mappedTargets) 42 | } 43 | } 44 | 45 | public func toTry() -> Try { 46 | fatalError(ABSTRACT_METHOD) 47 | } 48 | 49 | public func killAll() { 50 | kill() 51 | descendants.forEach { 52 | $0.kill() 53 | } 54 | } 55 | 56 | public func recalc() { 57 | Propagator.propagate(toSet(NodeTuple(self, self))) 58 | } 59 | 60 | public func onChange(skipInitial skipInitial: Bool = true, callback: (T) -> ()) -> ChangeObserver { 61 | return onChangeDo(self, skipInitial: skipInitial) { 62 | callback($0) 63 | } 64 | } 65 | 66 | public func onError(callback: (NSError) -> ()) -> ErrorObserver { 67 | return onErrorDo(self) { 68 | callback($0) 69 | } 70 | } 71 | 72 | } 73 | 74 | extension Hub: Dispatchable { 75 | 76 | public func dispatchOnQueue(dispatchQueue: dispatch_queue_t?) -> Hub { 77 | self.dispatchQueue = dispatchQueue 78 | return self 79 | } 80 | 81 | } 82 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerp/Common/Core/Incrementing.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belényesi on 01/12/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | public class Incrementing: Hub { 7 | 8 | private var _state: AtomicReference> 9 | 10 | override init() { 11 | _state = AtomicReference(UpdateState(Try(noValueError))) 12 | super.init() 13 | _state = AtomicReference(self.makeState()) 14 | } 15 | 16 | var state: UpdateState { 17 | return _state.value 18 | } 19 | 20 | func toTry() -> UpdateState { 21 | return self.state 22 | } 23 | 24 | func makeState() -> UpdateState { 25 | fatalError(ABSTRACT_METHOD) 26 | } 27 | 28 | override func ping(incoming: Set) -> Set { 29 | let newState = makeState() 30 | 31 | if newState.id <= _state.value.id { 32 | return Set() 33 | } 34 | 35 | if newState.value.description == _state.value.value.description { 36 | return Set() 37 | } 38 | 39 | _state.value = newState 40 | return children 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerp/Common/Core/Mapper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 13/05/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | class Mapper: Wrapper { 7 | 8 | private let transformer: Try -> Try 9 | 10 | init(_ source: Hub, _ transformer: Try -> Try) { 11 | self.transformer = transformer 12 | super.init(source) 13 | } 14 | 15 | override var state: UpdateState { 16 | return makeState() 17 | } 18 | 19 | override func makeState() -> UpdateState { 20 | return UpdateState(nextID(), transformer(source.toTry())) 21 | } 22 | 23 | } 24 | 25 | public extension Hub { 26 | 27 | public func map(f: T -> A) -> Hub { 28 | return Mapper(self) { x in 29 | return x.map(f) 30 | } 31 | } 32 | 33 | public func mapAll(f: Try -> Try) -> Hub { 34 | return Mapper(self, f) 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerp/Common/Core/Node.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 19/04/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | public class Node: Hashable { 7 | 8 | private static var hashCounter = AtomicLong(0) 9 | private let childrenHolder = AtomicReference>(WeakSet()) 10 | public let hashValue = Int(Node.hashCounter.getAndIncrement()) // Hashable 11 | 12 | public var children: Set { 13 | return childrenHolder.value.filter { 14 | $0.parents.contains(self) 15 | }.set 16 | } 17 | 18 | public var descendants: Set { 19 | return children ++ children.flatMap { 20 | $0.descendants 21 | } 22 | } 23 | 24 | public var parents: Set { 25 | return Set() 26 | } 27 | 28 | public var ancestors: Set { 29 | return parents ++ parents.flatMap { 30 | $0.ancestors 31 | } 32 | } 33 | 34 | func isSuccess() -> Bool { 35 | return true 36 | } 37 | 38 | func error() -> NSError { 39 | fatalError(ABSTRACT_METHOD) 40 | } 41 | 42 | func linkChild(child: Node) { 43 | if (!childrenHolder.value.hasElementPassingTest {$0 == child}) { 44 | childrenHolder.value.insert(child) 45 | } 46 | } 47 | 48 | func unlinkChild(child: Node) { 49 | childrenHolder.value = childrenHolder.value.filter {$0 != child} 50 | } 51 | 52 | func ping(incoming: Set) -> Set { 53 | fatalError(ABSTRACT_METHOD) 54 | } 55 | 56 | private var alive = true 57 | 58 | public func kill() { 59 | alive = false 60 | parents.forEach { 61 | $0.unlinkChild(self) 62 | } 63 | } 64 | 65 | var level: long { 66 | return 0 67 | } 68 | 69 | } 70 | 71 | public func ==(lhs: Node, rhs: Node) -> Bool { 72 | return lhs.hashValue == rhs.hashValue 73 | } 74 | 75 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerp/Common/Core/NodeTuple.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 11/3/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | struct NodeTuple: Hashable { 7 | let source: Node 8 | let reactor: Node 9 | 10 | init(_ source: Node, _ reactor: Node) { 11 | self.source = source 12 | self.reactor = reactor 13 | } 14 | 15 | var hashValue: Int { 16 | return source.hashValue ^ reactor.hashValue 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerp/Common/Core/Propagator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 18/04/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | func ==(lhs: NodeTuple, rhs: NodeTuple) -> Bool { 7 | return lhs.source.hashValue == rhs.source.hashValue && lhs.reactor.hashValue == rhs.source.hashValue 8 | } 9 | 10 | 11 | public class Propagator { 12 | 13 | public static var async: Bool = false 14 | public static let propagationQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0) 15 | 16 | static func dispatch(block: () -> ()) { 17 | if async { 18 | dispatch_async(propagationQueue) { 19 | block() 20 | } 21 | } else { 22 | block() 23 | } 24 | } 25 | 26 | static func propagate(nodes: [NodeTuple]) { 27 | self.propagate(Set(nodes)) 28 | } 29 | 30 | static func propagate(nodes: Set) { 31 | guard nodes.count > 0 else { 32 | return 33 | } 34 | 35 | let minLevel = nodes.map{ $0.reactor.level }.min(0) 36 | let (now, later) = nodes.partition { 37 | $0.reactor.level == minLevel 38 | } 39 | 40 | let next = now.groupBy{ 41 | $0.reactor 42 | }.mapValues{ 43 | $0.map{ $0.source } 44 | }.map{ (target, pingers) in 45 | return target.ping(pingers).map { 46 | NodeTuple(target, $0) 47 | } 48 | }.flatten() 49 | 50 | propagate(next + later) 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerp/Common/Core/Reducer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 13/09/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | class Reducer: Wrapper { 7 | 8 | private let transformer: (UpdateState, Try) -> UpdateState 9 | 10 | init(_ source: Hub, _ transformer: (UpdateState, Try) -> UpdateState) { 11 | self.transformer = transformer 12 | super.init(source) 13 | } 14 | 15 | override func makeState() -> UpdateState { 16 | return transformer(self.state, source.toTry()) 17 | } 18 | 19 | } 20 | 21 | public extension Hub { 22 | 23 | public func filter(successPred: T -> Bool) -> Hub { 24 | return Reducer(self) { (x, y) in 25 | switch (x, y) { 26 | case (_, .Success(let value)) where successPred(value): return UpdateState(y) 27 | case (_, .Failure(_)): return UpdateState(y) 28 | default: return x 29 | } 30 | } 31 | } 32 | 33 | public func filterAll(predicate: Try -> Bool) -> Hub { 34 | return Reducer(self) { (x, y) in 35 | guard predicate(y) else { 36 | return x 37 | } 38 | return UpdateState(y) 39 | } 40 | } 41 | 42 | public func reduce(combiner: (T, T) -> T) -> Hub { 43 | return Reducer(self) { (x, y) in 44 | switch (x.value, y) { 45 | case (.Success(let a), .Success(let b)): return UpdateState(Try(combiner(a, b))) 46 | default: return UpdateState(y) 47 | } 48 | } 49 | } 50 | 51 | public func reduceAll(combiner: (UpdateState, Try) -> Try) -> Hub { 52 | return Reducer(self) { (x, y) in 53 | UpdateState(combiner(x, y)) 54 | } 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerp/Common/Core/Source.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 15/09/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | let noValueError = NSError(domain: "no value", code: -1, userInfo: nil) 7 | 8 | public class Source: Hub { 9 | 10 | let state: AtomicReference> 11 | 12 | public init(initValue: T) { 13 | state = AtomicReference(Try(initValue)) 14 | super.init() 15 | } 16 | 17 | public override init() { 18 | state = AtomicReference(Try(noValueError)) 19 | super.init() 20 | } 21 | 22 | public func update(newValue: T) { 23 | self.updateSilent(newValue) 24 | self.propagate() 25 | } 26 | 27 | public func error(error: NSError) { 28 | self.state.value = Try(error) 29 | self.propagate() 30 | } 31 | 32 | public func updateSilent(newValue:T) { 33 | self.state.value = Try(newValue) 34 | } 35 | 36 | override func isSuccess() -> Bool { 37 | return toTry().isSuccess() 38 | } 39 | 40 | override public func toTry() -> Try { 41 | return state.value 42 | } 43 | 44 | override func ping(incoming: Set) -> Set { 45 | return children 46 | } 47 | 48 | public func hasValue() -> Bool { 49 | if !isSuccess() { 50 | switch toTry() { 51 | case .Success(_): return true 52 | case .Failure(let e): return e !== noValueError 53 | } 54 | } 55 | return true 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerp/Common/Core/Throttle.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 16/10/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | class Throttle: Dynamic { 7 | 8 | private let interval: NSTimeInterval 9 | private let debounceLevel: long 10 | private var timer: NSTimer? 11 | private var incoming: Set? 12 | 13 | init(source: Hub, interval: NSTimeInterval) { 14 | self.interval = interval 15 | self.debounceLevel = source.level + 1 16 | super.init { 17 | source.value() 18 | } 19 | } 20 | 21 | func callSelectorAsync(selector: Selector, object: AnyObject?, delay: NSTimeInterval) -> NSTimer { 22 | return NSTimer.scheduledTimerWithTimeInterval(delay, target: self, selector: selector, userInfo: object, repeats: false) 23 | } 24 | 25 | @objc func pingAsync() { 26 | if let i = self.incoming { 27 | if super.ping(i).count > 0 { 28 | self.propagate() 29 | } 30 | } 31 | } 32 | 33 | override func ping(incoming: Set) -> Set { 34 | self.incoming = incoming 35 | if let t = self.timer { 36 | t.invalidate() 37 | self.timer = nil 38 | } 39 | self.timer = callSelectorAsync(Selector("pingAsync"), object:nil, delay: self.interval) 40 | return Set() 41 | } 42 | 43 | override var level: long { 44 | return self.debounceLevel 45 | } 46 | } 47 | 48 | public extension Hub { 49 | 50 | public func throttle(interval: NSTimeInterval) -> Hub { 51 | return Throttle(source: self, interval: interval) 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerp/Common/Core/Wrapper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 27/09/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | class Wrapper: Incrementing { 7 | 8 | let source: Hub 9 | 10 | init(_ source: Hub) { 11 | self.source = source 12 | super.init() 13 | source.linkChild(self) 14 | } 15 | 16 | override var level: long { 17 | return self.source.level + 1 18 | } 19 | 20 | override var parents: Set { 21 | return toSet(source) 22 | } 23 | 24 | override func toTry() -> Try { 25 | return self.state.value 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerp/Common/Threading/AtomicLong.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 18/04/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | public typealias long = Int32 7 | 8 | public class AtomicLong: Hashable { 9 | 10 | private let lock = Spinlock() 11 | 12 | private var _value: long 13 | 14 | var value: long { 15 | 16 | get { 17 | return self.lock.around { 18 | self._value 19 | } 20 | } 21 | 22 | set(value) { 23 | self.lock.around { 24 | self._value = value 25 | } 26 | } 27 | } 28 | 29 | public init(_ value: long) { 30 | _value = value 31 | } 32 | 33 | public func getAndIncrement() -> long { 34 | let current = value 35 | self.value = current + 1 36 | return current 37 | } 38 | 39 | 40 | public var hashValue: Int { 41 | return value.hashValue 42 | } 43 | 44 | } 45 | 46 | public func ==(lhs: AtomicLong, rhs: AtomicLong) -> Bool { 47 | return lhs.value == rhs.value 48 | } 49 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerp/Common/Threading/AtomicReference.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 18/04/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | class AtomicReference { 7 | 8 | private let lock = Spinlock() 9 | 10 | private var _value: T 11 | 12 | var value: T { 13 | 14 | get { 15 | return self.lock.around { 16 | self._value 17 | } 18 | } 19 | 20 | set(value) { 21 | self.lock.around { 22 | self._value = value 23 | } 24 | } 25 | } 26 | 27 | init(_ value: T) { 28 | _value = value 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerp/Common/Threading/Dispatchable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 27/09/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | // TODO: add tests 7 | public protocol Dispatchable { 8 | typealias D 9 | 10 | func dispatchOnQueue(dispatchQueue: dispatch_queue_t?) -> D 11 | func dispatchOnMainQueue() -> D 12 | func dispatchOnCurrentQueue() -> D 13 | func dispatchOnBackgroundQueue() -> D 14 | 15 | } 16 | 17 | public extension Dispatchable { 18 | 19 | public func dispatchOnMainQueue() -> D { 20 | return self.dispatchOnQueue(dispatch_get_main_queue()!) 21 | } 22 | 23 | public func dispatchOnCurrentQueue() -> D { 24 | return self.dispatchOnQueue(nil) 25 | } 26 | 27 | public func dispatchOnBackgroundQueue() -> D { 28 | return self.dispatchOnQueue(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0)!) 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerp/Common/Threading/DynamicVariable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 18/04/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | // Inspired by 6 | // http://www.scala-lang.org/api/2.11.5/index.html#scala.util.DynamicVariable 7 | // and 8 | // https://searchcode.com/codesearch/view/18473763/ 9 | // and 10 | // http://scalageek.blogspot.hu/2013/02/when-to-use-dynamic-variables.html?m=1 11 | 12 | public class DynamicVariable { 13 | 14 | private let threadLocal: ThreadLocal 15 | 16 | public var value: T? { 17 | set { 18 | threadLocal.value = newValue 19 | } 20 | get { 21 | return threadLocal.value 22 | } 23 | } 24 | 25 | public init(_ value: T?) { 26 | self.threadLocal = ThreadLocal(value: value, key: "DynamicVariable") 27 | } 28 | 29 | public func withValue(newval: T, @noescape _ thunk: () -> S) -> S { 30 | let oldval = value 31 | 32 | // set the context 33 | self.value = newval 34 | 35 | let result = thunk() 36 | 37 | // restore the context 38 | self.value = oldval 39 | 40 | return result 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerp/Common/Threading/Spinlock.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 11/30/15. 3 | // Copyright © 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | class Spinlock { 7 | private var lock: OSSpinLock = OS_SPINLOCK_INIT 8 | 9 | func around(code: Void -> Void) { 10 | OSSpinLockLock(&lock) 11 | code() 12 | OSSpinLockUnlock(&lock) 13 | } 14 | 15 | func around(code: Void -> T) -> T { 16 | OSSpinLockLock(&lock) 17 | let result = code() 18 | OSSpinLockUnlock(&lock) 19 | 20 | return result 21 | } 22 | } 23 | 24 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerp/Common/Threading/ThreadLocal.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 18/04/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | final class Box { 7 | 8 | var value: T 9 | 10 | init(_ value: T) { 11 | self.value = value 12 | } 13 | 14 | } 15 | 16 | public class ThreadLocal { 17 | 18 | private let key: NSString 19 | 20 | public init(value: T?, key: NSString) { 21 | self.key = key 22 | self.value = value 23 | } 24 | 25 | public var value: T? { 26 | set { 27 | if let v = newValue { 28 | NSThread.currentThread().threadDictionary[key] = Box(v) 29 | } else { 30 | // TODO: add test 31 | NSThread.currentThread().threadDictionary.removeObjectForKey(key) 32 | } 33 | } 34 | get { 35 | guard let r: Box = NSThread.currentThread().threadDictionary[key] as? Box else { 36 | return nil 37 | } 38 | return r.value 39 | } 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerp/Common/Timer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 10/18/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | public class Timer { 7 | 8 | private var timer: NSTimer? 9 | private var tick: (() -> ())? 10 | 11 | private init() {} 12 | 13 | @objc private func timerHandler() { 14 | self.tick!() 15 | } 16 | 17 | public func cancel() { 18 | guard let t = self.timer else { 19 | return 20 | } 21 | t.invalidate() 22 | self.timer = nil 23 | } 24 | 25 | class func timer(interval: NSTimeInterval, tick: () ->()) -> Timer { 26 | let result = Timer() 27 | let t = NSTimer.scheduledTimerWithTimeInterval(interval, target: result, selector: Selector("timerHandler"), userInfo: nil, repeats: true) 28 | result.tick = tick 29 | result.timer = t 30 | return result 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerp/Common/Util/CommonConstants.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 18/04/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | let ABSTRACT_METHOD = "Abstract method: override me" 7 | let UNREACHABLE_CODE = "Unreachable code: you should not see this" 8 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerp/Common/Util/ExceptionHandling.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 18/06/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | func try2(maker: ()->(()->(), catch2:((NSException!)->())?, finally:(()->())? )) { 7 | let (action, catch2, finally) = maker() 8 | VRPNSObjectHelper.try2InvokeBlock(action, catch2: catch2, finally: finally) 9 | } 10 | 11 | func try2(maker: ()->( ()->T, catch2:((NSException!)->())?, finally: (()->())? )) -> T? { 12 | let (action, catch2, finally) = maker() 13 | let result : AnyObject! = VRPNSObjectHelper.try2InvokeBlockWithReturn(action, catch2: { ex in 14 | if let catch2Clause = catch2 { 15 | catch2Clause(ex) 16 | } 17 | return nil 18 | }, finally: finally) 19 | if (result != nil) { 20 | return result as? T 21 | } else { 22 | return nil 23 | } 24 | } 25 | 26 | func try2(maker: ()->( ()->T, catch2:((NSException!)->T)?, finally: (()->())? )) -> T? { 27 | let (action, catch2, finally) = maker() 28 | let result : AnyObject! = VRPNSObjectHelper.try2InvokeBlockWithReturn(action, catch2: catch2, finally: finally) 29 | if (result != nil) { 30 | return result as? T 31 | } else { 32 | return nil 33 | } 34 | } 35 | 36 | func throw2(name:String, message:String) { 37 | VRPNSObjectHelper.throwExceptionNamed(name, message: message) 38 | } 39 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerp/Common/Util/Flattenable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Flattenable.swift 3 | // VinceRP 4 | // 5 | // Created by Viktor Belényesi on 11/3/15. 6 | // Copyright © 2015 Viktor Belenyesi. All rights reserved. 7 | // 8 | 9 | public protocol Flattenable { 10 | func flatten() -> Self 11 | } 12 | 13 | extension Flattenable where Self: SequenceType { 14 | 15 | public func flatten() -> Self { 16 | return self 17 | } 18 | 19 | } 20 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerp/Common/Util/FunctionalArray.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 18/04/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | extension Array { 7 | 8 | public func arrayByPrepending(elem: Element) -> Array { 9 | var mutableCopy = self 10 | mutableCopy.insert(elem, atIndex: 0) 11 | let result = mutableCopy 12 | return result 13 | } 14 | 15 | public func arrayByAppending(elem: Element) -> Array { 16 | var mutableCopy = self 17 | mutableCopy.append(elem) 18 | let result = mutableCopy 19 | return result 20 | } 21 | 22 | } 23 | 24 | extension Array where Element: SequenceType { 25 | 26 | public func flatten() -> [Element.Generator.Element] { 27 | return self.flatMap{$0} 28 | } 29 | 30 | } 31 | 32 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerp/Common/Util/FunctionalDictionary.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 21/04/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | extension Dictionary { 7 | 8 | init(_ seq: S) { 9 | self.init() 10 | for (k, v) in seq { 11 | self[k] = v 12 | } 13 | } 14 | 15 | public func mapValues(transform: Value -> S) -> Dictionary { 16 | return Dictionary(zip(self.keys, self.values.map(transform))) 17 | } 18 | 19 | public func map(transform: (Key, Value) -> S) -> [S] { 20 | var results = [S]() 21 | for k in self.keys { 22 | guard let v = self[k] else { 23 | continue 24 | } 25 | results.append(transform(k, v)) 26 | } 27 | return results 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerp/Common/Util/FunctionalSet.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 21/04/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | public func toSet(e: T...) -> Set { 7 | return Set(e) 8 | } 9 | 10 | extension Set { 11 | 12 | public func partition(@noescape filterFunc: (Generator.Element) -> Bool) -> (Set, Set) { 13 | let result1 = self.filter(filterFunc) 14 | let result2 = self.filter{!filterFunc($0)} 15 | return (Set(result1), Set(result2)) 16 | } 17 | 18 | public func groupBy(@noescape filterFunc: (Generator.Element) -> U) -> [U : Set] { 19 | var result = [U: Set]() 20 | for i in self { 21 | let u = filterFunc(i) 22 | guard let bag = result[u] else { 23 | result[u] = toSet(i) 24 | continue 25 | } 26 | result[u] = bag ++ toSet(i) 27 | } 28 | return result 29 | } 30 | 31 | } 32 | 33 | extension SequenceType where Generator.Element: Hashable { 34 | 35 | public func hasElementPassingTest(@noescape filterFunc: (Self.Generator.Element) -> Bool) -> Bool { 36 | let result = self.filter(filterFunc) 37 | return result.count > 0 38 | } 39 | 40 | } 41 | 42 | extension SequenceType where Generator.Element: Comparable { 43 | 44 | public func min(fallbackValue: Self.Generator.Element) -> Self.Generator.Element { 45 | guard let result = self.minElement() else { 46 | return fallbackValue 47 | } 48 | return result 49 | } 50 | 51 | public func max(fallbackValue: Self.Generator.Element) -> Self.Generator.Element { 52 | guard let result = self.maxElement() else { 53 | return fallbackValue 54 | } 55 | return result 56 | } 57 | } 58 | 59 | extension Set: Flattenable { 60 | 61 | public func filter(@noescape includeElement: (Element) -> Bool) -> Set { 62 | let arr = Array(self) 63 | let result = arr.filter(includeElement) 64 | return Set(result) 65 | } 66 | 67 | public func map(@noescape transform: (Element) -> U) -> Set { 68 | let arr = Array(self) 69 | let result = arr.map(transform) 70 | return Set(result) 71 | } 72 | 73 | public func flatMap(transform: (Element) -> U) -> Set { 74 | let arr = Array(self) 75 | let result = arr.flatMap(transform) 76 | return Set(result) 77 | } 78 | 79 | public func flatMap(@noescape transform: (Element) -> T?) -> Set { 80 | let arr = Array(self) 81 | let result = arr.flatMap(transform) 82 | return Set(result) 83 | } 84 | 85 | } 86 | 87 | extension Set where Element:SequenceType, Element.Generator.Element: Hashable { 88 | 89 | public func flatten() -> Set { 90 | return self.flatMap{$0} 91 | } 92 | 93 | } 94 | 95 | infix operator ++ { associativity left precedence 160 } 96 | public func ++(left: Set, right: Set) -> Set { 97 | return left.union(right) 98 | } 99 | 100 | public func ++(left: Set, right: T) -> Set { 101 | return left ++ toSet(right) 102 | } 103 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerp/Common/Util/ReactivePropertyGenerator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 25/09/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | public class ReactivePropertyGenerator { 7 | 8 | // TODO: unittest -> check for memory leaks 9 | private static var propertyMap = [String: Node]() 10 | private static var observerMap = [String: PropertyObserver]() 11 | 12 | init() {} 13 | 14 | static func getKey(targetObject: AnyObject, propertyName: String) -> String { 15 | return "\(targetObject.hash)\(propertyName) " 16 | } 17 | 18 | static func getEmitter(targetObject: AnyObject, propertyName: String) -> Source? { 19 | let key = getKey(targetObject, propertyName: propertyName) 20 | return propertyMap[key] as! Source? 21 | } 22 | 23 | static func synthesizeEmitter(initValue: T) -> Source { 24 | return Source(initValue: initValue) 25 | } 26 | 27 | static func synthesizeObserver(targetObject: AnyObject, propertyName: String, initValue: T) -> PropertyObserver { 28 | return PropertyObserver(targetObject: targetObject as! NSObject, propertyName: propertyName) { (currentTargetObject: NSObject, currentPropertyName:String, currentValue:AnyObject) in 29 | if let existingEmitter:Source = getEmitter(currentTargetObject, propertyName: propertyName) { 30 | existingEmitter.update(currentValue as! T) 31 | } 32 | } 33 | } 34 | 35 | static func createEmitter(targetObject: AnyObject, propertyName: String, initValue: T) -> Source { 36 | let result = synthesizeEmitter(initValue) 37 | let key = getKey(targetObject, propertyName: propertyName) 38 | propertyMap[key] = result 39 | return result 40 | } 41 | 42 | static func createObserver(targetObject: AnyObject, propertyName: String, initValue: T) -> PropertyObserver { 43 | let result = synthesizeObserver(targetObject, propertyName: propertyName, initValue: initValue) 44 | let key = getKey(targetObject, propertyName: propertyName) 45 | observerMap[key] = result 46 | return result 47 | } 48 | 49 | public static func source(targetObject: AnyObject, propertyName: String, initValue: T, initializer: ((Source) -> ())? = nil) -> Source { 50 | if let emitter:Source = getEmitter(targetObject, propertyName: propertyName) { 51 | return emitter 52 | } 53 | 54 | let emitter = createEmitter(targetObject, propertyName: propertyName, initValue: initValue) 55 | if let i = initializer { 56 | i(emitter) 57 | } 58 | return emitter 59 | } 60 | 61 | public static func property(targetObject: AnyObject, propertyName: String, initValue: T, initializer: ((Source) -> ())? = nil) -> Hub { 62 | let result = source(targetObject, propertyName: propertyName, initValue: initValue, initializer: initializer) 63 | createObserver(targetObject, propertyName: propertyName, initValue: initValue) 64 | return result as Hub 65 | } 66 | } 67 | 68 | private var propertyObserverContext = 0 69 | 70 | class PropertyObserver: NSObject { 71 | let targetObject: NSObject 72 | let propertyName: String 73 | let propertyChangeHandler: (NSObject, String, AnyObject) -> Void 74 | 75 | init(targetObject:NSObject, propertyName: String, propertyChangeHandler: (NSObject, String, AnyObject) -> Void) { 76 | self.targetObject = targetObject 77 | self.propertyName = propertyName 78 | self.propertyChangeHandler = propertyChangeHandler 79 | super.init() 80 | self.targetObject.addObserver(self, forKeyPath: propertyName, options: .New, context: &propertyObserverContext) 81 | } 82 | 83 | override func observeValueForKeyPath(keyPath: String?, ofObject object: AnyObject?, change: [String : AnyObject]?, context: UnsafeMutablePointer) { 84 | if context == &propertyObserverContext { 85 | if self.propertyName == keyPath { 86 | if let newValue = change?[NSKeyValueChangeNewKey] { 87 | self.propertyChangeHandler(self.targetObject, self.propertyName, newValue) 88 | } 89 | } 90 | } else { 91 | super.observeValueForKeyPath(keyPath, ofObject: object, change: change, context: context) 92 | } 93 | } 94 | 95 | deinit { 96 | self.targetObject.removeObserver(self, forKeyPath: propertyName, context: &propertyObserverContext) 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerp/Common/Util/Try.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 20/04/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | // https://github.com/scala/scala/blob/2.11.x/src/library/scala/util/Try.scala 6 | 7 | public enum Try: CustomStringConvertible { 8 | 9 | case Success(T) 10 | case Failure(NSError) 11 | 12 | public init(_ error: NSError) { 13 | self = .Failure(error) 14 | } 15 | 16 | public init(_ value: T) { 17 | self = .Success(value) 18 | } 19 | 20 | public func isSuccess() -> Bool { 21 | switch self { 22 | case .Success: 23 | return true 24 | case .Failure: 25 | return false 26 | } 27 | } 28 | 29 | public func isFailure() -> Bool { 30 | return !isSuccess() 31 | } 32 | 33 | public func map(transformFunc: T -> U) -> Try { 34 | switch self { 35 | case .Success(let value): 36 | return .Success(transformFunc(value)) 37 | case .Failure(let error): 38 | return .Failure(error) 39 | } 40 | } 41 | 42 | public var description : String { 43 | switch self { 44 | case .Success(let value): return "\(value)" 45 | case .Failure(let error): return "\(error)" 46 | } 47 | } 48 | } 49 | 50 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerp/Common/Util/VRPNSObjectHelper.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 18/06/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | #import 7 | 8 | @interface VRPNSObjectHelper : NSObject 9 | + (void)try2InvokeBlock:(void(^)(void))try2Block catch2:(void(^)(NSException*))catch2Block finally:(void(^)(void))finallyBlock; 10 | + (id)try2InvokeBlockWithReturn:(id(^)(void))try2Block catch2:(id(^)(NSException*))catch2Block finally:(void(^)(void))finallyBlock; 11 | + (void)throwExceptionNamed:(NSString *)name message:(NSString *)message; 12 | @end 13 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerp/Common/Util/VRPNSObjectHelper.m: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 18/06/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | #import "VRPNSObjectHelper.h" 7 | 8 | #import 9 | 10 | @implementation VRPNSObjectHelper 11 | 12 | + (void)try2InvokeBlock:(void(^)(void))try2Block catch2:(void(^)(NSException*))catch2Block finally:(void(^)(void))finallyBlock { 13 | NSAssert(try2Block != NULL, @"try2 block cannot be null"); 14 | NSAssert(catch2Block != NULL || finallyBlock != NULL, @"catch2 or finally block must be provided"); 15 | @try { 16 | try2Block(); 17 | } 18 | @catch (NSException *ex) { 19 | if(catch2Block != NULL) { 20 | catch2Block(ex); 21 | } 22 | } 23 | @finally { 24 | if(finallyBlock != NULL) { 25 | finallyBlock(); 26 | } 27 | } 28 | } 29 | 30 | + (id)try2InvokeBlockWithReturn:(id(^)(void))try2Block catch2:(id(^)(NSException*))catch2Block finally:(void(^)(void))finallyBlock { 31 | NSAssert(try2Block != NULL, @"try2 block cannot be null"); 32 | NSAssert(catch2Block != NULL || finallyBlock != NULL, @"catch2 or finally block must be provided"); 33 | 34 | id returnValue = nil; 35 | @try { 36 | returnValue = try2Block(); 37 | } 38 | @catch (NSException *ex) { 39 | if(catch2Block != NULL) { 40 | returnValue = catch2Block(ex); 41 | } 42 | } 43 | @finally { 44 | if(finallyBlock != NULL) { 45 | finallyBlock(); 46 | } 47 | } 48 | return returnValue; 49 | } 50 | 51 | + (void)throwExceptionNamed:(NSString *)name message:(NSString *)message { 52 | [NSException raise:name format:@"message:%@", message]; 53 | } 54 | 55 | @end 56 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerp/Common/Util/WeakReference.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 21/04/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | // https://github.com/scala/scala/blob/2.11.x/src/library/scala/ref/WeakReference.scala 6 | 7 | public class WeakReference: Hashable { 8 | public weak var value: T? 9 | 10 | public init(_ value: T) { 11 | self.value = value 12 | } 13 | 14 | public var hashValue: Int { 15 | guard let v = self.value else { 16 | return 0 17 | } 18 | return v.hashValue 19 | } 20 | } 21 | 22 | public func ==(lhs: WeakReference, rhs: WeakReference) -> Bool { 23 | return lhs.hashValue == rhs.hashValue 24 | } 25 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerp/Common/Util/WeakSet.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 18/04/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | public class WeakSet { 7 | private var _array: [WeakReference] 8 | 9 | public init() { 10 | _array = Array() 11 | } 12 | 13 | public init(_ array: [T]) { 14 | _array = array.map { 15 | WeakReference($0) 16 | } 17 | } 18 | 19 | public var set: Set { 20 | return Set(self.array()) 21 | } 22 | 23 | public func insert(member: T) { 24 | for e in _array { 25 | if e.hashValue == member.hashValue { 26 | return 27 | } 28 | } 29 | _array.append(WeakReference(member)) 30 | } 31 | 32 | public func filter(@noescape includeElement: (T) -> Bool) -> WeakSet { 33 | return WeakSet(self.array().filter(includeElement)) 34 | } 35 | 36 | public func hasElementPassingTest(@noescape filterFunc: (T) -> Bool) -> Bool { 37 | return self.array().hasElementPassingTest(filterFunc) 38 | } 39 | 40 | public func map(@noescape transform: (T) -> U) -> WeakSet { 41 | return WeakSet(self.array().map(transform)) 42 | } 43 | 44 | public func flatMap(@noescape transform: (T) -> U?) -> WeakSet { 45 | return WeakSet(self.array().flatMap(transform)) 46 | } 47 | 48 | private func array() -> Array { 49 | var result = Array() 50 | for item in _array { 51 | if let v = item.value { 52 | result.append(v) 53 | } 54 | } 55 | return result 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerp/Extension/AppKit/NSResponder+VinceRP.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Agnes Vasarhelyi on 04/11/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | import AppKit 7 | 8 | public extension NSResponder { 9 | 10 | public func reactiveProperty(forProperty propertyName: String, initValue: T, initializer: ((Source) -> ())? = nil) -> Hub { 11 | return ReactivePropertyGenerator.property(self, propertyName: propertyName, initValue: initValue, initializer: initializer) 12 | } 13 | 14 | public func reactiveEmitter(name propertyName: String, initValue: T) -> Source { 15 | return ReactivePropertyGenerator.source(self, propertyName: propertyName, initValue: initValue) 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerp/Extension/AppKit/NSTextField+VinceRP.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Agnes Vasarhelyi on 17/11/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | import AppKit 7 | 8 | extension NSTextField { 9 | 10 | public var reactiveText: Hub { 11 | get { 12 | return reactiveProperty(forProperty: "text", initValue: self.stringValue) { emitter in 13 | self.addChangeHandler() { textField in 14 | emitter <- textField.stringValue 15 | } 16 | } 17 | } 18 | 19 | set { 20 | newValue.onChange { 21 | self.stringValue = $0 22 | }.dispatchOnMainQueue() 23 | } 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerp/Extension/AppKit/NSTextField+didChange.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Agnes Vasarhelyi on 17/11/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | import AppKit 7 | 8 | private typealias EventHandler = (NSTextField) -> () 9 | private var eventHandlers = [NSTextField: [EventHandler]]() 10 | 11 | extension NSTextField { 12 | 13 | public func addChangeHandler(actionBlock: (NSTextField) -> ()) { 14 | if let handlers = eventHandlers[self] { 15 | eventHandlers[self] = handlers.arrayByAppending(actionBlock) 16 | } else { 17 | eventHandlers[self] = [actionBlock] 18 | } 19 | 20 | self.action = Selector("eventHandler:") 21 | self.target = self 22 | } 23 | 24 | // TODO: add removeChangeHandler 25 | public func eventHandler(sender: NSTextField) { 26 | if let handlers = eventHandlers[sender] { 27 | for handler in handlers { 28 | handler(sender) 29 | } 30 | } 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerp/Extension/AppKit/NSView+VinceRP.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Agnes Vasarhelyi on 17/11/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | import AppKit 7 | 8 | public extension NSView { 9 | 10 | public var reactiveHidden: Hub { 11 | get { 12 | return reactiveProperty(forProperty: "hidden", initValue: true) 13 | } 14 | 15 | set { 16 | newValue.onChange { 17 | self.hidden = self.mapReactiveHidden($0) 18 | self.reactiveHiddenDidChange() 19 | }.dispatchOnMainQueue() 20 | } 21 | } 22 | 23 | public func mapReactiveHidden(value: Bool) -> Bool { 24 | return value 25 | } 26 | 27 | public func reactiveHiddenDidChange() {} 28 | 29 | } 30 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerp/Extension/Common/Foundation/String+vincerp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 05/09/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | public extension String { 7 | 8 | public var length: Index.Distance { 9 | return self.characters.count 10 | } 11 | 12 | public func trim() -> String { 13 | return self.stringByTrimmingCharactersInSet(NSCharacterSet.whitespaceCharacterSet()) 14 | } 15 | 16 | } 17 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerp/Extension/Common/NSObject+vincerp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 14/09/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | extension NSObject { 7 | 8 | class func swizzleMethodSelector(origSelector: String!, withSelector: String!, forClass:AnyClass!) -> Bool { 9 | 10 | var originalMethod: Method? 11 | var swizzledMethod: Method? 12 | 13 | originalMethod = class_getInstanceMethod(forClass, Selector(origSelector)) 14 | swizzledMethod = class_getInstanceMethod(forClass, Selector(withSelector)) 15 | 16 | if (originalMethod != nil && swizzledMethod != nil) { 17 | if class_addMethod(forClass, Selector(origSelector), method_getImplementation(swizzledMethod!), method_getTypeEncoding(swizzledMethod!)) { 18 | class_replaceMethod(forClass, Selector(withSelector), method_getImplementation(originalMethod!), method_getTypeEncoding(originalMethod!)); 19 | } else { 20 | method_exchangeImplementations(originalMethod!, swizzledMethod!) 21 | } 22 | return true 23 | } 24 | return false 25 | } 26 | 27 | class func swizzleStaticMethodSelector(origSelector: String!, withSelector: String!, forClass:AnyClass!) -> Bool { 28 | 29 | var originalMethod: Method? 30 | var swizzledMethod: Method? 31 | 32 | originalMethod = class_getClassMethod(forClass, Selector(origSelector)) 33 | swizzledMethod = class_getClassMethod(forClass, Selector(withSelector)) 34 | 35 | if (originalMethod != nil && swizzledMethod != nil) { 36 | method_exchangeImplementations(originalMethod!, swizzledMethod!) 37 | return true 38 | } 39 | return false 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerp/Extension/UIKit/UIActivityIndicatorView+vincerp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 27/09/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | import UIKit 7 | 8 | public extension UIActivityIndicatorView { 9 | 10 | override public func reactiveHiddenDidChange() { 11 | if self.hidden { 12 | self.stopAnimating() 13 | } else { 14 | self.startAnimating() 15 | } 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerp/Extension/UIKit/UIButton+vincerp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 05/09/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | import UIKit 7 | 8 | // TODO: Add tests for eventhandlers 9 | // TODO: move separate file 10 | public typealias ClickHandler = (UIButton) -> () 11 | 12 | public class ClickHandlerAction: Dispatchable { 13 | let handler: ClickHandler 14 | var dispatchQueue: dispatch_queue_t! 15 | 16 | init(handler: ClickHandler) { 17 | self.handler = handler 18 | } 19 | 20 | func execute(button:UIButton) { 21 | self.handler(button) 22 | } 23 | 24 | public func dispatchOnQueue(dispatchQueue: dispatch_queue_t?) -> ClickHandlerAction { 25 | self.dispatchQueue = dispatchQueue 26 | return self 27 | } 28 | 29 | } 30 | 31 | public func definedAs(handler: ClickHandler) -> ClickHandlerAction { 32 | return ClickHandlerAction(handler: handler) 33 | } 34 | 35 | private var eventHandlers: [UIButton: ClickHandlerAction] = [UIButton: ClickHandlerAction]() 36 | private let clickHandlerMethodName = "onClick:" 37 | 38 | public extension UIButton { 39 | 40 | override public func mapReactiveEnabled(value: Bool) -> Bool { 41 | return value && !self.executing* 42 | } 43 | 44 | public var clickHandler: ClickHandlerAction? { 45 | get { 46 | return eventHandlers[self] 47 | } 48 | 49 | set { 50 | if let v = newValue { 51 | eventHandlers[self] = v 52 | self.executing <- false 53 | self.addTarget(self, action: Selector(clickHandlerMethodName), forControlEvents: .TouchUpInside) 54 | } else { 55 | eventHandlers.removeValueForKey(self) 56 | self.removeTarget(self, action: Selector(clickHandlerMethodName), forControlEvents: .TouchUpInside) 57 | } 58 | } 59 | } 60 | 61 | public func onClick(sender: UIButton) { 62 | 63 | dispatch_async(dispatch_get_main_queue(), { 64 | self.enabled = false 65 | self.executing <- true 66 | }) 67 | 68 | let handler = eventHandlers[self]! 69 | 70 | if let q = handler.dispatchQueue { 71 | dispatch_async(q) { 72 | self.onClickThreadless(handler) 73 | } 74 | } else { 75 | self.onClickThreadless(handler) 76 | } 77 | 78 | } 79 | 80 | public func done() { 81 | dispatch_async(dispatch_get_main_queue(), { 82 | self.executing <- false 83 | self.enabled = true 84 | }) 85 | } 86 | 87 | private func onClickThreadless(handler: ClickHandlerAction) { 88 | self.executing <- true 89 | handler.execute(self) 90 | } 91 | 92 | public var executing: Source { 93 | get { 94 | return reactiveSource(name: "executing", initValue: false) 95 | } 96 | 97 | set { 98 | } 99 | } 100 | 101 | } 102 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerp/Extension/UIKit/UIControl+vincerp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 27/09/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | import UIKit 7 | 8 | public extension UIControl { 9 | 10 | public var reactiveEnabled: Hub { 11 | get { 12 | return reactiveSource(name: "enabled", initValue: self.enabled) 13 | } 14 | 15 | set { 16 | newValue.onChange { 17 | self.enabled = self.mapReactiveEnabled($0) 18 | self.reactiveEnabledDidChange() 19 | }.dispatchOnMainQueue() 20 | } 21 | } 22 | 23 | public func mapReactiveEnabled(value: Bool) -> Bool { 24 | return value 25 | } 26 | 27 | public func reactiveEnabledDidChange() { 28 | } 29 | } 30 | 31 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerp/Extension/UIKit/UILabel+vincerp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 14/09/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | import UIKit 7 | 8 | public extension UILabel { 9 | 10 | public var reactiveText: Hub { 11 | get { 12 | return reactiveProperty(forProperty: "text", initValue: self.text!) 13 | } 14 | 15 | set { 16 | newValue.onChange { 17 | self.text = $0 18 | }.dispatchOnMainQueue() 19 | } 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerp/Extension/UIKit/UIResponder+vincerp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Agnes Vasarhelyi on 30/10/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | import UIKit 7 | 8 | public extension UIResponder { 9 | 10 | public func reactiveProperty(forProperty propertyName: String, initValue: T, initializer: ((Source) -> ())? = nil) -> Hub { 11 | return ReactivePropertyGenerator.property(self, propertyName: propertyName, initValue: initValue, initializer: initializer) 12 | } 13 | 14 | public func reactiveSource(name propertyName: String, initValue: T) -> Source { 15 | return ReactivePropertyGenerator.source(self, propertyName: propertyName, initValue: initValue) 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerp/Extension/UIKit/UISearchBar+didChange.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 26/09/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | import UIKit 7 | 8 | private typealias EventHandler = (UISearchBar) -> () 9 | private var eventHandlers = [UISearchBar: [EventHandler]]() 10 | 11 | extension UISearchBar { 12 | 13 | public func addChangeHandler(actionBlock: (UISearchBar) -> ()) { 14 | if let handlers = eventHandlers[self] { 15 | eventHandlers[self] = handlers.arrayByAppending(actionBlock) 16 | } else { 17 | eventHandlers[self] = [actionBlock] 18 | } 19 | self.delegate = self 20 | } 21 | 22 | } 23 | 24 | // TODO: add removeChangeHandler 25 | extension UISearchBar : UISearchBarDelegate { 26 | 27 | public func searchBar(sender: UISearchBar, textDidChange searchText: String) { 28 | if let handlers = eventHandlers[sender] { 29 | for handler in handlers { 30 | handler(sender) 31 | } 32 | } 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerp/Extension/UIKit/UISearchBar+vincerp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 16/10/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | import UIKit 7 | import ObjectiveC 8 | 9 | extension UISearchBar { 10 | 11 | public var reactiveText: Hub { 12 | get { 13 | return reactiveProperty(forProperty: "text", initValue: self.text!) { emitter in 14 | self.addChangeHandler() { textField in 15 | emitter <- textField.text! 16 | } 17 | } 18 | } 19 | 20 | set { 21 | newValue.onChange { 22 | self.text = $0 23 | }.dispatchOnMainQueue() 24 | } 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerp/Extension/UIKit/UIStepper+vincerp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 14/09/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | import UIKit 7 | 8 | public extension UIStepper { 9 | 10 | public var reactiveValue: Hub { 11 | get { 12 | return reactiveProperty(forProperty: "value", initValue: self.value) 13 | } 14 | 15 | set { 16 | newValue.onChange { 17 | self.value = $0 18 | }.dispatchOnMainQueue() 19 | } 20 | } 21 | 22 | } 23 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerp/Extension/UIKit/UITextField+didChange.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 26/09/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | import UIKit 7 | 8 | private typealias EventHandler = (UITextField) -> () 9 | private var eventHandlers = [UITextField: [EventHandler]]() 10 | 11 | extension UITextField { 12 | 13 | public func addChangeHandler(actionBlock: (UITextField) -> ()) { 14 | if let handlers = eventHandlers[self] { 15 | eventHandlers[self] = handlers.arrayByAppending(actionBlock) 16 | } else { 17 | eventHandlers[self] = [actionBlock] 18 | } 19 | 20 | self.addTarget(self, action: Selector("eventHandler:"), forControlEvents: .EditingChanged) 21 | } 22 | 23 | // TODO: add removeChangeHandler 24 | public func eventHandler(sender: UITextField) { 25 | if let handlers = eventHandlers[sender] { 26 | for handler in handlers { 27 | handler(sender) 28 | } 29 | } 30 | } 31 | 32 | } 33 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerp/Extension/UIKit/UITextField+vincerp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 05/09/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | import UIKit 7 | 8 | extension UITextField { 9 | 10 | public var reactiveText: Hub { 11 | get { 12 | return reactiveProperty(forProperty: "text", initValue: self.text!) { emitter in 13 | self.addChangeHandler() { textField in 14 | emitter <- textField.text! 15 | } 16 | } 17 | } 18 | 19 | set { 20 | newValue.onChange { 21 | self.text = $0 22 | }.dispatchOnMainQueue() 23 | } 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerp/Extension/UIKit/UIView+vincerp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 27/09/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | import UIKit 7 | 8 | public extension UIView { 9 | 10 | public var reactiveHidden: Hub { 11 | get { 12 | return reactiveProperty(forProperty: "hidden", initValue: true) 13 | } 14 | 15 | set { 16 | newValue.onChange { 17 | self.hidden = self.mapReactiveHidden($0) 18 | self.reactiveHiddenDidChange() 19 | }.dispatchOnMainQueue() 20 | } 21 | } 22 | 23 | public func mapReactiveHidden(value: Bool) -> Bool { 24 | return value 25 | } 26 | 27 | public func reactiveHiddenDidChange() { 28 | } 29 | 30 | } 31 | 32 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerp/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 | v0.1.3 19 | CFBundleSignature 20 | ???? 21 | CFBundleVersion 22 | $(CURRENT_PROJECT_VERSION) 23 | NSPrincipalClass 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerp/UpdateState.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belényesi on 01/12/15. 3 | // Copyright © 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | private let globalID = AtomicLong(0) 7 | 8 | public func nextID() -> long { 9 | return globalID.getAndIncrement() 10 | } 11 | 12 | public struct UpdateState { 13 | 14 | let id: long 15 | let value: Try 16 | let parents: Set 17 | let level: long 18 | 19 | init(_ parents: Set, _ level: long, _ timestamp: long, _ value: Try) { 20 | self.parents = parents 21 | self.level = level 22 | self.id = timestamp 23 | self.value = value 24 | } 25 | 26 | init(_ value: Try) { 27 | self.init(nextID(), value) 28 | } 29 | 30 | init(_ timestamp: long, _ value: Try) { 31 | self.init(Set(), 0, nextID(), value) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerp/vincerp.h: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 18/04/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | #import 7 | 8 | //! Project version number for VinceRP. 9 | FOUNDATION_EXPORT double VinceRPVersionNumber; 10 | 11 | //! Project version string for VinceRP. 12 | FOUNDATION_EXPORT const unsigned char VinceRPVersionString[]; 13 | 14 | // In this header, you should import all the public headers of your framework using statements like #import 15 | 16 | #import "VRPNSObjectHelper.h" 17 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerpTests/Common/AdvancedSpec.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 18/04/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | @testable import VinceRP 7 | 8 | import Quick 9 | import Nimble 10 | 11 | func myRandom() -> Int { 12 | return Int(arc4random()) 13 | } 14 | 15 | let fakeError = NSError(domain: "domain.com", code: 1, userInfo: nil) 16 | 17 | class AdvancedSpec: QuickSpec { 18 | 19 | override func spec() { 20 | 21 | describe("advanced") { 22 | /* 23 | context("performance") { 24 | 25 | it("init") { 26 | // given 27 | let start = CACurrentMediaTime() 28 | var n = 0 29 | 30 | // when 31 | while CACurrentMediaTime() < start + 1.0 { 32 | let (_, _, _, _, _, _) = testGraph() 33 | n += 1 34 | } 35 | 36 | // then 37 | expect(n) > 30000 38 | } 39 | 40 | it("propagation") { 41 | // given 42 | let (a, b, c, d, e, f) = testGraph() 43 | let start = CACurrentMediaTime() 44 | var n = 0 45 | 46 | // when 47 | while CACurrentMediaTime() < start + 1.0 { 48 | a <- n 49 | n += 1 50 | } 51 | 52 | // then 53 | expect(n) > 9500.0 54 | } 55 | 56 | } 57 | */ 58 | 59 | context("nesting") { 60 | 61 | it("works with nested reactives") { 62 | // given 63 | let a = reactive(1) 64 | let b = definedAs { 65 | (definedAs{ a* }, definedAs{ myRandom() }) 66 | } 67 | let r = b*.1* 68 | 69 | // when 70 | a <- 2 71 | 72 | // then 73 | expect(b*.1*) =~ r 74 | } 75 | 76 | it("works with recalcs") { 77 | // given 78 | var source = 0 79 | let a = definedAs{source} 80 | var i = 0 81 | _ = onChangeDo(a){ _ in 82 | i += 1 83 | } 84 | 85 | // then 86 | expect(i) =~ 1 87 | expect(a*) =~ 0 88 | 89 | // when 90 | source = 1 91 | 92 | // then 93 | expect(a*) =~ 0 94 | 95 | // when 96 | a.recalc() 97 | 98 | // then 99 | expect(a*) =~ 1 100 | expect(i) =~ 2 101 | } 102 | 103 | it("can update multiple variables in a batch") { 104 | // given 105 | let a = reactive(1) 106 | let b = reactive(1) 107 | let c = reactive(1) 108 | let d = definedAs { 109 | a* + b* + c* 110 | } 111 | var i = 0 112 | 113 | // when 114 | _ = onChangeDo(d) { _ in 115 | i += 1 116 | } 117 | 118 | // then 119 | expect(i) =~ 1 120 | a <- 2 121 | expect(i) =~ 2 122 | b <- 2 123 | expect(i) =~ 3 124 | c <- 2 125 | expect(i) =~ 4 126 | 127 | BatchUpdate(a, withValue:3).and(b, withValue:3).and(c, withValue:3).now() 128 | 129 | expect(i) =~ 5 130 | 131 | BatchUpdate(a, withValue:4).and(b, withValue:5).and(c, withValue:6).now() 132 | 133 | expect(i) =~ 6 134 | expect(a*) =~ 4 135 | expect(b*) =~ 5 136 | expect(c*) =~ 6 137 | } 138 | 139 | } 140 | 141 | } 142 | 143 | describe("combinators") { 144 | 145 | it("blocks observers") { 146 | // given 147 | let a = reactive(10) 148 | let b = a.filter { 149 | $0 > 5 150 | } 151 | var sideeffect = 0 152 | onChangeDo(b) { _ in 153 | sideeffect = sideeffect + 1 154 | } 155 | 156 | // when 157 | a <- 1 158 | 159 | // then 160 | expect(b*) =~ 10 161 | expect(sideeffect) =~ 1 162 | 163 | // when 164 | a <- 2 165 | 166 | // then 167 | expect(b*) =~ 10 168 | expect(sideeffect) =~ 1 169 | 170 | // when 171 | a <- 6 172 | 173 | // then 174 | expect(b*) =~ 6 175 | expect(sideeffect) =~ 2 176 | } 177 | 178 | } 179 | 180 | context("kill") { 181 | 182 | it("kills observer") { 183 | // given 184 | let a = reactive(1) 185 | let b = definedAs { 2 * a* } 186 | var target = 0 187 | let o = onChangeDo(b) { _ in 188 | target = b* 189 | } 190 | 191 | // then 192 | expect(a.children) =~ toSet(b) 193 | expect(b.children) =~ toSet(o) 194 | expect(target) =~ 2 195 | 196 | // when 197 | a <- 2 198 | 199 | // then 200 | expect(target) =~ 4 201 | 202 | // when 203 | o.kill() 204 | 205 | // then 206 | expect(a.children) =~ toSet(b) 207 | expect(b.children) =~ Set() 208 | 209 | // when 210 | a <- 3 211 | 212 | // then 213 | expect(target) =~ 4 214 | } 215 | 216 | it("kills reactive") { 217 | // given 218 | let (a, b, c, d, e, f) = testGraph() 219 | 220 | // then 221 | expect(c*) =~ 2 222 | expect(e*) =~ 1 223 | expect(f*) =~ 10 224 | 225 | // when 226 | a <- 3 227 | 228 | // then 229 | expect(c*) =~ 6 230 | expect(e*) =~ 3 231 | expect(f*) =~ 12 232 | 233 | // when 234 | d.kill() 235 | a <- 1 236 | 237 | // then 238 | expect(f*) =~ 14 239 | expect(e.children) =~ toSet(f) 240 | 241 | // when 242 | f.kill() 243 | 244 | // then 245 | expect(e.children) =~ Set() 246 | 247 | // when 248 | a <- 3 249 | 250 | // then 251 | expect(c*) =~ 6 252 | expect(e*) =~ 3 253 | expect(f*) =~ 14 254 | expect(a.children) =~ toSet(c) 255 | expect(b.children) =~ toSet(c) 256 | 257 | // when 258 | c.kill() 259 | 260 | // then 261 | expect(a.children) =~ Set() 262 | expect(b.children) =~ Set() 263 | 264 | // when 265 | a <- 1 266 | 267 | // then 268 | expect(c*) =~ 6 269 | expect(e*) =~ 3 270 | expect(f*) =~ 14 271 | } 272 | 273 | 274 | it("kills all Hub") { 275 | // given 276 | let (a, _, c, d, e, f) = testGraph() 277 | 278 | // when 279 | // killAll-ing d makes f die too 280 | d.killAll() 281 | 282 | // then 283 | a <- 3 284 | expect(c*) =~ 6 285 | expect(e*) =~ 3 286 | expect(f*) =~ 10 287 | } 288 | } 289 | 290 | } 291 | 292 | } 293 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerpTests/Common/Core/Hub+operatorsSpec.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 11/26/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | @testable import VinceRP 7 | 8 | import Quick 9 | import Nimble 10 | 11 | class HubOperatorsSpec: QuickSpec { 12 | 13 | override func spec() { 14 | 15 | it("should negate") { 16 | // given 17 | let x = reactive(true) 18 | 19 | // when 20 | expect(x.value()) == true 21 | 22 | // then 23 | expect(x.not()*) == false 24 | } 25 | 26 | it("can skip errors") { 27 | // given 28 | let x = reactive(1) 29 | let y = definedAs { x* + 1 }.skipErrors() 30 | var count = 0 31 | onErrorDo(y) { _ in 32 | count++ 33 | } 34 | 35 | // when 36 | x <- fakeError 37 | 38 | // then 39 | expect(count) == 0 40 | } 41 | 42 | it("works with foreach") { 43 | // given 44 | let x = reactive(1) 45 | var history = [Int]() 46 | 47 | // when 48 | x.foreach { 49 | history.append(2 * $0) 50 | } 51 | 52 | // then 53 | expect(history).toEventually(equal([2])) 54 | 55 | // when 56 | x <- 2 57 | 58 | // then 59 | expect(history).toEventually(equal([2, 4])) 60 | } 61 | 62 | 63 | } 64 | 65 | } 66 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerpTests/Common/Core/MapperSpec.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 11/27/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | @testable import VinceRP 7 | 8 | import Quick 9 | import Nimble 10 | 11 | class MapperSpec: QuickSpec { 12 | 13 | override func spec() { 14 | 15 | it("can map values") { 16 | // given 17 | let x = reactive(10) 18 | let y = definedAs { x* + 2 } 19 | let z = x.map { $0 * 2 } 20 | let s = y.map { $0 + 3 } 21 | 22 | // then 23 | expect(z*) =~ 20 24 | expect(s*) =~ 15 25 | 26 | // when 27 | x <- 1 28 | 29 | // then 30 | expect(z*) =~ 2 31 | expect(s*) =~ 6 32 | } 33 | 34 | it("handles division by zero") { 35 | // given 36 | let numerator = reactive(4) 37 | let denominator = reactive(1) 38 | 39 | let frac = definedAs { 40 | (numerator*, denominator*) 41 | }.mapAll { (p:Try<(Int, Int)>) -> Try in 42 | switch p { 43 | case .Success(let value): 44 | let (n, d) = value 45 | if d == 0 { 46 | return Try(NSError(domain: "division by zero", code: -0, userInfo: nil)) 47 | } 48 | return Try(n/d) 49 | case .Failure(let error): return Try(error) 50 | } 51 | } 52 | 53 | expect(frac*) =~ 4 54 | 55 | // when 56 | denominator <- 0 57 | 58 | // then 59 | expect(frac.toTry().isFailure()).toEventually(beTrue()) 60 | 61 | // when 62 | denominator <- 2 63 | 64 | // then 65 | expect(frac.toTry().isSuccess()).toEventually(beTrue()) 66 | expect(frac*) =~ 2 67 | } 68 | 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerpTests/Common/Core/ReducerSpec.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 11/27/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | @testable import VinceRP 7 | 8 | import Quick 9 | import Nimble 10 | 11 | class ReducerSpec: QuickSpec { 12 | 13 | override func spec() { 14 | 15 | it("filters") { 16 | // given 17 | let x = reactive(10) 18 | let y = x.filter { $0 > 5 } 19 | 20 | // when 21 | x <- 1 22 | 23 | // then 24 | expect(y*) == 10 25 | 26 | // when 27 | x <- 6 28 | 29 | // then 30 | expect(y*) =~ 6 31 | 32 | // when 33 | x <- 2 34 | 35 | // then 36 | expect(y*) == 6 37 | 38 | // when 39 | x <- 19 40 | 41 | // then 42 | expect(y*) =~ 19 43 | } 44 | 45 | it("filters all") { 46 | // given 47 | let x = reactive(10) 48 | let y = definedAs { 100 / x* } 49 | let z = y.filterAll { $0.isSuccess() } 50 | 51 | // then 52 | expect(z*) == 10 53 | 54 | // when 55 | x <- 9 56 | 57 | // then 58 | expect(z*) =~ 11 59 | 60 | // when 61 | x <- fakeError 62 | 63 | // then 64 | expect(z*) == 11 65 | 66 | // when 67 | x <- 1 68 | 69 | // then 70 | expect(z*) =~ 100 71 | } 72 | 73 | it("reduces") { 74 | // given 75 | let x = reactive(1) 76 | let y = x.reduce { $0 * $1 } 77 | 78 | // when 79 | x <- 2 80 | expect(y*) =~ 2 81 | 82 | // when 83 | x <- 3 84 | expect(y*) =~ 6 85 | 86 | // when 87 | x <- 4 88 | expect(y*) =~ 24 89 | } 90 | 91 | it("reduces all") { 92 | // given 93 | let x = reactive(0) 94 | let sum = x.reduceAll { (x, y) in 95 | switch (x.value, y) { 96 | case (.Success(let a), .Success(let b)): return Try(a + b) 97 | default: return Try(0) 98 | } 99 | } 100 | 101 | // then 102 | expect(sum*) == 0 103 | 104 | // when 105 | x <- 1 106 | 107 | // then 108 | expect(sum*) =~ 1 109 | 110 | // when 111 | x <- 2 112 | 113 | // then 114 | expect(sum*) =~ 3 115 | 116 | // when 117 | x <- fakeError 118 | 119 | // then 120 | expect(sum*) =~ 0 121 | 122 | // when 123 | x <- 5 124 | 125 | // then 126 | expect(sum*) =~ 5 127 | } 128 | 129 | } 130 | 131 | } -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerpTests/Common/Core/ThrottleSpec.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 11/27/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | @testable import VinceRP 7 | 8 | import Quick 9 | import Nimble 10 | 11 | class ThrottleSpec: QuickSpec { 12 | 13 | override func spec() { 14 | 15 | it("waits") { 16 | // given 17 | let x = reactive(0) 18 | let y = x.throttle(0.1) 19 | 20 | // then 21 | expect(y*) =~ 0 22 | 23 | // when 24 | x <- 1 25 | 26 | // then 27 | expect(y*) =~ 0 28 | 29 | // then 30 | expect(y*) =~ 1 31 | } 32 | 33 | } 34 | 35 | } -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerpTests/Common/SmokeSpec.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 18/04/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | @testable import VinceRP 7 | 8 | import Quick 9 | import Nimble 10 | 11 | func function(a: Int) -> Int { 12 | return 3 + a 13 | } 14 | 15 | class SmokeSpec: QuickSpec { 16 | 17 | override func spec() { 18 | 19 | describe("basic") { 20 | 21 | context("reactive variable") { 22 | 23 | it("holds the initial value") { 24 | // given 25 | let a = reactive(1) 26 | 27 | // then 28 | expect(a*) =~ 1 29 | } 30 | 31 | it("updates it's value") { 32 | // given 33 | let a = reactive(1) 34 | 35 | // when 36 | a <- 6 37 | 38 | // then 39 | expect(a*) =~ 6 40 | } 41 | 42 | it("updates it's value using double arrow") { 43 | // given 44 | let a = reactive(1) 45 | 46 | // when 47 | a <- 6 <- 3 48 | 49 | // then 50 | expect(a*) =~ 3 51 | } 52 | 53 | it("updates it's value from a function") { 54 | // given 55 | let a = reactive(1) 56 | 57 | // when 58 | a <- function(1) 59 | 60 | // then 61 | expect(a*) =~ 4 62 | } 63 | 64 | } 65 | 66 | context("reactive hub") { 67 | 68 | it("calculates it's value initially") { 69 | // given 70 | let a = reactive(1) 71 | let b = reactive(2) 72 | 73 | // when 74 | let c = definedAs { 75 | a* + b* 76 | } 77 | 78 | // then 79 | expect(c*) =~ 3 80 | } 81 | 82 | it("updates it's value on parent gets update") { 83 | 84 | // given 85 | let a = reactive(1) 86 | let b = reactive(2) 87 | let c = definedAs { 88 | a* + b* 89 | } 90 | 91 | // when 92 | a <- 2 93 | 94 | // then 95 | expect(c*) =~ 4 96 | 97 | } 98 | 99 | it("updates it's value on parents get update") { 100 | // given 101 | let a = reactive(1) 102 | let b = reactive(2) 103 | let c = definedAs { 104 | a* + b* 105 | } 106 | 107 | // when 108 | a <- 2 109 | b <- 3 110 | 111 | // then 112 | expect(c*) =~ 5 113 | } 114 | 115 | } 116 | 117 | context("side effect") { 118 | 119 | it("runs sideeffect initially") { 120 | // given 121 | let a = reactive(1) 122 | var counter = 0 123 | 124 | // when 125 | _ = onChangeDo(a) { _ in 126 | counter++ 127 | } 128 | 129 | // then 130 | expect(counter) =~ 1 131 | } 132 | 133 | it("runs sideeffect when parent gets update") { 134 | // given 135 | let a = reactive(1) 136 | 137 | var counter = 0 138 | _ = onChangeDo(a) { _ in 139 | counter++ 140 | } 141 | 142 | // when 143 | a <- 2 144 | 145 | // then 146 | expect(counter) =~ 2 147 | } 148 | 149 | it("runs sideeffect on hub gets update") { 150 | // given 151 | let a = reactive(1) 152 | let b = reactive(2) 153 | let c = definedAs { 154 | "test\(a*)+\(b*)" 155 | } 156 | var counter = 0 157 | _ = onChangeDo(c) { _ in 158 | counter++ 159 | } 160 | 161 | // when 162 | a <- 8 163 | expect(c*) =~ "test8+2" 164 | b <- 6 165 | 166 | // then 167 | expect(c*) =~ "test8+6" 168 | expect(counter) =~ 3 169 | } 170 | 171 | it("runs sideeffect on complex") { 172 | // given 173 | let a = reactive(1) 174 | let b = reactive(2) 175 | let e = reactive("buda") 176 | let c = definedAs { 177 | "pest=\(a*)+\(b*)" 178 | } 179 | let f = definedAs { 180 | e* + c* 181 | } 182 | var counter = 0 183 | _ = onChangeDo(c) { _ in 184 | counter++ 185 | } 186 | 187 | // when 188 | a <- 8 189 | 190 | expect(c*) =~ "pest=8+2" 191 | 192 | b <- 6 193 | 194 | // then 195 | expect(c*) =~ "pest=8+6" 196 | expect(f*) =~ "budapest=8+6" 197 | expect(counter) =~ 3 198 | } 199 | 200 | it("is happy without default value") { 201 | // given 202 | let a: Source = reactive() 203 | 204 | // then 205 | expect(a.hasValue()) =~ false 206 | 207 | // when 208 | a <- 1 209 | 210 | // then 211 | expect(a*) =~ 1 212 | expect(a.hasValue()) =~ true 213 | 214 | // when 215 | a <- fakeError 216 | 217 | // then 218 | expect(a.hasValue()) =~ true 219 | } 220 | 221 | 222 | it("is works with optionals") { 223 | // given 224 | let a: Source = reactive(1) 225 | 226 | // then 227 | expect(a*) =~ 1 228 | 229 | // when 230 | a <- nil 231 | 232 | // then 233 | expect(a*).to(beNil()) 234 | 235 | // when 236 | a <- fakeError 237 | 238 | // then 239 | expect(a.toTry().isFailure()) =~ true 240 | } 241 | 242 | } 243 | 244 | } 245 | 246 | } 247 | 248 | } 249 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerpTests/Common/Threading/DynamicVariableSpec.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 18/04/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | @testable import VinceRP 7 | 8 | import Quick 9 | import Nimble 10 | 11 | class Runnable { 12 | typealias Closure = () -> () 13 | let closure: Closure 14 | let name: String 15 | 16 | init(_ name:String, _ closure: Closure) { 17 | self.name = name 18 | self.closure = closure 19 | } 20 | 21 | @objc func run() { 22 | NSThread.currentThread().name = self.name 23 | self.closure() 24 | } 25 | 26 | func start() { 27 | let thread = NSThread(target:self, selector:"run", object:nil) 28 | thread.start() 29 | } 30 | } 31 | 32 | class DynamicVariableSpec: QuickSpec { 33 | 34 | override func spec() { 35 | 36 | describe("dynamic variable") { 37 | 38 | var dyn: DynamicVariable! 39 | var doSomething: (() -> String)! 40 | var r1: String! 41 | var r2: String! 42 | 43 | beforeEach { 44 | dyn = DynamicVariable(0) 45 | doSomething = { 46 | guard let v = dyn.value else { 47 | return "" 48 | } 49 | return "\(NSThread.currentThread().name!): \(v)" 50 | } 51 | r1 = nil 52 | r2 = nil 53 | 54 | } 55 | 56 | it("ensures the different context for different threads") { 57 | // when 58 | Runnable("thread-1") { 59 | r1 = dyn.withValue(10, doSomething) 60 | }.start() 61 | 62 | Runnable("thread-2") { 63 | r2 = dyn.withValue(20, doSomething) 64 | }.start() 65 | 66 | // then 67 | expect(r1).toEventually(equal("thread-1: 10")) 68 | expect(r2).toEventually(equal("thread-2: 20")) 69 | } 70 | 71 | it("shares the same variable in same thread") { 72 | // when 73 | Runnable("thread-1") { 74 | r1 = dyn.withValue(10, doSomething) 75 | r2 = dyn.withValue(20, doSomething) 76 | }.start() 77 | 78 | // then 79 | expect(r1).toEventually(equal("thread-1: 10")) 80 | expect(r2).toEventually(equal("thread-1: 20")) 81 | } 82 | 83 | // This test does not pass since there is no hiearchy between NSThreads 84 | // So you cannot inherit the parent thread's dynvalue :-( 85 | // That is the difference between ThreadLocal and InheritedThreadLocal 86 | 87 | /* 88 | it("passes the value to the embedded thread") { 89 | // when 90 | dyn.withValue(10) { () -> String in 91 | Runnable("thread-1") { 92 | r1 = doSomething() 93 | }.start() 94 | Runnable("thread-2") { 95 | r2 = doSomething() 96 | }.start() 97 | return "" 98 | } 99 | 100 | // then 101 | expect(r1).toEventually(equal("thread-1: 10")) 102 | expect(r2).toEventually(equal("thread-2: 10")) 103 | } 104 | */ 105 | } 106 | 107 | } 108 | 109 | } 110 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerpTests/Common/Util/FunctionalArraySpec.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 21/04/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | import Quick 7 | import Nimble 8 | 9 | class FunctionalArraySpec: QuickSpec { 10 | 11 | override func spec() { 12 | 13 | describe("prepend") { 14 | 15 | it("prepends to non-empty array") { 16 | // given 17 | let s = [1, 2] 18 | 19 | // when 20 | let r = s.arrayByPrepending(0) 21 | 22 | // then 23 | expect(r) == [0, 1, 2] 24 | } 25 | 26 | it("prepends to empty array") { 27 | // given 28 | let s = [Int]() 29 | 30 | // when 31 | let r = s.arrayByPrepending(0) 32 | 33 | // then 34 | expect(r) == [0] 35 | } 36 | 37 | } 38 | 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerpTests/Common/Util/FunctionalDictionarySpec.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 21/04/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | import Quick 7 | import Nimble 8 | 9 | class FunctionalDictionarySpec: QuickSpec { 10 | 11 | override func spec() { 12 | 13 | describe("mapValues") { 14 | 15 | it("maps empty dic to empty dic") { 16 | // given 17 | let s = Dictionary() 18 | 19 | // when 20 | let r = s.mapValues{$0*2} 21 | 22 | // then 23 | expect(r.count) == 0 24 | } 25 | 26 | it("maps values correctly") { 27 | // given 28 | let s:Dictionary = [1:1, 2:2, 3:3] 29 | 30 | // when 31 | let r = s.mapValues{$0*2} 32 | 33 | // then 34 | expect(r) == [1:2, 2:4, 3:6] 35 | } 36 | 37 | } 38 | 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerpTests/Common/Util/FunctionalSetSpec.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 21/04/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | @testable import VinceRP 7 | 8 | import Quick 9 | import Nimble 10 | 11 | class FunctionalSetSpec: QuickSpec { 12 | 13 | override func spec() { 14 | 15 | context("filter") { 16 | 17 | it("filters something") { 18 | // given 19 | let s = toSet(1, 2, 3) 20 | 21 | // when 22 | let r = s.filter{$0 > 2} 23 | 24 | // then 25 | expect(r) == toSet(3) 26 | } 27 | 28 | it("filters nothing") { 29 | // given 30 | let s = toSet(1, 2, 3) 31 | 32 | // when 33 | let r = s.filter{$0 > 3} 34 | 35 | // then 36 | expect(r.count) == 0 37 | } 38 | 39 | } 40 | 41 | context("foreach") { 42 | 43 | it("does nothing on empty set") { 44 | // given 45 | var r = 0 46 | let s = Set() 47 | 48 | // when 49 | s.forEach {r = r + $0} 50 | 51 | // then 52 | expect(r) == 0 53 | } 54 | 55 | it("sums the members of the set") { 56 | // given 57 | var r = 0 58 | let s = toSet(1, 2, 3) 59 | 60 | // when 61 | s.forEach {r = r + $0} 62 | 63 | // then 64 | expect(r) == 6 65 | } 66 | 67 | } 68 | 69 | context("exists") { 70 | 71 | it("finds if exists") { 72 | // given 73 | let s = toSet(1, 2, 3) 74 | 75 | // when 76 | let r = s.hasElementPassingTest{$0 > 2} 77 | 78 | // then 79 | expect(r) == true 80 | } 81 | 82 | it("finds nothing") { 83 | // given 84 | let s = toSet(1, 2, 3) 85 | 86 | // when 87 | let r = s.hasElementPassingTest{$0 > 3} 88 | 89 | // then 90 | expect(r) == false 91 | } 92 | 93 | } 94 | 95 | context("map") { 96 | 97 | it("maps empty to empty") { 98 | // given 99 | let s = Set() 100 | 101 | // when 102 | let r = s.map{$0 * 2} 103 | 104 | // then 105 | expect(r.count) == 0 106 | } 107 | 108 | it("maps every item") { 109 | // given 110 | let s = toSet(1, 2, 3) 111 | 112 | // when 113 | let r = s.map{$0 * 2} 114 | 115 | // then 116 | expect(r) == toSet(2, 4, 6) 117 | } 118 | 119 | } 120 | 121 | context("min") { 122 | 123 | it("returns 0 if empty set") { 124 | // given 125 | let s: Set = Set() 126 | 127 | // when 128 | let r = s.min(0) 129 | 130 | // then 131 | expect(r) == 0 132 | } 133 | 134 | it("maps every item") { 135 | // given 136 | let s: Set = toSet(3, 1, 2) 137 | 138 | // when 139 | let r = s.min(0) 140 | 141 | // then 142 | expect(r) == 1 143 | } 144 | 145 | } 146 | 147 | context("partition") { 148 | 149 | it("returns empty tuple of sets if empty set") { 150 | // given 151 | let s = Set() 152 | 153 | // when 154 | let (r1, r2) = s.partition{$0 % 2 == 0} 155 | 156 | // then 157 | expect(r1.count) == 0 158 | expect(r2.count) == 0 159 | } 160 | 161 | it("returns 2 sets") { 162 | // given 163 | let s = toSet(3, 1, 2) 164 | 165 | // when 166 | let (r1, r2) = s.partition{$0 % 2 == 0} 167 | 168 | // then 169 | expect(r1) == toSet(2) 170 | expect(r2) == toSet(1, 3) 171 | } 172 | 173 | } 174 | 175 | 176 | context("groupBy") { 177 | 178 | it("returns emptyMap if empty map") { 179 | // given 180 | let s = Set() 181 | 182 | // when 183 | let r = s.groupBy{$0 % 2} 184 | 185 | // then 186 | expect(r.count) == 0 187 | } 188 | 189 | it("returns 2 sets") { 190 | // given 191 | let s = toSet(3, 1, 2) 192 | 193 | // when 194 | let r = s.groupBy{$0 % 2} 195 | 196 | // then 197 | expect(r) == [0 : toSet(2), 1 : toSet(1, 3)] 198 | } 199 | 200 | } 201 | 202 | context("flattenMap") { 203 | 204 | it("flattens empty set to empty set") { 205 | // given 206 | let s = Set() 207 | 208 | // when 209 | let r = s.flatMap{c in toSet(c)} 210 | 211 | // then 212 | expect(r.count) == 0 213 | } 214 | 215 | it("flattens flat set to flat set") { 216 | // given 217 | let s = toSet(3, 1, 2) 218 | 219 | // when 220 | let r = s.flatMap{c in toSet(c * 2)} 221 | 222 | // then 223 | expect(r) == toSet(6, 4, 2) 224 | } 225 | 226 | it("flattens two level set to flat set") { 227 | // given 228 | let s = toSet(toSet(3, 1, 2), toSet(4, 5, 3)) 229 | 230 | // when 231 | let r = s.flatMap{c in c} 232 | 233 | // then 234 | expect(r) == toSet(1, 2, 3, 4, 5) 235 | } 236 | 237 | } 238 | 239 | context("flatten") { 240 | 241 | it("flattens empty set to empty set") { 242 | // given 243 | let s = Set() 244 | 245 | // when 246 | let r = s.flatten() 247 | 248 | // then 249 | expect(r.count) == 0 250 | } 251 | 252 | it("flattens flat set to flat set") { 253 | // given 254 | let s = toSet(3, 1, 2) 255 | 256 | // when 257 | let r = s.flatten() 258 | 259 | // then 260 | expect(r) == toSet(3, 2, 1) 261 | } 262 | 263 | it("flattens two level set to flat set") { 264 | // given 265 | let s = toSet(toSet(3, 1, 2), toSet(4, 5, 3)) 266 | 267 | // when 268 | let r = s.flatten() 269 | 270 | // then 271 | expect(r) == toSet(1, 2, 3, 4, 5) 272 | } 273 | 274 | } 275 | 276 | } 277 | 278 | } 279 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerpTests/Common/Util/TrySpec.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 20/04/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | @testable import VinceRP 7 | 8 | import Quick 9 | import Nimble 10 | 11 | struct TestFailure { 12 | static let error = NSError(domain:"Wrappers Tests", code:100, userInfo:[NSLocalizedDescriptionKey:"Testing"]) 13 | } 14 | 15 | class TrySpec: QuickSpec { 16 | 17 | override func spec() { 18 | 19 | describe("basic") { 20 | 21 | it("maps error to non-success") { 22 | // when 23 | let result = Try(false) 24 | 25 | // then 26 | expect(result.isSuccess()) == true 27 | } 28 | 29 | it("maps false to non-failure") { 30 | // when 31 | let result = Try(false) 32 | 33 | // then 34 | expect(result.isFailure()) == false 35 | } 36 | 37 | 38 | it("maps error to failure") { 39 | // when 40 | let result = Try(TestFailure.error) 41 | 42 | // then 43 | expect(result.isFailure()) == true 44 | } 45 | 46 | it("maps false to success") { 47 | // when 48 | let result = Try(TestFailure.error) 49 | 50 | // then 51 | expect(result.isSuccess()) == false 52 | } 53 | 54 | } 55 | 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerpTests/Common/Util/WeakReferenceTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 20/04/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | @testable import VinceRP 7 | 8 | import Quick 9 | import Nimble 10 | 11 | let v = AtomicLong(2) 12 | 13 | class WeakReferenceSpec: QuickSpec { 14 | 15 | override func spec() { 16 | 17 | context("value") { 18 | 19 | it("does not hold it's value") { 20 | // when 21 | let wr = WeakReference(AtomicLong(2)) 22 | 23 | // then 24 | expect(wr.value).to(beNil()) 25 | } 26 | 27 | it("keeps it's value if it got hold outside") { 28 | // when 29 | let wr = WeakReference(v) 30 | 31 | // then 32 | expect(wr.value) == v 33 | } 34 | 35 | } 36 | 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerpTests/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 | -------------------------------------------------------------------------------- /Carthage/Checkouts/VinceRP/vincerpTests/TestUtil.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 09/05/15. 3 | // Copyright (c) 2015 Viktor Belenyesi. All rights reserved. 4 | // 5 | 6 | @testable import VinceRP 7 | 8 | import Quick 9 | import Nimble 10 | 11 | infix operator =~ { associativity left precedence 130 } 12 | func =~ (left: Expectation, right: T?) -> Void { 13 | return left.toEventually(equal(right)) 14 | } 15 | 16 | public func testGraph() -> (Source, Source, Dynamic, Dynamic, Dynamic, Dynamic) { 17 | let n1 = reactive(1) 18 | let n2 = reactive(2) 19 | 20 | let n3 = definedAs { n1* * n2* } 21 | let n4 = definedAs { n3* + 5 } 22 | let n5 = definedAs { n3* / 2 } 23 | let n6 = definedAs { n4* - n5* + 4 } 24 | 25 | return (n1, n2, n3, n4, n5, n6) 26 | } 27 | -------------------------------------------------------------------------------- /CopyPasta.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /CopyPasta.xcodeproj/xcshareddata/xcschemes/CopyPasta.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 43 | 49 | 50 | 51 | 52 | 53 | 59 | 60 | 61 | 62 | 63 | 64 | 74 | 76 | 82 | 83 | 84 | 85 | 86 | 87 | 93 | 95 | 101 | 102 | 103 | 104 | 106 | 107 | 110 | 111 | 112 | -------------------------------------------------------------------------------- /CopyPasta.xcodeproj/xcuserdata/vasarhelyia.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 5 | 6 | -------------------------------------------------------------------------------- /CopyPasta.xcodeproj/xcuserdata/vasarhelyia.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | CopyPasta.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | 02C488831BE21A0F004FD281 16 | 17 | primary 18 | 19 | 20 | 02C488941BE21A0F004FD281 21 | 22 | primary 23 | 24 | 25 | 02C4889F1BE21A0F004FD281 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /CopyPasta/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // CopyPasta 4 | // 5 | // Created by Agnes Vasarhelyi on 29/10/15. 6 | // Copyright © 2015 Agnes Vasarhelyi. All rights reserved. 7 | // 8 | 9 | import AppKit 10 | 11 | @NSApplicationMain 12 | class AppDelegate: NSObject, NSApplicationDelegate { 13 | 14 | let statusItem = NSStatusBar.systemStatusBar().statusItemWithLength(-2) 15 | let popover = NSPopover() 16 | var popoverTransiencyMonitor: AnyObject? 17 | 18 | func applicationDidFinishLaunching(aNotification: NSNotification) { 19 | statusItem.button!.image = NSImage(named: "pasta_icon") 20 | statusItem.button!.action = Selector("togglePopover:") 21 | 22 | let mainViewController = MainViewController(nibName: "MainViewController", bundle: nil) 23 | popover.behavior = .Semitransient 24 | popover.contentViewController = mainViewController 25 | } 26 | 27 | func applicationWillTerminate(aNotification: NSNotification) { } 28 | 29 | // MARK: popover actions 30 | 31 | func togglePopover(sender: AnyObject?) { 32 | if popover.shown { 33 | closePopover(sender) 34 | } else { 35 | openPopover(sender) 36 | } 37 | } 38 | 39 | func openPopover(sender: AnyObject?) { 40 | popover.showRelativeToRect(statusItem.button!.bounds, ofView: statusItem.button!, preferredEdge: .MinY) 41 | 42 | guard popoverTransiencyMonitor == nil else { 43 | return 44 | } 45 | 46 | popoverTransiencyMonitor = NSEvent.addGlobalMonitorForEventsMatchingMask([.RightMouseDownMask, .LeftMouseDownMask]) { _ in 47 | self.closePopover(sender) 48 | } 49 | } 50 | 51 | func closePopover(sender: AnyObject?) { 52 | popover.performClose(sender) 53 | 54 | guard let monitor = popoverTransiencyMonitor else { 55 | return 56 | } 57 | 58 | NSEvent.removeMonitor(monitor) 59 | popoverTransiencyMonitor = nil 60 | } 61 | 62 | 63 | } 64 | -------------------------------------------------------------------------------- /CopyPasta/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "mac", 5 | "size" : "16x16", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "mac", 10 | "size" : "16x16", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "idiom" : "mac", 15 | "size" : "32x32", 16 | "scale" : "1x" 17 | }, 18 | { 19 | "idiom" : "mac", 20 | "size" : "32x32", 21 | "scale" : "2x" 22 | }, 23 | { 24 | "idiom" : "mac", 25 | "size" : "128x128", 26 | "scale" : "1x" 27 | }, 28 | { 29 | "idiom" : "mac", 30 | "size" : "128x128", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "idiom" : "mac", 35 | "size" : "256x256", 36 | "scale" : "1x" 37 | }, 38 | { 39 | "idiom" : "mac", 40 | "size" : "256x256", 41 | "scale" : "2x" 42 | }, 43 | { 44 | "idiom" : "mac", 45 | "size" : "512x512", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "mac", 50 | "size" : "512x512", 51 | "scale" : "2x" 52 | } 53 | ], 54 | "info" : { 55 | "version" : 1, 56 | "author" : "xcode" 57 | } 58 | } -------------------------------------------------------------------------------- /CopyPasta/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /CopyPasta/Assets.xcassets/pasta_icon.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "idiom" : "universal", 9 | "filename" : "pasta_icon.png", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "version" : 1, 19 | "author" : "xcode" 20 | } 21 | } -------------------------------------------------------------------------------- /CopyPasta/Assets.xcassets/pasta_icon.imageset/pasta_icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alltheflow/CopyPasta/79460ff452600945de8c7ef1560c948dfe7b6942/CopyPasta/Assets.xcassets/pasta_icon.imageset/pasta_icon.png -------------------------------------------------------------------------------- /CopyPasta/ImageItemCell.xib: -------------------------------------------------------------------------------- 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 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /CopyPasta/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 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 | LSMinimumSystemVersion 26 | $(MACOSX_DEPLOYMENT_TARGET) 27 | LSUIElement 28 | 29 | NSHumanReadableCopyright 30 | Copyright © 2015 Agnes Vasarhelyi. All rights reserved. 31 | NSMainNibFile 32 | MainMenu 33 | NSPrincipalClass 34 | NSApplication 35 | 36 | 37 | -------------------------------------------------------------------------------- /CopyPasta/MainViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MainViewController.swift 3 | // CopyPasta 4 | // 5 | // Created by Agnes Vasarhelyi on 07/12/15. 6 | // Copyright © 2015 Agnes Vasarhelyi. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class MainViewController: NSViewController { 12 | 13 | @IBOutlet weak var contentView: NSView! 14 | @IBOutlet weak var settingsButton: NSButton! 15 | 16 | override func awakeFromNib() { 17 | let pasteboardViewController = PasteboardViewController(nibName: "PasteboardViewController", bundle: nil) 18 | contentView.addSubview(pasteboardViewController.view) 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /CopyPasta/MainViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /CopyPasta/PastePopoverBackgroundView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 11/24/15. 3 | // Copyright © 2015 Agnes Vasarhelyi. All rights reserved. 4 | // 5 | 6 | import AppKit 7 | 8 | class PastePopoverBackgroundView: NSView { 9 | 10 | override var opaque: Bool { 11 | return true 12 | } 13 | 14 | override func drawRect(dirtyRect: NSRect) { 15 | NSColor.clearColor().set() 16 | NSRectFill(self.bounds) 17 | } 18 | 19 | } -------------------------------------------------------------------------------- /CopyPasta/PasteViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PasteViewModel.swift 3 | // CopyPasta 4 | // 5 | // Created by Agnes Vasarhelyi on 23/11/15. 6 | // Copyright © 2015 Agnes Vasarhelyi. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import VinceRP 11 | 12 | class PasteViewModel { 13 | let pasteboardService = PasteboardService() 14 | 15 | init() { 16 | timer(1.0) { 17 | self.pasteboardService.pollPasteboardItems() 18 | } 19 | } 20 | 21 | func pasteboardItems() -> observable { 22 | return pasteboardService.pasteboardItems 23 | } 24 | 25 | func items() -> [PasteboardItem] { 26 | return pasteboardItems()* 27 | } 28 | 29 | func countHidden() -> Hub { 30 | return pasteboardItems().map { $0.count == 0 } 31 | } 32 | 33 | func itemCountString() -> Hub { 34 | return pasteboardItems() 35 | .dispatchOnMainQueue() 36 | .map { "\($0.count) items" } 37 | } 38 | 39 | func selectItemAtIndex(index: Int) { 40 | let item = self.items()[index] 41 | pasteboardService.addItemToPasteboard(item) 42 | } 43 | 44 | func itemAtIndex(index: Int) -> PasteboardItem { 45 | return self.items()[index] 46 | } 47 | 48 | } 49 | -------------------------------------------------------------------------------- /CopyPasta/PasteboardCollectionViewItem.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PasteboardCollectionViewItem.swift 3 | // CopyPasta 4 | // 5 | // Created by Agnes Vasarhelyi on 01/12/15. 6 | // Copyright © 2015 Agnes Vasarhelyi. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | class PasteboardCollectionViewItem: NSCollectionViewItem { 12 | @IBOutlet weak var dateLabel: NSTextField! 13 | } 14 | -------------------------------------------------------------------------------- /CopyPasta/PasteboardItem.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PasteboardItem.swift 3 | // CopyPasta 4 | // 5 | // Created by Agnes Vasarhelyi on 23/11/15. 6 | // Copyright © 2015 Agnes Vasarhelyi. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | 11 | extension String { 12 | func heightWithConstrainedWidth(width: CGFloat, font: NSFont) -> CGFloat { 13 | let constraintRect = CGSize(width: width, height: CGFloat.max) 14 | let boundingBox = self.boundingRectWithSize( 15 | constraintRect, 16 | options: NSStringDrawingOptions.UsesLineFragmentOrigin, 17 | attributes: [NSFontAttributeName: font], 18 | context: nil) 19 | 20 | return boundingBox.height 21 | } 22 | } 23 | 24 | extension NSImage { 25 | 26 | public override func isEqualTo(object: AnyObject?) -> Bool { 27 | guard let otherImage = object as? NSImage else { 28 | return false 29 | } 30 | 31 | guard let lhsiTiff = self.TIFFRepresentation, 32 | let rhsiTiff = otherImage.TIFFRepresentation else { 33 | return false 34 | } 35 | 36 | return lhsiTiff.isEqualToData(rhsiTiff) 37 | } 38 | 39 | } 40 | 41 | func ==(lhs: PasteboardItem, rhs: PasteboardItem) -> Bool { 42 | switch (lhs, rhs) { 43 | case (.Text(let str1), .Text(let str2)): return str1 == str2 44 | case (.Image(let img1), .Image(let img2)): return img1.isEqualTo(img2) 45 | case (.URL(let str1), .URL(let str2)): return str1 == str2 46 | default: return false 47 | } 48 | } 49 | 50 | func !=(lhs: PasteboardItem, rhs: PasteboardItem) -> Bool { 51 | return !(lhs == rhs) 52 | } 53 | 54 | enum PasteboardItem { 55 | case Text(String) 56 | case URL(NSURL) 57 | case Image(NSImage) 58 | } 59 | -------------------------------------------------------------------------------- /CopyPasta/PasteboardService.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PasteboardService.swift 3 | // CopyPasta 4 | // 5 | // Created by Agnes Vasarhelyi on 03/11/15. 6 | // Copyright © 2015 Agnes Vasarhelyi. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import VinceRP 11 | 12 | typealias observable = Hub<[PasteboardItem]> 13 | 14 | class PasteboardService { 15 | 16 | let pasteboard = NSPasteboard.generalPasteboard() 17 | let pasteboardItems = reactive([PasteboardItem]()) 18 | let changeCount = reactive(0) 19 | 20 | // MARK: public methods 21 | 22 | @objc func pollPasteboardItems() { 23 | 24 | if (changeCount* == pasteboard.changeCount) { 25 | return 26 | } 27 | 28 | guard let items = pasteboard.readObjectsForClasses([NSString.self, NSImage.self, NSURL.self], options: nil) 29 | where items.count > 0 else { 30 | return 31 | } 32 | 33 | guard let item = pasteboardItem(items.first) else { 34 | return 35 | } 36 | 37 | pasteboardItems <- pasteboardItems* 38 | .filter { $0 != item } 39 | .arrayByPrepending(item) 40 | changeCount <- pasteboard.changeCount 41 | } 42 | 43 | func addItemToPasteboard(item: PasteboardItem) { 44 | pasteboard.clearContents() 45 | switch item { 46 | case .Text(let string): 47 | pasteboard.writeObjects([string as NSPasteboardWriting]) 48 | case .Image(let image): 49 | pasteboard.writeObjects([image as NSPasteboardWriting]) 50 | case .URL(let url): 51 | pasteboard.writeObjects([url as NSPasteboardWriting]) 52 | } 53 | pollPasteboardItems() 54 | } 55 | 56 | func items() -> observable { 57 | return self.pasteboardItems 58 | } 59 | 60 | // MARK: private methods 61 | 62 | private func pasteboardItem(item: AnyObject?) -> PasteboardItem? { 63 | if let string = item as? String { 64 | return .Text(string) 65 | } 66 | 67 | if let image = item as? NSImage { 68 | return .Image(image) 69 | } 70 | 71 | if let url = item as? NSURL { 72 | return .URL(url) 73 | } 74 | 75 | fatalError("unsupported types") 76 | } 77 | 78 | } 79 | -------------------------------------------------------------------------------- /CopyPasta/PasteboardViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PasteboardViewController.swift 3 | // CopyPasta 4 | // 5 | // Created by Agnes Vasarhelyi on 29/10/15. 6 | // Copyright © 2015 Agnes Vasarhelyi. All rights reserved. 7 | // 8 | 9 | import Cocoa 10 | import VinceRP 11 | 12 | class PasteboardViewController: NSViewController, NSCollectionViewDataSource, NSCollectionViewDelegate, NSCollectionViewDelegateFlowLayout { 13 | 14 | let textItemCellID = "TextItemCell" 15 | let imageItemCellID = "ImageItemCell" 16 | let pasteViewModel = PasteViewModel() 17 | 18 | @IBOutlet weak var collectionView: NSCollectionView! 19 | @IBOutlet weak var countLabel: NSTextField! 20 | 21 | override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?) { 22 | super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)! 23 | } 24 | 25 | required init?(coder: NSCoder) { 26 | fatalError("init(coder:) has not been implemented") 27 | } 28 | 29 | override func awakeFromNib() { 30 | let textItemNib = NSNib(nibNamed: textItemCellID, bundle: nil) 31 | let imageItemNib = NSNib(nibNamed: imageItemCellID, bundle: nil) 32 | 33 | collectionView.registerNib(textItemNib, forItemWithIdentifier: textItemCellID) 34 | collectionView.registerNib(imageItemNib, forItemWithIdentifier: imageItemCellID) 35 | 36 | pasteViewModel.pasteboardItems() 37 | .dispatchOnMainQueue().onChange { _ in self.collectionView.reloadData() } 38 | 39 | countLabel.reactiveHidden = pasteViewModel.countHidden() 40 | countLabel.reactiveText = pasteViewModel.itemCountString() 41 | } 42 | 43 | // MARK: NSCollectionViewDataSource 44 | 45 | func collectionView(collectionView: NSCollectionView, numberOfItemsInSection section: Int) -> Int { 46 | return pasteViewModel.items().count 47 | } 48 | 49 | func collectionView(collectionView: NSCollectionView, itemForRepresentedObjectAtIndexPath indexPath: NSIndexPath) -> NSCollectionViewItem { 50 | var cell = PasteboardCollectionViewItem() 51 | let item = pasteViewModel.itemAtIndex(indexPath.item) 52 | 53 | switch (item) { 54 | case .Text(let string): 55 | cell = collectionView.makeItemWithIdentifier(textItemCellID, forIndexPath: indexPath) as! PasteboardCollectionViewItem 56 | cell.textField!.stringValue = string 57 | cell.textField?.toolTip = string 58 | case .Image(let image): 59 | cell = collectionView.makeItemWithIdentifier(imageItemCellID, forIndexPath: indexPath) as! PasteboardCollectionViewItem 60 | cell.imageView!.image = image 61 | default: break 62 | } 63 | 64 | cell.dateLabel.stringValue = self.timestamp() 65 | return cell 66 | } 67 | 68 | func collectionView(collectionView: NSCollectionView, layout collectionViewLayout: NSCollectionViewLayout, sizeForItemAtIndexPath indexPath: NSIndexPath) -> NSSize { 69 | let items = pasteViewModel.items() 70 | return sizeForItem(items[indexPath.item]) 71 | } 72 | 73 | // MARK: NSCollectionViewDelegate 74 | 75 | func collectionView(collectionView: NSCollectionView, didSelectItemsAtIndexPaths indexPaths: Set) { 76 | pasteViewModel.selectItemAtIndex(indexPaths.first!.item) 77 | } 78 | 79 | // MARK: Helper functions 80 | 81 | func timestamp() -> String { 82 | let date = NSDate() 83 | let dateFormatter = NSDateFormatter() 84 | dateFormatter.dateFormat = "MMM. dd HH:mm" 85 | return dateFormatter.stringFromDate(date) 86 | } 87 | 88 | func heightForItem(item: PasteboardItem) -> CGFloat { 89 | let h = collectionView.frame.size.height 90 | switch (item) { 91 | case .Text(let text): 92 | let font = NSFont.systemFontOfSize(13) 93 | let textHeight = text.heightWithConstrainedWidth(348.0, font:font) 94 | return textHeight < 150 ? textHeight + 90 : 150 95 | case .Image(let image): 96 | return h > 159.0 ? 159.0 : image.size.height 97 | default: return h 98 | } 99 | } 100 | 101 | func sizeForItem(item: PasteboardItem) -> NSSize { 102 | let w = collectionView.frame.size.width 103 | return NSSize(width: w, height: heightForItem(item)) 104 | } 105 | 106 | @IBAction func quit(sender: AnyObject) { 107 | NSApplication.sharedApplication().terminate(sender) 108 | } 109 | 110 | } 111 | -------------------------------------------------------------------------------- /CopyPasta/PasteboardViewController.xib: -------------------------------------------------------------------------------- 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 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 61 | 62 | 63 | 64 | 65 | 66 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | -------------------------------------------------------------------------------- /CopyPasta/TextItemCell.xib: -------------------------------------------------------------------------------- 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 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /CopyPasta/View/Popover/PastePopoverRootView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Created by Viktor Belenyesi on 11/24/15. 3 | // Copyright © 2015 Agnes Vasarhelyi. All rights reserved. 4 | // 5 | 6 | import AppKit 7 | 8 | class PastePopoverRootView: NSView { 9 | 10 | override func viewDidMoveToWindow() { 11 | let frameView = self.window?.contentView?.superview 12 | let backgroundView = PastePopoverBackgroundView(frame: frameView!.bounds) 13 | backgroundView.alphaValue = 0.9 14 | backgroundView.autoresizingMask = [.ViewWidthSizable, .ViewHeightSizable] 15 | frameView?.addSubview(backgroundView, positioned: .Below, relativeTo: frameView) 16 | super.viewDidMoveToWindow() 17 | } 18 | 19 | } -------------------------------------------------------------------------------- /CopyPastaTests/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 | -------------------------------------------------------------------------------- /CopyPastaTests/PasteViewModelTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PasteViewModelTests.swift 3 | // CopyPasta 4 | // 5 | // Created by Agnes Vasarhelyi on 06/12/15. 6 | // Copyright © 2015 Agnes Vasarhelyi. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import CopyPasta 11 | 12 | class PasteViewModelTests: XCTestCase { 13 | 14 | func testSelectItemAtIndex() { 15 | let pasteViewModel = PasteViewModel() 16 | pasteViewModel.pasteboardService.addItemToPasteboard(.Text("copy")) 17 | pasteViewModel.pasteboardService.addItemToPasteboard(.Text("pasta")) 18 | 19 | pasteViewModel.selectItemAtIndex(0) 20 | 21 | XCTAssertTrue(pasteViewModel.items()[0] == .Text("pasta"), "it should readd existing items") 22 | XCTAssertTrue(pasteViewModel.items().count == 2, "it should not duplicate readded item") 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /CopyPastaTests/PasteboardItemTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PasteboardItemTests.swift 3 | // CopyPasta 4 | // 5 | // Created by Agnes Vasarhelyi on 06/12/15. 6 | // Copyright © 2015 Agnes Vasarhelyi. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import CopyPasta 11 | 12 | class PasteboardItemTests: XCTestCase { 13 | 14 | func testTextItem() { 15 | let textItem = PasteboardItem.Text("copy") 16 | XCTAssert(textItem == .Text("copy"), "should handle equal operator") 17 | XCTAssert(textItem != .Text("copy pasta"), "should handle not equal operator") 18 | } 19 | 20 | func testImageItem() { 21 | let imageItem = PasteboardItem.Image(NSImage(named: "NSAdvanced")!) 22 | XCTAssert(imageItem == .Image(NSImage(named: "NSAdvanced")!), "should handle equal operator") 23 | XCTAssert(imageItem != .Image(NSImage(named: "NSApplicationIcon")!), "should handle not equal operator") 24 | } 25 | 26 | func testURLItem() { 27 | let urlItem = PasteboardItem.URL(NSURL(string: "http://url.com")!) 28 | XCTAssert(urlItem == .URL(NSURL(string: "http://url.com")!), "should handle equal operator") 29 | XCTAssert(urlItem != .URL(NSURL(string: "http://url.co.uk")!), "should handle not equal operator") 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /CopyPastaTests/PasteboardPollingTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PasteboardPollingTests.swift 3 | // CopyPasta 4 | // 5 | // Created by Agnes Vasarhelyi on 11/12/15. 6 | // Copyright © 2015 Agnes Vasarhelyi. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import CopyPasta 11 | 12 | class PasteboardPollingTests: XCTestCase { 13 | 14 | func testpollPasteboardItems() { 15 | let pasteboardService = PasteboardService() 16 | pasteboardService.pasteboard.writeObjects(["pasta"]) 17 | 18 | pasteboardService.pollPasteboardItems() 19 | 20 | XCTAssertNotNil(pasteboardService.pasteboardItems.value()[0], "it should have an item") 21 | XCTAssertTrue(pasteboardService.pasteboardItems.value()[0] == .Text("pasta"), "it should poll the new item as pasteboarditem") 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /CopyPastaTests/PasteboardServiceTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PasteboardServiceTests.swift 3 | // CopyPasta 4 | // 5 | // Created by Agnes Vasarhelyi on 06/12/15. 6 | // Copyright © 2015 Agnes Vasarhelyi. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | import VinceRP 11 | @testable import CopyPasta 12 | 13 | class PasteboardServiceTests: XCTestCase { 14 | 15 | func addItemToPasteboard() { 16 | let pasteboardService = PasteboardService() 17 | pasteboardService.addItemToPasteboard(.Text("pasta")) 18 | XCTAssertTrue(pasteboardService.pasteboardItems.value()[0] == .Text("pasta"), "it should add new items") 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Agnes Vasarhelyi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![CopyPasta](https://cloud.githubusercontent.com/assets/1460573/11615156/7a3b6454-9c59-11e5-95fa-0780af681f2e.png) 2 | 3 | [![Bitrise](https://www.bitrise.io/app/d78e051b3ee27e5e.svg?token=9MT0hRDGtgJZXhC_JQJNFA&branch=master)](https://www.bitrise.io/) 4 | 5 | Just a basic pasteboard feed. Check out the blogpost about its purpose on [alltheflow blog](https://blog.alltheflow.com/lightweight-reactive-coding-with-swift-and-vincerp/). 6 | 7 | ## Install with [Carthage](https://github.com/Carthage/Carthage) 8 | 9 | ``` 10 | carthage update --platform mac 11 | ``` 12 | 13 | ### Dependencies 14 | 15 | - [VinceRP](https://github.com/bvic23/VinceRP) 0.2.5 16 | 17 | ## App 18 | 19 | Lists your pasteboard items. Select any, it will jump back to the top of the stack and you'll be able to paste it anywhere in OS X. 20 | 21 | ![CopyPasta in action](https://cloud.githubusercontent.com/assets/1460573/11615326/bad4be3a-9c5d-11e5-98ce-dac5c9fea45b.png) 22 | -------------------------------------------------------------------------------- /bitrise.yml: -------------------------------------------------------------------------------- 1 | format_version: 1.1.0 2 | default_step_lib_source: https://github.com/bitrise-io/bitrise-steplib.git 3 | 4 | app: 5 | envs: 6 | - BITRISE_PROJECT_PATH: "./CopyPasta.xcodeproj" 7 | - BITRISE_SCHEME: CopyPasta 8 | - BUILD_TOOL: xcodebuild 9 | 10 | trigger_map: 11 | - pattern: "*" 12 | is_pull_request_allowed: true 13 | workflow: test 14 | 15 | workflows: 16 | test: 17 | steps: 18 | - git::https://github.com/vasarhelyia/steps-carthage.git@1.0.8: 19 | title: Carthage 20 | inputs: 21 | - carthage_command: update 22 | - no_use_binaries: 'true' 23 | - verbose_output: 'true' 24 | - platform: Mac 25 | - git::https://github.com/vasarhelyia/steps-xcode-test-mac.git@1.0.2: 26 | title: Xcode Test Mac 27 | inputs: 28 | - generate_code_coverage_files: "no" 29 | - is_clean_build: "yes" 30 | --------------------------------------------------------------------------------