This file was deleted.

This file was deleted.

This file was deleted.

@@ -586,6 +586,7 @@
"${BUILT_PRODUCTS_DIR}/Moya/Moya.framework",
"${BUILT_PRODUCTS_DIR}/RSSwizzle/RSSwizzle.framework",
"${BUILT_PRODUCTS_DIR}/Result/Result.framework",
"${PODS_ROOT}/Reveal-SDK/RevealServer-15/iOS/RevealServer.framework",
"${BUILT_PRODUCTS_DIR}/RxCocoa/RxCocoa.framework",
"${BUILT_PRODUCTS_DIR}/RxSwift/RxSwift.framework",
"${BUILT_PRODUCTS_DIR}/RxSwiftExt/RxSwiftExt.framework",
@@ -603,6 +604,7 @@
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Moya.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RSSwizzle.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Result.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RevealServer.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxCocoa.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxSwift.framework",
"${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxSwiftExt.framework",
@@ -4,6 +4,8 @@
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>$(DEVELOPMENT_LANGUAGE)</string>
<key>CFBundleDisplayName</key>
<string>SpaceX.io</string>
<key>CFBundleExecutable</key>
<string>$(EXECUTABLE_NAME)</string>
<key>CFBundleIdentifier</key>
@@ -87,6 +87,8 @@ extension LaunchDetailViewController: UITableViewDataSource {
let cell = tableView.dequeueCell(LaunchVideoCell.self, indexPath: indexPath)
if let videoId = viewModel.object.value?.links?.youTubeVideoId {
cell.configure(with: videoId)
} else {
cell.showEmpty()
}
return cell

@@ -63,7 +63,9 @@ class LaunchDetailViewModel: ValueViewModel<Launch> {
sections.append(contentsOf: secondStage.payloads.map { generatePayloadSection(from: $0) })
}

sections.append(generateLinksSection(from: launch))
if let links = generateLinksSection(from: launch) {
sections.append(links)
}

return sections
}
@@ -88,17 +90,28 @@ class LaunchDetailViewModel: ValueViewModel<Launch> {
}

