Adds basic GameSyncStatusViewController to view status of game-related records

This commit is contained in:
Riley Testut 2018-11-20 14:47:47 -06:00
parent 5354d779c1
commit ca4ccfc3ae
5 changed files with 226 additions and 14 deletions

View File

@ -99,6 +99,7 @@
BF7AE80A1C2E8C7600B1B5BC /* UIColor+Delta.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF7AE8091C2E8C7600B1B5BC /* UIColor+Delta.swift */; }; BF7AE80A1C2E8C7600B1B5BC /* UIColor+Delta.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF7AE8091C2E8C7600B1B5BC /* UIColor+Delta.swift */; };
BF80E1D21F13117000847008 /* ControllerInputsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF616A121F08184A0077F8B2 /* ControllerInputsViewController.swift */; }; BF80E1D21F13117000847008 /* ControllerInputsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF616A121F08184A0077F8B2 /* ControllerInputsViewController.swift */; };
BF8A333421A484A000A42FD4 /* BadgedTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF8A333321A484A000A42FD4 /* BadgedTableViewCell.swift */; }; BF8A333421A484A000A42FD4 /* BadgedTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF8A333321A484A000A42FD4 /* BadgedTableViewCell.swift */; };
BF8A334621A4926F00A42FD4 /* GameSyncStatusViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF8A334521A4926F00A42FD4 /* GameSyncStatusViewController.swift */; };
BF8CA9361F5F651900499FDD /* PopoverMenuController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF8CA9351F5F651900499FDD /* PopoverMenuController.swift */; }; BF8CA9361F5F651900499FDD /* PopoverMenuController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF8CA9351F5F651900499FDD /* PopoverMenuController.swift */; };
BF8DDD241F4F6C880088A21B /* InputCalloutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF8DDD231F4F6C880088A21B /* InputCalloutView.swift */; }; BF8DDD241F4F6C880088A21B /* InputCalloutView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF8DDD231F4F6C880088A21B /* InputCalloutView.swift */; };
BF95E2771E4977BF0030E7AD /* GameMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF95E2761E4977BF0030E7AD /* GameMetadata.swift */; }; BF95E2771E4977BF0030E7AD /* GameMetadata.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF95E2761E4977BF0030E7AD /* GameMetadata.swift */; };
@ -247,6 +248,7 @@
BF7AE8041C2E858400B1B5BC /* GridMenuViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GridMenuViewController.swift; sourceTree = "<group>"; }; BF7AE8041C2E858400B1B5BC /* GridMenuViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GridMenuViewController.swift; sourceTree = "<group>"; };
BF7AE8091C2E8C7600B1B5BC /* UIColor+Delta.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIColor+Delta.swift"; sourceTree = "<group>"; }; BF7AE8091C2E8C7600B1B5BC /* UIColor+Delta.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UIColor+Delta.swift"; sourceTree = "<group>"; };
BF8A333321A484A000A42FD4 /* BadgedTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BadgedTableViewCell.swift; sourceTree = "<group>"; }; BF8A333321A484A000A42FD4 /* BadgedTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BadgedTableViewCell.swift; sourceTree = "<group>"; };
BF8A334521A4926F00A42FD4 /* GameSyncStatusViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GameSyncStatusViewController.swift; sourceTree = "<group>"; };
BF8CA9351F5F651900499FDD /* PopoverMenuController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopoverMenuController.swift; sourceTree = "<group>"; }; BF8CA9351F5F651900499FDD /* PopoverMenuController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PopoverMenuController.swift; sourceTree = "<group>"; };
BF8DDD231F4F6C880088A21B /* InputCalloutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputCalloutView.swift; sourceTree = "<group>"; }; BF8DDD231F4F6C880088A21B /* InputCalloutView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InputCalloutView.swift; sourceTree = "<group>"; };
BF95E2761E4977BF0030E7AD /* GameMetadata.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GameMetadata.swift; sourceTree = "<group>"; }; BF95E2761E4977BF0030E7AD /* GameMetadata.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GameMetadata.swift; sourceTree = "<group>"; };
@ -375,6 +377,7 @@
children = ( children = (
BF48F74D219A16DA00BC2FC1 /* SyncingServicesViewController.swift */, BF48F74D219A16DA00BC2FC1 /* SyncingServicesViewController.swift */,
BFDB3417219E4B1700595A62 /* SyncStatusViewController.swift */, BFDB3417219E4B1700595A62 /* SyncStatusViewController.swift */,
BF8A334521A4926F00A42FD4 /* GameSyncStatusViewController.swift */,
); );
path = Syncing; path = Syncing;
sourceTree = "<group>"; sourceTree = "<group>";
@ -930,6 +933,7 @@
BFBAB2E31EB685A2004E0B0E /* DeltaCoreProtocol+Delta.swift in Sources */, BFBAB2E31EB685A2004E0B0E /* DeltaCoreProtocol+Delta.swift in Sources */,
BF3540081C5DAFAD00C1184C /* PauseTransitionCoordinator.swift in Sources */, BF3540081C5DAFAD00C1184C /* PauseTransitionCoordinator.swift in Sources */,
BF525EEA1FF6CD12004AA849 /* DeepLink.swift in Sources */, BF525EEA1FF6CD12004AA849 /* DeepLink.swift in Sources */,
BF8A334621A4926F00A42FD4 /* GameSyncStatusViewController.swift in Sources */,
BF59427E1E09BC830051894B /* Game.swift in Sources */, BF59427E1E09BC830051894B /* Game.swift in Sources */,
BFAA1FED1B8AA4FA00495943 /* Settings.swift in Sources */, BFAA1FED1B8AA4FA00495943 /* Settings.swift in Sources */,
BFA0D1271D3AE1F600565894 /* GameViewController.swift in Sources */, BFA0D1271D3AE1F600565894 /* GameViewController.swift in Sources */,

