mirror of
https://github.com/zhigang1992/GiphySearch.git
synced 2026-04-29 03:05:52 +08:00
Add parser
This commit is contained in:
@@ -8,12 +8,19 @@
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
0409FE901D145707001EBAA3 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0409FE8F1D145707001EBAA3 /* AppDelegate.swift */; };
|
||||
0409FE921D145707001EBAA3 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0409FE911D145707001EBAA3 /* ViewController.swift */; };
|
||||
0409FE921D145707001EBAA3 /* HomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0409FE911D145707001EBAA3 /* HomeViewController.swift */; };
|
||||
0409FE951D145707001EBAA3 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0409FE931D145707001EBAA3 /* Main.storyboard */; };
|
||||
0409FE971D145707001EBAA3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 0409FE961D145707001EBAA3 /* Assets.xcassets */; };
|
||||
0409FE9A1D145707001EBAA3 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 0409FE981D145707001EBAA3 /* LaunchScreen.storyboard */; };
|
||||
0409FEA51D145707001EBAA3 /* GiphySearchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0409FEA41D145707001EBAA3 /* GiphySearchTests.swift */; };
|
||||
0409FEA51D145707001EBAA3 /* QuickConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0409FEA41D145707001EBAA3 /* QuickConfig.swift */; };
|
||||
0409FEB01D145707001EBAA3 /* GiphySearchUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0409FEAF1D145707001EBAA3 /* GiphySearchUITests.swift */; };
|
||||
0453382E1D14E8B200C5239B /* HomeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0453382D1D14E8B200C5239B /* HomeViewModel.swift */; };
|
||||
045338301D14EA5D00C5239B /* GiphyAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0453382F1D14EA5D00C5239B /* GiphyAPI.swift */; };
|
||||
045338321D14EA9A00C5239B /* GiphyAPITest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 045338311D14EA9A00C5239B /* GiphyAPITest.swift */; };
|
||||
045338361D14EBD000C5239B /* Giphy.swift in Sources */ = {isa = PBXBuildFile; fileRef = 045338351D14EBD000C5239B /* Giphy.swift */; };
|
||||
0453383C1D14F0A100C5239B /* NotYetReleasedParser.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0453383B1D14F0A100C5239B /* NotYetReleasedParser.swift */; };
|
||||
0453383E1D14FCA300C5239B /* Result.swift in Sources */ = {isa = PBXBuildFile; fileRef = 0453383D1D14FCA300C5239B /* Result.swift */; };
|
||||
04FAC7451D14E27800230CF3 /* Injector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04FAC7441D14E27800230CF3 /* Injector.swift */; };
|
||||
14B32E654239D09C164A3F2B /* Pods_GiphySearchTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A8CB399D083241D2E4B2EF15 /* Pods_GiphySearchTests.framework */; };
|
||||
3E0BAE035A5120D1ED29BBC6 /* Pods_GiphySearchUITests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B3A076BE654A5741DBC60FFA /* Pods_GiphySearchUITests.framework */; };
|
||||
7F3846BE13129F0CA635AA00 /* Pods_GiphySearch.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A8221C6E6BE8EFD3C0ECD317 /* Pods_GiphySearch.framework */; };
|
||||
@@ -39,17 +46,24 @@
|
||||
/* Begin PBXFileReference section */
|
||||
0409FE8C1D145707001EBAA3 /* GiphySearch.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = GiphySearch.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
0409FE8F1D145707001EBAA3 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
0409FE911D145707001EBAA3 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
|
||||
0409FE911D145707001EBAA3 /* HomeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeViewController.swift; sourceTree = "<group>"; };
|
||||
0409FE941D145707001EBAA3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
||||
0409FE961D145707001EBAA3 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
0409FE991D145707001EBAA3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||
0409FE9B1D145707001EBAA3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
0409FEA01D145707001EBAA3 /* GiphySearchTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = GiphySearchTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
0409FEA41D145707001EBAA3 /* GiphySearchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GiphySearchTests.swift; sourceTree = "<group>"; };
|
||||
0409FEA41D145707001EBAA3 /* QuickConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QuickConfig.swift; sourceTree = "<group>"; };
|
||||
0409FEA61D145707001EBAA3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
0409FEAB1D145707001EBAA3 /* GiphySearchUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = GiphySearchUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
0409FEAF1D145707001EBAA3 /* GiphySearchUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GiphySearchUITests.swift; sourceTree = "<group>"; };
|
||||
0409FEB11D145707001EBAA3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
0453382D1D14E8B200C5239B /* HomeViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HomeViewModel.swift; sourceTree = "<group>"; };
|
||||
0453382F1D14EA5D00C5239B /* GiphyAPI.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GiphyAPI.swift; sourceTree = "<group>"; };
|
||||
045338311D14EA9A00C5239B /* GiphyAPITest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GiphyAPITest.swift; sourceTree = "<group>"; };
|
||||
045338351D14EBD000C5239B /* Giphy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Giphy.swift; sourceTree = "<group>"; };
|
||||
0453383B1D14F0A100C5239B /* NotYetReleasedParser.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotYetReleasedParser.swift; sourceTree = "<group>"; };
|
||||
0453383D1D14FCA300C5239B /* Result.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Result.swift; sourceTree = "<group>"; };
|
||||
04FAC7441D14E27800230CF3 /* Injector.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Injector.swift; sourceTree = "<group>"; };
|
||||
0C7E0838CB946C82B888F7F3 /* Pods-GiphySearchUITests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-GiphySearchUITests.debug.xcconfig"; path = "Pods/Target Support Files/Pods-GiphySearchUITests/Pods-GiphySearchUITests.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
36B16EBFDCC5405A55B2E3F8 /* Pods-GiphySearch.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-GiphySearch.debug.xcconfig"; path = "Pods/Target Support Files/Pods-GiphySearch/Pods-GiphySearch.debug.xcconfig"; sourceTree = "<group>"; };
|
||||
437F19F5739EFE071BA53F4F /* Pods-GiphySearchUITests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-GiphySearchUITests.release.xcconfig"; path = "Pods/Target Support Files/Pods-GiphySearchUITests/Pods-GiphySearchUITests.release.xcconfig"; sourceTree = "<group>"; };
|
||||
@@ -114,12 +128,13 @@
|
||||
0409FE8E1D145707001EBAA3 /* GiphySearch */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0409FE8F1D145707001EBAA3 /* AppDelegate.swift */,
|
||||
0409FE911D145707001EBAA3 /* ViewController.swift */,
|
||||
0409FE931D145707001EBAA3 /* Main.storyboard */,
|
||||
0409FE961D145707001EBAA3 /* Assets.xcassets */,
|
||||
0409FE981D145707001EBAA3 /* LaunchScreen.storyboard */,
|
||||
0409FE9B1D145707001EBAA3 /* Info.plist */,
|
||||
045338341D14EBC700C5239B /* Model */,
|
||||
0453382C1D14E88F00C5239B /* View */,
|
||||
0453382B1D14E88B00C5239B /* ViewModel */,
|
||||
045338331D14EAED00C5239B /* API */,
|
||||
04FAC7431D14E22500230CF3 /* Misc */,
|
||||
0453383A1D14F08600C5239B /* Vendor */,
|
||||
04FAC7441D14E27800230CF3 /* Injector.swift */,
|
||||
);
|
||||
path = GiphySearch;
|
||||
sourceTree = "<group>";
|
||||
@@ -127,7 +142,8 @@
|
||||
0409FEA31D145707001EBAA3 /* GiphySearchTests */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0409FEA41D145707001EBAA3 /* GiphySearchTests.swift */,
|
||||
0409FEA41D145707001EBAA3 /* QuickConfig.swift */,
|
||||
045338311D14EA9A00C5239B /* GiphyAPITest.swift */,
|
||||
0409FEA61D145707001EBAA3 /* Info.plist */,
|
||||
);
|
||||
path = GiphySearchTests;
|
||||
@@ -142,6 +158,59 @@
|
||||
path = GiphySearchUITests;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0453382B1D14E88B00C5239B /* ViewModel */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0453382D1D14E8B200C5239B /* HomeViewModel.swift */,
|
||||
);
|
||||
name = ViewModel;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0453382C1D14E88F00C5239B /* View */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0409FE911D145707001EBAA3 /* HomeViewController.swift */,
|
||||
0409FE931D145707001EBAA3 /* Main.storyboard */,
|
||||
);
|
||||
name = View;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
045338331D14EAED00C5239B /* API */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0453382F1D14EA5D00C5239B /* GiphyAPI.swift */,
|
||||
);
|
||||
name = API;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
045338341D14EBC700C5239B /* Model */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
045338351D14EBD000C5239B /* Giphy.swift */,
|
||||
);
|
||||
name = Model;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
0453383A1D14F08600C5239B /* Vendor */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0453383B1D14F0A100C5239B /* NotYetReleasedParser.swift */,
|
||||
0453383D1D14FCA300C5239B /* Result.swift */,
|
||||
);
|
||||
name = Vendor;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
04FAC7431D14E22500230CF3 /* Misc */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
0409FE9B1D145707001EBAA3 /* Info.plist */,
|
||||
0409FE961D145707001EBAA3 /* Assets.xcassets */,
|
||||
0409FE981D145707001EBAA3 /* LaunchScreen.storyboard */,
|
||||
0409FE8F1D145707001EBAA3 /* AppDelegate.swift */,
|
||||
);
|
||||
name = Misc;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
D893AA2AA20F5EC761C23427 /* Pods */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -427,8 +496,14 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
0409FE921D145707001EBAA3 /* ViewController.swift in Sources */,
|
||||
0409FE921D145707001EBAA3 /* HomeViewController.swift in Sources */,
|
||||
04FAC7451D14E27800230CF3 /* Injector.swift in Sources */,
|
||||
0453382E1D14E8B200C5239B /* HomeViewModel.swift in Sources */,
|
||||
045338361D14EBD000C5239B /* Giphy.swift in Sources */,
|
||||
0409FE901D145707001EBAA3 /* AppDelegate.swift in Sources */,
|
||||
045338301D14EA5D00C5239B /* GiphyAPI.swift in Sources */,
|
||||
0453383E1D14FCA300C5239B /* Result.swift in Sources */,
|
||||
0453383C1D14F0A100C5239B /* NotYetReleasedParser.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -436,7 +511,8 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
0409FEA51D145707001EBAA3 /* GiphySearchTests.swift in Sources */,
|
||||
045338321D14EA9A00C5239B /* GiphyAPITest.swift in Sources */,
|
||||
0409FEA51D145707001EBAA3 /* QuickConfig.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
||||
@@ -5,10 +5,10 @@
|
||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="10085"/>
|
||||
</dependencies>
|
||||
<scenes>
|
||||
<!--View Controller-->
|
||||
<!--Home View Controller-->
|
||||
<scene sceneID="tne-QT-ifu">
|
||||
<objects>
|
||||
<viewController id="BYZ-38-t0r" customClass="ViewController" customModule="GiphySearch" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<viewController id="BYZ-38-t0r" customClass="HomeViewController" customModule="GiphySearch" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<layoutGuides>
|
||||
<viewControllerLayoutGuide type="top" id="y3c-jy-aDJ"/>
|
||||
<viewControllerLayoutGuide type="bottom" id="wfy-db-euE"/>
|
||||
|
||||
28
GiphySearch/Giphy.swift
Normal file
28
GiphySearch/Giphy.swift
Normal file
@@ -0,0 +1,28 @@
|
||||
//
|
||||
// Giphy.swift
|
||||
// GiphySearch
|
||||
//
|
||||
// Created by Kyle Fang on 6/18/16.
|
||||
// Copyright © 2016 Kyle Fang. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Curry
|
||||
|
||||
struct Giphy {
|
||||
let id: String
|
||||
let slug: String
|
||||
let url: String
|
||||
let images: String
|
||||
}
|
||||
|
||||
extension Giphy: Parsable {
|
||||
static var parser: Parser<Giphy> {
|
||||
return curry(self.init)
|
||||
<^> "id"
|
||||
<*> "slug"
|
||||
<*> "url"
|
||||
<*> "images.fixed_width.url"
|
||||
}
|
||||
}
|
||||
|
||||
40
GiphySearch/GiphyAPI.swift
Normal file
40
GiphySearch/GiphyAPI.swift
Normal file
@@ -0,0 +1,40 @@
|
||||
//
|
||||
// GiphyAPI.swift
|
||||
// GiphySearch
|
||||
//
|
||||
// Created by Kyle Fang on 6/18/16.
|
||||
// Copyright © 2016 Kyle Fang. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import RxSwift
|
||||
import RxCocoa
|
||||
|
||||
protocol GiphyAPIType {
|
||||
func trending() -> Observable<[Giphy]>
|
||||
func search(keyword:[String]) -> Observable<[Giphy]>
|
||||
}
|
||||
|
||||
private let treandingAPI = NSURL(string: "https://api.giphy.com/v1/gifs/trending?api_key=dc6zaTOxFJmzC")!
|
||||
|
||||
private func searchAPI(keyword: [String]) -> NSURL {
|
||||
return NSURL(string: "https://api.giphy.com/v1/gifs/search?q=\(keyword.joinWithSeparator("+"))&api_key=dc6zaTOxFJmzC")!
|
||||
}
|
||||
|
||||
struct GiphyAPI : GiphyAPIType {
|
||||
let session: NSURLSession = Injector.resolve(NSURLSession.self)
|
||||
|
||||
let responseParser = dictionaryParser(
|
||||
key: "data",
|
||||
parser: arrayParser(Giphy.parser)
|
||||
)
|
||||
|
||||
func trending() -> Observable<[Giphy]> {
|
||||
return session.rx_JSON(treandingAPI)
|
||||
.flatMap({ self.responseParser.parse($0).toObservable() })
|
||||
}
|
||||
func search(keywords: [String]) -> Observable<[Giphy]> {
|
||||
return session.rx_JSON(searchAPI(keywords))
|
||||
.flatMap({self.responseParser.parse($0).toObservable()})
|
||||
}
|
||||
}
|
||||
@@ -8,11 +8,16 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
class ViewController: UIViewController {
|
||||
class HomeViewController: UIViewController {
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
// Do any additional setup after loading the view, typically from a nib.
|
||||
Injector.resolve(GiphyAPI.self)
|
||||
.trending()
|
||||
.subscribe({
|
||||
print($0)
|
||||
})
|
||||
}
|
||||
|
||||
override func didReceiveMemoryWarning() {
|
||||
13
GiphySearch/HomeViewModel.swift
Normal file
13
GiphySearch/HomeViewModel.swift
Normal file
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// ViewModel.swift
|
||||
// GiphySearch
|
||||
//
|
||||
// Created by Kyle Fang on 6/18/16.
|
||||
// Copyright © 2016 Kyle Fang. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
class ViewModel {
|
||||
|
||||
}
|
||||
29
GiphySearch/Injector.swift
Normal file
29
GiphySearch/Injector.swift
Normal file
@@ -0,0 +1,29 @@
|
||||
//
|
||||
// Injector.swift
|
||||
// GiphySearch
|
||||
//
|
||||
// Created by Kyle Fang on 6/18/16.
|
||||
// Copyright © 2016 Kyle Fang. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Swinject
|
||||
|
||||
struct Injector {
|
||||
static func initialRegisteration(container: Container) {
|
||||
container.register(ViewModel.self) { _ in ViewModel() }
|
||||
container.register(NSURLSession.self) { _ in .sharedSession() }
|
||||
container.register(GiphyAPI.self, factory: { _ in GiphyAPI() })
|
||||
}
|
||||
|
||||
static private(set) var sharedContainer = Container(registeringClosure: Injector.initialRegisteration)
|
||||
|
||||
static func resolve<T>(type: T.Type) -> T {
|
||||
return self.sharedContainer.resolve(type)!
|
||||
}
|
||||
|
||||
// mainly for testing
|
||||
static func reset() {
|
||||
self.sharedContainer = Container(registeringClosure: Injector.initialRegisteration)
|
||||
}
|
||||
}
|
||||
162
GiphySearch/NotYetReleasedParser.swift
Normal file
162
GiphySearch/NotYetReleasedParser.swift
Normal file
@@ -0,0 +1,162 @@
|
||||
//
|
||||
// NotYetReleasedParser.swift
|
||||
// GiphySearch
|
||||
//
|
||||
// Created by Kyle Fang on 6/18/16.
|
||||
// Copyright © 2016 Kyle Fang. All rights reserved.
|
||||
//
|
||||
|
||||
|
||||
// Please don't look at the code here... :(
|
||||
// It's WIP
|
||||
import Foundation
|
||||
|
||||
|
||||
|
||||
extension String : ErrorType {
|
||||
|
||||
}
|
||||
|
||||
struct Parser<T> {
|
||||
let parse: (AnyObject) -> Result<T>
|
||||
}
|
||||
|
||||
extension Parser {
|
||||
|
||||
static func failed(error: ErrorType) -> Parser<T> {
|
||||
return Parser { _ in .Error(error) }
|
||||
}
|
||||
|
||||
static func unit(value:T) -> Parser<T> {
|
||||
return Parser { _ in .Success(value) }
|
||||
}
|
||||
|
||||
func flatMap<U>(function: T-> Parser<U>) -> Parser<U> {
|
||||
return Parser<U> { input in
|
||||
self.parse(input).flatMap({function($0).parse(input)})
|
||||
}
|
||||
}
|
||||
|
||||
func map<U>(function: T->U) -> Parser<U> {
|
||||
return self.flatMap({.unit(function($0))})
|
||||
}
|
||||
|
||||
func apply<U>(applicative: Parser<T->U>) -> Parser<U> {
|
||||
return applicative.flatMap({self.map($0)})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
infix operator <^> { associativity left }
|
||||
infix operator <*> { associativity left }
|
||||
|
||||
func <^><T, U>(left: T->U, right: Parser<T>) -> Parser<U> {
|
||||
return right.map(left)
|
||||
}
|
||||
|
||||
func <*><T, U>(left: Parser<T->U>, right: Parser<T>) -> Parser<U> {
|
||||
return right.apply(left)
|
||||
}
|
||||
|
||||
func dictionaryParser<T>(key key: String, parser: Parser<T>) -> Parser<T> {
|
||||
let keys = key.componentsSeparatedByString(".").reverse()
|
||||
return keys.reduce(parser, combine: { loop, currentKey in
|
||||
return Parser<T> { input in
|
||||
guard let dictionary = input as? NSDictionary else {
|
||||
return .Error("\(input) is not an dictionary")
|
||||
}
|
||||
guard let value = dictionary[currentKey] else {
|
||||
return .Error("key \(currentKey) does not exist in \(input)")
|
||||
}
|
||||
return loop.parse(value)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func arrayParser<T>(parser:Parser<T>) -> Parser<[T]> {
|
||||
let arrayOfAnyObject = Parser<NSArray> { input in
|
||||
guard let array = input as? NSArray else {
|
||||
return .Error("\(input) is not an array")
|
||||
}
|
||||
return .Success(array)
|
||||
}
|
||||
return arrayOfAnyObject.flatMap({ array in
|
||||
let arrayOfParser = array.map({parser.parse($0).toParser()})
|
||||
return arrayOfParser.reduce(.unit([])) { loop, nextParser in
|
||||
{result in {next in result+[next]}} <^> loop <*> nextParser
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func optionalParser<T>(parser:Parser<T>) -> Parser<Optional<T>> {
|
||||
return Parser { input in
|
||||
if case .Success(let value) = parser.parse(input) {
|
||||
return .Success(value)
|
||||
}
|
||||
return .Success(nil)
|
||||
}
|
||||
}
|
||||
|
||||
func <^><T:Parsable, U>(left: T->U, key: String ) -> Parser<U> {
|
||||
return left <^> dictionaryParser(key: key, parser: T.parser)
|
||||
}
|
||||
|
||||
func <*><T:Parsable, U>(left: Parser<T->U>, key: String) -> Parser<U> {
|
||||
return left <*> dictionaryParser(key: key, parser: T.parser)
|
||||
}
|
||||
|
||||
//: These are workaround because swift's lack of support in partial confirm to protocol, you can't do `extension Array: Parsable where Element: Parsable`
|
||||
func <^><T:Parsable, U>(left: [T]->U, key: String ) -> Parser<U> {
|
||||
return left <^> dictionaryParser(
|
||||
key: key,
|
||||
parser: arrayParser(T.parser)
|
||||
)
|
||||
}
|
||||
|
||||
func <*><T:Parsable, U>(left: Parser<[T]->U>, key: String) -> Parser<U> {
|
||||
return left <*> dictionaryParser(
|
||||
key: key,
|
||||
parser: arrayParser(T.parser)
|
||||
)
|
||||
}
|
||||
|
||||
func <^><T:Parsable, U>(left: Optional<T>->U, key: String ) -> Parser<U> {
|
||||
return left <^> optionalParser(dictionaryParser(key: key,parser: T.parser))
|
||||
}
|
||||
|
||||
func <*><T:Parsable, U>(left: Parser<Optional<T>->U>, key: String) -> Parser<U> {
|
||||
return left <*> optionalParser(dictionaryParser(key: key,parser: T.parser))
|
||||
}
|
||||
|
||||
protocol Parsable {
|
||||
static var parser: Parser<Self> { get }
|
||||
}
|
||||
|
||||
extension Double: Parsable {
|
||||
static var parser: Parser<Double> {
|
||||
return Parser {
|
||||
if let number = $0 as? Double {
|
||||
return .Success(number)
|
||||
}
|
||||
return .Error("\($0) is not an number")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Int: Parsable {
|
||||
static var parser: Parser<Int> {
|
||||
return Double.parser.map(Int.init)
|
||||
}
|
||||
}
|
||||
|
||||
extension String: Parsable {
|
||||
static var parser: Parser<String> {
|
||||
return Parser {
|
||||
if let string = $0 as? String {
|
||||
return .Success(string)
|
||||
}
|
||||
return .Error("\($0) is not an string")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
44
GiphySearch/Result.swift
Normal file
44
GiphySearch/Result.swift
Normal file
@@ -0,0 +1,44 @@
|
||||
//
|
||||
// Result.swift
|
||||
// GiphySearch
|
||||
//
|
||||
// Created by Kyle Fang on 6/18/16.
|
||||
// Copyright © 2016 Kyle Fang. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import RxSwift
|
||||
|
||||
enum Result<T> {
|
||||
case Success(T)
|
||||
case Error(ErrorType)
|
||||
}
|
||||
|
||||
extension Result {
|
||||
func flatMap<U>(function: T->Result<U>) -> Result<U> {
|
||||
switch self {
|
||||
case .Success(let value): return function(value)
|
||||
case .Error(let error): return .Error(error)
|
||||
}
|
||||
}
|
||||
var value: T? {
|
||||
switch self {
|
||||
case .Success(let value): return value
|
||||
default: return nil
|
||||
}
|
||||
}
|
||||
|
||||
func toObservable() -> Observable<T> {
|
||||
switch self {
|
||||
case .Success(let value): return .just(value)
|
||||
case .Error(let error): return .error(error)
|
||||
}
|
||||
}
|
||||
|
||||
func toParser() -> Parser<T> {
|
||||
switch self {
|
||||
case .Success(let value): return .unit(value)
|
||||
case .Error(let error): return .failed(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
9
GiphySearchTests/GiphyAPITest.swift
Normal file
9
GiphySearchTests/GiphyAPITest.swift
Normal file
@@ -0,0 +1,9 @@
|
||||
//
|
||||
// GiphyAPITest.swift
|
||||
// GiphySearch
|
||||
//
|
||||
// Created by Kyle Fang on 6/18/16.
|
||||
// Copyright © 2016 Kyle Fang. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
@@ -1,36 +0,0 @@
|
||||
//
|
||||
// GiphySearchTests.swift
|
||||
// GiphySearchTests
|
||||
//
|
||||
// Created by Kyle Fang on 6/18/16.
|
||||
// Copyright © 2016 Kyle Fang. All rights reserved.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
@testable import GiphySearch
|
||||
|
||||
class GiphySearchTests: XCTestCase {
|
||||
|
||||
override func setUp() {
|
||||
super.setUp()
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
func testExample() {
|
||||
// This is an example of a functional test case.
|
||||
// Use XCTAssert and related functions to verify your tests produce the correct results.
|
||||
}
|
||||
|
||||
func testPerformanceExample() {
|
||||
// This is an example of a performance test case.
|
||||
self.measureBlock {
|
||||
// Put the code you want to measure the time of here.
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
24
GiphySearchTests/QuickConfig.swift
Normal file
24
GiphySearchTests/QuickConfig.swift
Normal file
@@ -0,0 +1,24 @@
|
||||
//
|
||||
// GiphySearchTests.swift
|
||||
// GiphySearchTests
|
||||
//
|
||||
// Created by Kyle Fang on 6/18/16.
|
||||
// Copyright © 2016 Kyle Fang. All rights reserved.
|
||||
//
|
||||
|
||||
@testable import GiphySearch
|
||||
import Quick
|
||||
|
||||
class QuickConfig: QuickConfiguration {
|
||||
override class func configure(configuration: Configuration) {
|
||||
configuration.beforeEach {
|
||||
Injector.reset()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension Injector {
|
||||
static func registerMock<T>(type:T.Type, withFactory factory:()->T) {
|
||||
self.sharedContainer.register(type, factory: {_ in return factory() })
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user