mirror of
https://github.com/SideStore/SideStore.git
synced 2026-02-10 15:23:27 +01:00
Compare commits
8 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8c7f554909 | ||
|
|
2b0e629dd1 | ||
|
|
7a1f402c5d | ||
|
|
ab56ce6004 | ||
|
|
53e948c0a9 | ||
|
|
b4f8ae00db | ||
|
|
9e610ddb73 | ||
|
|
7fc822948c |
@@ -194,6 +194,50 @@ extension ALTDeviceManager
|
||||
|
||||
func fetchTeam(for account: ALTAccount, completionHandler: @escaping (Result<ALTTeam, Error>) -> Void)
|
||||
{
|
||||
func finish(_ result: Result<ALTTeam, Error>)
|
||||
{
|
||||
switch result
|
||||
{
|
||||
case .failure(let error):
|
||||
completionHandler(.failure(error))
|
||||
|
||||
case .success(let team):
|
||||
|
||||
var isCancelled = false
|
||||
|
||||
if team.type != .free
|
||||
{
|
||||
DispatchQueue.main.sync {
|
||||
let alert = NSAlert()
|
||||
alert.messageText = NSLocalizedString("Installing AltStore will revoke your iOS development certificate.", comment: "")
|
||||
alert.informativeText = NSLocalizedString("""
|
||||
This will not affect apps you've submitted to the App Store, but may cause apps you've installed to your devices with Xcode to stop working until you reinstall them.
|
||||
|
||||
To prevent this from happening, feel free to try again with another Apple ID to install AltStore.
|
||||
""", comment: "")
|
||||
|
||||
alert.addButton(withTitle: NSLocalizedString("Continue", comment: ""))
|
||||
alert.addButton(withTitle: NSLocalizedString("Cancel", comment: ""))
|
||||
|
||||
NSRunningApplication.current.activate(options: .activateIgnoringOtherApps)
|
||||
|
||||
let buttonIndex = alert.runModal()
|
||||
if buttonIndex == NSApplication.ModalResponse.alertSecondButtonReturn
|
||||
{
|
||||
isCancelled = true
|
||||
}
|
||||
}
|
||||
|
||||
if isCancelled
|
||||
{
|
||||
return completionHandler(.failure(InstallError.cancelled))
|
||||
}
|
||||
}
|
||||
|
||||
completionHandler(.success(team))
|
||||
}
|
||||
}
|
||||
|
||||
ALTAppleAPI.shared.fetchTeams(for: account) { (teams, error) in
|
||||
do
|
||||
{
|
||||
@@ -201,15 +245,15 @@ extension ALTDeviceManager
|
||||
|
||||
if let team = teams.first(where: { $0.type == .free })
|
||||
{
|
||||
return completionHandler(.success(team))
|
||||
return finish(.success(team))
|
||||
}
|
||||
else if let team = teams.first(where: { $0.type == .individual })
|
||||
{
|
||||
return completionHandler(.success(team))
|
||||
return finish(.success(team))
|
||||
}
|
||||
else if let team = teams.first
|
||||
{
|
||||
return completionHandler(.success(team))
|
||||
return finish(.success(team))
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -218,7 +262,7 @@ extension ALTDeviceManager
|
||||
}
|
||||
catch
|
||||
{
|
||||
completionHandler(.failure(error))
|
||||
finish(.failure(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<string>1.0.1</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>LSMinimumSystemVersion</key>
|
||||
|
||||
@@ -131,7 +131,6 @@
|
||||
BF9ABA4F22DD41A9008935CF /* UIColor+Hex.swift in Sources */ = {isa = PBXBuildFile; fileRef = BF9ABA4E22DD41A9008935CF /* UIColor+Hex.swift */; };
|
||||
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 */; };
|
||||
BFB3645A2325985F00CD0EB1 /* FindServerOperation.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFB364592325985F00CD0EB1 /* FindServerOperation.swift */; };
|
||||
BFB4323F22DE852000B7F8BC /* UpdateCollectionViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = BFB4323E22DE852000B7F8BC /* UpdateCollectionViewCell.xib */; };
|
||||
BFB6B21B23186D640022A802 /* NewsItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFB6B21A23186D640022A802 /* NewsItem.swift */; };
|
||||
@@ -1274,7 +1273,6 @@
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
BFB1169D22932DB100BB457C /* apps.json in Resources */,
|
||||
BFB4323F22DE852000B7F8BC /* UpdateCollectionViewCell.xib in Resources */,
|
||||
BFE60738231ADF49002B0E8E /* Settings.storyboard in Resources */,
|
||||
BFD2477A2284B9A700981D42 /* LaunchScreen.storyboard in Resources */,
|
||||
|
||||
@@ -52,7 +52,10 @@ private let ReceivedApplicationState: @convention(c) (CFNotificationCenter?, Uns
|
||||
|
||||
extension AppDelegate
|
||||
{
|
||||
static let openPatreonSettingsDeepLinkNotification = Notification.Name("openPatreonSettingsDeepLinkNotification")
|
||||
static let openPatreonSettingsDeepLinkNotification = Notification.Name("com.rileytestut.AltStore.OpenPatreonSettingsDeepLinkNotification")
|
||||
static let importAppDeepLinkNotification = Notification.Name("com.rileytestut.AltStore.ImportAppDeepLinkNotification")
|
||||
|
||||
static let importAppDeepLinkURLKey = "fileURL"
|
||||
}
|
||||
|
||||
@UIApplicationMain
|
||||
@@ -115,14 +118,27 @@ private extension AppDelegate
|
||||
|
||||
func open(_ url: URL) -> Bool
|
||||
{
|
||||
guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false) else { return false }
|
||||
guard let host = components.host, host.lowercased() == "patreon" else { return false }
|
||||
|
||||
DispatchQueue.main.async {
|
||||
NotificationCenter.default.post(name: AppDelegate.openPatreonSettingsDeepLinkNotification, object: nil)
|
||||
if url.isFileURL
|
||||
{
|
||||
guard url.pathExtension.lowercased() == "ipa" else { return false }
|
||||
|
||||
DispatchQueue.main.async {
|
||||
NotificationCenter.default.post(name: AppDelegate.importAppDeepLinkNotification, object: nil, userInfo: [AppDelegate.importAppDeepLinkURLKey: url])
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
else
|
||||
{
|
||||
guard let components = URLComponents(url: url, resolvingAgainstBaseURL: false) else { return false }
|
||||
guard let host = components.host, host.lowercased() == "patreon" else { return false }
|
||||
|
||||
DispatchQueue.main.async {
|
||||
NotificationCenter.default.post(name: AppDelegate.openPatreonSettingsDeepLinkNotification, object: nil)
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -161,7 +161,7 @@
|
||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" translatesAutoresizingMaskIntoConstraints="NO" id="Glz-dw-2Eg">
|
||||
<rect key="frame" x="0.0" y="76" width="343" height="33.5"/>
|
||||
<subviews>
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="If you used an app-specific password to install AltStore, please use that same password again." textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="a51-OQ-f3j">
|
||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="If you used an app-specific password to install AltStore, please use that same password again." textAlignment="natural" lineBreakMode="tailTruncation" numberOfLines="2" baselineAdjustment="alignBaselines" minimumScaleFactor="0.25" translatesAutoresizingMaskIntoConstraints="NO" id="a51-OQ-f3j">
|
||||
<rect key="frame" x="14" y="0.0" width="315" height="33.5"/>
|
||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
||||
<color key="textColor" white="1" alpha="0.75" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
||||
|
||||
@@ -8,6 +8,23 @@
|
||||
<string>1AAAB6FD-E8CE-4B70-8F26-4073215C03B0</string>
|
||||
<key>CFBundleDevelopmentRegion</key>
|
||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||
<key>CFBundleDocumentTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
<key>CFBundleTypeIconFiles</key>
|
||||
<array/>
|
||||
<key>CFBundleTypeName</key>
|
||||
<string>iOS App</string>
|
||||
<key>CFBundleTypeRole</key>
|
||||
<string>Viewer</string>
|
||||
<key>LSHandlerRank</key>
|
||||
<string>Alternate</string>
|
||||
<key>LSItemContentTypes</key>
|
||||
<array>
|
||||
<string>com.apple.itunes.ipa</string>
|
||||
</array>
|
||||
</dict>
|
||||
</array>
|
||||
<key>CFBundleExecutable</key>
|
||||
<string>$(EXECUTABLE_NAME)</string>
|
||||
<key>CFBundleIdentifier</key>
|
||||
@@ -19,7 +36,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>APPL</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>1.0</string>
|
||||
<string>1.0.1</string>
|
||||
<key>CFBundleURLTypes</key>
|
||||
<array>
|
||||
<dict>
|
||||
|
||||
@@ -60,6 +60,7 @@ class MyAppsViewController: UICollectionViewController
|
||||
super.init(coder: aDecoder)
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(MyAppsViewController.didFetchSource(_:)), name: AppManager.didFetchSourceNotification, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(MyAppsViewController.importApp(_:)), name: AppDelegate.importAppDeepLinkNotification, object: nil)
|
||||
}
|
||||
|
||||
override func viewDidLoad()
|
||||
@@ -577,23 +578,83 @@ private extension MyAppsViewController
|
||||
|
||||
@IBAction func sideloadApp(_ sender: UIBarButtonItem)
|
||||
{
|
||||
func sideloadApp()
|
||||
{
|
||||
self.presentSideloadingAlert { (shouldContinue) in
|
||||
guard shouldContinue else { return }
|
||||
|
||||
let iOSAppUTI = "com.apple.itunes.ipa" // Declared by the system.
|
||||
|
||||
let documentPickerViewController = UIDocumentPickerViewController(documentTypes: [iOSAppUTI], in: .import)
|
||||
documentPickerViewController.delegate = self
|
||||
self.present(documentPickerViewController, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func presentSideloadingAlert(completion: @escaping (Bool) -> Void)
|
||||
{
|
||||
let alertController = UIAlertController(title: NSLocalizedString("Sideload Apps (Beta)", comment: ""), message: NSLocalizedString("You may only install 10 apps + app extensions per week due to Apple's restrictions.\n\nIf you encounter an app that is not able to be sideloaded, please report the app to support@altstore.io.", comment: ""), preferredStyle: .alert)
|
||||
alertController.addAction(UIAlertAction(title: RSTSystemLocalizedString("OK"), style: .default, handler: { (action) in
|
||||
sideloadApp()
|
||||
completion(true)
|
||||
}))
|
||||
alertController.addAction(UIAlertAction(title: UIAlertAction.cancel.title, style: UIAlertAction.cancel.style, handler: { (action) in
|
||||
completion(false)
|
||||
}))
|
||||
alertController.addAction(.cancel)
|
||||
self.present(alertController, animated: true, completion: nil)
|
||||
}
|
||||
|
||||
func installApp(at fileURL: URL, completion: @escaping (Result<Void, Error>) -> Void)
|
||||
{
|
||||
self.navigationItem.leftBarButtonItem?.isIndicatingActivity = true
|
||||
|
||||
DispatchQueue.global().async {
|
||||
let temporaryDirectory = FileManager.default.uniqueTemporaryURL()
|
||||
|
||||
do
|
||||
{
|
||||
try FileManager.default.createDirectory(at: temporaryDirectory, withIntermediateDirectories: true, attributes: nil)
|
||||
|
||||
let unzippedApplicationURL = try FileManager.default.unzipAppBundle(at: fileURL, toDirectory: temporaryDirectory)
|
||||
|
||||
guard let application = ALTApplication(fileURL: unzippedApplicationURL) else { return }
|
||||
|
||||
self.sideloadingProgress = AppManager.shared.install(application, presentingViewController: self) { (result) in
|
||||
try? FileManager.default.removeItem(at: temporaryDirectory)
|
||||
|
||||
DispatchQueue.main.async {
|
||||
if let error = result.error
|
||||
{
|
||||
let toastView = ToastView(text: error.localizedDescription, detailText: nil)
|
||||
toastView.show(in: self.view, duration: 2.0)
|
||||
}
|
||||
else
|
||||
{
|
||||
print("Successfully installed app:", application.bundleIdentifier)
|
||||
}
|
||||
|
||||
self.navigationItem.leftBarButtonItem?.isIndicatingActivity = false
|
||||
self.sideloadingProgressView.observedProgress = nil
|
||||
self.sideloadingProgressView.setHidden(true, animated: true)
|
||||
|
||||
completion(.success(()))
|
||||
}
|
||||
}
|
||||
|
||||
DispatchQueue.main.async {
|
||||
self.sideloadingProgressView.progress = 0
|
||||
self.sideloadingProgressView.isHidden = false
|
||||
self.sideloadingProgressView.observedProgress = self.sideloadingProgress
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
try? FileManager.default.removeItem(at: temporaryDirectory)
|
||||
|
||||
self.navigationItem.leftBarButtonItem?.isIndicatingActivity = false
|
||||
|
||||
completion(.failure(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@objc func presentAlert(for installedApp: InstalledApp)
|
||||
{
|
||||
let alertController = UIAlertController(title: nil, message: NSLocalizedString("Removing a sideloaded app only removes it from AltStore. You must also delete it from the home screen to fully uninstall the app.", comment: ""), preferredStyle: .actionSheet)
|
||||
@@ -643,6 +704,41 @@ private extension MyAppsViewController
|
||||
|
||||
self.presentAlert(for: installedApp)
|
||||
}
|
||||
|
||||
@objc func importApp(_ notification: Notification)
|
||||
{
|
||||
#if BETA
|
||||
|
||||
guard let fileURL = notification.userInfo?[AppDelegate.importAppDeepLinkURLKey] as? URL else { return }
|
||||
guard self.presentedViewController == nil else { return }
|
||||
|
||||
func finish()
|
||||
{
|
||||
do
|
||||
{
|
||||
try FileManager.default.removeItem(at: fileURL)
|
||||
}
|
||||
catch
|
||||
{
|
||||
print("Unable to remove imported .ipa.", error)
|
||||
}
|
||||
}
|
||||
|
||||
self.presentSideloadingAlert { (shouldContinue) in
|
||||
if shouldContinue
|
||||
{
|
||||
self.installApp(at: fileURL) { (result) in
|
||||
finish()
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
finish()
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
extension MyAppsViewController
|
||||
@@ -834,51 +930,8 @@ extension MyAppsViewController: UIDocumentPickerDelegate
|
||||
{
|
||||
guard let fileURL = urls.first else { return }
|
||||
|
||||
self.navigationItem.leftBarButtonItem?.isIndicatingActivity = true
|
||||
|
||||
DispatchQueue.global().async {
|
||||
let temporaryDirectory = FileManager.default.uniqueTemporaryURL()
|
||||
|
||||
do
|
||||
{
|
||||
try FileManager.default.createDirectory(at: temporaryDirectory, withIntermediateDirectories: true, attributes: nil)
|
||||
|
||||
let unzippedApplicationURL = try FileManager.default.unzipAppBundle(at: fileURL, toDirectory: temporaryDirectory)
|
||||
|
||||
guard let application = ALTApplication(fileURL: unzippedApplicationURL) else { return }
|
||||
|
||||
self.sideloadingProgress = AppManager.shared.install(application, presentingViewController: self) { (result) in
|
||||
try? FileManager.default.removeItem(at: temporaryDirectory)
|
||||
|
||||
DispatchQueue.main.async {
|
||||
if let error = result.error
|
||||
{
|
||||
let toastView = ToastView(text: error.localizedDescription, detailText: nil)
|
||||
toastView.show(in: self.navigationController?.view ?? self.view, duration: 2.0)
|
||||
}
|
||||
else
|
||||
{
|
||||
print("Successfully installed app:", application.bundleIdentifier)
|
||||
}
|
||||
|
||||
self.navigationItem.leftBarButtonItem?.isIndicatingActivity = false
|
||||
self.sideloadingProgressView.observedProgress = nil
|
||||
self.sideloadingProgressView.setHidden(true, animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
DispatchQueue.main.async {
|
||||
self.sideloadingProgressView.progress = 0
|
||||
self.sideloadingProgressView.isHidden = false
|
||||
self.sideloadingProgressView.observedProgress = self.sideloadingProgress
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
try? FileManager.default.removeItem(at: temporaryDirectory)
|
||||
|
||||
self.navigationItem.leftBarButtonItem?.isIndicatingActivity = false
|
||||
}
|
||||
self.installApp(at: fileURL) { (result) in
|
||||
print("Sideloaded app at \(fileURL) with result:", result)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ import AuthenticationServices
|
||||
|
||||
private let clientID = "ZMx0EGUWe4TVWYXNZZwK_fbIK5jHFVWoUf1Qb-sqNXmT-YzAGwDPxxq7ak3_W5Q2"
|
||||
private let clientSecret = "1hktsZB89QyN69cB4R0tu55R4TCPQGXxvebYUUh7Y-5TLSnRswuxs6OUjdJ74IJt"
|
||||
private let creatorAccessToken = "mBh0yyK40Ibjzwb_cYeKIuzq8nNFBdEIlNPfgAQlhcU"
|
||||
private let creatorAccessToken = "NSX1ts9Rf9IzKRCu8GjbwsZ6wll8bDtoJxNbPbp2eZo"
|
||||
|
||||
private let campaignID = "2863968"
|
||||
|
||||
@@ -21,12 +21,14 @@ extension PatreonAPI
|
||||
{
|
||||
case unknown
|
||||
case notAuthenticated
|
||||
case invalidAccessToken
|
||||
|
||||
var errorDescription: String? {
|
||||
switch self
|
||||
{
|
||||
case .unknown: return NSLocalizedString("An unknown error occurred.", comment: "")
|
||||
case .notAuthenticated: return NSLocalizedString("No connected Patreon account.", comment: "")
|
||||
case .invalidAccessToken: return NSLocalizedString("Invalid access token.", comment: "")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -365,7 +367,7 @@ private extension PatreonAPI
|
||||
}
|
||||
else
|
||||
{
|
||||
completion(.failure(Error.notAuthenticated))
|
||||
completion(.failure(Error.invalidAccessToken))
|
||||
}
|
||||
|
||||
return
|
||||
|
||||
@@ -66,7 +66,7 @@
|
||||
"developerName": "Riley Testut",
|
||||
"subtitle": "Classic games in your pocket.",
|
||||
"version": "1.0",
|
||||
"versionDate": "2019-09-28T14:30:00-07:00",
|
||||
"versionDate": "2019-09-28T12:00:00-07:00",
|
||||
"versionDescription": "Initial version.",
|
||||
"downloadURL": "https://f000.backblazeb2.com/file/altstore/delta.ipa",
|
||||
"localizedDescription": "Delta is an all-in-one emulator for iOS. Delta builds upon the strengths of its predecessor, GBA4iOS, while expanding to include support for more game systems such as NES, SNES, and N64.\n\nFEATURES\n\nSupported Game Systems\n• Nintendo Entertainment System\n• Super Nintendo Entertainment System\n• Nintendo 64\n• Game Boy (Color)\n• Game Boy Advance\n• And plenty more to come!\n\nController Support\n• Supports PS4, Xbox One S, and MFi game controllers.\n• Supports bluetooth (and wired) keyboards, as well as the Apple Smart Keyboard.\n• Completely customize button mappings on a per-system, per-controller basis.\n• Map buttons to special “Quick Save”, “Quick Load,” and “Fast Forward” actions.\n\nSave States\n• Save and load save states for any game from the pause menu.\n• Lock save states to prevent them from being accidentally overwritten.\n• Automatically makes backup save states to ensure you never lose your progress.\n• Support for “Quick Saves,” save states that can be quickly saved/loaded with a single button press (requires external controller).\n\nCheats\n• Supports various types of cheat codes for each supported system:\n• NES: Game Genie\n• SNES: Game Genie, Pro Action Replay\n• N64: GameShark\n• GBC: Game Genie, GameShark\n• GBA: Action Replay, Code Breaker, GameShark\n\nDelta Sync\n• Sync your games, game saves, save states, cheats, controller skins, and controller mappings between devices.\n• View version histories of everything you sync and optionally restore them to earlier versions.\n• Supports both Google Drive and Dropbox.\n\nCustom Controller Skins\n• Beautiful built-in controller skins for all systems.\n• Import controller skins made by others, or even make your own to share with the world!\n\nHold Button\n• Choose buttons for Delta to hold down on your behalf, freeing up your thumbs to press other buttons instead.\n• Perfect for games that typically require one button be held down constantly (ex: run button in Mario games, or the A button in Mario Kart).\n\nFast Forward\n• Speed through slower parts of games by running the game much faster than normal.\n• Easily enable or disable from the pause menu, or optionally with a mapped button on an external controller.\n\n3D/Haptic Touch\n• Use 3D or Haptic Touch to “peek” at games, save states, and cheat codes.\n• App icon shortcuts allow quick access to your most recently played games, or optionally customize the shortcuts to always include certain games.\n\nGame Artwork\n• Automatically displays appropriate box art for imported games.\n• Change a game’s artwork to anything you want, or select from the built-in game artwork database.\n\nMisc.\n• Gyroscope support for WarioWare: Twisted!\n• Support for delta:// URL scheme to jump directly into a specific game.\n\n**Delta and AltStore LLC are in no way affiliated with Nintendo. The name \"Nintendo\" and all associated game console names are registered trademarks of Nintendo Co., Ltd.**",
|
||||
@@ -91,7 +91,7 @@
|
||||
"developerName": "Riley Testut",
|
||||
"subtitle": "Classic games in your pocket.",
|
||||
"version": "1.0b",
|
||||
"versionDate": "2019-09-28T02:30:00-07:00",
|
||||
"versionDate": "2019-09-28T12:00:00-07:00",
|
||||
"versionDescription": "Includes initial support for DS games.",
|
||||
"downloadURL": "https://f000.backblazeb2.com/file/altstore/delta-beta.ipa",
|
||||
"localizedDescription": "The next console for Delta is coming: this beta version of Delta brings support for playing DS games!\n\nDS support currently includes:\n• Playing DS games\n• Save States\n• Hold Button\n\nFeatures I'm still working on:\n• Fast Forward\n• Cheats\n• Controller skin (using placeholder controller skin for now)\n\nPlease report any issues you find to support@altstore.io. Thanks!",
|
||||
@@ -167,7 +167,7 @@
|
||||
"subtitle": "Manage your clipboard history with ease.",
|
||||
"developerName": "Riley Testut",
|
||||
"version": "1.0",
|
||||
"versionDate": "2019-09-28T02:30:00-07:00",
|
||||
"versionDate": "2019-09-28T12:00:00-07:00",
|
||||
"versionDescription": "Initial version.",
|
||||
"downloadURL": "https://f000.backblazeb2.com/file/altstore/clip.ipa",
|
||||
"localizedDescription": "Clip is a simple clipboard manager for iOS. \n\nUnlike other clipboard managers, Clip can continue monitoring your clipboard while in the background. No longer do you need to remember to manually open or share to an app to save your clipboard; just copy and paste as you would normally do, and Clip will have your back.\n\nIn addition to background monitoring, Clip also has these features:\n\n• Automatically track text, URLs, and images copied to the clipboard.\n• Shows notification upon saving a new item to the clipboard with a preview of the copied text, URL, or image.\n• Copy, delete, or share any clippings saved to Clip.\n• Customizable history limit.\n\nDownload Clip today, and never worry about losing your clipboard again!",
|
||||
|
||||
@@ -8,6 +8,17 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
extension TabBarController
|
||||
{
|
||||
private enum Tab: Int, CaseIterable
|
||||
{
|
||||
case news
|
||||
case browse
|
||||
case myApps
|
||||
case settings
|
||||
}
|
||||
}
|
||||
|
||||
class TabBarController: UITabBarController
|
||||
{
|
||||
required init?(coder aDecoder: NSCoder)
|
||||
@@ -15,6 +26,7 @@ class TabBarController: UITabBarController
|
||||
super.init(coder: aDecoder)
|
||||
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(TabBarController.openPatreonSettings(_:)), name: AppDelegate.openPatreonSettingsDeepLinkNotification, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(TabBarController.importApp(_:)), name: AppDelegate.importAppDeepLinkNotification, object: nil)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,7 +34,11 @@ private extension TabBarController
|
||||
{
|
||||
@objc func openPatreonSettings(_ notification: Notification)
|
||||
{
|
||||
guard let items = self.tabBar.items else { return }
|
||||
self.selectedIndex = items.count - 1
|
||||
self.selectedIndex = Tab.settings.rawValue
|
||||
}
|
||||
|
||||
@objc func importApp(_ notification: Notification)
|
||||
{
|
||||
self.selectedIndex = Tab.myApps.rawValue
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user