mirror of
https://github.com/SideStore/SideStore.git
synced 2026-02-08 22:33:26 +01:00
Adds basic MyAppsViewController implementation
This commit is contained in:
@@ -14,9 +14,10 @@
|
||||
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 */; };
|
||||
BFBBE2E122931F81002097FA /* InstalledApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFBBE2E022931F81002097FA /* InstalledApp.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 */; };
|
||||
BFD247722284B9A500981D42 /* MyAppsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFD247712284B9A500981D42 /* MyAppsViewController.swift */; };
|
||||
BFD247752284B9A500981D42 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BFD247732284B9A500981D42 /* Main.storyboard */; };
|
||||
BFD247772284B9A700981D42 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BFD247762284B9A700981D42 /* Assets.xcassets */; };
|
||||
BFD2477A2284B9A700981D42 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BFD247782284B9A700981D42 /* LaunchScreen.storyboard */; };
|
||||
@@ -51,10 +52,11 @@
|
||||
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>"; };
|
||||
BFBBE2E022931F81002097FA /* InstalledApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstalledApp.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>"; };
|
||||
BFD247712284B9A500981D42 /* SecondViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SecondViewController.swift; sourceTree = "<group>"; };
|
||||
BFD247712284B9A500981D42 /* MyAppsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MyAppsViewController.swift; sourceTree = "<group>"; };
|
||||
BFD247742284B9A500981D42 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
|
||||
BFD247762284B9A700981D42 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
BFD247792284B9A700981D42 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
|
||||
@@ -80,6 +82,14 @@
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
BFBBE2E2229320A2002097FA /* My Apps */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
BFD247712284B9A500981D42 /* MyAppsViewController.swift */,
|
||||
);
|
||||
path = "My Apps";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
BFD247612284B9A500981D42 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -103,10 +113,10 @@
|
||||
BFD2476D2284B9A500981D42 /* AppDelegate.swift */,
|
||||
BFD247732284B9A500981D42 /* Main.storyboard */,
|
||||
BFD2478A2284C49000981D42 /* Apps */,
|
||||
BFBBE2E2229320A2002097FA /* My Apps */,
|
||||
BFD247982284D7FC00981D42 /* Model */,
|
||||
BFD2478D2284C4C700981D42 /* Components */,
|
||||
BFD2479D2284FBBD00981D42 /* Extensions */,
|
||||
BFD247712284B9A500981D42 /* SecondViewController.swift */,
|
||||
BFD247962284D7C100981D42 /* Resources */,
|
||||
BFD247972284D7D800981D42 /* Supporting Files */,
|
||||
);
|
||||
@@ -165,6 +175,7 @@
|
||||
BFBBE2DB22931B20002097FA /* AltStore.xcdatamodeld */,
|
||||
BFB11691229322E400BB457C /* DatabaseManager.swift */,
|
||||
BFBBE2DE22931F73002097FA /* App.swift */,
|
||||
BFBBE2E022931F81002097FA /* InstalledApp.swift */,
|
||||
);
|
||||
path = Model;
|
||||
sourceTree = "<group>";
|
||||
@@ -252,8 +263,9 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
BFD2478F2284C8F900981D42 /* Button.swift in Sources */,
|
||||
BFD247722284B9A500981D42 /* SecondViewController.swift in Sources */,
|
||||
BFD247722284B9A500981D42 /* MyAppsViewController.swift in Sources */,
|
||||
BFB11692229322E400BB457C /* DatabaseManager.swift in Sources */,
|
||||
BFBBE2E122931F81002097FA /* InstalledApp.swift in Sources */,
|
||||
BFBBE2DF22931F73002097FA /* App.swift in Sources */,
|
||||
BFD2478C2284C4C300981D42 /* AppIconImageView.swift in Sources */,
|
||||
BFD2479C2284E19A00981D42 /* AppDetailViewController.swift in Sources */,
|
||||
|
||||
@@ -48,6 +48,8 @@ class AppDetailViewController: UITableViewController
|
||||
self.tableView.delegate = self
|
||||
self.screenshotsCollectionView.dataSource = self.screenshotsDataSource
|
||||
|
||||
self.downloadButton.activityIndicatorView.style = .white
|
||||
|
||||
self.update()
|
||||
}
|
||||
|
||||
@@ -95,6 +97,39 @@ private extension AppDetailViewController
|
||||
}
|
||||
}
|
||||
|
||||
private extension AppDetailViewController
|
||||
{
|
||||
@IBAction func downloadApp(_ sender: UIButton)
|
||||
{
|
||||
guard self.app.installedApp == nil else { return }
|
||||
|
||||
sender.isIndicatingActivity = true
|
||||
|
||||
DatabaseManager.shared.persistentContainer.performBackgroundTask { (context) in
|
||||
let app = context.object(with: self.app.objectID) as! App
|
||||
|
||||
_ = InstalledApp(app: app,
|
||||
bundleIdentifier: app.identifier,
|
||||
signedDate: Date(),
|
||||
expirationDate: Date().addingTimeInterval(60 * 60 * 24 * 7),
|
||||
context: context)
|
||||
|
||||
do
|
||||
{
|
||||
try context.save()
|
||||
}
|
||||
catch
|
||||
{
|
||||
print("Failed to download app.", error)
|
||||
}
|
||||
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
|
||||
sender.isIndicatingActivity = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extension AppDetailViewController
|
||||
{
|
||||
override func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat
|
||||
|
||||
@@ -59,6 +59,15 @@ private extension AppsViewController
|
||||
cell.developerLabel.text = app.developerName
|
||||
cell.appIconImageView.image = UIImage(named: app.iconName)
|
||||
|
||||
if app.installedApp != nil
|
||||
{
|
||||
cell.button.isEnabled = false
|
||||
cell.button.setTitle(NSLocalizedString("Installed", comment: ""), for: .normal)
|
||||
}
|
||||
else
|
||||
{
|
||||
cell.button.isEnabled = true
|
||||
cell.button.setTitle(NSLocalizedString("Download", comment: ""), for: .normal)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,47 +6,9 @@
|
||||
<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>
|
||||
<scenes>
|
||||
<!--Second-->
|
||||
<scene sceneID="wg7-f3-ORb">
|
||||
<objects>
|
||||
<viewController id="8rJ-Kc-sve" customClass="SecondViewController" customModule="AltStore" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<view key="view" contentMode="scaleToFill" id="QS5-Rx-YEW">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
||||
<subviews>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" text="Second View" textAlignment="center" lineBreakMode="tailTruncation" minimumFontSize="10" translatesAutoresizingMaskIntoConstraints="NO" id="zEq-FU-wV5">
|
||||
<rect key="frame" x="87" y="312" width="201.5" height="43"/>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="36"/>
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" clipsSubviews="YES" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Loaded by SecondViewController" textAlignment="center" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="NDk-cv-Gan">
|
||||
<rect key="frame" x="80" y="363" width="215" height="17"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<color key="textColor" cocoaTouchSystemColor="darkTextColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
<color key="backgroundColor" red="1" green="1" blue="1" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
|
||||
<constraints>
|
||||
<constraint firstItem="NDk-cv-Gan" firstAttribute="top" secondItem="zEq-FU-wV5" secondAttribute="bottom" constant="8" symbolic="YES" id="Day-4N-Vmt"/>
|
||||
<constraint firstItem="NDk-cv-Gan" firstAttribute="centerX" secondItem="zEq-FU-wV5" secondAttribute="centerX" id="JgO-Fn-dHn"/>
|
||||
<constraint firstAttribute="centerX" secondItem="zEq-FU-wV5" secondAttribute="centerX" id="qqM-NS-xev"/>
|
||||
<constraint firstAttribute="centerY" secondItem="zEq-FU-wV5" secondAttribute="centerY" id="qzY-Ky-pLD"/>
|
||||
</constraints>
|
||||
<viewLayoutGuide key="safeArea" id="O1u-W8-tvY"/>
|
||||
</view>
|
||||
<tabBarItem key="tabBarItem" title="Second" image="second" id="cPa-gy-q4n"/>
|
||||
</viewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="4Nw-L8-lE0" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="750" y="360"/>
|
||||
</scene>
|
||||
<!--Tab Bar Controller-->
|
||||
<scene sceneID="yl2-sM-qoP">
|
||||
<objects>
|
||||
@@ -58,13 +20,58 @@
|
||||
</tabBar>
|
||||
<connections>
|
||||
<segue destination="XF0-gk-CxQ" kind="relationship" relationship="viewControllers" id="uf8-vX-M3B"/>
|
||||
<segue destination="8rJ-Kc-sve" kind="relationship" relationship="viewControllers" id="0YU-bO-QiZ"/>
|
||||
<segue destination="v8c-d9-T9x" kind="relationship" relationship="viewControllers" id="fqx-5p-YCD"/>
|
||||
</connections>
|
||||
</tabBarController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="HuB-VB-40B" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="0.0" y="0.0"/>
|
||||
</scene>
|
||||
<!--My Apps-->
|
||||
<scene sceneID="eRf-VD-MSX">
|
||||
<objects>
|
||||
<tableViewController id="Xf1-LZ-1vU" customClass="MyAppsViewController" customModule="AltStore" customModuleProvider="target" sceneMemberID="viewController">
|
||||
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" id="hkn-c3-iKp">
|
||||
<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"/>
|
||||
<prototypes>
|
||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="Cell" textLabel="kY8-9c-qLE" detailTextLabel="XWn-JG-SYe" style="IBUITableViewCellStyleSubtitle" id="Lwi-IB-I9S">
|
||||
<rect key="frame" x="0.0" y="28" width="375" height="44"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="Lwi-IB-I9S" id="4Vg-jI-f8V">
|
||||
<rect key="frame" x="0.0" y="0.0" width="375" height="43.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<subviews>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Title" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="kY8-9c-qLE">
|
||||
<rect key="frame" x="16" y="5" width="33.5" height="20.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
<label opaque="NO" multipleTouchEnabled="YES" contentMode="left" insetsLayoutMarginsFromSafeArea="NO" text="Subtitle" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" id="XWn-JG-SYe">
|
||||
<rect key="frame" x="16" y="25.5" width="44" height="14.5"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="12"/>
|
||||
<nil key="textColor"/>
|
||||
<nil key="highlightedColor"/>
|
||||
</label>
|
||||
</subviews>
|
||||
</tableViewCellContentView>
|
||||
</tableViewCell>
|
||||
</prototypes>
|
||||
<connections>
|
||||
<outlet property="dataSource" destination="Xf1-LZ-1vU" id="Lsq-Jy-ugM"/>
|
||||
<outlet property="delegate" destination="Xf1-LZ-1vU" id="0YS-oa-D9d"/>
|
||||
</connections>
|
||||
</tableView>
|
||||
<navigationItem key="navigationItem" title="My Apps" id="dz9-0e-LKa"/>
|
||||
</tableViewController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="nb5-5T-hHT" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="1518" y="420"/>
|
||||
</scene>
|
||||
<!--Apps-->
|
||||
<scene sceneID="JlP-x7-lBT">
|
||||
<objects>
|
||||
@@ -219,6 +226,9 @@
|
||||
<rect key="frame" x="0.0" y="129" width="343" height="34"/>
|
||||
<fontDescription key="fontDescription" type="system" weight="semibold" pointSize="18"/>
|
||||
<state key="normal" title="Download"/>
|
||||
<connections>
|
||||
<action selector="downloadApp:" destination="hR3-go-2DG" eventType="primaryActionTriggered" id="TZ5-aD-2Bp"/>
|
||||
</connections>
|
||||
</button>
|
||||
</subviews>
|
||||
</stackView>
|
||||
@@ -376,6 +386,25 @@
|
||||
</objects>
|
||||
<point key="canvasLocation" x="749.60000000000002" y="-318.89055472263868"/>
|
||||
</scene>
|
||||
<!--My Apps-->
|
||||
<scene sceneID="Edw-HT-TTT">
|
||||
<objects>
|
||||
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="v8c-d9-T9x" sceneMemberID="viewController">
|
||||
<tabBarItem key="tabBarItem" title="My Apps" image="second" id="Pld-qe-pas"/>
|
||||
<toolbarItems/>
|
||||
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" largeTitles="YES" id="PKd-0h-fii">
|
||||
<rect key="frame" x="0.0" y="20" width="375" height="96"/>
|
||||
<autoresizingMask key="autoresizingMask"/>
|
||||
</navigationBar>
|
||||
<nil name="viewControllers"/>
|
||||
<connections>
|
||||
<segue destination="Xf1-LZ-1vU" kind="relationship" relationship="rootViewController" id="uaQ-4h-SJu"/>
|
||||
</connections>
|
||||
</navigationController>
|
||||
<placeholder placeholderIdentifier="IBFirstResponder" id="G7O-77-OQq" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
||||
</objects>
|
||||
<point key="canvasLocation" x="749.60000000000002" y="420.53973013493254"/>
|
||||
</scene>
|
||||
</scenes>
|
||||
<resources>
|
||||
<image name="DeltaIcon" width="512" height="512"/>
|
||||
|
||||
@@ -41,12 +41,25 @@ class Button: UIButton
|
||||
self.update()
|
||||
}
|
||||
}
|
||||
|
||||
override var isEnabled: Bool {
|
||||
didSet {
|
||||
self.update()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private extension Button
|
||||
{
|
||||
func update()
|
||||
{
|
||||
if self.isEnabled
|
||||
{
|
||||
self.backgroundColor = self.tintColor
|
||||
}
|
||||
else
|
||||
{
|
||||
self.backgroundColor = .lightGray
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,13 +7,22 @@
|
||||
<attribute name="localizedDescription" attributeType="String" syncable="YES"/>
|
||||
<attribute name="name" attributeType="String" syncable="YES"/>
|
||||
<attribute name="screenshotNames" attributeType="Transformable" syncable="YES"/>
|
||||
<relationship name="installedApp" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="InstalledApp" inverseName="app" inverseEntity="InstalledApp" syncable="YES"/>
|
||||
<uniquenessConstraints>
|
||||
<uniquenessConstraint>
|
||||
<constraint value="identifier"/>
|
||||
</uniquenessConstraint>
|
||||
</uniquenessConstraints>
|
||||
</entity>
|
||||
<entity name="InstalledApp" representedClassName="InstalledApp" syncable="YES">
|
||||
<attribute name="bundleIdentifier" attributeType="String" syncable="YES"/>
|
||||
<attribute name="expirationDate" attributeType="Date" usesScalarValueType="NO" syncable="YES"/>
|
||||
<attribute name="isBeta" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES" syncable="YES"/>
|
||||
<attribute name="signedDate" attributeType="Date" usesScalarValueType="NO" syncable="YES"/>
|
||||
<relationship name="app" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="App" inverseName="installedApp" inverseEntity="App" syncable="YES"/>
|
||||
</entity>
|
||||
<elements>
|
||||
<element name="App" positionX="-63" positionY="-18" width="128" height="135"/>
|
||||
<element name="App" positionX="-63" positionY="-18" width="128" height="150"/>
|
||||
<element name="InstalledApp" positionX="-63" positionY="0" width="128" height="120"/>
|
||||
</elements>
|
||||
</model>
|
||||
@@ -22,6 +22,9 @@ class App: NSManagedObject, Decodable
|
||||
@NSManaged private(set) var iconName: String
|
||||
@NSManaged private(set) var screenshotNames: [String]
|
||||
|
||||
/* Relationships */
|
||||
@NSManaged private(set) var installedApp: InstalledApp?
|
||||
|
||||
private override init(entity: NSEntityDescription, insertInto context: NSManagedObjectContext?)
|
||||
{
|
||||
super.init(entity: entity, insertInto: context)
|
||||
|
||||
50
AltStore/Model/InstalledApp.swift
Normal file
50
AltStore/Model/InstalledApp.swift
Normal file
@@ -0,0 +1,50 @@
|
||||
//
|
||||
// InstalledApp.swift
|
||||
// AltStore
|
||||
//
|
||||
// Created by Riley Testut on 5/20/19.
|
||||
// Copyright © 2019 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CoreData
|
||||
|
||||
@objc(InstalledApp)
|
||||
class InstalledApp: NSManagedObject
|
||||
{
|
||||
/* Properties */
|
||||
@NSManaged var bundleIdentifier: String
|
||||
|
||||
@NSManaged var signedDate: Date
|
||||
@NSManaged var expirationDate: Date
|
||||
|
||||
@NSManaged var isBeta: Bool
|
||||
|
||||
/* Relationships */
|
||||
@NSManaged private(set) var app: App?
|
||||
|
||||
private override init(entity: NSEntityDescription, insertInto context: NSManagedObjectContext?)
|
||||
{
|
||||
super.init(entity: entity, insertInto: context)
|
||||
}
|
||||
|
||||
init(app: App, bundleIdentifier: String, signedDate: Date, expirationDate: Date, context: NSManagedObjectContext)
|
||||
{
|
||||
super.init(entity: InstalledApp.entity(), insertInto: context)
|
||||
|
||||
let app = context.object(with: app.objectID) as! App
|
||||
self.app = app
|
||||
|
||||
self.bundleIdentifier = bundleIdentifier
|
||||
self.signedDate = signedDate
|
||||
self.expirationDate = expirationDate
|
||||
}
|
||||
}
|
||||
|
||||
extension InstalledApp
|
||||
{
|
||||
@nonobjc class func fetchRequest() -> NSFetchRequest<InstalledApp>
|
||||
{
|
||||
return NSFetchRequest<InstalledApp>(entityName: "InstalledApp")
|
||||
}
|
||||
}
|
||||
58
AltStore/My Apps/MyAppsViewController.swift
Normal file
58
AltStore/My Apps/MyAppsViewController.swift
Normal file
@@ -0,0 +1,58 @@
|
||||
//
|
||||
// MyAppsViewController.swift
|
||||
// AltStore
|
||||
//
|
||||
// Created by Riley Testut on 5/9/19.
|
||||
// Copyright © 2019 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
import Roxas
|
||||
|
||||
class MyAppsViewController: UITableViewController
|
||||
{
|
||||
private lazy var dataSource = self.makeDataSource()
|
||||
|
||||
private lazy var dateFormatter: DateFormatter = {
|
||||
let dateFormatter = DateFormatter()
|
||||
dateFormatter.dateStyle = .short
|
||||
dateFormatter.timeStyle = .none
|
||||
return dateFormatter
|
||||
}()
|
||||
|
||||
override func viewDidLoad()
|
||||
{
|
||||
super.viewDidLoad()
|
||||
|
||||
self.tableView.dataSource = self.dataSource
|
||||
}
|
||||
}
|
||||
|
||||
private extension MyAppsViewController
|
||||
{
|
||||
func makeDataSource() -> RSTFetchedResultsTableViewDataSource<InstalledApp>
|
||||
{
|
||||
let fetchRequest = InstalledApp.fetchRequest() as NSFetchRequest<InstalledApp>
|
||||
fetchRequest.relationshipKeyPathsForPrefetching = [#keyPath(InstalledApp.app)]
|
||||
fetchRequest.sortDescriptors = [NSSortDescriptor(keyPath: \InstalledApp.app?.name, ascending: true)]
|
||||
fetchRequest.returnsObjectsAsFaults = false
|
||||
|
||||
let dataSource = RSTFetchedResultsTableViewDataSource(fetchRequest: fetchRequest, managedObjectContext: DatabaseManager.shared.viewContext)
|
||||
dataSource.cellConfigurationHandler = { (cell, installedApp, indexPath) in
|
||||
guard let app = installedApp.app else { return }
|
||||
|
||||
cell.textLabel?.text = app.name
|
||||
|
||||
let detailText =
|
||||
"""
|
||||
Signed: \(self.dateFormatter.string(from: installedApp.signedDate))
|
||||
Expires: \(self.dateFormatter.string(from: installedApp.expirationDate))
|
||||
"""
|
||||
|
||||
cell.detailTextLabel?.numberOfLines = 2
|
||||
cell.detailTextLabel?.text = detailText
|
||||
}
|
||||
|
||||
return dataSource
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user