Skip to content

Commit

Permalink
Add single store example. Feedback and Reduce composition. (#49)
Browse files Browse the repository at this point in the history
* Add pullbacks, Store, Context, reducer composition

* Add counter example

* Add single store example

* Add Picker example and parent child state composition

* Update Example/AppDelegate.swift

Co-Authored-By: Michael Brown <[email protected]>

* Remove SwiftUI 🙈

* Use Xcode 11.1

* update .circleci/config.yml

* Update kingsfisher

* Rename to forward

* Remove Kingfisher

* Implement single store example with new Feedback loops

* Typos

Co-authored-by: Michael Brown <[email protected]>
Co-authored-by: Ilya Puchka <[email protected]>
  • Loading branch information
3 people authored Apr 3, 2020
1 parent 20514bb commit de7454c
Show file tree
Hide file tree
Showing 29 changed files with 1,295 additions and 602 deletions.
1 change: 0 additions & 1 deletion Cartfile.private
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
# Example app.
github "ReactiveCocoa/ReactiveCocoa" ~> 10.0.0
github "onevcat/Kingfisher" ~> 5.2

# Tests
github "Quick/Nimble" ~> 8.0
4 changes: 3 additions & 1 deletion Example/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,9 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
var window: UIWindow?

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Comment these lines if you want to see multi store example
window?.rootViewController = RootViewController()
window?.makeKeyAndVisible()
return true
}
}

4 changes: 2 additions & 2 deletions Example/Base.lproj/Main.storyboard
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="15505" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="zMS-h2-fCg">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="15400" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" colorMatched="YES" initialViewController="zMS-h2-fCg">
<device id="retina4_7" orientation="portrait" appearance="light"/>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15509"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="15404"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
Expand Down
129 changes: 129 additions & 0 deletions Example/MultiStoreExample/PaginationViewController.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
import UIKit
import ReactiveSwift
import ReactiveCocoa
import ReactiveFeedback

final class PaginationViewController: UIViewController {
private lazy var contentView = MoviesView.loadFromNib()
private let viewModel = Movies.ViewModel()

override func loadView() {
self.view = contentView
}

override func viewDidLoad() {
super.viewDidLoad()
viewModel.state.producer.startWithValues(contentView.render)
}
}

extension Movies {
final class ViewModel: Store<State, Event> {
init() {
super.init(
initial: Movies.State(),
reducer: Movies.reduce,
feedbacks: [Movies.feedback]
)
}
}
}

// Key for https://www.themoviedb.org API
let correctKey = "d4f0bdb3e246e2cb3555211e765c89e3"

struct Results: Codable {
let page: Int
let totalResults: Int
let totalPages: Int
let results: [Movie]

static func empty() -> Results {
return Results.init(page: 0, totalResults: 0, totalPages: 0, results: [])
}

enum CodingKeys: String, CodingKey {
case page
case totalResults = "total_results"
case totalPages = "total_pages"
case results
}
}

struct Movie: Codable {
let id: Int
let overview: String
let title: String
let posterPath: String?

var posterURL: URL? {
return posterPath
.map {
"https://image.tmdb.org/t/p/w342/\($0)"
}
.flatMap(URL.init(string:))
}

enum CodingKeys: String, CodingKey {
case id
case overview
case title
case posterPath = "poster_path"
}
}

extension URLSession {
func fetchMovies(page: Int) -> SignalProducer<Results, NSError> {
return SignalProducer.init({ (observer, lifetime) in
let url = URL(string: "https://api.themoviedb.org/3/discover/movie?api_key=\(correctKey)&sort_by=popularity.desc&page=\(page)")!
let task = self.dataTask(with: url, completionHandler: { (data, response, error) in
if let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 401 {
let error = NSError(domain: "come.reactivefeedback",
code: 401,
userInfo: [NSLocalizedDescriptionKey: "Forced failure to illustrate Retry"])
observer.send(error: error)
} else if let data = data {
do {
let results = try JSONDecoder().decode(Results.self, from: data)
observer.send(value: results)
} catch {
observer.send(error: error as NSError)
}
} else if let error = error {
observer.send(error: error as NSError)
observer.sendCompleted()
} else {
observer.sendCompleted()
}
})

lifetime += AnyDisposable(task.cancel)
task.resume()
})
}
}

final class ArrayCollectionViewDataSource<T>: NSObject, UICollectionViewDataSource {
typealias CellFactory = (UICollectionView, IndexPath, T) -> UICollectionViewCell

private(set) var items: [T] = []
var cellFactory: CellFactory!

func update(with items: [T]) {
self.items = items
}

func item(atIndexPath indexPath: IndexPath) -> T {
return items[indexPath.row]
}

// MARK: UICollectionViewDataSource

func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return items.count
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
return cellFactory(collectionView, indexPath, item(atIndexPath: indexPath))
}
}
File renamed without changes.
33 changes: 33 additions & 0 deletions Example/MultiStoreExample/ViewController.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import UIKit
import ReactiveSwift
import ReactiveCocoa
import ReactiveFeedback

class ViewController: UIViewController {
@IBOutlet weak var plusButton: UIButton!
@IBOutlet weak var minusButton: UIButton!
@IBOutlet weak var label: UILabel!
private lazy var contentView = CounterView.loadFromNib()
private let viewModel = Counter.ViewModel()

override func loadView() {
self.view = contentView
}

override func viewDidLoad() {
super.viewDidLoad()
viewModel.state.producer.startWithValues(contentView.render)
}
}

extension Counter {
final class ViewModel: Store<State, Event> {
init() {
super.init(
initial: State(),
reducer: Counter.reduce,
feedbacks: []
)
}
}
}
Loading

0 comments on commit de7454c

Please sign in to comment.