Skip to content

Commit

Permalink
Implemented Releases module
Browse files Browse the repository at this point in the history
  • Loading branch information
khoren93 committed Apr 11, 2019
1 parent f0e27c1 commit 90bf675
Show file tree
Hide file tree
Showing 20 changed files with 389 additions and 9 deletions.
36 changes: 36 additions & 0 deletions SwiftHub.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,13 @@
07241D2321B18A8600F800BD /* PullRequestCellViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07241D2221B18A8600F800BD /* PullRequestCellViewModel.swift */; };
07241D2521B18AA000F800BD /* PullRequestsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07241D2421B18AA000F800BD /* PullRequestsViewController.swift */; };
072B8EDA21B4143200B2C28C /* RepositoryContributors.json in Resources */ = {isa = PBXBuildFile; fileRef = 072B8ED921B4143200B2C28C /* RepositoryContributors.json */; };
072BD798225FD4F200E6C580 /* RepositoryReleases.json in Resources */ = {isa = PBXBuildFile; fileRef = 072BD797225FD4F200E6C580 /* RepositoryReleases.json */; };
072BD79A225FD50400E6C580 /* RepositoryRelease.json in Resources */ = {isa = PBXBuildFile; fileRef = 072BD799225FD50400E6C580 /* RepositoryRelease.json */; };
072BD79D225FD6C800E6C580 /* ReleasesViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 072BD79C225FD6C800E6C580 /* ReleasesViewModel.swift */; };
072BD79F225FD6DF00E6C580 /* ReleasesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 072BD79E225FD6DF00E6C580 /* ReleasesViewController.swift */; };
072BD7A2225FD6FD00E6C580 /* ReleaseCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 072BD7A0225FD6FD00E6C580 /* ReleaseCell.swift */; };
072BD7A3225FD6FD00E6C580 /* ReleaseCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 072BD7A1225FD6FD00E6C580 /* ReleaseCell.xib */; };
072BD7A5225FD70F00E6C580 /* ReleaseCellViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 072BD7A4225FD70F00E6C580 /* ReleaseCellViewModel.swift */; };
07320453214128ED00C1E39F /* Event.swift in Sources */ = {isa = PBXBuildFile; fileRef = 07320452214128ED00C1E39F /* Event.swift */; };
073204552141377D00C1E39F /* Events.json in Resources */ = {isa = PBXBuildFile; fileRef = 073204542141377D00C1E39F /* Events.json */; };
073204572141379500C1E39F /* EventsRepository.json in Resources */ = {isa = PBXBuildFile; fileRef = 073204562141379500C1E39F /* EventsRepository.json */; };
Expand Down Expand Up @@ -315,6 +322,13 @@
07241D2221B18A8600F800BD /* PullRequestCellViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PullRequestCellViewModel.swift; sourceTree = "<group>"; };
07241D2421B18AA000F800BD /* PullRequestsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PullRequestsViewController.swift; sourceTree = "<group>"; };
072B8ED921B4143200B2C28C /* RepositoryContributors.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = RepositoryContributors.json; sourceTree = "<group>"; };
072BD797225FD4F200E6C580 /* RepositoryReleases.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = RepositoryReleases.json; sourceTree = "<group>"; };
072BD799225FD50400E6C580 /* RepositoryRelease.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = RepositoryRelease.json; sourceTree = "<group>"; };
072BD79C225FD6C800E6C580 /* ReleasesViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReleasesViewModel.swift; sourceTree = "<group>"; };
072BD79E225FD6DF00E6C580 /* ReleasesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReleasesViewController.swift; sourceTree = "<group>"; };
072BD7A0225FD6FD00E6C580 /* ReleaseCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReleaseCell.swift; sourceTree = "<group>"; };
072BD7A1225FD6FD00E6C580 /* ReleaseCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ReleaseCell.xib; sourceTree = "<group>"; };
072BD7A4225FD70F00E6C580 /* ReleaseCellViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ReleaseCellViewModel.swift; sourceTree = "<group>"; };
07320452214128ED00C1E39F /* Event.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Event.swift; sourceTree = "<group>"; };
073204542141377D00C1E39F /* Events.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = Events.json; sourceTree = "<group>"; };
073204562141379500C1E39F /* EventsRepository.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = EventsRepository.json; sourceTree = "<group>"; };
Expand Down Expand Up @@ -683,6 +697,18 @@
path = "Pull Requests";
sourceTree = "<group>";
};
072BD79B225FD64B00E6C580 /* Releases */ = {
isa = PBXGroup;
children = (
072BD7A0225FD6FD00E6C580 /* ReleaseCell.swift */,
072BD7A1225FD6FD00E6C580 /* ReleaseCell.xib */,
072BD7A4225FD70F00E6C580 /* ReleaseCellViewModel.swift */,
072BD79E225FD6DF00E6C580 /* ReleasesViewController.swift */,
072BD79C225FD6C800E6C580 /* ReleasesViewModel.swift */,
);
path = Releases;
sourceTree = "<group>";
};
0732045C2141395200C1E39F /* Events */ = {
isa = PBXGroup;
children = (
Expand Down Expand Up @@ -864,6 +890,7 @@
07FC33741FFFD46D00D53888 /* Modules */ = {
isa = PBXGroup;
children = (
072BD79B225FD64B00E6C580 /* Releases */,
0795E5C52259205C008EFD50 /* Branches */,
07241D0121B173A400F800BD /* Commits */,
A07DC84621EDF67F00AA7951 /* Contacts */,
Expand Down Expand Up @@ -1049,6 +1076,8 @@
07241D0E21B1745D00F800BD /* RepositoryPullRequest.json */,
07241D0C21B1744900F800BD /* RepositoryPullRequests.json */,
07D46E5421920E8D008E5369 /* RepositoryReadme.json */,
072BD799225FD50400E6C580 /* RepositoryRelease.json */,
072BD797225FD4F200E6C580 /* RepositoryReleases.json */,
07DFF6D620E90584004DAA68 /* RepositorySearch.json */,
A0E2B0942108B00D000DA282 /* RepositoryStargazers.json */,
07A34DC421E29ACC00B05857 /* RepositoryTrendings.json */,
Expand Down Expand Up @@ -1437,13 +1466,15 @@
A0CE88B821C28DA700C4C0E5 /* EmptyObject.json in Resources */,
07B4AB652190C56E001E9337 /* RepositoryDetailCell.xib in Resources */,
A0E2B0932108AFF0000DA282 /* RepositoryWatchers.json in Resources */,
072BD79A225FD50400E6C580 /* RepositoryRelease.json in Resources */,
A07DB584223FC02E000E3312 /* User.graphql in Resources */,
07241D1921B1768E00F800BD /* CommitCell.xib in Resources */,
073A1F792233F2F000D6F487 /* Search.graphql in Resources */,
A09DE8AF20E7A99000F6F2BF /* UserRepositories.json in Resources */,
073204552141377D00C1E39F /* Events.json in Resources */,
07DFF6D520E90574004DAA68 /* UserSearch.json in Resources */,
A024C7C621BAB5F400FEA133 /* GoogleService-Info.plist in Resources */,
072BD798225FD4F200E6C580 /* RepositoryReleases.json in Resources */,
07A34DC921E29C2400B05857 /* Languages.json in Resources */,
C762C6881E1D5C3A00C09EBD /* Icons.xcassets in Resources */,
07241D0B21B1743900F800BD /* RepositoryBranch.json in Resources */,
Expand All @@ -1456,6 +1487,7 @@
0757B53A2166A6A000C04163 /* Localizable.strings in Resources */,
A034AF79215280EF004C310B /* NotificationCell.xib in Resources */,
07B2C1F9213AF74B00FE7AC0 /* Profile.json in Resources */,
072BD7A3225FD6FD00E6C580 /* ReleaseCell.xib in Resources */,
A0E2B0952108B00D000DA282 /* RepositoryStargazers.json in Resources */,
07241D0921B1742700F800BD /* RepositoryBranches.json in Resources */,
07F9E4622234010C0012BA6D /* schema.json in Resources */,
Expand Down Expand Up @@ -1798,6 +1830,7 @@
07FC337E1FFFE4E300D53888 /* LibsManager.swift in Sources */,
C762C6601E1D559000C09EBD /* Button.swift in Sources */,
A09DE8E120EB9B2500F6F2BF /* SlideImageView.swift in Sources */,
072BD7A5225FD70F00E6C580 /* ReleaseCellViewModel.swift in Sources */,
071F761B21C6756D003EEB35 /* WhatsNewManager.swift in Sources */,
0732046721413B6400C1E39F /* EventCellViewModel.swift in Sources */,
07FC337F1FFFE4E300D53888 /* LogManager.swift in Sources */,
Expand Down Expand Up @@ -1844,6 +1877,7 @@
070FF83E20F221AB0070F5E8 /* UserViewModel.swift in Sources */,
A09DE8A520E7A15A00F6F2BF /* User.swift in Sources */,
C762C6631E1D559000C09EBD /* CollectionViewController.swift in Sources */,
072BD7A2225FD6FD00E6C580 /* ReleaseCell.swift in Sources */,
0795E5D1225920FA008EFD50 /* BranchCellViewModel.swift in Sources */,
A09499561E1D1CB600EFDE29 /* AppDelegate.swift in Sources */,
C762C6681E1D559000C09EBD /* TableView.swift in Sources */,
Expand Down Expand Up @@ -1871,6 +1905,8 @@
C762C67F1E1D5A7300C09EBD /* Observable+Logging.swift in Sources */,
A034AF7021527EF8004C310B /* Notification.swift in Sources */,
C762C66D1E1D559000C09EBD /* ViewController.swift in Sources */,
072BD79D225FD6C800E6C580 /* ReleasesViewModel.swift in Sources */,
072BD79F225FD6DF00E6C580 /* ReleasesViewController.swift in Sources */,
A0E2B08D2106293A000DA282 /* Switch.swift in Sources */,
07B183862172269C007CC23B /* UserDetailCellViewModel.swift in Sources */,
0757B53221669DAC00C04163 /* LanguageViewModel.swift in Sources */,
Expand Down
6 changes: 6 additions & 0 deletions SwiftHub/Application/Navigator.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ class Navigator {
case source(viewModel: SourceViewModel)
case commits(viewModel: CommitsViewModel)
case branches(viewModel: BranchesViewModel)
case releases(viewModel: ReleasesViewModel)
case pullRequests(viewModel: PullRequestsViewModel)
case events(viewModel: EventsViewModel)
case notifications(viewModel: NotificationsViewModel)
Expand Down Expand Up @@ -124,6 +125,11 @@ class Navigator {
vc.viewModel = viewModel
return vc

case .releases(let viewModel):
let vc = R.storyboard.main.releasesViewController()!
vc.viewModel = viewModel
return vc

case .pullRequests(let viewModel):
let vc = R.storyboard.main.pullRequestsViewController()!
vc.viewModel = viewModel
Expand Down
8 changes: 4 additions & 4 deletions SwiftHub/Models/Release.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,14 @@ struct Release: Mappable {
var assetsUrl: String?
var author: User?
var body: String?
var createdAt: String?
var createdAt: Date?
var draft: Bool?
var htmlUrl: String?
var id: Int?
var name: String?
var nodeId: String?
var prerelease: Bool?
var publishedAt: String?
var publishedAt: Date?
var tagName: String?
var tarballUrl: String?
var targetCommitish: String?
Expand All @@ -38,14 +38,14 @@ struct Release: Mappable {
assetsUrl <- map["assets_url"]
author <- map["author"]
body <- map["body"]
createdAt <- map["created_at"]
createdAt <- (map["created_at"], ISO8601DateTransform())
draft <- map["draft"]
htmlUrl <- map["html_url"]
id <- map["id"]
name <- map["name"]
nodeId <- map["node_id"]
prerelease <- map["prerelease"]
publishedAt <- map["published_at"]
publishedAt <- (map["published_at"], ISO8601DateTransform())
tagName <- map["tag_name"]
tarballUrl <- map["tarball_url"]
targetCommitish <- map["target_commitish"]
Expand Down
2 changes: 0 additions & 2 deletions SwiftHub/Modules/Branches/BranchCellViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,6 @@ class BranchCellViewModel {

let branch: Branch

let userSelected = PublishSubject<User>()

init(with branch: Branch) {
self.branch = branch
title = Driver.just("\(branch.name ?? "")")
Expand Down
2 changes: 1 addition & 1 deletion SwiftHub/Modules/Branches/BranchesViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ class BranchesViewController: TableViewController {
cell.bind(to: viewModel)
}.disposed(by: rx.disposeBag)

viewModel.branchSelected.subscribe(onNext: { [weak self] (url) in
viewModel.branchSelected.subscribe(onNext: { [weak self] (branch) in
self?.navigator.pop(sender: self)
}).disposed(by: rx.disposeBag)

Expand Down
2 changes: 1 addition & 1 deletion SwiftHub/Modules/Commits/CommitsViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ class CommitsViewController: TableViewController {
}).disposed(by: rx.disposeBag)

output.userSelected.drive(onNext: { [weak self] (viewModel) in
self?.navigator.show(segue: .userDetails(viewModel: viewModel), sender: self, transition: .detail)
self?.navigator.show(segue: .userDetails(viewModel: viewModel), sender: self)
}).disposed(by: rx.disposeBag)

viewModel.error.asDriver().drive(onNext: { [weak self] (error) in
Expand Down
15 changes: 15 additions & 0 deletions SwiftHub/Modules/Main/Base.lproj/Main.storyboard
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,21 @@
</objects>
<point key="canvasLocation" x="4786" y="883"/>
</scene>
<!--Releases View Controller-->
<scene sceneID="qbl-60-ibl">
<objects>
<viewController storyboardIdentifier="ReleasesViewController" id="9If-nc-6DY" customClass="ReleasesViewController" customModule="SwiftHub" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="tYP-cf-db3">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<viewLayoutGuide key="safeArea" id="Y0t-Ny-l3g"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="ORF-AH-cpO" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="5567" y="883"/>
</scene>
<!--Contacts View Controller-->
<scene sceneID="Hrz-mO-5wB">
<objects>
Expand Down
35 changes: 35 additions & 0 deletions SwiftHub/Modules/Releases/ReleaseCell.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//
// ReleaseCell.swift
// SwiftHub
//
// Created by Sygnoos9 on 4/12/19.
// Copyright © 2019 Khoren Markosyan. All rights reserved.
//

import UIKit

class ReleaseCell: DetailedTableViewCell {

override func makeUI() {
super.makeUI()
leftImageView.cornerRadius = 25
secondDetailLabel.numberOfLines = 0
}

func bind(to viewModel: ReleaseCellViewModel) {
viewModel.title.drive(titleLabel.rx.text).disposed(by: rx.disposeBag)
viewModel.detail.drive(detailLabel.rx.text).disposed(by: rx.disposeBag)
viewModel.secondDetail.drive(secondDetailLabel.rx.text).disposed(by: rx.disposeBag)
viewModel.imageUrl.drive(leftImageView.rx.imageURL).disposed(by: rx.disposeBag)
viewModel.imageUrl.drive(onNext: { [weak self] (url) in
if let url = url {
self?.leftImageView.hero.id = url.absoluteString
}
}).disposed(by: rx.disposeBag)
viewModel.badge.drive(badgeImageView.rx.image).disposed(by: rx.disposeBag)
viewModel.badgeColor.drive(badgeImageView.rx.tintColor).disposed(by: rx.disposeBag)

leftImageView.rx.tap().map { _ in viewModel.release.author }.filterNil()
.bind(to: viewModel.userSelected).disposed(by: cellDisposeBag)
}
}
25 changes: 25 additions & 0 deletions SwiftHub/Modules/Releases/ReleaseCell.xib
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.XIB" version="3.0" toolsVersion="14490.70" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES">
<device id="retina6_1" orientation="portrait">
<adaptation id="fullscreen"/>
</device>
<dependencies>
<deployment identifier="iOS"/>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="14490.49"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<objects>
<placeholder placeholderIdentifier="IBFilesOwner" id="-1" userLabel="File's Owner"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="-2" customClass="UIResponder"/>
<tableViewCell contentMode="scaleToFill" selectionStyle="default" indentationWidth="10" reuseIdentifier="ReleaseCell" id="KGk-i7-Jjw" customClass="ReleaseCell" customModule="SwiftHub" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="320" height="44"/>
<autoresizingMask key="autoresizingMask" flexibleMaxX="YES" flexibleMaxY="YES"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" tableViewCell="KGk-i7-Jjw" id="H2p-sc-9uM">
<rect key="frame" x="0.0" y="0.0" width="320" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
</tableViewCellContentView>
<viewLayoutGuide key="safeArea" id="njF-e1-oar"/>
</tableViewCell>
</objects>
</document>
34 changes: 34 additions & 0 deletions SwiftHub/Modules/Releases/ReleaseCellViewModel.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
//
// ReleaseCellViewModel.swift
// SwiftHub
//
// Created by Sygnoos9 on 4/12/19.
// Copyright © 2019 Khoren Markosyan. All rights reserved.
//

import Foundation
import RxSwift
import RxCocoa

class ReleaseCellViewModel {
let title: Driver<String>
let detail: Driver<String>
let secondDetail: Driver<String>
let imageUrl: Driver<URL?>
let badge: Driver<UIImage?>
let badgeColor: Driver<UIColor>

let release: Release

let userSelected = PublishSubject<User>()

init(with release: Release) {
self.release = release
title = Driver.just("\(release.tagName ?? "") - \(release.name ?? "")")
detail = Driver.just("\(release.publishedAt?.toRelative() ?? "")")
secondDetail = Driver.just("\(release.body ?? "")")
imageUrl = Driver.just(release.author?.avatarUrl?.url)
badge = Driver.just(R.image.icon_cell_badge_tag()?.template)
badgeColor = Driver.just(UIColor.flatGreenDark)
}
}
66 changes: 66 additions & 0 deletions SwiftHub/Modules/Releases/ReleasesViewController.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
//
// ReleasesViewController.swift
// SwiftHub
//
// Created by Sygnoos9 on 4/12/19.
// Copyright © 2019 Khoren Markosyan. All rights reserved.
//

import UIKit
import RxSwift
import RxCocoa
import RxDataSources

private let reuseIdentifier = R.reuseIdentifier.releaseCell.identifier

class ReleasesViewController: TableViewController {

var viewModel: ReleasesViewModel!

override func viewDidLoad() {
super.viewDidLoad()

// Do any additional setup after loading the view.
}

override func makeUI() {
super.makeUI()

tableView.register(R.nib.releaseCell)
}

override func bindViewModel() {
super.bindViewModel()

let refresh = Observable.of(Observable.just(()), headerRefreshTrigger).merge()
let input = ReleasesViewModel.Input(headerRefresh: refresh,
footerRefresh: footerRefreshTrigger,
selection: tableView.rx.modelSelected(ReleaseCellViewModel.self).asDriver())
let output = viewModel.transform(input: input)

viewModel.loading.asObservable().bind(to: isLoading).disposed(by: rx.disposeBag)
viewModel.headerLoading.asObservable().bind(to: isHeaderLoading).disposed(by: rx.disposeBag)
viewModel.footerLoading.asObservable().bind(to: isFooterLoading).disposed(by: rx.disposeBag)

output.navigationTitle.drive(onNext: { [weak self] (title) in
self?.navigationTitle = title
}).disposed(by: rx.disposeBag)

output.items.asDriver(onErrorJustReturn: [])
.drive(tableView.rx.items(cellIdentifier: reuseIdentifier, cellType: ReleaseCell.self)) { tableView, viewModel, cell in
cell.bind(to: viewModel)
}.disposed(by: rx.disposeBag)

output.releaseSelected.drive(onNext: { [weak self] (url) in
self?.navigator.show(segue: .webController(url), sender: self)
}).disposed(by: rx.disposeBag)

output.userSelected.drive(onNext: { [weak self] (viewModel) in
self?.navigator.show(segue: .userDetails(viewModel: viewModel), sender: self)
}).disposed(by: rx.disposeBag)

viewModel.error.asDriver().drive(onNext: { [weak self] (error) in
self?.showAlert(title: R.string.localizable.commonError.key.localized(), message: error.localizedDescription)
}).disposed(by: rx.disposeBag)
}
}
Loading

0 comments on commit 90bf675

Please sign in to comment.