├── .gitignore
├── .gitmodules
├── Cartfile.private
├── Cartfile.resolved
├── LICENSE
├── README.md
├── ReactKitCatalog-OSX
├── AppDelegate.swift
├── Base.lproj
│ └── Main.storyboard
├── Images.xcassets
│ └── AppIcon.appiconset
│ │ └── Contents.json
├── Info.plist
└── ViewController.swift
├── ReactKitCatalog-iOS
├── AppDelegate.swift
├── Base.lproj
│ ├── LaunchScreen.xib
│ └── Main.storyboard
├── Catalog.swift
├── Images.xcassets
│ └── AppIcon.appiconset
│ │ ├── Contents.json
│ │ ├── logo-60@2x.png
│ │ ├── logo-60@3x.png
│ │ ├── logo-76.png
│ │ └── logo-76@2x.png
├── Info.plist
├── MasterViewController.swift
└── Samples
│ ├── ArrayKVOViewController.swift
│ ├── ArrayKVOViewController.xib
│ ├── ArrayKVOViewModel.swift
│ ├── ButtonViewController.swift
│ ├── ButtonViewController.xib
│ ├── GestureViewController.swift
│ ├── GestureViewController.xib
│ ├── IncrementalSearchViewController.swift
│ ├── MultipleTextFieldViewController.swift
│ ├── MultipleTextFieldViewController.xib
│ ├── TextFieldViewController.swift
│ ├── TextFieldViewController.xib
│ ├── TimerViewController.swift
│ ├── TimerViewController.xib
│ ├── WhoToFollowViewController.swift
│ └── WhoToFollowViewController.xib
├── ReactKitCatalog.xcodeproj
├── project.pbxproj
└── project.xcworkspace
│ └── contents.xcworkspacedata
└── Shared
└── Helper.swift
/.gitignore:
--------------------------------------------------------------------------------
1 | # Xcode
2 | build/
3 | *.pbxuser
4 | !default.pbxuser
5 | *.mode1v3
6 | !default.mode1v3
7 | *.mode2v3
8 | !default.mode2v3
9 | *.perspectivev3
10 | !default.perspectivev3
11 | xcuserdata
12 | *.xccheckout
13 | *.moved-aside
14 | DerivedData
15 | *.hmap
16 | *.ipa
17 | *.xcuserstate
18 |
19 | Carthage/Build
20 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "Carthage/Checkouts/SwiftTask"]
2 | path = Carthage/Checkouts/SwiftTask
3 | url = https://github.com/ReactKit/SwiftTask.git
4 | [submodule "Carthage/Checkouts/ReactKit"]
5 | path = Carthage/Checkouts/ReactKit
6 | url = https://github.com/ReactKit/ReactKit.git
7 | [submodule "Carthage/Checkouts/Alamofire"]
8 | path = Carthage/Checkouts/Alamofire
9 | url = https://github.com/Alamofire/Alamofire.git
10 | [submodule "Carthage/Checkouts/SwiftyJSON"]
11 | path = Carthage/Checkouts/SwiftyJSON
12 | url = https://github.com/SwiftyJSON/SwiftyJSON.git
13 | [submodule "Carthage/Checkouts/Async"]
14 | path = Carthage/Checkouts/Async
15 | url = https://github.com/duemunk/Async.git
16 | [submodule "Carthage/Checkouts/Dollar.swift"]
17 | path = Carthage/Checkouts/Dollar.swift
18 | url = https://github.com/ankurp/Dollar.swift.git
19 | [submodule "Carthage/Checkouts/HanekeSwift"]
20 | path = Carthage/Checkouts/HanekeSwift
21 | url = https://github.com/meteochu/HanekeSwift.git
22 | [submodule "Carthage/Checkouts/BigBrother"]
23 | path = Carthage/Checkouts/BigBrother
24 | url = https://github.com/marcelofabri/BigBrother.git
25 | [submodule "Carthage/Checkouts/ReactKitCalculator"]
26 | url = https://github.com/ReactKit/ReactKitCalculator.git
27 | path = Carthage/Checkouts/ReactKitCalculator
28 |
--------------------------------------------------------------------------------
/Cartfile.private:
--------------------------------------------------------------------------------
1 | github "ReactKit/ReactKitCalculator" "swift/2.0"
2 |
3 | github "Alamofire/Alamofire" "3.0.0-beta.1" #~> 1.2.3
4 | github "SwiftyJSON/SwiftyJSON" "xcode7" #~> 2.2.0
5 | github "duemunk/Async" "feature/Swift_2.0"
6 | github "ankurp/Dollar.swift" ~> 4.0.1
7 | #github "Haneke/HanekeSwift" ~> 0.9.1
8 | github "meteochu/HanekeSwift" "swift-2.0"
9 | github "marcelofabri/BigBrother" ~> 0.3.0
--------------------------------------------------------------------------------
/Cartfile.resolved:
--------------------------------------------------------------------------------
1 | github "Alamofire/Alamofire" "3.0.0-beta.1"
2 | github "duemunk/Async" "abe23c18d8d3a708a325b5db10231388e1ca5b88"
3 | github "marcelofabri/BigBrother" "0.3.0"
4 | github "ankurp/Dollar.swift" "4.0.1"
5 | github "meteochu/HanekeSwift" "f87220fccba7810955fe5392ebd492a5c9baf050"
6 | github "ReactKit/SwiftTask" "4.0.0"
7 | github "SwiftyJSON/SwiftyJSON" "6eb4cda062fa09c4658e4184f92d822058f226e3"
8 | github "ReactKit/ReactKit" "0.12.0"
9 | github "ReactKit/ReactKitCalculator" "54d5d49696292f385f22fa9fd722da5af3548974"
10 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 ReactKit
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 | ReactKitCatalog
2 | ===============
3 |
4 | ReactKit UI examples.
5 |
--------------------------------------------------------------------------------
/ReactKitCatalog-OSX/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // ReactKitCatalog-OSX
4 | //
5 | // Created by Yasuhiro Inami on 2014/10/06.
6 | // Copyright (c) 2014年 Yasuhiro Inami. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 |
11 | @NSApplicationMain
12 | class AppDelegate: NSObject, NSApplicationDelegate {
13 |
14 | func applicationDidFinishLaunching(aNotification: NSNotification) {
15 | println("2014/10/06 Mac Demo is not ready yet.")
16 | }
17 |
18 | func applicationWillTerminate(aNotification: NSNotification) {
19 | // Insert code here to tear down your application
20 | }
21 |
22 | }
23 |
24 |
--------------------------------------------------------------------------------
/ReactKitCatalog-OSX/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
640 |
641 |
642 |
643 |
644 |
645 |
646 |
647 |
648 |
649 |
650 |
651 |
652 |
653 |
654 |
655 |
656 |
657 |
658 |
659 |
660 |
661 |
662 |
663 |
664 |
665 |
666 |
667 |
668 |
669 |
670 |
671 |
672 |
673 |
674 |
675 |
676 |
677 |
678 |
679 |
680 |
681 |
682 |
--------------------------------------------------------------------------------
/ReactKitCatalog-OSX/Images.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 | }
--------------------------------------------------------------------------------
/ReactKitCatalog-OSX/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 © 2014年 Yasuhiro Inami. All rights reserved.
29 | NSMainStoryboardFile
30 | Main
31 | NSPrincipalClass
32 | NSApplication
33 |
34 |
35 |
--------------------------------------------------------------------------------
/ReactKitCatalog-OSX/ViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController.swift
3 | // ReactKitCatalog-OSX
4 | //
5 | // Created by Yasuhiro Inami on 2014/10/06.
6 | // Copyright (c) 2014年 Yasuhiro Inami. All rights reserved.
7 | //
8 |
9 | import Cocoa
10 |
11 | class ViewController: NSViewController {
12 |
13 | override func viewDidLoad() {
14 | super.viewDidLoad()
15 |
16 | // Do any additional setup after loading the view.
17 | }
18 |
19 | override var representedObject: AnyObject? {
20 | didSet {
21 | // Update the view, if already loaded.
22 | }
23 | }
24 |
25 |
26 | }
27 |
28 |
--------------------------------------------------------------------------------
/ReactKitCatalog-iOS/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // ReactKitCatalog-iOS
4 | //
5 | // Created by Yasuhiro Inami on 2014/10/06.
6 | // Copyright (c) 2014年 Yasuhiro Inami. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Alamofire
11 | //import BigBrother
12 |
13 | @UIApplicationMain
14 | class AppDelegate: UIResponder, UIApplicationDelegate, UISplitViewControllerDelegate {
15 |
16 | var window: UIWindow?
17 |
18 | func _setupAppearance()
19 | {
20 | let font = UIFont(name: "AvenirNext-Medium", size: 16)!
21 |
22 | UINavigationBar.appearance().titleTextAttributes = [ NSFontAttributeName : font ]
23 | UIBarButtonItem.appearance().setTitleTextAttributes([ NSFontAttributeName : font ], forState: .Normal)
24 | // UIButton.appearance().titleLabel?.font = font
25 | // UILabel.appearance().font = font
26 | // UITextField.appearance().font = font
27 | // UITextView.appearance().font = font
28 | }
29 |
30 | func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool
31 | {
32 | // 2015/07/18 comment-out: BigBrother not working in Swift 2 (Xcode7-beta3)
33 | // setup BigBrother (networkActivityIndicator)
34 | // BigBrother.addToSharedSession()
35 | // BigBrother.addToSessionConfiguration(Alamofire.Manager.sharedInstance.session.configuration)
36 |
37 | self._setupAppearance()
38 |
39 | let splitVC = self.window!.rootViewController as! UISplitViewController
40 | splitVC.delegate = self
41 |
42 | let mainNavC = splitVC.viewControllers[0] as! UINavigationController
43 | // let detailNavC = splitVC.viewControllers[1] as UINavigationController
44 |
45 | let mainVC = mainNavC.topViewController as! MasterViewController
46 |
47 | // NOTE: use dispatch_after to check `splitVC.collapsed` after delegation is complete (for iPad)
48 | // FIXME: look for better solution
49 | dispatch_after(dispatch_time(DISPATCH_TIME_NOW, 1_000_000), dispatch_get_main_queue()) {
50 | if !splitVC.collapsed {
51 | mainVC.showDetailViewControllerAtIndex(0)
52 | }
53 | }
54 |
55 | return true
56 | }
57 |
58 | func splitViewController(splitViewController: UISplitViewController, collapseSecondaryViewController secondaryViewController:UIViewController, ontoPrimaryViewController primaryViewController:UIViewController) -> Bool
59 | {
60 | return true
61 | }
62 | }
63 |
64 |
--------------------------------------------------------------------------------
/ReactKitCatalog-iOS/Base.lproj/LaunchScreen.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
20 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
--------------------------------------------------------------------------------
/ReactKitCatalog-iOS/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
59 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
--------------------------------------------------------------------------------
/ReactKitCatalog-iOS/Catalog.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Catalog.swift
3 | // ReactKitCatalog
4 | //
5 | // Created by Yasuhiro Inami on 2014/10/06.
6 | // Copyright (c) 2014年 Yasuhiro Inami. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | struct Catalog
12 | {
13 | let title: String?
14 | let description: String?
15 | let class_: UIViewController.Type
16 | let hasXib: Bool
17 | let selected: Bool
18 |
19 | static func allCatalogs() -> [Catalog]
20 | {
21 | return [
22 | Catalog(title: "NSTimer", description: "pause()/resume()", class_: TimerViewController.self),
23 | Catalog(title: "UIButton/BarButton", description: "Basic", class_: ButtonViewController.self),
24 | Catalog(title: "UITextField", description: "throttle()/debounce()", class_: TextFieldViewController.self),
25 | Catalog(title: "UITextField (Multiple)", description: "Login example", class_: MultipleTextFieldViewController.self),
26 | Catalog(title: "UIGestureRecognizer", description: "merge2All()", class_: GestureViewController.self),
27 | Catalog(title: "Who To Follow", description: "Suggestion box", class_: WhoToFollowViewController.self),
28 | Catalog(title: "Calculator", description: "Mimics iOS Calculator.app", class_: CalculatorViewController.self),
29 | Catalog(title: "Array + Table", description: "DynamicArray + KVO.detailedStream()", class_: ArrayKVOViewController.self),
30 | Catalog(title: "Incremental Search", description: "throttle + distinct + switchLatestInner", class_: IncrementalSearchViewController.self, hasXib: false, selected: true)
31 | ]
32 | }
33 |
34 | init(title: String?, description: String?, class_: UIViewController.Type, hasXib: Bool = true, selected: Bool = false)
35 | {
36 | self.title = title
37 | self.description = description
38 | self.class_ = class_
39 | self.hasXib = hasXib
40 | self.selected = selected
41 | }
42 | }
--------------------------------------------------------------------------------
/ReactKitCatalog-iOS/Images.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 | "size" : "60x60",
25 | "idiom" : "iphone",
26 | "filename" : "logo-60@2x.png",
27 | "scale" : "2x"
28 | },
29 | {
30 | "size" : "60x60",
31 | "idiom" : "iphone",
32 | "filename" : "logo-60@3x.png",
33 | "scale" : "3x"
34 | },
35 | {
36 | "idiom" : "ipad",
37 | "size" : "29x29",
38 | "scale" : "1x"
39 | },
40 | {
41 | "idiom" : "ipad",
42 | "size" : "29x29",
43 | "scale" : "2x"
44 | },
45 | {
46 | "idiom" : "ipad",
47 | "size" : "40x40",
48 | "scale" : "1x"
49 | },
50 | {
51 | "idiom" : "ipad",
52 | "size" : "40x40",
53 | "scale" : "2x"
54 | },
55 | {
56 | "size" : "76x76",
57 | "idiom" : "ipad",
58 | "filename" : "logo-76.png",
59 | "scale" : "1x"
60 | },
61 | {
62 | "size" : "76x76",
63 | "idiom" : "ipad",
64 | "filename" : "logo-76@2x.png",
65 | "scale" : "2x"
66 | }
67 | ],
68 | "info" : {
69 | "version" : 1,
70 | "author" : "xcode"
71 | }
72 | }
--------------------------------------------------------------------------------
/ReactKitCatalog-iOS/Images.xcassets/AppIcon.appiconset/logo-60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ReactKit/ReactKitCatalog/f8bd8e8755f2004c9da5ce30787976977c5abc8e/ReactKitCatalog-iOS/Images.xcassets/AppIcon.appiconset/logo-60@2x.png
--------------------------------------------------------------------------------
/ReactKitCatalog-iOS/Images.xcassets/AppIcon.appiconset/logo-60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ReactKit/ReactKitCatalog/f8bd8e8755f2004c9da5ce30787976977c5abc8e/ReactKitCatalog-iOS/Images.xcassets/AppIcon.appiconset/logo-60@3x.png
--------------------------------------------------------------------------------
/ReactKitCatalog-iOS/Images.xcassets/AppIcon.appiconset/logo-76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ReactKit/ReactKitCatalog/f8bd8e8755f2004c9da5ce30787976977c5abc8e/ReactKitCatalog-iOS/Images.xcassets/AppIcon.appiconset/logo-76.png
--------------------------------------------------------------------------------
/ReactKitCatalog-iOS/Images.xcassets/AppIcon.appiconset/logo-76@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ReactKit/ReactKitCatalog/f8bd8e8755f2004c9da5ce30787976977c5abc8e/ReactKitCatalog-iOS/Images.xcassets/AppIcon.appiconset/logo-76@2x.png
--------------------------------------------------------------------------------
/ReactKitCatalog-iOS/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleInfoDictionaryVersion
12 | 6.0
13 | CFBundleName
14 | $(PRODUCT_NAME)
15 | CFBundlePackageType
16 | APPL
17 | CFBundleShortVersionString
18 | 1.0
19 | CFBundleSignature
20 | ????
21 | CFBundleVersion
22 | 1
23 | LSRequiresIPhoneOS
24 |
25 | UILaunchStoryboardName
26 | LaunchScreen
27 | UIMainStoryboardFile
28 | Main
29 | UIRequiredDeviceCapabilities
30 |
31 | armv7
32 |
33 | UIStatusBarTintParameters
34 |
35 | UINavigationBar
36 |
37 | Style
38 | UIBarStyleDefault
39 | Translucent
40 |
41 |
42 |
43 | UISupportedInterfaceOrientations
44 |
45 | UIInterfaceOrientationPortrait
46 | UIInterfaceOrientationLandscapeLeft
47 | UIInterfaceOrientationLandscapeRight
48 |
49 | UISupportedInterfaceOrientations~ipad
50 |
51 | UIInterfaceOrientationPortrait
52 | UIInterfaceOrientationPortraitUpsideDown
53 | UIInterfaceOrientationLandscapeLeft
54 | UIInterfaceOrientationLandscapeRight
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/ReactKitCatalog-iOS/MasterViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MasterViewController.swift
3 | // ReactKitCatalog-iOS
4 | //
5 | // Created by Yasuhiro Inami on 2014/10/06.
6 | // Copyright (c) 2014年 Yasuhiro Inami. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import Dollar
11 |
12 | class MasterViewController: UITableViewController
13 | {
14 | let catalogs = Catalog.allCatalogs()
15 |
16 | override func awakeFromNib()
17 | {
18 | super.awakeFromNib()
19 |
20 | if UIDevice.currentDevice().userInterfaceIdiom == .Pad {
21 | self.clearsSelectionOnViewWillAppear = false
22 | self.preferredContentSize = CGSize(width: 320.0, height: 600.0)
23 | }
24 | }
25 |
26 | override func viewDidLoad()
27 | {
28 | super.viewDidLoad()
29 |
30 | // auto-select
31 | if let index = ($.findIndex(self.catalogs) { $0.selected }) {
32 | self.showDetailViewControllerAtIndex(index)
33 | }
34 | }
35 |
36 | //--------------------------------------------------
37 | // MARK: - UITableViewDataSource
38 | //--------------------------------------------------
39 |
40 | override func numberOfSectionsInTableView(tableView: UITableView) -> Int
41 | {
42 | return 1
43 | }
44 |
45 | override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
46 | {
47 | return self.catalogs.count
48 | }
49 |
50 | override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
51 | {
52 | let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath)
53 |
54 | let catalog = self.catalogs[indexPath.row]
55 | cell.textLabel?.text = catalog.title
56 | cell.detailTextLabel?.text = catalog.description
57 |
58 | return cell
59 | }
60 |
61 | // MARK: UITableViewDelegate
62 |
63 | override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath)
64 | {
65 | self.showDetailViewControllerAtIndex(indexPath.row)
66 | }
67 |
68 | func showDetailViewControllerAtIndex(index: Int)
69 | {
70 | let catalog = self.catalogs[index]
71 |
72 | //
73 | // change Swift's className to Obj-C nibName
74 | //
75 | // e.g.
76 | // NSStringFromClass(catalog.class_)
77 | // = "ReactKitCatalog_iOS.TextFieldViewController"
78 | //
79 | let className = NSStringFromClass(catalog.class_).componentsSeparatedByString(".").last
80 |
81 | if let className = className {
82 | let newVC: UIViewController
83 | if catalog.hasXib {
84 | newVC = catalog.class_.init(nibName: className, bundle: nil)
85 | }
86 | else {
87 | newVC = catalog.class_.init()
88 | }
89 |
90 | // let newVC = catalog.class_(nibName: className, bundle: nil)
91 | let newNavC = UINavigationController(rootViewController: newVC)
92 | self.splitViewController?.showDetailViewController(newNavC, sender: self)
93 |
94 | newVC.navigationItem.leftBarButtonItem = self.splitViewController?.displayModeButtonItem()
95 | newVC.navigationItem.leftItemsSupplementBackButton = true
96 |
97 | }
98 | }
99 |
100 | }
101 |
102 |
--------------------------------------------------------------------------------
/ReactKitCatalog-iOS/Samples/ArrayKVOViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ArrayKVOViewController.swift
3 | // ReactKitCatalog
4 | //
5 | // Created by Yasuhiro Inami on 2015/03/21.
6 | // Copyright (c) 2015年 Yasuhiro Inami. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import ReactKit
11 | import Dollar
12 | import Async
13 |
14 | let CELL_IDENTIFIER = "Cell"
15 |
16 | class ArrayKVOViewController: UITableViewController
17 | {
18 | typealias SectionData = ArrayKVOViewModel.SectionData
19 | typealias RowData = ArrayKVOViewModel.RowData
20 |
21 | let viewModel: ArrayKVOViewModel
22 |
23 | let insertButtonItem = UIBarButtonItem(title: "Insert", style: .Plain, target: nil, action: nil)
24 | let replaceButtonItem = UIBarButtonItem(title: "Replace", style: .Plain, target: nil, action: nil)
25 | let removeButtonItem = UIBarButtonItem(title: "Remove", style: .Plain, target: nil, action: nil)
26 | let decrementButtonItem = UIBarButtonItem(title: "[-]", style: .Plain, target: nil, action: nil)
27 | let toggleButtonItem = UIBarButtonItem(title: nil, style: .Plain, target: nil, action: nil)
28 | let incrementButtonItem = UIBarButtonItem(title: "[+]", style: .Plain, target: nil, action: nil)
29 | let flexibleButtonItem = UIBarButtonItem(barButtonSystemItem: .FlexibleSpace, target: nil, action: nil)
30 |
31 | override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: NSBundle?)
32 | {
33 | self.viewModel = ArrayKVOViewModel()
34 |
35 | super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil)
36 | }
37 |
38 | required init?(coder aDecoder: NSCoder)
39 | {
40 | self.viewModel = ArrayKVOViewModel()
41 |
42 | super.init(coder: aDecoder)
43 | }
44 |
45 | deinit
46 | {
47 | // print("[deinit] \(self)")
48 | }
49 |
50 | override func viewDidLoad()
51 | {
52 | super.viewDidLoad()
53 |
54 | self._setupViews()
55 | self._setupStreams()
56 | self._performDemo()
57 | }
58 |
59 | func _setupViews()
60 | {
61 | self.tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: CELL_IDENTIFIER)
62 | self.tableView.editing = true
63 |
64 | self.toolbarItems = [
65 | self.flexibleButtonItem,
66 | self.insertButtonItem, self.replaceButtonItem, self.removeButtonItem,
67 | self.flexibleButtonItem,
68 | self.decrementButtonItem, self.toggleButtonItem, self.incrementButtonItem,
69 | self.flexibleButtonItem
70 | ]
71 | self.navigationController?.setToolbarHidden(false, animated: false)
72 | }
73 |
74 | func _setupStreams()
75 | {
76 | // REACT: insert button
77 | let insertButtonStream = self.insertButtonItem.stream().ownedBy(self)
78 | insertButtonStream ~> { [unowned self] _ in
79 | print("")
80 | print("[insert button]")
81 |
82 | self.viewModel.insertRandomSectionsOrRows()
83 | }
84 |
85 | // REACT: replace button
86 | let replaceButtonStream = self.replaceButtonItem.stream().ownedBy(self)
87 | replaceButtonStream ~> { [unowned self] _ in
88 | print("")
89 | print("[replace button]")
90 |
91 | self.viewModel.replaceRandomSectionsOrRows()
92 | }
93 |
94 | // REACT: remove button
95 | let removeButtonStream = self.removeButtonItem.stream().ownedBy(self)
96 | removeButtonStream ~> { [unowned self] _ in
97 | print("")
98 | print("[remove button]")
99 |
100 | self.viewModel.removeRandomSectionsOrRows()
101 | }
102 |
103 | // REACT: decrement button
104 | let decrementButtonStream = self.decrementButtonItem.stream().ownedBy(self)
105 | decrementButtonStream ~> { [unowned self] _ in
106 | print("")
107 | print("[decrement button]")
108 |
109 | self.viewModel.changeMaxCount = max(self.viewModel.changeMaxCount-1, 1)
110 | }
111 |
112 | // REACT: increment button
113 | let incrementButtonStream = self.incrementButtonItem.stream().ownedBy(self)
114 | incrementButtonStream ~> { [unowned self] _ in
115 | print("")
116 | print("[increment button]")
117 |
118 | self.viewModel.changeMaxCount = min(self.viewModel.changeMaxCount+1, Int.max)
119 | }
120 |
121 | // REACT: section/row toggle button
122 | let toggleButtonStream = self.toggleButtonItem.stream().ownedBy(self)
123 | toggleButtonStream ~> { [unowned self] _ in
124 | print("")
125 | print("[toggle button]")
126 |
127 | self.viewModel.tableLocation.toggle()
128 | }
129 |
130 | // REACT: changeMaxCount label
131 | let changeMaxCountStream: Stream = [
132 | KVO.startingStream(self.viewModel, "changeMaxCount"),
133 | KVO.startingStream(self.viewModel, "tableLocationString")
134 | ]
135 | |> combineLatestAll
136 | |> map { values -> AnyObject? in "\(values[0]!) \(values[1]!)" } // e.g. "1 Section"
137 |
138 | changeMaxCountStream.ownedBy(self)
139 | (self.toggleButtonItem, "title") <~ changeMaxCountStream
140 |
141 | // REACT: sections changed
142 | let sectionDatasChangedStream = self.viewModel.sectionDatas.stream().ownedBy(self.viewModel)
143 | sectionDatasChangedStream ~> { [unowned self] sectionDatas, sectionChange, sectionIndexSet in
144 |
145 | print("")
146 | print("[sectionDatas changed]")
147 | print("sectionChange = \(sectionChange)")
148 | print("sectionDatas = \(sectionDatas ?? [])")
149 | print("sectionIndexSet = \(sectionIndexSet)")
150 |
151 | if sectionChange == .Insertion || sectionChange == .Replacement {
152 |
153 | sectionIndexSet.enumerateIndexesUsingBlock { sectionIndex, stop in
154 |
155 | let sectionData = self.viewModel.sectionDatas.proxy[sectionIndex] as! SectionData
156 |
157 | // REACT: rows changed
158 | let rowDatasChangedStream = sectionData.rowDatas.stream().ownedBy(sectionData)
159 | rowDatasChangedStream ~> { [weak sectionData] rowDatas, rowChange, rowIndexSet in
160 |
161 | let sectionData: SectionData! = sectionData // strongify
162 | if sectionData == nil { return }
163 |
164 | print("")
165 | print("[rowDatas changed]")
166 | print("rowChange = \(rowChange)")
167 | print("rowDatas = \(rowDatas ?? [])")
168 | print("rowIndexSet = \(rowIndexSet)")
169 |
170 | // NOTE: sectionIndex needs to be re-evaluated on rows changed
171 | let sectionIndex = self.viewModel.sectionDatas.proxy.indexOfObject(sectionData)
172 |
173 | var indexPaths: [NSIndexPath] = []
174 | rowIndexSet.enumerateIndexesUsingBlock { rowIndex, stop in
175 | indexPaths.append(NSIndexPath(forRow: rowIndex, inSection: sectionIndex))
176 | }
177 |
178 | self.tableView.beginUpdates()
179 |
180 | switch rowChange {
181 | case .Insertion:
182 | self.tableView.insertRowsAtIndexPaths(indexPaths, withRowAnimation: .Right)
183 | case .Replacement:
184 | self.tableView.reloadRowsAtIndexPaths(indexPaths, withRowAnimation: .Right)
185 | case .Removal:
186 | self.tableView.deleteRowsAtIndexPaths(indexPaths, withRowAnimation: .Left)
187 | default:
188 | break
189 | }
190 |
191 | self.tableView.endUpdates()
192 | }
193 | }
194 | }
195 |
196 | self.tableView.beginUpdates()
197 |
198 | switch sectionChange {
199 | case .Insertion:
200 | self.tableView.insertSections(sectionIndexSet, withRowAnimation: .Right)
201 | case .Replacement:
202 | self.tableView.reloadSections(sectionIndexSet, withRowAnimation: .Right)
203 | case .Removal:
204 | self.tableView.deleteSections(sectionIndexSet, withRowAnimation: .Left)
205 | default:
206 | break
207 | }
208 |
209 | self.tableView.endUpdates()
210 | }
211 |
212 | }
213 |
214 | func _performDemo()
215 | {
216 | self.tableView.userInteractionEnabled = false
217 |
218 | let step = 0.5 // sec
219 |
220 | Async.main(after: 0.2 + step * 0) {
221 | print("*** addObject (section & row) ***")
222 |
223 | self.viewModel.sectionDatas.proxy.addObject(SectionData(title: "Section 1", rowDataArray: [
224 | RowData(title: "title 1-0"),
225 | RowData(title: "title 1-1")
226 | ]))
227 | }
228 |
229 | Async.main(after: 0.2 + step * 1) {
230 | print("*** insertObject ***")
231 |
232 | self.viewModel.sectionDatas.proxy.insertObject(SectionData(title: "Section 0", rowDataArray: [
233 | RowData(title: "title 0-0")
234 | ]), atIndex: 0)
235 | }
236 |
237 | Async.main(after: 0.2 + step * 2) {
238 | print("*** replaceObjectAtIndex ***")
239 |
240 | self.viewModel.sectionDatas.proxy.replaceObjectAtIndex(0, withObject: SectionData(title: "Section 0b", rowDataArray: [
241 | RowData(title: "title 0-1b")
242 | ]))
243 | }
244 |
245 | Async.main(after: 0.2 + step * 3) {
246 | print("*** removeObjectAtIndex ***")
247 |
248 | self.viewModel.sectionDatas.proxy.removeObjectAtIndex(0)
249 | }
250 |
251 | Async.main(after: 0.2 + step * 4) {
252 | print("*** addObject (row) ***")
253 |
254 | let sectionData = self.viewModel.sectionDatas.proxy[0] as! SectionData
255 | sectionData.rowDatas.proxy.addObject(RowData(title: "title 1-2"))
256 |
257 | self.tableView.userInteractionEnabled = true
258 | }
259 | }
260 |
261 | // MARK: - Table view data source
262 |
263 | override func numberOfSectionsInTableView(tableView: UITableView) -> Int
264 | {
265 | return self.viewModel.sectionDatas.proxy.count
266 | }
267 |
268 | override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
269 | {
270 | let sectionData = self.viewModel.sectionDatas.proxy[section] as! SectionData
271 | return sectionData.rowDatas.proxy.count
272 | }
273 |
274 | override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
275 | {
276 | let cell = tableView.dequeueReusableCellWithIdentifier(CELL_IDENTIFIER, forIndexPath: indexPath)
277 |
278 | cell.textLabel?.text = self.viewModel.sectionDatas.proxy[indexPath.section][indexPath.row].title
279 |
280 | return cell
281 | }
282 |
283 | override func tableView(tableView: UITableView, canEditRowAtIndexPath indexPath: NSIndexPath) -> Bool
284 | {
285 | // Return NO if you do not want the specified item to be editable.
286 | return true
287 | }
288 |
289 | override func tableView(tableView: UITableView, commitEditingStyle editingStyle: UITableViewCellEditingStyle, forRowAtIndexPath indexPath: NSIndexPath)
290 | {
291 | if editingStyle == .Delete {
292 |
293 | let sectionData = self.viewModel.sectionDatas.proxy[indexPath.section] as! SectionData
294 | sectionData.rowDatas.proxy.removeObjectAtIndex(indexPath.row)
295 |
296 | } else if editingStyle == .Insert {
297 |
298 | }
299 | }
300 |
301 | /*
302 | // Override to support rearranging the table view.
303 | override func tableView(tableView: UITableView, moveRowAtIndexPath fromIndexPath: NSIndexPath, toIndexPath: NSIndexPath) {
304 |
305 | }
306 | */
307 |
308 | /*
309 | // Override to support conditional rearranging of the table view.
310 | override func tableView(tableView: UITableView, canMoveRowAtIndexPath indexPath: NSIndexPath) -> Bool {
311 | // Return NO if you do not want the item to be re-orderable.
312 | return true
313 | }
314 | */
315 |
316 | override func tableView(tableView: UITableView, titleForHeaderInSection section: Int) -> String?
317 | {
318 | return self.viewModel.sectionDatas.proxy[section].title
319 | }
320 |
321 | // MARK: UITableViewDelegate
322 |
323 | override func tableView(tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat
324 | {
325 | return 30
326 | }
327 |
328 | }
329 |
--------------------------------------------------------------------------------
/ReactKitCatalog-iOS/Samples/ArrayKVOViewController.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 |
--------------------------------------------------------------------------------
/ReactKitCatalog-iOS/Samples/ArrayKVOViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ArrayKVOViewModel.swift
3 | // ReactKitCatalog
4 | //
5 | // Created by Yasuhiro Inami on 2015/03/21.
6 | // Copyright (c) 2015年 Yasuhiro Inami. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import ReactKit
11 | import Dollar
12 |
13 | let defaultTableLocation = ArrayKVOViewModel.TableLocation.Section
14 |
15 | class ArrayKVOViewModel: NSObject
16 | {
17 | var sectionDatas = DynamicArray/**/()
18 |
19 | /// used as insert/replace/remove maxCount
20 | dynamic var changeMaxCount = 1
21 |
22 | // workaround for KVO-streaming Swift enum
23 | private(set) dynamic var tableLocationString = defaultTableLocation.rawValue
24 |
25 | // NOTE: `dynamic var` is not available
26 | var tableLocation: TableLocation = defaultTableLocation
27 | {
28 | didSet(oldValue) {
29 | self.tableLocationString = self.tableLocation.rawValue
30 | }
31 | }
32 | }
33 |
34 | /// helper methods
35 | extension ArrayKVOViewModel
36 | {
37 | func insertRandomSectionsOrRows()
38 | {
39 | switch self.tableLocation {
40 | case .Section:
41 | self._insertRandomSections()
42 |
43 | case .Row:
44 | self._insertRandomRows()
45 | }
46 | }
47 |
48 | private func _insertRandomSections()
49 | {
50 | precondition(self.changeMaxCount > 0)
51 |
52 | let sectionCount = self.sectionDatas.proxy.count
53 |
54 | let indexes = _pickRandom(0...sectionCount, self.changeMaxCount)
55 | let indexSet = NSMutableIndexSet(indexes: indexes)
56 |
57 | let sectionDatas = [Int](count: indexSet.count, repeatedValue: 0)
58 | .map { _ in SectionData.randomData() }
59 |
60 | // insert sections
61 | self.sectionDatas.proxy.insertObjects(sectionDatas, atIndexes: indexSet)
62 | }
63 |
64 | private func _insertRandomRows()
65 | {
66 | precondition(self.changeMaxCount > 0)
67 |
68 | let sectionCount = self.sectionDatas.proxy.count
69 |
70 | // insert section if needed
71 | if sectionCount == 0 {
72 | self.sectionDatas.proxy.addObject(SectionData.emptyData())
73 | }
74 |
75 | let section = $.random(sectionCount)
76 | let sectionData = self.sectionDatas.proxy[section] as! SectionData
77 | let rowCount = sectionData.rowDatas.proxy.count
78 |
79 | let indexes = _pickRandom(0...rowCount, self.changeMaxCount)
80 | let indexSet = NSMutableIndexSet(indexes: indexes)
81 |
82 | let rowDatas = [Int](count: indexSet.count, repeatedValue: 0).map { _ in RowData.randomData() }
83 |
84 | // insert rows
85 | sectionData.rowDatas.proxy.insertObjects(rowDatas, atIndexes: indexSet)
86 | }
87 |
88 | func replaceRandomSectionsOrRows()
89 | {
90 | switch self.tableLocation {
91 | case .Section:
92 | self._replaceRandomSections()
93 |
94 | case .Row:
95 | self._replaceRandomRows()
96 | }
97 | }
98 |
99 | private func _replaceRandomSections()
100 | {
101 | precondition(self.changeMaxCount > 0)
102 |
103 | let sectionCount = self.sectionDatas.proxy.count
104 |
105 | let indexes = _pickRandom(0.. 0)
117 |
118 | let sectionCount = self.sectionDatas.proxy.count
119 |
120 | let section = $.random(sectionCount)
121 | let sectionData = self.sectionDatas.proxy[section] as! SectionData
122 | let rowCount = sectionData.rowDatas.proxy.count
123 |
124 | let indexes = _pickRandom(0.. 0)
147 |
148 | let sectionCount = self.sectionDatas.proxy.count
149 |
150 | let indexes = _pickRandom(0.. 0)
160 |
161 | let sectionCount = self.sectionDatas.proxy.count
162 |
163 | let section = $.random(sectionCount)
164 | let sectionData = self.sectionDatas.proxy[section] as! SectionData
165 | let rowCount = sectionData.rowDatas.proxy.count
166 |
167 | let indexes = _pickRandom(0..*/
183 |
184 | init(title: String, rowDatas: DynamicArray/**/)
185 | {
186 | self.title = title
187 | self.rowDatas = rowDatas
188 | }
189 |
190 | convenience init(title: String, rowDataArray: [RowData])
191 | {
192 | self.init(title: title, rowDatas: DynamicArray/**/(rowDataArray))
193 | }
194 |
195 | subscript(i: Int) -> RowData
196 | {
197 | return self.rowDatas.proxy[i] as! RowData
198 | }
199 |
200 | /// return 1 sectionData with random (0..<3) rowDatas
201 | class func randomData() -> SectionData
202 | {
203 | let dateString = _dateString(NSDate())
204 |
205 | let rowDatas = Array(0..<$.random(3))
206 | .map { i -> RowData in
207 | return RowData(title: "\(dateString)-\(i)")
208 | }
209 |
210 | return SectionData(title: "\(dateString)", rowDataArray: rowDatas)
211 | }
212 |
213 | /// return 1 sectionData with 0 rowDatas
214 | class func emptyData() -> SectionData
215 | {
216 | let dateString = _dateString(NSDate())
217 |
218 | return SectionData(title: "\(dateString)", rowDataArray: [])
219 | }
220 | }
221 |
222 | // inner class
223 | class RowData: NSObject
224 | {
225 | let title: String
226 |
227 | init(title: String)
228 | {
229 | self.title = title
230 | }
231 |
232 | class func randomData() -> RowData
233 | {
234 | let dateString = _dateString(NSDate())
235 |
236 | return RowData(title: "\(dateString)")
237 | }
238 | }
239 |
240 | // inner enum
241 | enum TableLocation: String, CustomStringConvertible
242 | {
243 | case Section = "Section"
244 | case Row = "Row"
245 |
246 | var description: String
247 | {
248 | return self.rawValue
249 | }
250 |
251 | mutating func toggle()
252 | {
253 | switch self {
254 | case .Section: self = .Row
255 | case .Row: self = .Section
256 | }
257 | }
258 | }
259 | }
--------------------------------------------------------------------------------
/ReactKitCatalog-iOS/Samples/ButtonViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ButtonViewController.swift
3 | // ReactKitCatalog
4 | //
5 | // Created by Yasuhiro Inami on 2014/10/06.
6 | // Copyright (c) 2014年 Yasuhiro Inami. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import ReactKit
11 |
12 | class ButtonViewController: UIViewController
13 | {
14 | @IBOutlet var label: UILabel!
15 | @IBOutlet var button: UIButton!
16 | @IBOutlet var barButtonItem: UIBarButtonItem!
17 |
18 | var buttonStream: Stream?
19 | var barButtonStream: Stream?
20 |
21 | override func viewDidLoad()
22 | {
23 | super.viewDidLoad()
24 |
25 | self._setupButton()
26 | self._setupBarButtonItem()
27 | }
28 |
29 | func _setupButton()
30 | {
31 | // self.buttonStream = self.button?.buttonStream("OK")
32 | self.buttonStream = self.button?.buttonStream { _ in "Button \(arc4random_uniform(UInt32.max))" }
33 |
34 | // REACT: button ~> label
35 | (self.label, "text") <~ self.buttonStream!
36 |
37 | // REACT: button ~> print
38 | ^{ print($0!) } <~ self.buttonStream!
39 | }
40 |
41 | func _setupBarButtonItem()
42 | {
43 | // self.barButtonStream = self.barButtonItem?.stream("OK")
44 | self.barButtonStream = self.barButtonItem?.stream { _ in "BarButton \(arc4random_uniform(UInt32.max))" }
45 |
46 | // REACT: button ~> label
47 | (self.label, "text") <~ self.barButtonStream!
48 |
49 | // REACT: button ~> print
50 | ^{ print($0!) } <~ self.barButtonStream!
51 | }
52 | }
--------------------------------------------------------------------------------
/ReactKitCatalog-iOS/Samples/ButtonViewController.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
26 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/ReactKitCatalog-iOS/Samples/GestureViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GestureViewController.swift
3 | // ReactKitCatalog
4 | //
5 | // Created by Yasuhiro Inami on 2014/10/06.
6 | // Copyright (c) 2014年 Yasuhiro Inami. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import ReactKit
11 |
12 | class GestureViewController: UIViewController, UIGestureRecognizerDelegate
13 | {
14 | @IBOutlet var gestures: [UIGestureRecognizer]!
15 | @IBOutlet var label: UILabel!
16 |
17 | var streams: [Stream] = []
18 |
19 | override func viewDidLoad()
20 | {
21 | super.viewDidLoad()
22 |
23 | for gesture in self.gestures {
24 |
25 | gesture.delegate = self
26 | let gestureClassName = NSStringFromClass(gesture.dynamicType)
27 |
28 | let stream: Stream = gesture.stream { gesture -> String? in
29 | // e.g. UITapGestureRecognizer state=3 (161.0,325.0)
30 | return "\(gestureClassName) state=\(gesture!.state.rawValue) \(gesture!.locationInView(gesture?.view))"
31 | }
32 |
33 | // REACT: gesture ~> print
34 | ^{ print($0!) } <~ stream
35 |
36 | self.streams += [stream]
37 |
38 | }
39 |
40 | // combinedStream (concatenating above stream-strings)
41 | let combinedStream = self.streams
42 | |> merge2All
43 | |> map { (values: [String??], changedValue: String?) -> String? in
44 |
45 | return values
46 | .map { ($0 ?? "")!! }
47 | .filter { !$0.isEmpty }
48 | .joinWithSeparator("\n")
49 | }
50 |
51 | // REACT
52 | (self.label, "text") <~ combinedStream
53 |
54 | self.streams += [combinedStream]
55 |
56 | }
57 |
58 | func gestureRecognizer(gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer otherGestureRecognizer: UIGestureRecognizer) -> Bool
59 | {
60 | return true
61 | }
62 | }
--------------------------------------------------------------------------------
/ReactKitCatalog-iOS/Samples/GestureViewController.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 |
31 |
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 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/ReactKitCatalog-iOS/Samples/IncrementalSearchViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // IncrementalSearchViewController.swift
3 | // ReactKitCatalog
4 | //
5 | // Created by Yasuhiro Inami on 2015/06/01.
6 | // Copyright (c) 2015年 Yasuhiro Inami. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import ReactKit
11 | import Alamofire
12 | import SwiftyJSON
13 |
14 | private func _searchUrl(query: String) -> String
15 | {
16 | let escapedQuery = query.stringByAddingPercentEncodingWithAllowedCharacters(.URLHostAllowedCharacterSet()) ?? ""
17 | return "https://api.bing.com/osjson.aspx?query=\(escapedQuery)"
18 | }
19 |
20 | private let _reuseIdentifier = "reuseIdentifier"
21 |
22 | class IncrementalSearchViewController: UITableViewController, UISearchBarDelegate
23 | {
24 | var searchController: UISearchController?
25 | var searchResultStream: Stream?
26 | var searchResult: [String]?
27 |
28 | dynamic var searchText: String = ""
29 |
30 | override func viewDidLoad()
31 | {
32 | super.viewDidLoad()
33 |
34 | let searchController = UISearchController(searchResultsController: nil)
35 | searchController.dimsBackgroundDuringPresentation = false
36 | searchController.searchBar.delegate = self
37 |
38 | self.tableView.registerClass(UITableViewCell.self, forCellReuseIdentifier: _reuseIdentifier)
39 | self.tableView.tableHeaderView = searchController.searchBar
40 |
41 | // http://useyourloaf.com/blog/2015/04/26/search-bar-not-showing-without-a-scope-bar.html
42 | searchController.searchBar.sizeToFit()
43 |
44 | self.searchController = searchController
45 |
46 | self.searchResultStream = KVO.stream(self, "searchText")
47 | // |> peek(print)
48 | |> debounce(0.15)
49 | |> map { ($0 as? String) ?? "" } // map to Equatable String for `distinctUntilChanged()`
50 | |> distinctUntilChanged
51 | |> map { query -> Stream in
52 | let request = Alamofire.request(.GET, _searchUrl(query), parameters: nil, encoding: .URL)
53 | return Stream.fromTask(_requestTask(request))
54 | }
55 | |> switchLatestInner
56 |
57 | // REACT
58 | self.searchResultStream! ~> { print($0) }
59 |
60 | // REACT
61 | self.searchResultStream! ~> { [weak self] json in
62 | self?.searchResult = json[1].arrayValue.map { $0.stringValue }
63 | self?.tableView.reloadData()
64 | }
65 | }
66 |
67 | // MARK: - UISearchBarDelegate
68 |
69 | func searchBar(searchBar: UISearchBar, textDidChange searchText: String)
70 | {
71 | self.searchText = searchText
72 | }
73 |
74 | // MARK: - UITableViewDataSource
75 |
76 | override func numberOfSectionsInTableView(tableView: UITableView) -> Int
77 | {
78 | return 1
79 | }
80 |
81 | override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
82 | {
83 | return self.searchResult?.count ?? 0
84 | }
85 |
86 | override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell
87 | {
88 | let cell = tableView.dequeueReusableCellWithIdentifier(_reuseIdentifier, forIndexPath: indexPath)
89 |
90 | cell.textLabel?.text = self.searchResult?[indexPath.row]
91 |
92 | return cell
93 | }
94 |
95 | }
96 |
--------------------------------------------------------------------------------
/ReactKitCatalog-iOS/Samples/MultipleTextFieldViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MultipleTextFieldViewController.swift
3 | // ReactKitCatalog
4 | //
5 | // Created by Yasuhiro Inami on 2014/10/06.
6 | // Copyright (c) 2014年 Yasuhiro Inami. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import ReactKit
11 |
12 | private let MIN_PASSWORD_LENGTH = 4
13 |
14 | ///
15 | /// Original demo:
16 | /// iOS - ReactiveCocoaをかじってみた - Qiita
17 | /// http://qiita.com/paming/items/9ac189ab0fe5b25fe722
18 | ///
19 | class MultipleTextFieldViewController: UIViewController
20 | {
21 | @IBOutlet var usernameTextField: UITextField!
22 | @IBOutlet var emailTextField: UITextField!
23 | @IBOutlet var passwordTextField: UITextField!
24 | @IBOutlet var password2TextField: UITextField!
25 |
26 | @IBOutlet var messageLabel: UILabel!
27 | @IBOutlet var okButton: UIButton!
28 |
29 | var buttonEnablingStream: Stream?
30 | var buttonEnablingStream2: Stream<[AnyObject?]>?
31 | var errorMessagingStream: Stream?
32 | var buttonTappedStream: Stream?
33 |
34 | override func viewDidLoad()
35 | {
36 | super.viewDidLoad()
37 |
38 | self._setupViews()
39 | self._setupStreams()
40 | }
41 |
42 | func _setupViews()
43 | {
44 | self.messageLabel.text = ""
45 | self.okButton.enabled = false
46 | }
47 |
48 | func _setupStreams()
49 | {
50 | //--------------------------------------------------
51 | // Create Streams
52 | //--------------------------------------------------
53 |
54 | let usernameTextStream = self.usernameTextField.textChangedStream()
55 | let emailTextStream = self.emailTextField.textChangedStream()
56 | let passwordTextStream = self.passwordTextField.textChangedStream()
57 | let password2TextStream = self.password2TextField.textChangedStream()
58 |
59 | let combinedTextStream = [usernameTextStream, emailTextStream, passwordTextStream, password2TextStream]
60 | |> merge2All
61 |
62 | // create button-enabling stream via any textField change
63 | self.buttonEnablingStream = combinedTextStream
64 | |> map { (values, changedValue) -> NSNumber? in
65 |
66 | let username = (values[0] ?? nil) ?? ""
67 | let email = (values[1] ?? nil) ?? ""
68 | let password = (values[2] ?? nil) ?? ""
69 | let password2 = (values[3] ?? nil) ?? ""
70 |
71 | print("username=\(username), email=\(email), password=\(password), password2=\(password2)")
72 |
73 | // validation
74 | let buttonEnabled = username.characters.count > 0 && email.characters.count > 0 && password.characters.count >= MIN_PASSWORD_LENGTH && password == password2
75 |
76 | print("buttonEnabled = \(buttonEnabled)")
77 |
78 | return NSNumber(bool: buttonEnabled) // NOTE: use NSNumber because KVO does not understand Bool
79 | }
80 |
81 | // create error-messaging stream via any textField change
82 | self.errorMessagingStream = combinedTextStream
83 | |> map { (values, changedValue) -> String? in
84 |
85 | let username = (values[0] ?? nil) ?? ""
86 | let email = (values[1] ?? nil) ?? ""
87 | let password = (values[2] ?? nil) ?? ""
88 | let password2 = (values[3] ?? nil) ?? ""
89 |
90 | if username.characters.count <= 0 {
91 | return "Username is not set."
92 | }
93 | else if email.characters.count <= 0 {
94 | return "Email is not set."
95 | }
96 | else if password.characters.count < MIN_PASSWORD_LENGTH {
97 | return "Password requires at least \(MIN_PASSWORD_LENGTH) characters."
98 | }
99 | else if password != password2 {
100 | return "Password is not same."
101 | }
102 |
103 | return nil
104 | }
105 |
106 | // create button-tapped stream via okButton
107 | self.buttonTappedStream = self.okButton.buttonStream("OK")
108 |
109 | //--------------------------------------------------
110 | // Stream callbacks on finished
111 | //--------------------------------------------------
112 |
113 | self.buttonEnablingStream?.then { value, errorInfo -> Void in
114 | print("buttonEnablingStream finished")
115 | }
116 | self.errorMessagingStream?.then { value, errorInfo -> Void in
117 | print("errorMessagingStream finished")
118 | }
119 | self.buttonTappedStream?.then { value, errorInfo -> Void in
120 | print("buttonTappedStream finished")
121 | }
122 |
123 | //--------------------------------------------------
124 | // Bind & React to Streams
125 | //--------------------------------------------------
126 |
127 | // REACT: enable/disable okButton
128 | (self.okButton, "enabled") <~ self.buttonEnablingStream!
129 |
130 | // REACT: update error-message
131 | (self.messageLabel, "text") <~ self.errorMessagingStream!
132 |
133 | // REACT: button tap
134 | self.buttonTappedStream! ~> { [weak self] (value: String?) -> Void in
135 | if let self_ = self {
136 | if value == "OK" {
137 | // release all streams when receiving "OK" stream
138 | self_.buttonEnablingStream = nil
139 | self_.errorMessagingStream = nil
140 | self_.buttonTappedStream = nil
141 | }
142 | }
143 | }
144 | }
145 | }
146 |
--------------------------------------------------------------------------------
/ReactKitCatalog-iOS/Samples/MultipleTextFieldViewController.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 |
62 |
71 |
77 |
86 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
--------------------------------------------------------------------------------
/ReactKitCatalog-iOS/Samples/TextFieldViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TextFieldViewController.swift
3 | // ReactKitCatalog
4 | //
5 | // Created by Yasuhiro Inami on 2014/10/06.
6 | // Copyright (c) 2014年 Yasuhiro Inami. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import ReactKit
11 |
12 | class TextFieldViewController: UIViewController
13 | {
14 | @IBOutlet var label: UILabel!
15 | @IBOutlet var throttleLabel: UILabel!
16 | @IBOutlet var debounceLabel: UILabel!
17 | @IBOutlet var textField: UITextField!
18 |
19 | var stream: Stream?
20 | var throttleStream: Stream?
21 | var debounceStream: Stream?
22 |
23 | override func viewDidLoad()
24 | {
25 | super.viewDidLoad()
26 |
27 | self.stream = self.textField?.textChangedStream()
28 | self.throttleStream = self.stream!
29 | |> throttle(1)
30 | |> map { text -> String? in "\(text!) (throttled)" }
31 | self.debounceStream = self.stream!
32 | |> debounce(1)
33 | |> map { text -> String? in "\(text!) (debounced)" }
34 |
35 | // REACT: textField ~> label
36 | (self.label, "text") <~ self.stream!
37 |
38 | // REACT: textField ~> throttleLabel
39 | (self.throttleLabel, "text") <~ self.throttleStream!
40 |
41 | // REACT: textField ~> debounceLabel
42 | (self.debounceLabel, "text") <~ self.debounceStream!
43 |
44 | // // REACT: textField ~> print
45 | // ^{ print($0!) } <~ self.stream!
46 | }
47 | }
--------------------------------------------------------------------------------
/ReactKitCatalog-iOS/Samples/TextFieldViewController.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 |
35 |
41 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/ReactKitCatalog-iOS/Samples/TimerViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TimerViewController.swift
3 | // ReactKitCatalog
4 | //
5 | // Created by Yasuhiro Inami on 2014/10/06.
6 | // Copyright (c) 2014年 Yasuhiro Inami. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import ReactKit
11 |
12 | class TimerViewController: UIViewController
13 | {
14 | @IBOutlet var label: UILabel!
15 | @IBOutlet var pauseResumeButton: UIButton!
16 | @IBOutlet var cancelButton: UIButton!
17 |
18 | var stream: Stream?
19 |
20 | override func viewDidLoad()
21 | {
22 | super.viewDidLoad()
23 |
24 | // NOTE: use class method (no need to create NSTimer-instance)
25 | self.stream = NSTimer.stream(timeInterval: 1) { (sender: NSTimer?) -> String? in
26 | return "\(NSDate())"
27 | }
28 |
29 | // REACT: button ~> label
30 | (self.label, "text") <~ self.stream!
31 |
32 | // REACT: button ~> print
33 | ^{ print($0!) } <~ self.stream!
34 | }
35 |
36 | override func viewDidDisappear(animated: Bool)
37 | {
38 | super.viewDidDisappear(animated)
39 |
40 | switch self.stream!.state {
41 | case .Cancelled:
42 | break
43 | default:
44 | print("")
45 | print("NOTE: TimerViewController is not deinited yet (due to iOS8-UISplitViewController's behavior) so timer-stream is still alive.")
46 | print("")
47 | }
48 | }
49 |
50 | // use IBAction instead of ReactKit.Stream for this tutorial
51 | @IBAction func handlePauseResumeButton(sender: AnyObject)
52 | {
53 | let button = sender as! UIButton
54 |
55 | switch self.stream!.state {
56 | case .Paused:
57 | self.stream?.resume()
58 | button.setTitle("Pause", forState: .Normal)
59 | case .Running:
60 | self.stream?.pause()
61 | button.setTitle("Resume", forState: .Normal)
62 | default:
63 | print("Do nothing (timer-stream is already cancelled)")
64 | break
65 | }
66 |
67 | }
68 |
69 | @IBAction func handleCancelButton(sender: AnyObject)
70 | {
71 | self.stream?.cancel()
72 | }
73 | }
--------------------------------------------------------------------------------
/ReactKitCatalog-iOS/Samples/TimerViewController.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
26 |
40 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/ReactKitCatalog-iOS/Samples/WhoToFollowViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WhoToFollowViewController.swift
3 | // ReactKitCatalog
4 | //
5 | // Created by Yasuhiro Inami on 2015/01/05.
6 | // Copyright (c) 2015年 Yasuhiro Inami. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import ReactKit
11 | import SwiftTask
12 |
13 | import Alamofire
14 | import SwiftyJSON
15 | import Haneke
16 |
17 | ///
18 | /// Original demo:
19 | ///
20 | /// - The introduction to Reactive Programming you've been missing"
21 | /// https://gist.github.com/staltz/868e7e9bc2a7b8c1f754)
22 | ///
23 | /// - "Who to follow" Demo (JavaScript)
24 | /// http://jsfiddle.net/staltz/8jFJH/48/
25 | ///
26 | class WhoToFollowViewController: UIViewController {
27 |
28 | @IBOutlet var user1Button: UIButton!
29 | @IBOutlet var user2Button: UIButton!
30 | @IBOutlet var user3Button: UIButton!
31 | @IBOutlet var refreshButton: UIButton!
32 |
33 | override func viewDidLoad()
34 | {
35 | super.viewDidLoad()
36 |
37 | self._setupButtons()
38 | }
39 |
40 | func _setupButtons()
41 | {
42 | let refreshButtonStream: Stream = self.refreshButton!.buttonStream("refresh")
43 |
44 | let user1ButtonStream = self.user1Button!.buttonStream(1)
45 | let user2ButtonStream = self.user2Button!.buttonStream(2)
46 | let user3ButtonStream = self.user3Button!.buttonStream(3)
47 |
48 | /// refreshButton -> random URL -> get JSON
49 | let jsonStream = refreshButtonStream
50 | |> startWith("refresh on start")
51 | |> map { _ -> Alamofire.Request in
52 | let since = Int(arc4random_uniform(500))
53 | return Alamofire.request(.GET, "https://api.github.com/users", parameters: ["since" : since], encoding: .URL)
54 | }
55 | |> flatMap { Stream.fromTask(_requestTask($0)) }
56 |
57 | typealias UserDict = [String : AnyObject]
58 |
59 | func createRandomUserStream(userButtonStream: Stream) -> Stream
60 | {
61 | let streams: [Stream] = [
62 | userButtonStream
63 | |> map { $0 as Any }
64 | |> startWith("clear"),
65 | jsonStream
66 | |> map { $0 as Any }
67 | ]
68 | return streams |> combineLatestAll
69 | |> map { values -> UserDict? in
70 |
71 | if let json = values.last as? SwiftyJSON.JSON {
72 | let randomIndex = Int(arc4random_uniform(UInt32(json.count)))
73 | return json[randomIndex].dictionaryObject ?? nil
74 | }
75 | else {
76 | return nil
77 | }
78 | }
79 | |> merge(refreshButtonStream |> map { _ in nil })
80 |
81 | }
82 | let randomUser1Stream = createRandomUserStream(user1ButtonStream)
83 | let randomUser2Stream = createRandomUserStream(user2ButtonStream)
84 | let randomUser3Stream = createRandomUserStream(user3ButtonStream)
85 |
86 | // OWNED: retain streams by `self` (convenient method in replace of `self.retainingStreams += [myStream]`)
87 | randomUser1Stream.ownedBy(self)
88 | randomUser2Stream.ownedBy(self)
89 | randomUser3Stream.ownedBy(self)
90 |
91 | //--------------------------------------------------
92 | // Render
93 | //--------------------------------------------------
94 |
95 | func renderUserButton(userButton: UIButton?, userDict: UserDict?)
96 | {
97 | userButton?.setTitle(userDict?["login"] as? String, forState: .Normal)
98 | userButton?.setImage(nil, forState: .Normal)
99 |
100 | // resize & update avatar using HanekeSwift
101 | if let avatarURLString = userDict?["avatar_url"] as? String {
102 | let avatarURL = NSURL(string: avatarURLString)
103 | if let avatarURL = avatarURL {
104 | userButton?.hnk_setImageFromURL(avatarURL, state: .Normal, placeholder: nil, format: nil, failure: nil, success: nil)
105 | }
106 | }
107 |
108 | }
109 |
110 | // REACT: userButton ~> re-render
111 | randomUser1Stream ~> { [weak self] userDict in
112 | renderUserButton(self?.user1Button, userDict: userDict)
113 | }
114 | randomUser2Stream ~> { [weak self] userDict in
115 | renderUserButton(self?.user2Button, userDict: userDict)
116 | }
117 | randomUser3Stream ~> { [weak self] userDict in
118 | renderUserButton(self?.user3Button, userDict: userDict)
119 | }
120 |
121 | }
122 |
123 |
124 | }
125 |
--------------------------------------------------------------------------------
/ReactKitCatalog-iOS/Samples/WhoToFollowViewController.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
32 |
44 |
56 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
--------------------------------------------------------------------------------
/ReactKitCatalog.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Shared/Helper.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Helper.swift
3 | // ReactKitCatalog
4 | //
5 | // Created by Yasuhiro Inami on 2015/03/21.
6 | // Copyright (c) 2015年 Yasuhiro Inami. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Dollar
11 | import SwiftTask
12 | import Alamofire
13 | import SwiftyJSON
14 | import Async
15 |
16 | // pick `count` random elements from `sequence`
17 | func _pickRandom(sequence: S, _ count: Int) -> [S.Generator.Element]
18 | {
19 | var array = Array(sequence)
20 | var pickedArray = Array()
21 |
22 | for _ in 0.. String
38 | {
39 | return dateFormatter.stringFromDate(date)
40 | }
41 |
42 | enum CatalogError: String, ErrorType
43 | {
44 | case InvalidArgument
45 | }
46 |
47 | /// analogous to JavaScript's `$.getJSON(requestUrl)` using Alamofire & SwiftyJSON
48 | func _requestTask(request: Alamofire.Request) -> Task
49 | {
50 | guard let urlString = request.request?.URLString else {
51 | return Task(error: CatalogError.InvalidArgument)
52 | }
53 |
54 | return Task { fulfill, reject in
55 |
56 | print("request to \(urlString)")
57 |
58 | request.responseJSON { response in
59 |
60 | print("response from \(urlString)")
61 |
62 | if let error = response.result.error {
63 | reject(error)
64 | return
65 | }
66 |
67 | Async.background {
68 | let json = JSON(response.result.value!)
69 |
70 | Async.main {
71 | fulfill(json)
72 | }
73 | }
74 |
75 | }
76 | return
77 | }
78 | }
--------------------------------------------------------------------------------