View File

@ -828,6 +828,9 @@
</label> </label>
</subviews> </subviews>
</tableViewCellContentView> </tableViewCellContentView>
<connections>
<segue destination="OnX-sX-bHK" kind="show" identifier="showGame" id="vUN-0T-oaK"/>
</connections>
</tableViewCell> </tableViewCell>
</prototypes> </prototypes>
<connections> <connections>
@ -840,5 +843,43 @@
</objects> </objects>
<point key="canvasLocation" x="3351" y="1872"/> <point key="canvasLocation" x="3351" y="1872"/>
</scene> </scene>
<!--Game Sync Status View Controller-->
<scene sceneID="iQk-cq-qsQ">
<objects>
<tableViewController id="OnX-sX-bHK" customClass="GameSyncStatusViewController" customModule="Delta" customModuleProvider="target" sceneMemberID="viewController">
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="grouped" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="18" sectionFooterHeight="18" id="AFt-Hn-fzR">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" cocoaTouchSystemColor="groupTableViewBackgroundColor"/>
<prototypes>
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" accessoryType="disclosureIndicator" indentationWidth="10" reuseIdentifier="Cell" textLabel="RWL-4W-NpH" style="IBUITableViewCellStyleDefault" id="q5G-Db-MXt">
<rect key="frame" x="0.0" y="55.5" width="375" height="44"/>
<autoresizingMask key="autoresizingMask"/>
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="q5G-Db-MXt" id="Ric-Km-AWj">
<rect key="frame" x="0.0" y="0.0" width="341" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Game" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="RWL-4W-NpH">
<rect key="frame" x="16" y="0.0" width="324" height="43.5"/>
<autoresizingMask key="autoresizingMask"/>
<fontDescription key="fontDescription" type="system" pointSize="17"/>
<nil key="textColor"/>
<nil key="highlightedColor"/>
</label>
</subviews>
</tableViewCellContentView>
</tableViewCell>
</prototypes>
<sections/>
<connections>
<outlet property="dataSource" destination="OnX-sX-bHK" id="TuS-hI-T9X"/>
<outlet property="delegate" destination="OnX-sX-bHK" id="vII-vu-IIw"/>
</connections>
</tableView>
</tableViewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="FHk-WU-IoC" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="4206" y="1872"/>
</scene>
</scenes> </scenes>
</document> </document>

View File