private func generateBoosterSection(from core: Rocket.Core, coreNumber: Int?) -> InfoSection {
var props = [PropertyWithDetail(propertyName: "Serial", propertyValue: core.coreSerial)]

var props = [PropertyWithDetail]()

if let coreSerial = core.coreSerial {
props.append(PropertyWithDetail(propertyName: "Serial", propertyValue: coreSerial))
}

if let block = core.block {
props.append(PropertyWithDetail(propertyName: "Block", propertyValue: "\(block)"))
}

if let landingType = core.landingType {
props.append(PropertyWithDetail(propertyName: "Landing type", propertyValue: landingType.rawValue))
}

props.append(contentsOf: [
PropertyWithDetail(propertyName: "Flight number", propertyValue: "\(core.flightNumber)"),
PropertyWithDetail(propertyName: "Reused", propertyValue: "\(core.reused ? "Yes" : "No")"),
])
if let flightNumber = core.flightNumber {
props.append(PropertyWithDetail(propertyName: "Flight number", propertyValue: "#\(flightNumber)"))
}

if let reused = core.reused {
props.append(PropertyWithDetail(propertyName: "Reused", propertyValue: "\(reused ? "Yes" : "No")"))
}

if let landingType = core.landingType {
props.append(PropertyWithDetail(propertyName: "Landing type", propertyValue: landingType.rawValue))
@@ -174,7 +187,7 @@ class LaunchDetailViewModel: ValueViewModel<Launch> {
return InfoSection(sectionName: "Payload: \(payload.payloadId)", properties: props)
}

private func generateLinksSection(from launch: Launch) -> InfoSection {
private func generateLinksSection(from launch: Launch) -> InfoSection? {
var props = [PropertyWithDetail]()

if let missionPatch = URL(string: launch.links?.missionPatch) {
@@ -209,6 +222,7 @@ class LaunchDetailViewModel: ValueViewModel<Launch> {
props.append(PropertyWithDetail(propertyName: "Wikipedia", propertyValue: wikipedia.host!, detail: .web(url: wikipedia)))
}

guard props.count > 0 else { return nil }
return InfoSection(sectionName: "Links", properties: props)
}

@@ -7,18 +7,25 @@
//
import Foundation
import AVFoundation
import UIKit
import youtube_ios_player_helper

class LaunchVideoCell: UITableViewCell {

private let noVideoYetLabel = UILabel().setUp {
$0.textColor = .lightTextColor
$0.font = UIFont.systemFont(ofSize: 14)
$0.text = "No video yet"
$0.isHidden = true
$0.textAlignment = .center
}

private let playerView = YTPlayerView()

override init(style: UITableViewCellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)

contentView.addSubviews([playerView])
contentView.addSubviews([noVideoYetLabel, playerView])
}

required init?(coder aDecoder: NSCoder) {
@@ -29,6 +36,7 @@ class LaunchVideoCell: UITableViewCell {
super.layoutSubviews()

playerView.frame = contentView.bounds
noVideoYetLabel.frame = contentView.bounds
}

func configure(with videoId: String) {
@@ -37,4 +45,8 @@ class LaunchVideoCell: UITableViewCell {
playerView.load(withVideoId: videoId)
}

func showEmpty() {
noVideoYetLabel.isHidden = false
}

}
@@ -9,6 +9,7 @@
import AlamofireImage
import Foundation
import UIKit
import DateToolsSwift

class LaunchOverviewCell: UITableViewCell {

@@ -49,6 +50,11 @@ class LaunchOverviewCell: UITableViewCell {
$0.font = UIFont.systemFont(ofSize: 14)
}

private let countdownLabel = UILabel().setUp {
$0.textColor = .lightTextColor
$0.font = UIFont.systemFont(ofSize: 14, weight: .medium)
}

private var payloadOrbitTagViews = [TagView]()
private var landingSiteTagViews = [TagView]()

@@ -58,7 +64,7 @@ class LaunchOverviewCell: UITableViewCell {
backgroundColor = .clear

contentView.addSubviews([backgroundImageView, actualContentView])
actualContentView.addSubviews([missionPatchImageView, nameLabel, dateIconImageView, launchDateLabel, launchSiteIconImageView, launchSiteLabel, vehicleIconImageView, vehicleNameLabel])
actualContentView.addSubviews([missionPatchImageView, nameLabel, dateIconImageView, launchDateLabel, launchSiteIconImageView, launchSiteLabel, vehicleIconImageView, vehicleNameLabel, countdownLabel])
}

required init?(coder aDecoder: NSCoder) {
@@ -84,8 +90,9 @@ class LaunchOverviewCell: UITableViewCell {
dateIconImageView.left = nameLabel.left
dateIconImageView.top = nameLabel.bottom + 4

launchDateLabel.sizeToFit()
launchDateLabel.left = dateIconImageView.right + 4
launchDateLabel.sizeToFit()
launchDateLabel.width = actualContentView.width - launchDateLabel.left - 10
launchDateLabel.centerY = dateIconImageView.centerY

launchSiteIconImageView.size = CGSize(width: 14, height: 14)
@@ -121,18 +128,31 @@ class LaunchOverviewCell: UITableViewCell {
default: tagView.left = self.landingSiteTagViews[index - 1].right + 4
}
}

countdownLabel.sizeToFit()
countdownLabel.left = missionPatchImageView.left
countdownLabel.width = missionPatchImageView.width
countdownLabel.top = missionPatchImageView.bottom + 10
}

func configure(with launch: Launch) {

nameLabel.text = launch.missionName

if let firsStage = launch.rocket.firstStage {
vehicleNameLabel.text = firsStage.cores.map { $0.coreSerial }.joined(separator: ", ")
vehicleNameLabel.text = firsStage.cores.compactMap { $0.coreSerial }.joined(separator: ", ")
}

if let launchDate = launch.launchDate {
launchDateLabel.text = DateFormatter.launchDateFormatter.string(from: launchDate)
launchDateLabel.text = (launch.upcoming ? "\(DateFormatter().shortWeekdaySymbols[Calendar.current.component(.weekday, from: launchDate) - 1]), " : "") + DateFormatter.launchDateFormatter.string(from: launchDate)

if launch.upcoming {
let lol = DateComponentsFormatter()
lol.unitsStyle = .full
lol.maximumUnitCount = 2
countdownLabel.text = lol.string(from: TimePeriod(beginning: Date(), end: launchDate).duration)
countdownLabel.isHidden = false
}
}

if let missionPatch = launch.links?.missionPatch, let missionPatchUrl = URL(string: missionPatch) {
@@ -199,6 +219,8 @@ class LaunchOverviewCell: UITableViewCell {
launchDateLabel.text = nil
launchSiteLabel.text = nil

countdownLabel.isHidden = true

missionPatchImageView.image = nil
missionPatchImageView.af_cancelImageRequest()

@@ -50,7 +50,7 @@ class LaunchesViewController: UIViewController {
searchController.searchBar.placeholder = "Search Launches"
searchController.searchBar.searchBarStyle = .minimal
searchController.hidesNavigationBarDuringPresentation = false
searchController.searchBar.scopeButtonTitles = ["Upcoming", "Past"]
searchController.searchBar.scopeButtonTitles = ["Past", "Upcoming"]
searchController.searchBar.showsScopeBar = true
searchController.searchBar.delegate = self
navigationItem.titleView = searchController.searchBar
@@ -60,7 +60,7 @@ class LaunchesViewController: UIViewController {

viewModel.dataState.asObservable().subscribe(onNext: { [weak self] (dataState) in
self?.listManager.state = dataState
self?.viewModel.scope.value = .upcoming
self?.viewModel.scope.value = .past
}).disposed(by: bag)

viewModel.filteredLaunches.asObservable().subscribe(onNext: { [weak self] _ in
@@ -73,6 +73,9 @@ class LaunchesViewController: UIViewController {
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)

navigationController?.view.setNeedsLayout() // force update layout...
navigationController?.view.layoutIfNeeded() // ... to fix height of the navigation bar
if let selectedIndexPath = _view.tableView.indexPathForSelectedRow {
_view.tableView.deselectRow(at: selectedIndexPath, animated: animated)
}
@@ -85,6 +88,13 @@ class LaunchesViewController: UIViewController {
searchController.isActive = false
}

override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)

navigationController?.view.setNeedsLayout() // force update layout...
navigationController?.view.layoutIfNeeded() // ... to fix height of the navigation bar
}

private func isFiltering() -> Bool {
return searchController.isActive && !searchBarIsEmpty()
}
@@ -107,6 +117,11 @@ extension LaunchesViewController: UISearchResultsUpdating {
}

extension LaunchesViewController: UISearchBarDelegate {

// func searchBarTextDidEndEditing(_ searchBar: UISearchBar) {
// searchController.searchBar.showsScopeBar = true
// }
func searchBar(_ searchBar: UISearchBar, selectedScopeButtonIndexDidChange selectedScope: Int) {
if let scope = LaunchesViewModel.Scope(rawValue: selectedScope) {
viewModel.scope.value = scope
@@ -12,15 +12,15 @@ import RxSwift
class LaunchesViewModel: ValuesViewModel<Launch> {

enum Scope: Int {
case upcoming, past
case past, upcoming
}

let filteredLaunches = Variable<[Launch]>([])

let pastLaunches = Variable<[Launch]>([])
let futureLaunches = Variable<[Launch]>([])

let scope = Variable<Scope>(.upcoming)
let scope = Variable<Scope>(.past)

let searchText = Variable<String?>(nil)

@@ -18,7 +18,7 @@ extension DateFormatter {
}

static let launchDateFormatter = DateFormatter().setUp {
$0.dateStyle = .short
$0.dateStyle = .medium
$0.timeStyle = .short
}
}
@@ -175,30 +175,30 @@ extension Rocket {
case ocean = "Ocean"
}

let coreSerial: String
let flightNumber: Int
let coreSerial: String?
let flightNumber: Int?
let block: Int?
let reused: Bool
let reused: Bool?
let landingSuccess: Bool?
let landingType: LandingType?
let landingVehicle: String?

init?(json: JSON) {
guard let serial = json["core_serial"].string else { return nil }
self.coreSerial = serial
self.flightNumber = json["flight"].intValue
self.flightNumber = json["flight"].int
self.block = json["block"].int
self.reused = json["reused"].boolValue
self.reused = json["reused"].bool
self.landingSuccess = json["land_success"].bool
self.landingType = LandingType(rawValue: json["landing_type"].stringValue)
self.landingVehicle = json["landing_vehicle"].string
}

var blockAndSerialDisplayName: String {
if let block = self.block {
return "B\(block) \(coreSerial)"
if let block = self.block, let serial = coreSerial {
return "B\(block) \(serial)"
} else {
return coreSerial
return coreSerial ?? ""
}
}
}
@@ -45,7 +45,7 @@ class WindowManager {

func initializeWindow() -> UIWindow {
let window = UIWindow(frame: UIScreen.main.bounds)
window.backgroundColor = UIColor.black
window.backgroundColor = #colorLiteral(red: 0.03137254902, green: 0.3254901961, blue: 0.5254901961, alpha: 1)

rootNavigationController.isNavigationBarHidden = true
rootNavigationController.pushViewController(UIViewController(), animated: false)