mirror of
https://github.com/SideStore/SideStore.git
synced 2026-02-09 06:43:25 +01:00
Switches to Core Data model objects
This commit is contained in:
@@ -9,6 +9,11 @@
|
||||
/* Begin PBXBuildFile section */
|
||||
BF5AB3A82285FE7500DC914B /* AltSign.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BF5AB3A72285FE6C00DC914B /* AltSign.framework */; };
|
||||
BF5AB3A92285FE7500DC914B /* AltSign.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = BF5AB3A72285FE6C00DC914B /* AltSign.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
BFB11692229322E400BB457C /* DatabaseManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFB11691229322E400BB457C /* DatabaseManager.swift */; };
|
||||
BFB1169B2293274D00BB457C /* JSONDecoder+ManagedObjectContext.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFB1169A2293274D00BB457C /* JSONDecoder+ManagedObjectContext.swift */; };
|
||||
BFB1169D22932DB100BB457C /* Apps.json in Resources */ = {isa = PBXBuildFile; fileRef = BFB1169C22932DB100BB457C /* Apps.json */; };
|
||||
BFBBE2DD22931B20002097FA /* AltStore.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = BFBBE2DB22931B20002097FA /* AltStore.xcdatamodeld */; };
|
||||
BFBBE2DF22931F73002097FA /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFBBE2DE22931F73002097FA /* App.swift */; };
|
||||
BFD2476E2284B9A500981D42 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFD2476D2284B9A500981D42 /* AppDelegate.swift */; };
|
||||
BFD247702284B9A500981D42 /* AppsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFD2476F2284B9A500981D42 /* AppsViewController.swift */; };
|
||||
BFD247722284B9A500981D42 /* SecondViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFD247712284B9A500981D42 /* SecondViewController.swift */; };
|
||||
@@ -20,8 +25,6 @@
|
||||
BFD2478C2284C4C300981D42 /* AppIconImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFD2478B2284C4C300981D42 /* AppIconImageView.swift */; };
|
||||
BFD2478F2284C8F900981D42 /* Button.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFD2478E2284C8F900981D42 /* Button.swift */; };
|
||||
BFD247932284D4B700981D42 /* AppTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFD247922284D4B700981D42 /* AppTableViewCell.swift */; };
|
||||
BFD247952284D7BD00981D42 /* Apps.plist in Resources */ = {isa = PBXBuildFile; fileRef = BFD247942284D7BD00981D42 /* Apps.plist */; };
|
||||
BFD2479A2284D80900981D42 /* App.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFD247992284D80900981D42 /* App.swift */; };
|
||||
BFD2479C2284E19A00981D42 /* AppDetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFD2479B2284E19A00981D42 /* AppDetailViewController.swift */; };
|
||||
BFD2479F2284FBD000981D42 /* UIColor+AltStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFD2479E2284FBD000981D42 /* UIColor+AltStore.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
@@ -43,6 +46,11 @@
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
BF5AB3A72285FE6C00DC914B /* AltSign.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = AltSign.framework; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
BFB11691229322E400BB457C /* DatabaseManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatabaseManager.swift; sourceTree = "<group>"; };
|
||||
BFB1169A2293274D00BB457C /* JSONDecoder+ManagedObjectContext.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "JSONDecoder+ManagedObjectContext.swift"; sourceTree = "<group>"; };
|
||||
BFB1169C22932DB100BB457C /* Apps.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = Apps.json; sourceTree = "<group>"; };
|
||||
BFBBE2DC22931B20002097FA /* AltStore.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = AltStore.xcdatamodel; sourceTree = "<group>"; };
|
||||
BFBBE2DE22931F73002097FA /* App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = "<group>"; };
|
||||
BFD2476A2284B9A500981D42 /* AltStore.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = AltStore.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
BFD2476D2284B9A500981D42 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
|
||||
BFD2476F2284B9A500981D42 /* AppsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppsViewController.swift; sourceTree = "<group>"; };
|
||||
@@ -55,8 +63,6 @@
|
||||
BFD2478B2284C4C300981D42 /* AppIconImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppIconImageView.swift; sourceTree = "<group>"; };
|
||||
BFD2478E2284C8F900981D42 /* Button.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Button.swift; sourceTree = "<group>"; };
|
||||
BFD247922284D4B700981D42 /* AppTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppTableViewCell.swift; sourceTree = "<group>"; };
|
||||
BFD247942284D7BD00981D42 /* Apps.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Apps.plist; sourceTree = "<group>"; };
|
||||
BFD247992284D80900981D42 /* App.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = App.swift; sourceTree = "<group>"; };
|
||||
BFD2479B2284E19A00981D42 /* AppDetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDetailViewController.swift; sourceTree = "<group>"; };
|
||||
BFD2479E2284FBD000981D42 /* UIColor+AltStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+AltStore.swift"; sourceTree = "<group>"; };
|
||||
/* End PBXFileReference section */
|
||||
@@ -139,7 +145,7 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BFD247762284B9A700981D42 /* Assets.xcassets */,
|
||||
BFD247942284D7BD00981D42 /* Apps.plist */,
|
||||
BFB1169C22932DB100BB457C /* Apps.json */,
|
||||
);
|
||||
path = Resources;
|
||||
sourceTree = "<group>";
|
||||
@@ -156,7 +162,9 @@
|
||||
BFD247982284D7FC00981D42 /* Model */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BFD247992284D80900981D42 /* App.swift */,
|
||||
BFBBE2DB22931B20002097FA /* AltStore.xcdatamodeld */,
|
||||
BFB11691229322E400BB457C /* DatabaseManager.swift */,
|
||||
BFBBE2DE22931F73002097FA /* App.swift */,
|
||||
);
|
||||
path = Model;
|
||||
sourceTree = "<group>";
|
||||
@@ -165,6 +173,7 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BFD2479E2284FBD000981D42 /* UIColor+AltStore.swift */,
|
||||
BFB1169A2293274D00BB457C /* JSONDecoder+ManagedObjectContext.swift */,
|
||||
);
|
||||
path = Extensions;
|
||||
sourceTree = "<group>";
|
||||
@@ -228,7 +237,7 @@
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
BFD247952284D7BD00981D42 /* Apps.plist in Resources */,
|
||||
BFB1169D22932DB100BB457C /* Apps.json in Resources */,
|
||||
BFD2477A2284B9A700981D42 /* LaunchScreen.storyboard in Resources */,
|
||||
BFD247772284B9A700981D42 /* Assets.xcassets in Resources */,
|
||||
BFD247752284B9A500981D42 /* Main.storyboard in Resources */,
|
||||
@@ -244,11 +253,14 @@
|
||||
files = (
|
||||
BFD2478F2284C8F900981D42 /* Button.swift in Sources */,
|
||||
BFD247722284B9A500981D42 /* SecondViewController.swift in Sources */,
|
||||
BFB11692229322E400BB457C /* DatabaseManager.swift in Sources */,
|
||||
BFBBE2DF22931F73002097FA /* App.swift in Sources */,
|
||||
BFD2478C2284C4C300981D42 /* AppIconImageView.swift in Sources */,
|
||||
BFD2479C2284E19A00981D42 /* AppDetailViewController.swift in Sources */,
|
||||
BFD2476E2284B9A500981D42 /* AppDelegate.swift in Sources */,
|
||||
BFD247702284B9A500981D42 /* AppsViewController.swift in Sources */,
|
||||
BFD2479A2284D80900981D42 /* App.swift in Sources */,
|
||||
BFBBE2DD22931B20002097FA /* AltStore.xcdatamodeld in Sources */,
|
||||
BFB1169B2293274D00BB457C /* JSONDecoder+ManagedObjectContext.swift in Sources */,
|
||||
BFD247932284D4B700981D42 /* AppTableViewCell.swift in Sources */,
|
||||
BFD2479F2284FBD000981D42 /* UIColor+AltStore.swift in Sources */,
|
||||
);
|
||||
@@ -450,6 +462,19 @@
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
|
||||
/* Begin XCVersionGroup section */
|
||||
BFBBE2DB22931B20002097FA /* AltStore.xcdatamodeld */ = {
|
||||
isa = XCVersionGroup;
|
||||
children = (
|
||||
BFBBE2DC22931B20002097FA /* AltStore.xcdatamodel */,
|
||||
);
|
||||
currentVersion = BFBBE2DC22931B20002097FA /* AltStore.xcdatamodel */;
|
||||
path = AltStore.xcdatamodeld;
|
||||
sourceTree = "<group>";
|
||||
versionGroupType = wrapper.xcdatamodel;
|
||||
};
|
||||
/* End XCVersionGroup section */
|
||||
};
|
||||
rootObject = BFD247622284B9A500981D42 /* Project object */;
|
||||
}
|
||||
|
||||
@@ -13,9 +13,19 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
|
||||
var window: UIWindow?
|
||||
|
||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool
|
||||
{
|
||||
DatabaseManager.shared.start { (error) in
|
||||
if let error = error
|
||||
{
|
||||
print("Failed to start DatabaseManager.", error)
|
||||
}
|
||||
else
|
||||
{
|
||||
print("Started DatabaseManager")
|
||||
}
|
||||
}
|
||||
|
||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
|
||||
// Override point for customization after application launch.
|
||||
return true
|
||||
}
|
||||
|
||||
|
||||
@@ -32,7 +32,6 @@ class AppDetailViewController: UITableViewController
|
||||
private lazy var screenshotsDataSource = self.makeScreenshotsDataSource()
|
||||
|
||||
@IBOutlet private var nameLabel: UILabel!
|
||||
@IBOutlet private var subtitleLabel: UILabel!
|
||||
@IBOutlet private var developerButton: UIButton!
|
||||
@IBOutlet private var appIconImageView: UIImageView!
|
||||
|
||||
@@ -73,8 +72,7 @@ private extension AppDetailViewController
|
||||
func update()
|
||||
{
|
||||
self.nameLabel.text = self.app.name
|
||||
self.subtitleLabel.text = self.app.subtitle
|
||||
self.developerButton.setTitle(self.app.developer, for: .normal)
|
||||
self.developerButton.setTitle(self.app.developerName, for: .normal)
|
||||
self.appIconImageView.image = UIImage(named: self.app.iconName)
|
||||
|
||||
let text = String(format: NSLocalizedString("Download %@", comment: ""), self.app.name)
|
||||
|
||||
@@ -11,7 +11,7 @@ import UIKit
|
||||
@objc class AppTableViewCell: UITableViewCell
|
||||
{
|
||||
@IBOutlet var nameLabel: UILabel!
|
||||
@IBOutlet var subtitleLabel: UILabel!
|
||||
@IBOutlet var developerLabel: UILabel!
|
||||
@IBOutlet var appIconImageView: UIImageView!
|
||||
@IBOutlet var button: UIButton!
|
||||
|
||||
|
||||
@@ -23,6 +23,13 @@ class AppsViewController: UITableViewController
|
||||
self.tableView.tableFooterView = UIView()
|
||||
}
|
||||
|
||||
override func viewWillAppear(_ animated: Bool)
|
||||
{
|
||||
super.viewWillAppear(animated)
|
||||
|
||||
self.fetchApps()
|
||||
}
|
||||
|
||||
override func prepare(for segue: UIStoryboardSegue, sender: Any?)
|
||||
{
|
||||
guard segue.identifier == "showAppDetail" else { return }
|
||||
@@ -38,27 +45,45 @@ class AppsViewController: UITableViewController
|
||||
|
||||
private extension AppsViewController
|
||||
{
|
||||
func makeDataSource() -> RSTArrayTableViewDataSource<App>
|
||||
func makeDataSource() -> RSTFetchedResultsTableViewDataSource<App>
|
||||
{
|
||||
let appsFileURL = Bundle.main.url(forResource: "Apps", withExtension: "plist")!
|
||||
let fetchRequest = App.fetchRequest() as NSFetchRequest<App>
|
||||
fetchRequest.relationshipKeyPathsForPrefetching = [#keyPath(InstalledApp.app)]
|
||||
fetchRequest.sortDescriptors = [NSSortDescriptor(keyPath: \App.name, ascending: true)]
|
||||
fetchRequest.returnsObjectsAsFaults = false
|
||||
|
||||
let dataSource = RSTFetchedResultsTableViewDataSource(fetchRequest: fetchRequest, managedObjectContext: DatabaseManager.shared.viewContext)
|
||||
dataSource.cellConfigurationHandler = { (cell, app, indexPath) in
|
||||
let cell = cell as! AppTableViewCell
|
||||
cell.nameLabel.text = app.name
|
||||
cell.developerLabel.text = app.developerName
|
||||
cell.appIconImageView.image = UIImage(named: app.iconName)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return dataSource
|
||||
}
|
||||
|
||||
func fetchApps()
|
||||
{
|
||||
DatabaseManager.shared.persistentContainer.performBackgroundTask { (context) in
|
||||
let appsFileURL = Bundle.main.url(forResource: "Apps", withExtension: "json")!
|
||||
|
||||
do
|
||||
{
|
||||
let data = try Data(contentsOf: appsFileURL)
|
||||
let apps = try PropertyListDecoder().decode([App].self, from: data)
|
||||
|
||||
let dataSource = RSTArrayTableViewDataSource(items: apps)
|
||||
dataSource.cellConfigurationHandler = { (cell, app, indexPath) in
|
||||
let cell = cell as! AppTableViewCell
|
||||
cell.nameLabel.text = app.name
|
||||
cell.subtitleLabel.text = app.subtitle
|
||||
cell.appIconImageView.image = UIImage(named: app.iconName)
|
||||
}
|
||||
return dataSource
|
||||
let decoder = JSONDecoder()
|
||||
decoder.managedObjectContext = context
|
||||
|
||||
_ = try decoder.decode([App].self, from: data)
|
||||
try context.save()
|
||||
}
|
||||
catch
|
||||
{
|
||||
fatalError("Failed to load apps. \(error)")
|
||||
fatalError("Failed to save fetched apps. \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,8 +143,8 @@
|
||||
<connections>
|
||||
<outlet property="appIconImageView" destination="gsm-sN-j56" id="1ip-RW-xBe"/>
|
||||
<outlet property="button" destination="ScM-Z2-rAe" id="VnW-vG-JCc"/>
|
||||
<outlet property="developerLabel" destination="xnf-XX-iX9" id="CYk-ud-YHz"/>
|
||||
<outlet property="nameLabel" destination="d4v-IK-LuC" id="wFw-Js-PXE"/>
|
||||
<outlet property="subtitleLabel" destination="xnf-XX-iX9" id="CYk-ud-YHz"/>
|
||||
<segue destination="hR3-go-2DG" kind="show" identifier="showAppDetail" id="F38-66-skN">
|
||||
<segue key="commit" inheritsFrom="parent" id="K6B-kg-jJz"/>
|
||||
<segue key="preview" inheritsFrom="commit" id="sJk-KX-3sw"/>
|
||||
@@ -196,7 +196,7 @@
|
||||
</constraints>
|
||||
</imageView>
|
||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="top" spacing="4" translatesAutoresizingMaskIntoConstraints="NO" id="F4R-0I-ucL">
|
||||
<rect key="frame" x="125" y="0.0" width="218" height="68"/>
|
||||
<rect key="frame" x="125" y="0.0" width="218" height="46"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="1000" text="Name" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="O5s-oz-KYW">
|
||||
<rect key="frame" x="0.0" y="0.0" width="55" height="24"/>
|
||||
@@ -204,14 +204,8 @@
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" verticalCompressionResistancePriority="1000" text="Subtitle" textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="ozE-C3-F1B">
|
||||
<rect key="frame" x="0.0" y="28" width="53" height="18"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="15"/>
|
||||
<color key="textColor" white="0.5" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<button opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="rMJ-KT-YRw">
|
||||
<rect key="frame" x="0.0" y="50" width="70" height="18"/>
|
||||
<rect key="frame" x="0.0" y="28" width="70" height="18"/>
|
||||
<constraints>
|
||||
<constraint firstAttribute="height" constant="18" id="2hT-PA-EjP"/>
|
||||
</constraints>
|
||||
@@ -272,7 +266,7 @@
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="OCS-uW-Big">
|
||||
<rect key="frame" x="0.0" y="0.0" width="138" height="300"/>
|
||||
<rect key="frame" x="0.0" y="0.0" width="138" height="253"/>
|
||||
</imageView>
|
||||
</subviews>
|
||||
</view>
|
||||
@@ -357,7 +351,6 @@
|
||||
<outlet property="downloadButton" destination="i1B-Mu-s1h" id="xbF-fk-xF8"/>
|
||||
<outlet property="nameLabel" destination="O5s-oz-KYW" id="seg-JJ-VfB"/>
|
||||
<outlet property="screenshotsCollectionView" destination="UJY-8X-bkB" id="6CI-Jg-yt6"/>
|
||||
<outlet property="subtitleLabel" destination="ozE-C3-F1B" id="daJ-dx-5se"/>
|
||||
</connections>
|
||||
</tableViewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="BLa-Qn-j83" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
|
||||
36
AltStore/Extensions/JSONDecoder+ManagedObjectContext.swift
Normal file
36
AltStore/Extensions/JSONDecoder+ManagedObjectContext.swift
Normal file
@@ -0,0 +1,36 @@
|
||||
//
|
||||
// JSONDecoder+ManagedObjectContext.swift
|
||||
// Harmony
|
||||
//
|
||||
// Created by Riley Testut on 10/3/18.
|
||||
// Copyright © 2018 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CoreData
|
||||
|
||||
private extension CodingUserInfoKey
|
||||
{
|
||||
static let managedObjectContext = CodingUserInfoKey(rawValue: "managedObjectContext")!
|
||||
}
|
||||
|
||||
public extension JSONDecoder
|
||||
{
|
||||
var managedObjectContext: NSManagedObjectContext? {
|
||||
get {
|
||||
let managedObjectContext = self.userInfo[.managedObjectContext] as? NSManagedObjectContext
|
||||
return managedObjectContext
|
||||
}
|
||||
set {
|
||||
self.userInfo[.managedObjectContext] = newValue
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public extension Decoder
|
||||
{
|
||||
var managedObjectContext: NSManagedObjectContext? {
|
||||
let managedObjectContext = self.userInfo[.managedObjectContext] as? NSManagedObjectContext
|
||||
return managedObjectContext
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
|
||||
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="14490.99" systemVersion="18D109" minimumToolsVersion="Automatic" sourceLanguage="Swift" userDefinedModelVersionIdentifier="">
|
||||
<entity name="App" representedClassName="App" syncable="YES">
|
||||
<attribute name="developerName" attributeType="String" syncable="YES"/>
|
||||
<attribute name="iconName" attributeType="String" syncable="YES"/>
|
||||
<attribute name="identifier" attributeType="String" syncable="YES"/>
|
||||
<attribute name="localizedDescription" attributeType="String" syncable="YES"/>
|
||||
<attribute name="name" attributeType="String" syncable="YES"/>
|
||||
<attribute name="screenshotNames" attributeType="Transformable" syncable="YES"/>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="identifier"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<elements>
|
||||
<element name="App" positionX="-63" positionY="-18" width="128" height="135"/>
|
||||
</elements>
|
||||
</model>
|
||||
@@ -2,20 +2,66 @@
|
||||
// App.swift
|
||||
// AltStore
|
||||
//
|
||||
// Created by Riley Testut on 5/9/19.
|
||||
// Created by Riley Testut on 5/20/19.
|
||||
// Copyright © 2019 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CoreData
|
||||
|
||||
class App: NSObject, Codable
|
||||
@objc(App)
|
||||
class App: NSManagedObject, Decodable
|
||||
{
|
||||
var name: String
|
||||
var subtitle: String
|
||||
var developer: String
|
||||
/* Properties */
|
||||
@NSManaged private(set) var name: String
|
||||
@NSManaged private(set) var identifier: String
|
||||
|
||||
var localizedDescription: String
|
||||
@NSManaged private(set) var developerName: String
|
||||
@NSManaged private(set) var localizedDescription: String
|
||||
|
||||
var iconName: String
|
||||
var screenshotNames: [String]
|
||||
@NSManaged private(set) var iconName: String
|
||||
@NSManaged private(set) var screenshotNames: [String]
|
||||
|
||||
private override init(entity: NSEntityDescription, insertInto context: NSManagedObjectContext?)
|
||||
{
|
||||
super.init(entity: entity, insertInto: context)
|
||||
}
|
||||
|
||||
private enum CodingKeys: String, CodingKey
|
||||
{
|
||||
case name
|
||||
case identifier
|
||||
case developerName
|
||||
case localizedDescription
|
||||
case iconName
|
||||
case screenshotNames
|
||||
}
|
||||
|
||||
required init(from decoder: Decoder) throws
|
||||
{
|
||||
guard let context = decoder.managedObjectContext else { preconditionFailure("Decoder must have non-nil NSManagedObjectContext.") }
|
||||
|
||||
super.init(entity: App.entity(), insertInto: nil)
|
||||
|
||||
let container = try decoder.container(keyedBy: CodingKeys.self)
|
||||
self.name = try container.decode(String.self, forKey: .name)
|
||||
self.identifier = try container.decode(String.self, forKey: .identifier)
|
||||
self.developerName = try container.decode(String.self, forKey: .developerName)
|
||||
self.localizedDescription = try container.decode(String.self, forKey: .localizedDescription)
|
||||
|
||||
self.iconName = try container.decode(String.self, forKey: .iconName)
|
||||
self.screenshotNames = try container.decodeIfPresent([String].self, forKey: .screenshotNames) ?? []
|
||||
|
||||
context.insert(self)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
extension App
|
||||
{
|
||||
@nonobjc class func fetchRequest() -> NSFetchRequest<App>
|
||||
{
|
||||
return NSFetchRequest<App>(entityName: "App")
|
||||
}
|
||||
}
|
||||
|
||||
48
AltStore/Model/DatabaseManager.swift
Normal file
48
AltStore/Model/DatabaseManager.swift
Normal file
@@ -0,0 +1,48 @@
|
||||
//
|
||||
// DatabaseManager.swift
|
||||
// AltStore
|
||||
//
|
||||
// Created by Riley Testut on 5/20/19.
|
||||
// Copyright © 2019 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import CoreData
|
||||
|
||||
import Roxas
|
||||
|
||||
public class DatabaseManager
|
||||
{
|
||||
public static let shared = DatabaseManager()
|
||||
|
||||
public let persistentContainer: RSTPersistentContainer
|
||||
|
||||
public private(set) var isStarted = false
|
||||
|
||||
private init()
|
||||
{
|
||||
self.persistentContainer = RSTPersistentContainer(name: "AltStore")
|
||||
}
|
||||
}
|
||||
|
||||
public extension DatabaseManager
|
||||
{
|
||||
func start(completionHandler: @escaping (Error?) -> Void)
|
||||
{
|
||||
guard !self.isStarted else { return completionHandler(nil) }
|
||||
|
||||
self.persistentContainer.loadPersistentStores { (description, error) in
|
||||
guard error == nil else { return completionHandler(error!) }
|
||||
|
||||
self.isStarted = true
|
||||
|
||||
completionHandler(error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public extension DatabaseManager
|
||||
{
|
||||
var viewContext: NSManagedObjectContext {
|
||||
return self.persistentContainer.viewContext
|
||||
}
|
||||
}
|
||||
22
AltStore/Resources/Apps.json
Normal file
22
AltStore/Resources/Apps.json
Normal file
@@ -0,0 +1,22 @@
|
||||
[
|
||||
{
|
||||
"name": "Delta",
|
||||
"identifier": "com.rileytestut.Delta",
|
||||
"developerName": "Riley Testut",
|
||||
"localizedDescription": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Sed enim ut sem viverra aliquet eget sit amet. Viverra mauris in aliquam sem fringilla ut. Egestas erat imperdiet sed euismod nisi porta. Sit amet dictum sit amet justo donec enim diam vulputate. Phasellus vestibulum lorem sed risus ultricies tristique nulla. Elit pellentesque habitant morbi tristique. Ut lectus arcu bibendum at. Ullamcorper a lacus vestibulum sed. Mi tempus imperdiet nulla malesuada pellentesque elit eget gravida. Nec feugiat nisl pretium fusce id velit ut. Amet nulla facilisi morbi tempus. Ut sem nulla pharetra diam sit amet nisl.\n\nTortor at auctor urna nunc id cursus metus. Commodo ullamcorper a lacus vestibulum sed arcu non odio. Faucibus turpis in eu mi bibendum neque egestas. Auctor augue mauris augue neque gravida in fermentum et sollicitudin. Aliquam vestibulum morbi blandit cursus risus at ultrices mi tempus. Placerat in egestas erat imperdiet sed euismod nisi. Aliquam id diam maecenas ultricies mi eget mauris. Nunc faucibus a pellentesque sit amet porttitor eget dolor. Sed faucibus turpis in eu mi. Tortor vitae purus faucibus ornare suspendisse sed nisi lacus sed. Id semper risus in hendrerit gravida rutrum quisque. At lectus urna duis convallis convallis. Egestas maecenas pharetra convallis posuere. Id velit ut tortor pretium viverra. Quam pellentesque nec nam aliquam sem et tortor consequat. Risus pretium quam vulputate dignissim. Urna nec tincidunt praesent semper feugiat nibh sed pulvinar.\n\nTempor orci eu lobortis elementum nibh tellus. Mattis rhoncus urna neque viverra justo nec. Maecenas pharetra convallis posuere morbi leo. Rhoncus mattis rhoncus urna neque viverra justo nec. Gravida dictum fusce ut placerat orci nulla pellentesque dignissim enim. At imperdiet dui accumsan sit amet. Elit sed vulputate mi sit amet mauris commodo. Pellentesque habitant morbi tristique senectus. Tortor id aliquet lectus proin nibh. Magna etiam tempor orci eu lobortis elementum. Est pellentesque elit ullamcorper dignissim. Dapibus ultrices in iaculis nunc. Commodo quis imperdiet massa tincidunt nunc pulvinar sapien et ligula. Diam vel quam elementum pulvinar. Vel turpis nunc eget lorem dolor sed viverra. Tortor pretium viverra suspendisse potenti nullam ac tortor vitae. Euismod nisi porta lorem mollis. Massa id neque aliquam vestibulum morbi blandit.\n\nMassa massa ultricies mi quis hendrerit dolor magna eget est. Augue interdum velit euismod in pellentesque massa. Sed risus ultricies tristique nulla aliquet enim. Risus viverra adipiscing at in tellus. Donec adipiscing tristique risus nec feugiat. Eget sit amet tellus cras adipiscing enim eu turpis. Auctor neque vitae tempus quam pellentesque nec. Sit amet tellus cras adipiscing enim eu turpis egestas. Dui faucibus in ornare quam viverra. Fermentum iaculis eu non diam phasellus vestibulum lorem. Odio ut enim blandit volutpat maecenas. Dolor sit amet consectetur adipiscing elit pellentesque habitant morbi tristique. Pellentesque diam volutpat commodo sed egestas egestas. Aliquam purus sit amet luctus venenatis lectus magna fringilla. Viverra mauris in aliquam sem fringilla ut morbi tincidunt. Elit duis tristique sollicitudin nibh sit. Fermentum dui faucibus in ornare quam viverra orci sagittis. Aliquet eget sit amet tellus cras adipiscing.",
|
||||
"iconName": "DeltaIcon",
|
||||
"screenshotNames": [
|
||||
"Delta1",
|
||||
"Delta2",
|
||||
"Delta3",
|
||||
"Delta4"
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "Clipboard Manager",
|
||||
"identifier": "com.rileytestut.ClipboardManager",
|
||||
"developerName": "Riley Testut",
|
||||
"localizedDescription": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Sed enim ut sem viverra aliquet eget sit amet. Viverra mauris in aliquam sem fringilla ut. Egestas erat imperdiet sed euismod nisi porta. Sit amet dictum sit amet justo donec enim diam vulputate. Phasellus vestibulum lorem sed risus ultricies tristique nulla. Elit pellentesque habitant morbi tristique. Ut lectus arcu bibendum at. Ullamcorper a lacus vestibulum sed. Mi tempus imperdiet nulla malesuada pellentesque elit eget gravida. Nec feugiat nisl pretium fusce id velit ut. Amet nulla facilisi morbi tempus. Ut sem nulla pharetra diam sit amet nisl.\n\nTortor at auctor urna nunc id cursus metus. Commodo ullamcorper a lacus vestibulum sed arcu non odio. Faucibus turpis in eu mi bibendum neque egestas. Auctor augue mauris augue neque gravida in fermentum et sollicitudin. Aliquam vestibulum morbi blandit cursus risus at ultrices mi tempus. Placerat in egestas erat imperdiet sed euismod nisi. Aliquam id diam maecenas ultricies mi eget mauris. Nunc faucibus a pellentesque sit amet porttitor eget dolor. Sed faucibus turpis in eu mi. Tortor vitae purus faucibus ornare suspendisse sed nisi lacus sed. Id semper risus in hendrerit gravida rutrum quisque. At lectus urna duis convallis convallis. Egestas maecenas pharetra convallis posuere. Id velit ut tortor pretium viverra. Quam pellentesque nec nam aliquam sem et tortor consequat. Risus pretium quam vulputate dignissim. Urna nec tincidunt praesent semper feugiat nibh sed pulvinar.\n\nTempor orci eu lobortis elementum nibh tellus. Mattis rhoncus urna neque viverra justo nec. Maecenas pharetra convallis posuere morbi leo. Rhoncus mattis rhoncus urna neque viverra justo nec. Gravida dictum fusce ut placerat orci nulla pellentesque dignissim enim. At imperdiet dui accumsan sit amet. Elit sed vulputate mi sit amet mauris commodo. Pellentesque habitant morbi tristique senectus. Tortor id aliquet lectus proin nibh. Magna etiam tempor orci eu lobortis elementum. Est pellentesque elit ullamcorper dignissim. Dapibus ultrices in iaculis nunc. Commodo quis imperdiet massa tincidunt nunc pulvinar sapien et ligula. Diam vel quam elementum pulvinar. Vel turpis nunc eget lorem dolor sed viverra. Tortor pretium viverra suspendisse potenti nullam ac tortor vitae. Euismod nisi porta lorem mollis. Massa id neque aliquam vestibulum morbi blandit.\n\nMassa massa ultricies mi quis hendrerit dolor magna eget est. Augue interdum velit euismod in pellentesque massa. Sed risus ultricies tristique nulla aliquet enim. Risus viverra adipiscing at in tellus. Donec adipiscing tristique risus nec feugiat. Eget sit amet tellus cras adipiscing enim eu turpis. Auctor neque vitae tempus quam pellentesque nec. Sit amet tellus cras adipiscing enim eu turpis egestas. Dui faucibus in ornare quam viverra. Fermentum iaculis eu non diam phasellus vestibulum lorem. Odio ut enim blandit volutpat maecenas. Dolor sit amet consectetur adipiscing elit pellentesque habitant morbi tristique. Pellentesque diam volutpat commodo sed egestas egestas. Aliquam purus sit amet luctus venenatis lectus magna fringilla. Viverra mauris in aliquam sem fringilla ut morbi tincidunt. Elit duis tristique sollicitudin nibh sit. Fermentum dui faucibus in ornare quam viverra orci sagittis. Aliquet eget sit amet tellus cras adipiscing.",
|
||||
"iconName": "ClipboardIcon"
|
||||
}
|
||||
]
|
||||
@@ -1,51 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<array>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>Delta</string>
|
||||
<key>subtitle</key>
|
||||
<string>All-in-one Nintendo emulator</string>
|
||||
<key>localizedDescription</key>
|
||||
<string>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Sed enim ut sem viverra aliquet eget sit amet. Viverra mauris in aliquam sem fringilla ut. Egestas erat imperdiet sed euismod nisi porta. Sit amet dictum sit amet justo donec enim diam vulputate. Phasellus vestibulum lorem sed risus ultricies tristique nulla. Elit pellentesque habitant morbi tristique. Ut lectus arcu bibendum at. Ullamcorper a lacus vestibulum sed. Mi tempus imperdiet nulla malesuada pellentesque elit eget gravida. Nec feugiat nisl pretium fusce id velit ut. Amet nulla facilisi morbi tempus. Ut sem nulla pharetra diam sit amet nisl.
|
||||
|
||||
Tortor at auctor urna nunc id cursus metus. Commodo ullamcorper a lacus vestibulum sed arcu non odio. Faucibus turpis in eu mi bibendum neque egestas. Auctor augue mauris augue neque gravida in fermentum et sollicitudin. Aliquam vestibulum morbi blandit cursus risus at ultrices mi tempus. Placerat in egestas erat imperdiet sed euismod nisi. Aliquam id diam maecenas ultricies mi eget mauris. Nunc faucibus a pellentesque sit amet porttitor eget dolor. Sed faucibus turpis in eu mi. Tortor vitae purus faucibus ornare suspendisse sed nisi lacus sed. Id semper risus in hendrerit gravida rutrum quisque. At lectus urna duis convallis convallis. Egestas maecenas pharetra convallis posuere. Id velit ut tortor pretium viverra. Quam pellentesque nec nam aliquam sem et tortor consequat. Risus pretium quam vulputate dignissim. Urna nec tincidunt praesent semper feugiat nibh sed pulvinar.
|
||||
|
||||
Tempor orci eu lobortis elementum nibh tellus. Mattis rhoncus urna neque viverra justo nec. Maecenas pharetra convallis posuere morbi leo. Rhoncus mattis rhoncus urna neque viverra justo nec. Gravida dictum fusce ut placerat orci nulla pellentesque dignissim enim. At imperdiet dui accumsan sit amet. Elit sed vulputate mi sit amet mauris commodo. Pellentesque habitant morbi tristique senectus. Tortor id aliquet lectus proin nibh. Magna etiam tempor orci eu lobortis elementum. Est pellentesque elit ullamcorper dignissim. Dapibus ultrices in iaculis nunc. Commodo quis imperdiet massa tincidunt nunc pulvinar sapien et ligula. Diam vel quam elementum pulvinar. Vel turpis nunc eget lorem dolor sed viverra. Tortor pretium viverra suspendisse potenti nullam ac tortor vitae. Euismod nisi porta lorem mollis. Massa id neque aliquam vestibulum morbi blandit.
|
||||
|
||||
Massa massa ultricies mi quis hendrerit dolor magna eget est. Augue interdum velit euismod in pellentesque massa. Sed risus ultricies tristique nulla aliquet enim. Risus viverra adipiscing at in tellus. Donec adipiscing tristique risus nec feugiat. Eget sit amet tellus cras adipiscing enim eu turpis. Auctor neque vitae tempus quam pellentesque nec. Sit amet tellus cras adipiscing enim eu turpis egestas. Dui faucibus in ornare quam viverra. Fermentum iaculis eu non diam phasellus vestibulum lorem. Odio ut enim blandit volutpat maecenas. Dolor sit amet consectetur adipiscing elit pellentesque habitant morbi tristique. Pellentesque diam volutpat commodo sed egestas egestas. Aliquam purus sit amet luctus venenatis lectus magna fringilla. Viverra mauris in aliquam sem fringilla ut morbi tincidunt. Elit duis tristique sollicitudin nibh sit. Fermentum dui faucibus in ornare quam viverra orci sagittis. Aliquet eget sit amet tellus cras adipiscing.</string>
|
||||
<key>developer</key>
|
||||
<string>Riley Testut</string>
|
||||
<key>iconName</key>
|
||||
<string>DeltaIcon</string>
|
||||
<key>screenshotNames</key>
|
||||
<array>
|
||||
<string>Delta1</string>
|
||||
<string>Delta2</string>
|
||||
<string>Delta3</string>
|
||||
<string>Delta4</string>
|
||||
</array>
|
||||
</dict>
|
||||
<dict>
|
||||
<key>name</key>
|
||||
<string>Clipboard Manager</string>
|
||||
<key>subtitle</key>
|
||||
<string>Simple but powerful clipboard manager</string>
|
||||
<key>localizedDescription</key>
|
||||
<string>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Sed enim ut sem viverra aliquet eget sit amet. Viverra mauris in aliquam sem fringilla ut. Egestas erat imperdiet sed euismod nisi porta. Sit amet dictum sit amet justo donec enim diam vulputate. Phasellus vestibulum lorem sed risus ultricies tristique nulla. Elit pellentesque habitant morbi tristique. Ut lectus arcu bibendum at. Ullamcorper a lacus vestibulum sed. Mi tempus imperdiet nulla malesuada pellentesque elit eget gravida. Nec feugiat nisl pretium fusce id velit ut. Amet nulla facilisi morbi tempus. Ut sem nulla pharetra diam sit amet nisl.
|
||||
|
||||
Tortor at auctor urna nunc id cursus metus. Commodo ullamcorper a lacus vestibulum sed arcu non odio. Faucibus turpis in eu mi bibendum neque egestas. Auctor augue mauris augue neque gravida in fermentum et sollicitudin. Aliquam vestibulum morbi blandit cursus risus at ultrices mi tempus. Placerat in egestas erat imperdiet sed euismod nisi. Aliquam id diam maecenas ultricies mi eget mauris. Nunc faucibus a pellentesque sit amet porttitor eget dolor. Sed faucibus turpis in eu mi. Tortor vitae purus faucibus ornare suspendisse sed nisi lacus sed. Id semper risus in hendrerit gravida rutrum quisque. At lectus urna duis convallis convallis. Egestas maecenas pharetra convallis posuere. Id velit ut tortor pretium viverra. Quam pellentesque nec nam aliquam sem et tortor consequat. Risus pretium quam vulputate dignissim. Urna nec tincidunt praesent semper feugiat nibh sed pulvinar.
|
||||
|
||||
Tempor orci eu lobortis elementum nibh tellus. Mattis rhoncus urna neque viverra justo nec. Maecenas pharetra convallis posuere morbi leo. Rhoncus mattis rhoncus urna neque viverra justo nec. Gravida dictum fusce ut placerat orci nulla pellentesque dignissim enim. At imperdiet dui accumsan sit amet. Elit sed vulputate mi sit amet mauris commodo. Pellentesque habitant morbi tristique senectus. Tortor id aliquet lectus proin nibh. Magna etiam tempor orci eu lobortis elementum. Est pellentesque elit ullamcorper dignissim. Dapibus ultrices in iaculis nunc. Commodo quis imperdiet massa tincidunt nunc pulvinar sapien et ligula. Diam vel quam elementum pulvinar. Vel turpis nunc eget lorem dolor sed viverra. Tortor pretium viverra suspendisse potenti nullam ac tortor vitae. Euismod nisi porta lorem mollis. Massa id neque aliquam vestibulum morbi blandit.
|
||||
|
||||
Massa massa ultricies mi quis hendrerit dolor magna eget est. Augue interdum velit euismod in pellentesque massa. Sed risus ultricies tristique nulla aliquet enim. Risus viverra adipiscing at in tellus. Donec adipiscing tristique risus nec feugiat. Eget sit amet tellus cras adipiscing enim eu turpis. Auctor neque vitae tempus quam pellentesque nec. Sit amet tellus cras adipiscing enim eu turpis egestas. Dui faucibus in ornare quam viverra. Fermentum iaculis eu non diam phasellus vestibulum lorem. Odio ut enim blandit volutpat maecenas. Dolor sit amet consectetur adipiscing elit pellentesque habitant morbi tristique. Pellentesque diam volutpat commodo sed egestas egestas. Aliquam purus sit amet luctus venenatis lectus magna fringilla. Viverra mauris in aliquam sem fringilla ut morbi tincidunt. Elit duis tristique sollicitudin nibh sit. Fermentum dui faucibus in ornare quam viverra orci sagittis. Aliquet eget sit amet tellus cras adipiscing.</string>
|
||||
<key>developer</key>
|
||||
<string>Riley Testut</string>
|
||||
<key>iconName</key>
|
||||
<string>ClipboardIcon</string>
|
||||
<key>screenshotNames</key>
|
||||
<array/>
|
||||
</dict>
|
||||
</array>
|
||||
</plist>
|
||||
@@ -1,20 +0,0 @@
|
||||
//
|
||||
// SecondViewController.swift
|
||||
// AltStore
|
||||
//
|
||||
// Created by Riley Testut on 5/9/19.
|
||||
// Copyright © 2019 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class SecondViewController: UIViewController {
|
||||
|
||||
override func viewDidLoad() {
|
||||
super.viewDidLoad()
|
||||
// Do any additional setup after loading the view.
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user