@ -0,0 +1,154 @@
//
// GameSyncStatusViewController.swift
// Delta
//
// Created by Riley Testut on 11/20/18.
// Copyright © 2018 Riley Testut. All rights reserved.
//
import UIKit
import Roxas
import Harmony
extension GameSyncStatusViewController
{
private enum Section: Int, CaseIterable
{
case game
case saveStates
case cheats
}
}
class GameSyncStatusViewController: UITableViewController
{
var game: Game!
private lazy var dataSource = self.makeDataSource()
private let dateFormatter: DateFormatter = {
let dateFormatter = DateFormatter()
dateFormatter.timeStyle = .short
dateFormatter.dateStyle = .short
return dateFormatter
}()
private var recordsByObjectURI = [URL: Record<NSManagedObject>]()
override func viewDidLoad()
{
super.viewDidLoad()
self.title = self.game.name
self.tableView.dataSource = self.dataSource
}
override func viewWillAppear(_ animated: Bool)
{
self.fetchRecords()
super.viewWillAppear(animated)
}
}
private extension GameSyncStatusViewController
{
private func makeDataSource() -> RSTCompositeTableViewDataSource<NSManagedObject>
{
func configure(_ cell: UITableViewCell, recordedObject: NSManagedObject)
{
if let record = self.recordsByObjectURI[recordedObject.objectID.uriRepresentation()], record.isConflicted
{
cell.textLabel?.textColor = .red
}
else
{
cell.textLabel?.textColor = .darkText
}
}
let gameDataSource = RSTArrayTableViewDataSource<Game>(items: [self.game])
gameDataSource.cellConfigurationHandler = { (cell, game, indexPath) in
cell.textLabel?.text = game.name
configure(cell, recordedObject: game)
}
let saveStatesFetchRequest = SaveState.fetchRequest() as NSFetchRequest<SaveState>
saveStatesFetchRequest.predicate = NSPredicate(format: "%K == %@", #keyPath(SaveState.game), self.game)
saveStatesFetchRequest.sortDescriptors = [NSSortDescriptor(keyPath: \SaveState.creationDate, ascending: true)]
let saveStatesDataSource = RSTFetchedResultsTableViewDataSource(fetchRequest: saveStatesFetchRequest, managedObjectContext: DatabaseManager.shared.viewContext)
saveStatesDataSource.cellConfigurationHandler = { (cell, saveState, indexPath) in
if let name = saveState.name
{
cell.textLabel?.text = name
}
else
{
cell.textLabel?.text = self.dateFormatter.string(from: saveState.modifiedDate)
}
configure(cell, recordedObject: saveState)
}
let cheatsFetchRequest = Cheat.fetchRequest() as NSFetchRequest<Cheat>
cheatsFetchRequest.predicate = NSPredicate(format: "%K == %@", #keyPath(Cheat.game), self.game)
cheatsFetchRequest.sortDescriptors = [NSSortDescriptor(keyPath: \Cheat.name, ascending: true)]
let cheatsDataSource = RSTFetchedResultsTableViewDataSource(fetchRequest: cheatsFetchRequest, managedObjectContext: DatabaseManager.shared.viewContext)
cheatsDataSource.cellConfigurationHandler = { (cell, cheat, indexPath) in
cell.textLabel?.text = cheat.name
configure(cell, recordedObject: cheat)
}
let dataSources = [gameDataSource, saveStatesDataSource, cheatsDataSource] as! [RSTArrayTableViewDataSource<NSManagedObject>]
let dataSource = RSTCompositeTableViewDataSource(dataSources: dataSources)
dataSource.proxy = self
return dataSource
}
func fetchRecords()
{
var recordsByObjectURI = [URL: Record<NSManagedObject>]()
do
{
let recordedObjects = ([self.game!] + Array(self.game.saveStates) + Array(self.game.cheats)) as! [SyncableManagedObject]
let records = try SyncManager.shared.recordController.fetchRecords(for: recordedObjects)
for record in records
{
guard let recordedObject = record.recordedObject else { continue }
recordsByObjectURI[recordedObject.objectID.uriRepresentation()] = record
}
}
catch
{
print(error)
}
self.recordsByObjectURI = recordsByObjectURI
}
}
extension GameSyncStatusViewController
{
override func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String?
{
guard self.dataSource.tableView(self.tableView, numberOfRowsInSection: section) > 0 else { return nil }
switch Section.allCases[section]
{
case .game: return NSLocalizedString("Game", comment: "")
case .saveStates: return NSLocalizedString("Save States", comment: "")
case .cheats: return NSLocalizedString("Cheats", comment: "")
}
}
}

View File

@ -30,6 +30,18 @@ class SyncStatusViewController: UITableViewController
self.fetchConflictedRecords() self.fetchConflictedRecords()
} }
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
guard segue.identifier == "showGame" else { return }
guard let cell = sender as? UITableViewCell, let indexPath = self.tableView.indexPath(for: cell) else { return }
let game = self.dataSource.item(at: indexPath)
let gameSyncStatusViewController = segue.destination as! GameSyncStatusViewController
gameSyncStatusViewController.game = game
}
} }
private extension SyncStatusViewController private extension SyncStatusViewController
@ -92,7 +104,7 @@ private extension SyncStatusViewController
for record in records for record in records
{ {
guard let recordedObject = record.recordedObject else { continue } guard let recordedObject = record.recordedObject else { continue }
recordedObject.managedObjectContext?.performAndWait {
let conflictedGame: Game? let conflictedGame: Game?
switch recordedObject switch recordedObject
@ -103,10 +115,11 @@ private extension SyncStatusViewController
default: conflictedGame = nil default: conflictedGame = nil
} }
guard let game = conflictedGame else { continue } guard let game = conflictedGame else { return }
gameConflictsCount[game.objectID.uriRepresentation(), default: 0] += 1 gameConflictsCount[game.objectID.uriRepresentation(), default: 0] += 1
} }
}
self.gameConflictsCount = gameConflictsCount self.gameConflictsCount = gameConflictsCount
} }

2
External/Harmony vendored

@ -1 +1 @@
Subproject commit 6f5dc280077cee8382ccc6622ee8383aadb59d48 Subproject commit ef2c685333abe1cce9ee0a43150656634ab06081