mirror of
https://github.com/SideStore/SideStore.git
synced 2026-02-09 06:43:25 +01:00
Merge branch 'develop' of github.com:rileytestut/AltStore into develop
This commit is contained in:
@@ -13,6 +13,12 @@ import AltSign
|
||||
|
||||
import LaunchAtLogin
|
||||
|
||||
#if STAGING
|
||||
private let altstoreAppURL = URL(string: "https://f000.backblazeb2.com/file/altstore-staging/altstore.ipa")!
|
||||
#else
|
||||
private let altstoreAppURL = URL(string: "https://f000.backblazeb2.com/file/altstore/altstore.ipa")!
|
||||
#endif
|
||||
|
||||
@NSApplicationMain
|
||||
class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
|
||||
@@ -26,6 +32,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
|
||||
@IBOutlet private var appMenu: NSMenu!
|
||||
@IBOutlet private var connectedDevicesMenu: NSMenu!
|
||||
@IBOutlet private var sideloadIPAConnectedDevicesMenu: NSMenu!
|
||||
@IBOutlet private var launchAtLoginMenuItem: NSMenuItem!
|
||||
@IBOutlet private var installMailPluginMenuItem: NSMenuItem!
|
||||
|
||||
@@ -48,6 +55,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
||||
|
||||
self.appMenu.delegate = self
|
||||
self.connectedDevicesMenu.delegate = self
|
||||
self.sideloadIPAConnectedDevicesMenu.delegate = self
|
||||
|
||||
UNUserNotificationCenter.current().requestAuthorization(options: [.alert]) { (success, error) in
|
||||
guard success else { return }
|
||||
@@ -81,8 +89,30 @@ private extension AppDelegate
|
||||
{
|
||||
@objc func installAltStore(_ item: NSMenuItem)
|
||||
{
|
||||
guard case let index = self.connectedDevicesMenu.index(of: item), index != -1 else { return }
|
||||
guard let index = item.menu?.index(of: item), index != -1 else { return }
|
||||
|
||||
let device = self.connectedDevices[index]
|
||||
self.installApplication(at: altstoreAppURL, to: device)
|
||||
}
|
||||
|
||||
@objc func sideloadIPA(_ item: NSMenuItem)
|
||||
{
|
||||
guard let index = item.menu?.index(of: item), index != -1 else { return }
|
||||
|
||||
let device = self.connectedDevices[index]
|
||||
|
||||
let openPanel = NSOpenPanel()
|
||||
openPanel.canChooseDirectories = false
|
||||
openPanel.allowsMultipleSelection = false
|
||||
openPanel.allowedFileTypes = ["ipa"]
|
||||
openPanel.begin { (response) in
|
||||
guard let fileURL = openPanel.url, response == .OK else { return }
|
||||
self.installApplication(at: fileURL, to: device)
|
||||
}
|
||||
}
|
||||
|
||||
func installApplication(at url: URL, to device: ALTDevice)
|
||||
{
|
||||
let alert = NSAlert()
|
||||
alert.messageText = NSLocalizedString("Please enter your Apple ID and password.", comment: "")
|
||||
alert.informativeText = NSLocalizedString("Your Apple ID and password are not saved and are only sent to Apple for authentication.", comment: "")
|
||||
@@ -125,18 +155,16 @@ private extension AppDelegate
|
||||
|
||||
let username = appleIDTextField.stringValue
|
||||
let password = passwordTextField.stringValue
|
||||
|
||||
let device = self.connectedDevices[index]
|
||||
|
||||
|
||||
func install()
|
||||
{
|
||||
ALTDeviceManager.shared.installAltStore(to: device, appleID: username, password: password) { (result) in
|
||||
ALTDeviceManager.shared.installApplication(at: url, to: device, appleID: username, password: password) { (result) in
|
||||
switch result
|
||||
{
|
||||
case .success:
|
||||
case .success(let application):
|
||||
let content = UNMutableNotificationContent()
|
||||
content.title = NSLocalizedString("Installation Succeeded", comment: "")
|
||||
content.body = String(format: NSLocalizedString("AltStore was successfully installed on %@.", comment: ""), device.name)
|
||||
content.body = String(format: NSLocalizedString("%@ was successfully installed on %@.", comment: ""), application.name, device.name)
|
||||
|
||||
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: nil)
|
||||
UNUserNotificationCenter.current().add(request)
|
||||
@@ -272,7 +300,7 @@ extension AppDelegate: NSMenuDelegate
|
||||
{
|
||||
guard menu == self.appMenu else { return }
|
||||
|
||||
self.connectedDevices = ALTDeviceManager.shared.connectedDevices
|
||||
self.connectedDevices = ALTDeviceManager.shared.availableDevices
|
||||
|
||||
self.launchAtLoginMenuItem.target = self
|
||||
self.launchAtLoginMenuItem.action = #selector(AppDelegate.toggleLaunchAtLogin(_:))
|
||||
@@ -296,14 +324,14 @@ extension AppDelegate: NSMenuDelegate
|
||||
|
||||
func numberOfItems(in menu: NSMenu) -> Int
|
||||
{
|
||||
guard menu == self.connectedDevicesMenu else { return -1 }
|
||||
guard menu == self.connectedDevicesMenu || menu == self.sideloadIPAConnectedDevicesMenu else { return -1 }
|
||||
|
||||
return self.connectedDevices.isEmpty ? 1 : self.connectedDevices.count
|
||||
}
|
||||
|
||||
func menu(_ menu: NSMenu, update item: NSMenuItem, at index: Int, shouldCancel: Bool) -> Bool
|
||||
{
|
||||
guard menu == self.connectedDevicesMenu else { return false }
|
||||
guard menu == self.connectedDevicesMenu || menu == self.sideloadIPAConnectedDevicesMenu else { return false }
|
||||
|
||||
if self.connectedDevices.isEmpty
|
||||
{
|
||||
@@ -318,7 +346,7 @@ extension AppDelegate: NSMenuDelegate
|
||||
item.title = device.name
|
||||
item.isEnabled = true
|
||||
item.target = self
|
||||
item.action = #selector(AppDelegate.installAltStore)
|
||||
item.action = (menu == self.connectedDevicesMenu) ? #selector(AppDelegate.installAltStore(_:)) : #selector(AppDelegate.sideloadIPA(_:))
|
||||
item.tag = index
|
||||
}
|
||||
|
||||
|
||||
@@ -64,6 +64,7 @@
|
||||
<outlet property="connectedDevicesMenu" destination="KJ9-WY-pW1" id="Mcv-64-iFU"/>
|
||||
<outlet property="installMailPluginMenuItem" destination="3CM-gV-X2G" id="lio-ha-z0S"/>
|
||||
<outlet property="launchAtLoginMenuItem" destination="IyR-FQ-upe" id="Fxn-EP-hwH"/>
|
||||
<outlet property="sideloadIPAConnectedDevicesMenu" destination="IuI-bV-fTY" id="QQw-St-HfG"/>
|
||||
</connections>
|
||||
</customObject>
|
||||
<customObject id="Arf-IC-5eb" customClass="SUUpdater"/>
|
||||
@@ -97,6 +98,22 @@
|
||||
</connections>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem title="Sideload .ipa" id="x0e-zI-0A2" userLabel="Install .ipa">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<menu key="submenu" title="Sideload .ipa" systemMenu="recentDocuments" id="IuI-bV-fTY">
|
||||
<items>
|
||||
<menuItem title="No Connected Devices" id="in5-an-MD0">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
<connections>
|
||||
<action selector="clearRecentDocuments:" target="Ady-hI-5gd" id="aUE-On-axK"/>
|
||||
</connections>
|
||||
</menuItem>
|
||||
</items>
|
||||
<connections>
|
||||
<outlet property="delegate" destination="Voe-Tx-rLC" id="N3K-su-XV6"/>
|
||||
</connections>
|
||||
</menu>
|
||||
</menuItem>
|
||||
<menuItem isSeparatorItem="YES" id="1ZZ-BB-xHy"/>
|
||||
<menuItem title="Launch at Login" id="IyR-FQ-upe" userLabel="Launch At Login">
|
||||
<modifierMask key="keyEquivalentModifierMask"/>
|
||||
|
||||
@@ -10,12 +10,6 @@ import Cocoa
|
||||
import UserNotifications
|
||||
import ObjectiveC
|
||||
|
||||
#if STAGING
|
||||
private let appURL = URL(string: "https://f000.backblazeb2.com/file/altstore-staging/altstore.ipa")!
|
||||
#else
|
||||
private let appURL = URL(string: "https://f000.backblazeb2.com/file/altstore/altstore.ipa")!
|
||||
#endif
|
||||
|
||||
private let appGroupsLock = NSLock()
|
||||
|
||||
enum InstallError: LocalizedError
|
||||
@@ -38,21 +32,14 @@ enum InstallError: LocalizedError
|
||||
|
||||
extension ALTDeviceManager
|
||||
{
|
||||
func installAltStore(to device: ALTDevice, appleID: String, password: String, completion: @escaping (Result<Void, Error>) -> Void)
|
||||
func installApplication(at url: URL, to device: ALTDevice, appleID: String, password: String, completion: @escaping (Result<ALTApplication, Error>) -> Void)
|
||||
{
|
||||
let destinationDirectoryURL = FileManager.default.temporaryDirectory.appendingPathComponent(UUID().uuidString)
|
||||
|
||||
func finish(_ error: Error?, title: String = "")
|
||||
func finish(_ result: Result<ALTApplication, Error>, title: String = "")
|
||||
{
|
||||
DispatchQueue.main.async {
|
||||
if let error = error
|
||||
{
|
||||
completion(.failure(error))
|
||||
}
|
||||
else
|
||||
{
|
||||
completion(.success(()))
|
||||
}
|
||||
completion(result)
|
||||
}
|
||||
|
||||
try? FileManager.default.removeItem(at: destinationDirectoryURL)
|
||||
@@ -83,14 +70,13 @@ extension ALTDeviceManager
|
||||
{
|
||||
let certificate = try result.get()
|
||||
|
||||
let content = UNMutableNotificationContent()
|
||||
content.title = String(format: NSLocalizedString("Installing AltStore to %@...", comment: ""), device.name)
|
||||
content.body = NSLocalizedString("This may take a few seconds.", comment: "")
|
||||
|
||||
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: nil)
|
||||
UNUserNotificationCenter.current().add(request)
|
||||
|
||||
self.downloadApp { (result) in
|
||||
if !url.isFileURL
|
||||
{
|
||||
// Show alert before downloading remote .ipa.
|
||||
self.showInstallationAlert(appName: NSLocalizedString("AltStore", comment: ""), deviceName: device.name)
|
||||
}
|
||||
|
||||
self.downloadApp(from: url) { (result) in
|
||||
do
|
||||
{
|
||||
let fileURL = try result.get()
|
||||
@@ -98,18 +84,14 @@ extension ALTDeviceManager
|
||||
try FileManager.default.createDirectory(at: destinationDirectoryURL, withIntermediateDirectories: true, attributes: nil)
|
||||
|
||||
let appBundleURL = try FileManager.default.unzipAppBundle(at: fileURL, toDirectory: destinationDirectoryURL)
|
||||
|
||||
do
|
||||
{
|
||||
try FileManager.default.removeItem(at: fileURL)
|
||||
}
|
||||
catch
|
||||
{
|
||||
print("Failed to remove downloaded .ipa.", error)
|
||||
}
|
||||
|
||||
guard let application = ALTApplication(fileURL: appBundleURL) else { throw ALTError(.invalidApp) }
|
||||
|
||||
if url.isFileURL
|
||||
{
|
||||
// Show alert after "downloading" local .ipa.
|
||||
self.showInstallationAlert(appName: application.name, deviceName: device.name)
|
||||
}
|
||||
|
||||
// Refresh anisette data to prevent session timeouts.
|
||||
AnisetteDataManager.shared.requestAnisetteData { (result) in
|
||||
do
|
||||
@@ -117,71 +99,79 @@ extension ALTDeviceManager
|
||||
let anisetteData = try result.get()
|
||||
session.anisetteData = anisetteData
|
||||
|
||||
self.prepareAllProvisioningProfiles(for: application, team: team, session: session) { (result) in
|
||||
self.prepareAllProvisioningProfiles(for: application, device: device, team: team, session: session) { (result) in
|
||||
do
|
||||
{
|
||||
let profiles = try result.get()
|
||||
|
||||
self.install(application, to: device, team: team, certificate: certificate, profiles: profiles) { (result) in
|
||||
finish(result.error, title: "Failed to Install AltStore")
|
||||
finish(result.map { application }, title: "Failed to Install AltStore")
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
finish(error, title: "Failed to Fetch Provisioning Profiles")
|
||||
finish(.failure(error), title: "Failed to Fetch Provisioning Profiles")
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
finish(error, title: "Failed to Refresh Anisette Data")
|
||||
finish(.failure(error), title: "Failed to Refresh Anisette Data")
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
finish(error, title: "Failed to Download AltStore")
|
||||
finish(.failure(error), title: "Failed to Download AltStore")
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
finish(error, title: "Failed to Fetch Certificate")
|
||||
finish(.failure(error), title: "Failed to Fetch Certificate")
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
finish(error, title: "Failed to Register Device")
|
||||
finish(.failure(error), title: "Failed to Register Device")
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
finish(error, title: "Failed to Fetch Team")
|
||||
finish(.failure(error), title: "Failed to Fetch Team")
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
finish(error, title: "Failed to Authenticate")
|
||||
finish(.failure(error), title: "Failed to Authenticate")
|
||||
}
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
finish(error, title: "Failed to Fetch Anisette Data")
|
||||
finish(.failure(error), title: "Failed to Fetch Anisette Data")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func downloadApp(completionHandler: @escaping (Result<URL, Error>) -> Void)
|
||||
}
|
||||
|
||||
private extension ALTDeviceManager
|
||||
{
|
||||
func downloadApp(from url: URL, completionHandler: @escaping (Result<URL, Error>) -> Void)
|
||||
{
|
||||
let downloadTask = URLSession.shared.downloadTask(with: appURL) { (fileURL, response, error) in
|
||||
guard !url.isFileURL else { return completionHandler(.success(url)) }
|
||||
|
||||
let downloadTask = URLSession.shared.downloadTask(with: url) { (fileURL, response, error) in
|
||||
do
|
||||
{
|
||||
let (fileURL, _) = try Result((fileURL, response), error).get()
|
||||
completionHandler(.success(fileURL))
|
||||
|
||||
do { try FileManager.default.removeItem(at: fileURL) }
|
||||
catch { print("Failed to remove downloaded .ipa.", error) }
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -244,26 +234,91 @@ extension ALTDeviceManager
|
||||
|
||||
func fetchTeam(for account: ALTAccount, session: ALTAppleAPISession, completionHandler: @escaping (Result<ALTTeam, Error>) -> Void)
|
||||
{
|
||||
func finish(_ result: Result<ALTTeam, Error>)
|
||||
{
|
||||
switch result
|
||||
ALTAppleAPI.shared.fetchTeams(for: account, session: session) { (teams, error) in
|
||||
do
|
||||
{
|
||||
case .failure(let error):
|
||||
completionHandler(.failure(error))
|
||||
let teams = try Result(teams, error).get()
|
||||
|
||||
case .success(let team):
|
||||
if let team = teams.first(where: { $0.type == .individual })
|
||||
{
|
||||
return completionHandler(.success(team))
|
||||
}
|
||||
else if let team = teams.first(where: { $0.type == .free })
|
||||
{
|
||||
return completionHandler(.success(team))
|
||||
}
|
||||
else if let team = teams.first
|
||||
{
|
||||
return completionHandler(.success(team))
|
||||
}
|
||||
else
|
||||
{
|
||||
throw InstallError.noTeam
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
completionHandler(.failure(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func fetchCertificate(for team: ALTTeam, session: ALTAppleAPISession, completionHandler: @escaping (Result<ALTCertificate, Error>) -> Void)
|
||||
{
|
||||
ALTAppleAPI.shared.fetchCertificates(for: team, session: session) { (certificates, error) in
|
||||
do
|
||||
{
|
||||
let certificates = try Result(certificates, error).get()
|
||||
|
||||
let applicationSupportDirectoryURL = FileManager.default.urls(for: .applicationSupportDirectory, in: .userDomainMask)[0]
|
||||
let altserverDirectoryURL = applicationSupportDirectoryURL.appendingPathComponent("com.rileytestut.AltServer")
|
||||
let certificatesDirectoryURL = altserverDirectoryURL.appendingPathComponent("Certificates")
|
||||
|
||||
try FileManager.default.createDirectory(at: certificatesDirectoryURL, withIntermediateDirectories: true, attributes: nil)
|
||||
|
||||
let certificateFileURL = certificatesDirectoryURL.appendingPathComponent(team.identifier + ".p12")
|
||||
|
||||
var isCancelled = false
|
||||
|
||||
// Check if there is another AltStore certificate, which means AltStore has been installed with this Apple ID before.
|
||||
if let previousCertificate = certificates.first(where: { $0.machineName?.starts(with: "AltStore") == true })
|
||||
{
|
||||
if FileManager.default.fileExists(atPath: certificateFileURL.path),
|
||||
let data = try? Data(contentsOf: certificateFileURL),
|
||||
let certificate = ALTCertificate(p12Data: data, password: previousCertificate.machineIdentifier)
|
||||
{
|
||||
return completionHandler(.success(certificate))
|
||||
}
|
||||
|
||||
DispatchQueue.main.sync {
|
||||
let alert = NSAlert()
|
||||
alert.messageText = NSLocalizedString("Multiple AltServers Not Supported", comment: "")
|
||||
alert.informativeText = NSLocalizedString("Please use the same AltServer you previously used with this Apple ID, or else apps installed with other AltServers will stop working.\n\nAre you sure you want to continue?", 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
|
||||
}
|
||||
}
|
||||
|
||||
guard !isCancelled else { return completionHandler(.failure(InstallError.cancelled)) }
|
||||
}
|
||||
|
||||
if team.type != .free
|
||||
{
|
||||
DispatchQueue.main.sync {
|
||||
let alert = NSAlert()
|
||||
alert.messageText = NSLocalizedString("Installing AltStore will revoke your iOS development certificate.", comment: "")
|
||||
alert.messageText = NSLocalizedString("Installing this app 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.
|
||||
To prevent this from happening, feel free to try again with another Apple ID.
|
||||
""", comment: "")
|
||||
|
||||
alert.addButton(withTitle: NSLocalizedString("Continue", comment: ""))
|
||||
@@ -278,78 +333,7 @@ To prevent this from happening, feel free to try again with another Apple ID to
|
||||
}
|
||||
}
|
||||
|
||||
if isCancelled
|
||||
{
|
||||
return completionHandler(.failure(InstallError.cancelled))
|
||||
}
|
||||
}
|
||||
|
||||
completionHandler(.success(team))
|
||||
}
|
||||
}
|
||||
|
||||
ALTAppleAPI.shared.fetchTeams(for: account, session: session) { (teams, error) in
|
||||
do
|
||||
{
|
||||
let teams = try Result(teams, error).get()
|
||||
|
||||
if let team = teams.first(where: { $0.type == .free })
|
||||
{
|
||||
return finish(.success(team))
|
||||
}
|
||||
else if let team = teams.first(where: { $0.type == .individual })
|
||||
{
|
||||
return finish(.success(team))
|
||||
}
|
||||
else if let team = teams.first
|
||||
{
|
||||
return finish(.success(team))
|
||||
}
|
||||
else
|
||||
{
|
||||
throw InstallError.noTeam
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
finish(.failure(error))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func fetchCertificate(for team: ALTTeam, session: ALTAppleAPISession, completionHandler: @escaping (Result<ALTCertificate, Error>) -> Void)
|
||||
{
|
||||
ALTAppleAPI.shared.fetchCertificates(for: team, session: session) { (certificates, error) in
|
||||
do
|
||||
{
|
||||
let certificates = try Result(certificates, error).get()
|
||||
|
||||
// Check if there is another AltStore certificate, which means AltStore has been installed with this Apple ID before.
|
||||
if certificates.contains(where: { $0.machineName?.starts(with: "AltStore") == true })
|
||||
{
|
||||
var isCancelled = false
|
||||
|
||||
DispatchQueue.main.sync {
|
||||
let alert = NSAlert()
|
||||
alert.messageText = NSLocalizedString("AltStore already installed on another device.", comment: "")
|
||||
alert.informativeText = NSLocalizedString("Apps installed with AltStore on your other devices will stop working. Are you sure you want to continue?", 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))
|
||||
}
|
||||
guard !isCancelled else { return completionHandler(.failure(InstallError.cancelled)) }
|
||||
}
|
||||
|
||||
if let certificate = certificates.first
|
||||
@@ -386,6 +370,14 @@ To prevent this from happening, feel free to try again with another Apple ID to
|
||||
certificate.privateKey = privateKey
|
||||
|
||||
completionHandler(.success(certificate))
|
||||
|
||||
if let machineIdentifier = certificate.machineIdentifier,
|
||||
let encryptedData = certificate.encryptedP12Data(withPassword: machineIdentifier)
|
||||
{
|
||||
// Cache certificate.
|
||||
do { try encryptedData.write(to: certificateFileURL, options: .atomic) }
|
||||
catch { print("Failed to cache certificate:", error) }
|
||||
}
|
||||
}
|
||||
catch
|
||||
{
|
||||
@@ -407,10 +399,10 @@ To prevent this from happening, feel free to try again with another Apple ID to
|
||||
}
|
||||
}
|
||||
|
||||
func prepareAllProvisioningProfiles(for application: ALTApplication, team: ALTTeam, session: ALTAppleAPISession,
|
||||
func prepareAllProvisioningProfiles(for application: ALTApplication, device: ALTDevice, team: ALTTeam, session: ALTAppleAPISession,
|
||||
completion: @escaping (Result<[String: ALTProvisioningProfile], Error>) -> Void)
|
||||
{
|
||||
self.prepareProvisioningProfile(for: application, team: team, session: session) { (result) in
|
||||
self.prepareProvisioningProfile(for: application, parentApp: nil, device: device, team: team, session: session) { (result) in
|
||||
do
|
||||
{
|
||||
let profile = try result.get()
|
||||
@@ -424,7 +416,7 @@ To prevent this from happening, feel free to try again with another Apple ID to
|
||||
{
|
||||
dispatchGroup.enter()
|
||||
|
||||
self.prepareProvisioningProfile(for: appExtension, team: team, session: session) { (result) in
|
||||
self.prepareProvisioningProfile(for: appExtension, parentApp: application, device: device, team: team, session: session) { (result) in
|
||||
switch result
|
||||
{
|
||||
case .failure(let e): error = e
|
||||
@@ -453,9 +445,35 @@ To prevent this from happening, feel free to try again with another Apple ID to
|
||||
}
|
||||
}
|
||||
|
||||
func prepareProvisioningProfile(for application: ALTApplication, team: ALTTeam, session: ALTAppleAPISession, completionHandler: @escaping (Result<ALTProvisioningProfile, Error>) -> Void)
|
||||
func prepareProvisioningProfile(for application: ALTApplication, parentApp: ALTApplication?, device: ALTDevice, team: ALTTeam, session: ALTAppleAPISession, completionHandler: @escaping (Result<ALTProvisioningProfile, Error>) -> Void)
|
||||
{
|
||||
self.registerAppID(name: application.name, identifier: application.bundleIdentifier, team: team, session: session) { (result) in
|
||||
let parentBundleID = parentApp?.bundleIdentifier ?? application.bundleIdentifier
|
||||
let updatedParentBundleID: String
|
||||
|
||||
if application.isAltStoreApp
|
||||
{
|
||||
// Use legacy bundle ID format for AltStore (and its extensions).
|
||||
updatedParentBundleID = "com.\(team.identifier).\(parentBundleID)"
|
||||
}
|
||||
else
|
||||
{
|
||||
updatedParentBundleID = parentBundleID + "." + team.identifier // Append just team identifier to make it harder to track.
|
||||
}
|
||||
|
||||
let bundleID = application.bundleIdentifier.replacingOccurrences(of: parentBundleID, with: updatedParentBundleID)
|
||||
|
||||
let preferredName: String
|
||||
|
||||
if let parentApp = parentApp
|
||||
{
|
||||
preferredName = parentApp.name + " " + application.name
|
||||
}
|
||||
else
|
||||
{
|
||||
preferredName = application.name
|
||||
}
|
||||
|
||||
self.registerAppID(name: preferredName, bundleID: bundleID, team: team, session: session) { (result) in
|
||||
do
|
||||
{
|
||||
let appID = try result.get()
|
||||
@@ -470,7 +488,7 @@ To prevent this from happening, feel free to try again with another Apple ID to
|
||||
{
|
||||
let appID = try result.get()
|
||||
|
||||
self.fetchProvisioningProfile(for: appID, team: team, session: session) { (result) in
|
||||
self.fetchProvisioningProfile(for: appID, device: device, team: team, session: session) { (result) in
|
||||
completionHandler(result)
|
||||
}
|
||||
}
|
||||
@@ -493,10 +511,8 @@ To prevent this from happening, feel free to try again with another Apple ID to
|
||||
}
|
||||
}
|
||||
|
||||
func registerAppID(name appName: String, identifier: String, team: ALTTeam, session: ALTAppleAPISession, completionHandler: @escaping (Result<ALTAppID, Error>) -> Void)
|
||||
func registerAppID(name appName: String, bundleID: String, team: ALTTeam, session: ALTAppleAPISession, completionHandler: @escaping (Result<ALTAppID, Error>) -> Void)
|
||||
{
|
||||
let bundleID = "com.\(team.identifier).\(identifier)"
|
||||
|
||||
ALTAppleAPI.shared.fetchAppIDs(for: team, session: session) { (appIDs, error) in
|
||||
do
|
||||
{
|
||||
@@ -652,7 +668,7 @@ To prevent this from happening, feel free to try again with another Apple ID to
|
||||
|
||||
func register(_ device: ALTDevice, team: ALTTeam, session: ALTAppleAPISession, completionHandler: @escaping (Result<ALTDevice, Error>) -> Void)
|
||||
{
|
||||
ALTAppleAPI.shared.fetchDevices(for: team, session: session) { (devices, error) in
|
||||
ALTAppleAPI.shared.fetchDevices(for: team, types: device.type, session: session) { (devices, error) in
|
||||
do
|
||||
{
|
||||
let devices = try Result(devices, error).get()
|
||||
@@ -663,7 +679,7 @@ To prevent this from happening, feel free to try again with another Apple ID to
|
||||
}
|
||||
else
|
||||
{
|
||||
ALTAppleAPI.shared.registerDevice(name: device.name, identifier: device.identifier, team: team, session: session) { (device, error) in
|
||||
ALTAppleAPI.shared.registerDevice(name: device.name, identifier: device.identifier, type: device.type, team: team, session: session) { (device, error) in
|
||||
completionHandler(Result(device, error))
|
||||
}
|
||||
}
|
||||
@@ -675,9 +691,9 @@ To prevent this from happening, feel free to try again with another Apple ID to
|
||||
}
|
||||
}
|
||||
|
||||
func fetchProvisioningProfile(for appID: ALTAppID, team: ALTTeam, session: ALTAppleAPISession, completionHandler: @escaping (Result<ALTProvisioningProfile, Error>) -> Void)
|
||||
func fetchProvisioningProfile(for appID: ALTAppID, device: ALTDevice, team: ALTTeam, session: ALTAppleAPISession, completionHandler: @escaping (Result<ALTProvisioningProfile, Error>) -> Void)
|
||||
{
|
||||
ALTAppleAPI.shared.fetchProvisioningProfile(for: appID, team: team, session: session) { (profile, error) in
|
||||
ALTAppleAPI.shared.fetchProvisioningProfile(for: appID, deviceType: device.type, team: team, session: session) { (profile, error) in
|
||||
completionHandler(Result(profile, error))
|
||||
}
|
||||
}
|
||||
@@ -723,17 +739,21 @@ To prevent this from happening, feel free to try again with another Apple ID to
|
||||
allURLSchemes.append(altstoreURLScheme)
|
||||
|
||||
var additionalValues: [String: Any] = [Bundle.Info.urlTypes: allURLSchemes]
|
||||
additionalValues[Bundle.Info.deviceID] = device.identifier
|
||||
additionalValues[Bundle.Info.serverID] = UserDefaults.standard.serverID
|
||||
|
||||
if
|
||||
let machineIdentifier = certificate.machineIdentifier,
|
||||
let encryptedData = certificate.encryptedP12Data(withPassword: machineIdentifier)
|
||||
if application.isAltStoreApp
|
||||
{
|
||||
additionalValues[Bundle.Info.certificateID] = certificate.serialNumber
|
||||
additionalValues[Bundle.Info.deviceID] = device.identifier
|
||||
additionalValues[Bundle.Info.serverID] = UserDefaults.standard.serverID
|
||||
|
||||
let certificateURL = application.fileURL.appendingPathComponent("ALTCertificate.p12")
|
||||
try encryptedData.write(to: certificateURL, options: .atomic)
|
||||
if
|
||||
let machineIdentifier = certificate.machineIdentifier,
|
||||
let encryptedData = certificate.encryptedP12Data(withPassword: machineIdentifier)
|
||||
{
|
||||
additionalValues[Bundle.Info.certificateID] = certificate.serialNumber
|
||||
|
||||
let certificateURL = application.fileURL.appendingPathComponent("ALTCertificate.p12")
|
||||
try encryptedData.write(to: certificateURL, options: .atomic)
|
||||
}
|
||||
}
|
||||
|
||||
try prepare(appBundle, additionalInfoDictionaryValues: additionalValues)
|
||||
@@ -750,7 +770,7 @@ To prevent this from happening, feel free to try again with another Apple ID to
|
||||
{
|
||||
try Result(success, error).get()
|
||||
|
||||
let activeProfiles: Set<String>? = (team.type == .free) ? Set(profiles.values.map(\.bundleIdentifier)) : nil
|
||||
let activeProfiles: Set<String>? = (team.type == .free && application.isAltStoreApp) ? Set(profiles.values.map(\.bundleIdentifier)) : nil
|
||||
ALTDeviceManager.shared.installApp(at: application.fileURL, toDeviceWithUDID: device.identifier, activeProvisioningProfiles: activeProfiles) { (success, error) in
|
||||
completionHandler(Result(success, error))
|
||||
}
|
||||
@@ -769,6 +789,16 @@ To prevent this from happening, feel free to try again with another Apple ID to
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func showInstallationAlert(appName: String, deviceName: String)
|
||||
{
|
||||
let content = UNMutableNotificationContent()
|
||||
content.title = String(format: NSLocalizedString("Installing %@ to %@...", comment: ""), appName, deviceName)
|
||||
content.body = NSLocalizedString("This may take a few seconds.", comment: "")
|
||||
|
||||
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: nil)
|
||||
UNUserNotificationCenter.current().add(request)
|
||||
}
|
||||
}
|
||||
|
||||
private var securityCodeAlertKey = 0
|
||||
|
||||
@@ -297,6 +297,9 @@ NSNotificationName const ALTDeviceManagerDeviceDidDisconnectNotification = @"ALT
|
||||
NSError *writeError = nil;
|
||||
if (![self writeDirectory:appBundleURL toDestinationURL:destinationURL client:afc progress:nil error:&writeError])
|
||||
{
|
||||
int removeResult = afc_remove_path_and_contents(afc, stagingURL.relativePath.fileSystemRepresentation);
|
||||
NSLog(@"Remove staging app result: %@", @(removeResult));
|
||||
|
||||
return finish(writeError);
|
||||
}
|
||||
|
||||
@@ -430,7 +433,7 @@ NSNotificationName const ALTDeviceManagerDeviceDidDisconnectNotification = @"ALT
|
||||
else
|
||||
{
|
||||
NSURL *destinationFileURL = [destinationURL URLByAppendingPathComponent:fileURL.lastPathComponent isDirectory:NO];
|
||||
if (![self writeFile:fileURL toDestinationURL:destinationFileURL client:afc error:error])
|
||||
if (![self writeFile:fileURL toDestinationURL:destinationFileURL progress:progress client:afc error:error])
|
||||
{
|
||||
return NO;
|
||||
}
|
||||
@@ -442,7 +445,7 @@ NSNotificationName const ALTDeviceManagerDeviceDidDisconnectNotification = @"ALT
|
||||
return YES;
|
||||
}
|
||||
|
||||
- (BOOL)writeFile:(NSURL *)fileURL toDestinationURL:(NSURL *)destinationURL client:(afc_client_t)afc error:(NSError **)error
|
||||
- (BOOL)writeFile:(NSURL *)fileURL toDestinationURL:(NSURL *)destinationURL progress:(NSProgress *)progress client:(afc_client_t)afc error:(NSError **)error
|
||||
{
|
||||
NSFileHandle *fileHandle = [NSFileHandle fileHandleForReadingAtPath:fileURL.path];
|
||||
if (fileHandle == nil)
|
||||
@@ -458,8 +461,16 @@ NSNotificationName const ALTDeviceManagerDeviceDidDisconnectNotification = @"ALT
|
||||
NSData *data = [fileHandle readDataToEndOfFile];
|
||||
|
||||
uint64_t af = 0;
|
||||
if ((afc_file_open(afc, destinationURL.relativePath.fileSystemRepresentation, AFC_FOPEN_WRONLY, &af) != AFC_E_SUCCESS) || af == 0)
|
||||
|
||||
int openResult = afc_file_open(afc, destinationURL.relativePath.fileSystemRepresentation, AFC_FOPEN_WRONLY, &af);
|
||||
if (openResult != AFC_E_SUCCESS || af == 0)
|
||||
{
|
||||
if (openResult == AFC_E_OBJECT_IS_DIR)
|
||||
{
|
||||
NSLog(@"Treating file as directory: %@ %@", fileURL, destinationURL);
|
||||
return [self writeDirectory:fileURL toDestinationURL:destinationURL client:afc progress:progress error:error];
|
||||
}
|
||||
|
||||
if (error)
|
||||
{
|
||||
*error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileWriteUnknownError userInfo:@{NSURLErrorKey: destinationURL}];
|
||||
@@ -475,10 +486,12 @@ NSNotificationName const ALTDeviceManagerDeviceDidDisconnectNotification = @"ALT
|
||||
{
|
||||
uint32_t count = 0;
|
||||
|
||||
if (afc_file_write(afc, af, (const char *)data.bytes + bytesWritten, (uint32_t)data.length - bytesWritten, &count) != AFC_E_SUCCESS)
|
||||
int writeResult = afc_file_write(afc, af, (const char *)data.bytes + bytesWritten, (uint32_t)data.length - bytesWritten, &count);
|
||||
if (writeResult != AFC_E_SUCCESS)
|
||||
{
|
||||
if (error)
|
||||
{
|
||||
NSLog(@"Failed writing file with error: %@ (%@ %@)", @(writeResult), fileURL, destinationURL);
|
||||
*error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileWriteUnknownError userInfo:@{NSURLErrorKey: destinationURL}];
|
||||
}
|
||||
|
||||
@@ -493,6 +506,7 @@ NSNotificationName const ALTDeviceManagerDeviceDidDisconnectNotification = @"ALT
|
||||
{
|
||||
if (error)
|
||||
{
|
||||
NSLog(@"Failed writing file due to mismatched sizes: %@ vs %@ (%@ %@)", @(bytesWritten), @(data.length), fileURL, destinationURL);
|
||||
*error = [NSError errorWithDomain:NSCocoaErrorDomain code:NSFileWriteUnknownError userInfo:@{NSURLErrorKey: destinationURL}];
|
||||
}
|
||||
|
||||
@@ -1113,13 +1127,51 @@ NSNotificationName const ALTDeviceManagerDeviceDidDisconnectNotification = @"ALT
|
||||
continue;
|
||||
}
|
||||
|
||||
plist_t device_type_plist = NULL;
|
||||
if (lockdownd_get_value(client, NULL, "ProductType", &device_type_plist) != LOCKDOWN_E_SUCCESS)
|
||||
{
|
||||
fprintf(stderr, "ERROR: Could not get device type for %s!\n", device_name);
|
||||
|
||||
lockdownd_client_free(client);
|
||||
idevice_free(device);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
ALTDeviceType deviceType = ALTDeviceTypeiPhone;
|
||||
|
||||
char *device_type_string = NULL;
|
||||
plist_get_string_val(device_type_plist, &device_type_string);
|
||||
|
||||
if ([@(device_type_string) hasPrefix:@"iPhone"])
|
||||
{
|
||||
deviceType = ALTDeviceTypeiPhone;
|
||||
}
|
||||
else if ([@(device_type_string) hasPrefix:@"iPad"])
|
||||
{
|
||||
deviceType = ALTDeviceTypeiPad;
|
||||
}
|
||||
else if ([@(device_type_string) hasPrefix:@"AppleTV"])
|
||||
{
|
||||
deviceType = ALTDeviceTypeAppleTV;
|
||||
}
|
||||
else
|
||||
{
|
||||
fprintf(stderr, "ERROR: Unknown device type %s for %s!\n", device_type_string, device_name);
|
||||
|
||||
lockdownd_client_free(client);
|
||||
idevice_free(device);
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
lockdownd_client_free(client);
|
||||
idevice_free(device);
|
||||
|
||||
NSString *name = [NSString stringWithCString:device_name encoding:NSUTF8StringEncoding];
|
||||
NSString *identifier = [NSString stringWithCString:udid encoding:NSUTF8StringEncoding];
|
||||
|
||||
ALTDevice *altDevice = [[ALTDevice alloc] initWithName:name identifier:identifier type:ALTDeviceTypeiPhone];
|
||||
ALTDevice *altDevice = [[ALTDevice alloc] initWithName:name identifier:identifier type:deviceType];
|
||||
[connectedDevices addObject:altDevice];
|
||||
|
||||
if (device_name != NULL)
|
||||
|
||||
@@ -335,6 +335,8 @@
|
||||
BFF0B696232242D3007A79E1 /* LicensesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF0B695232242D3007A79E1 /* LicensesViewController.swift */; };
|
||||
BFF0B6982322CAB8007A79E1 /* InstructionsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF0B6972322CAB8007A79E1 /* InstructionsViewController.swift */; };
|
||||
BFF0B69A2322D7D0007A79E1 /* UIScreen+CompactHeight.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF0B6992322D7D0007A79E1 /* UIScreen+CompactHeight.swift */; };
|
||||
BFF435D8255CBDAB00DD724F /* ALTApplication+AltStoreApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF435D7255CBDAB00DD724F /* ALTApplication+AltStoreApp.swift */; };
|
||||
BFF435D9255CBDAB00DD724F /* ALTApplication+AltStoreApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF435D7255CBDAB00DD724F /* ALTApplication+AltStoreApp.swift */; };
|
||||
BFF615A82510042B00484D3B /* AltStoreCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BF66EE7E2501AE50007EE018 /* AltStoreCore.framework */; };
|
||||
BFF767C82489A74E0097E58C /* WirelessConnectionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF767C72489A74E0097E58C /* WirelessConnectionHandler.swift */; };
|
||||
BFF7C90F257844C900E55F36 /* AltXPC.xpc in Embed XPC Services */ = {isa = PBXBuildFile; fileRef = BFF7C904257844C900E55F36 /* AltXPC.xpc */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
|
||||
@@ -769,6 +771,7 @@
|
||||
BFF0B695232242D3007A79E1 /* LicensesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LicensesViewController.swift; sourceTree = "<group>"; };
|
||||
BFF0B6972322CAB8007A79E1 /* InstructionsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InstructionsViewController.swift; sourceTree = "<group>"; };
|
||||
BFF0B6992322D7D0007A79E1 /* UIScreen+CompactHeight.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIScreen+CompactHeight.swift"; sourceTree = "<group>"; };
|
||||
BFF435D7255CBDAB00DD724F /* ALTApplication+AltStoreApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ALTApplication+AltStoreApp.swift"; sourceTree = "<group>"; };
|
||||
BFF767C72489A74E0097E58C /* WirelessConnectionHandler.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WirelessConnectionHandler.swift; sourceTree = "<group>"; };
|
||||
BFF767CB2489AB5C0097E58C /* ALTServerError+Conveniences.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ALTServerError+Conveniences.swift"; sourceTree = "<group>"; };
|
||||
BFF767CD2489ABE90097E58C /* NetworkConnection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkConnection.swift; sourceTree = "<group>"; };
|
||||
@@ -1629,6 +1632,7 @@
|
||||
BF1E314122A05D4C00370A3C /* Bundle+AltStore.swift */,
|
||||
BFF767CB2489AB5C0097E58C /* ALTServerError+Conveniences.swift */,
|
||||
BF1FE357251A9FB000C3CE09 /* NSXPCConnection+MachServices.swift */,
|
||||
BFF435D7255CBDAB00DD724F /* ALTApplication+AltStoreApp.swift */,
|
||||
);
|
||||
path = Extensions;
|
||||
sourceTree = "<group>";
|
||||
@@ -2260,6 +2264,7 @@
|
||||
BFECAC8124FD950B0077C41F /* ALTServerError+Conveniences.swift in Sources */,
|
||||
BFECAC7F24FD950B0077C41F /* CodableServerError.swift in Sources */,
|
||||
BFECAC8624FD950B0077C41F /* Result+Conveniences.swift in Sources */,
|
||||
BFF435D9255CBDAB00DD724F /* ALTApplication+AltStoreApp.swift in Sources */,
|
||||
BF718BD523C928A300A89F2D /* ALTNotificationConnection.mm in Sources */,
|
||||
BF1E312B229F474900370A3C /* RequestHandler.swift in Sources */,
|
||||
BF718BD123C91BD300A89F2D /* ALTWiredConnection.mm in Sources */,
|
||||
@@ -2501,6 +2506,7 @@
|
||||
BF9ABA4922DD0742008935CF /* ScreenshotCollectionViewCell.swift in Sources */,
|
||||
BF9ABA4D22DD16DE008935CF /* PillButton.swift in Sources */,
|
||||
BFE6326C22A86FF300F30809 /* AuthenticationOperation.swift in Sources */,
|
||||
BFF435D8255CBDAB00DD724F /* ALTApplication+AltStoreApp.swift in Sources */,
|
||||
BF4B78FE24B3D1DB008AB4AC /* SceneDelegate.swift in Sources */,
|
||||
BF6C8FB02429599900125131 /* TextCollectionReusableView.swift in Sources */,
|
||||
BF663C4F2433ED8200DAA738 /* FileManager+DirectorySize.swift in Sources */,
|
||||
|
||||
@@ -432,11 +432,11 @@ private extension AuthenticationOperation
|
||||
{
|
||||
func selectTeam(from teams: [ALTTeam])
|
||||
{
|
||||
if let team = teams.first(where: { $0.type == .free })
|
||||
if let team = teams.first(where: { $0.type == .individual })
|
||||
{
|
||||
return completionHandler(.success(team))
|
||||
}
|
||||
else if let team = teams.first(where: { $0.type == .individual })
|
||||
else if let team = teams.first(where: { $0.type == .free })
|
||||
{
|
||||
return completionHandler(.success(team))
|
||||
}
|
||||
@@ -582,7 +582,7 @@ private extension AuthenticationOperation
|
||||
return completionHandler(.failure(OperationError.unknownUDID))
|
||||
}
|
||||
|
||||
ALTAppleAPI.shared.fetchDevices(for: team, session: session) { (devices, error) in
|
||||
ALTAppleAPI.shared.fetchDevices(for: team, types: [.iphone, .ipad], session: session) { (devices, error) in
|
||||
do
|
||||
{
|
||||
let devices = try Result(devices, error).get()
|
||||
@@ -593,7 +593,7 @@ private extension AuthenticationOperation
|
||||
}
|
||||
else
|
||||
{
|
||||
ALTAppleAPI.shared.registerDevice(name: UIDevice.current.name, identifier: udid, team: team, session: session) { (device, error) in
|
||||
ALTAppleAPI.shared.registerDevice(name: UIDevice.current.name, identifier: udid, type: .iphone, team: team, session: session) { (device, error) in
|
||||
completionHandler(Result(device, error))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -133,7 +133,7 @@ extension FetchProvisioningProfilesOperation
|
||||
|
||||
#if DEBUG
|
||||
|
||||
if app.bundleIdentifier.hasPrefix(StoreApp.altstoreAppID) || StoreApp.alternativeAltStoreAppIDs.contains(where: app.bundleIdentifier.hasPrefix)
|
||||
if app.isAltStoreApp
|
||||
{
|
||||
// Use legacy bundle ID format for AltStore.
|
||||
preferredBundleID = "com.\(team.identifier).\(app.bundleIdentifier)"
|
||||
@@ -178,7 +178,7 @@ extension FetchProvisioningProfilesOperation
|
||||
let parentBundleID = parentApp?.bundleIdentifier ?? app.bundleIdentifier
|
||||
let updatedParentBundleID: String
|
||||
|
||||
if app.bundleIdentifier.hasPrefix(StoreApp.altstoreAppID) || StoreApp.alternativeAltStoreAppIDs.contains(where: app.bundleIdentifier.hasPrefix)
|
||||
if app.isAltStoreApp
|
||||
{
|
||||
// Use legacy bundle ID format for AltStore (and its extensions).
|
||||
updatedParentBundleID = "com.\(team.identifier).\(parentBundleID)"
|
||||
@@ -463,7 +463,7 @@ extension FetchProvisioningProfilesOperation
|
||||
|
||||
func fetchProvisioningProfile(for appID: ALTAppID, team: ALTTeam, session: ALTAppleAPISession, completionHandler: @escaping (Result<ALTProvisioningProfile, Error>) -> Void)
|
||||
{
|
||||
ALTAppleAPI.shared.fetchProvisioningProfile(for: appID, team: team, session: session) { (profile, error) in
|
||||
ALTAppleAPI.shared.fetchProvisioningProfile(for: appID, deviceType: .iphone, team: team, session: session) { (profile, error) in
|
||||
switch Result(profile, error)
|
||||
{
|
||||
case .failure(let error): completionHandler(.failure(error))
|
||||
@@ -477,7 +477,7 @@ extension FetchProvisioningProfilesOperation
|
||||
case .success:
|
||||
|
||||
// Fetch new provisiong profile
|
||||
ALTAppleAPI.shared.fetchProvisioningProfile(for: appID, team: team, session: session) { (profile, error) in
|
||||
ALTAppleAPI.shared.fetchProvisioningProfile(for: appID, deviceType: .iphone, team: team, session: session) { (profile, error) in
|
||||
completionHandler(Result(profile, error))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -169,7 +169,7 @@ private extension ResignAppOperation
|
||||
|
||||
var additionalValues: [String: Any] = [Bundle.Info.urlTypes: allURLSchemes]
|
||||
|
||||
if self.context.bundleIdentifier == StoreApp.altstoreAppID || StoreApp.alternativeAltStoreAppIDs.contains(self.context.bundleIdentifier)
|
||||
if app.isAltStoreApp
|
||||
{
|
||||
guard let udid = Bundle.main.object(forInfoDictionaryKey: Bundle.Info.deviceID) as? String else { throw OperationError.unknownUDID }
|
||||
additionalValues[Bundle.Info.deviceID] = udid
|
||||
|
||||
@@ -36,10 +36,10 @@
|
||||
"bundleIdentifier": "com.rileytestut.Delta.Alpha",
|
||||
"developerName": "Riley Testut",
|
||||
"subtitle": "Classic games in your pocket.",
|
||||
"version": "1.3a2",
|
||||
"versionDate": "2020-10-26T14:00:00-07:00",
|
||||
"versionDescription": "This alpha fixes incorrect inputs when playing DS games with melonDS core.",
|
||||
"downloadURL": "https://f000.backblazeb2.com/file/altstore/sources/alpha/delta/1_3_a2.ipa",
|
||||
"version": "1.3a4",
|
||||
"versionDate": "2020-11-30T20:00:00-06:00",
|
||||
"versionDescription": "BUG FIXES\n• Fixes garbled audio during emulation\n• Fixes audio not playing over AirPlay
• Fixes crash when activating Siri\n• Fixes MelonDS crashing on devices that don't support JIT",
|
||||
"downloadURL": "https://f000.backblazeb2.com/file/altstore/sources/alpha/delta/1_3_a4.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!",
|
||||
"iconURL": "https://user-images.githubusercontent.com/705880/63391976-4d311700-c37a-11e9-91a8-4fb0c454413d.png",
|
||||
"tintColor": "8A28F7",
|
||||
|
||||
@@ -92,14 +92,14 @@
|
||||
"bundleIdentifier": "com.rileytestut.Delta.Beta",
|
||||
"developerName": "Riley Testut",
|
||||
"subtitle": "Classic games in your pocket.",
|
||||
"version": "1.2b3",
|
||||
"versionDate": "2020-10-12T12:30:00-07:00",
|
||||
"versionDescription": "**Delta 1.2 GM**\n\nIMPROVEMENTS\n• Improves sync error messages when a game is missing or can’t be synced\n\nBUG FIXES\n• Fixes misplaced DS screens when using external controller\n• Fixes NES games crashing after playing an N64 game\n• Fixes various issues with controller skin filters\n• Fixes swapped DS screens\n• Fixes importing games from Files\n\nThanks for your help beta testing Delta these past few months! This should be the last beta before the public release of Delta 1.2, so please report any last-minute issues so we can have a smooth release 🙂",
|
||||
"downloadURL": "https://f000.backblazeb2.com/file/altstore/apps/delta/1_2_b3.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!",
|
||||
"version": "1.3b2",
|
||||
"versionDate": "2020-12-02T12:30:00-06:00",
|
||||
"versionDescription": "BUG FIXES\n• Fixes garbled audio during emulation\n• Fixes MelonDS core crashing on devices that don't support JIT\n• Fixes audio not playing over AirPlay
• Fixes crash when activating Siri\n\nPrevious update:\n\nNEW FEATURES\n• [MelonDS] DSi emulation (requires additional DSi BIOS files)\n• [MelonDS] Just-in-time (JIT) compilation on A12 devices running iOS 14.2 or newer.\n• [MelonDS] Microphone support — use your device's microphone to emulate the DS microphone.\n• [MelonDS] Emulate closing/opening lid — sleep device to \"close\" DS lid, wake to \"open\".\n\nIMPROVEMENTS\n• Dark mode support throughout app (thanks @ericlewis!)",
|
||||
"downloadURL": "https://f000.backblazeb2.com/file/altstore/apps/delta/1_3_b2.ipa",
|
||||
"localizedDescription": "The next console for Delta is coming: this beta version of Delta brings support for playing DS games!\n\nPlease report any issues you find to support@altstore.io. Thanks!",
|
||||
"iconURL": "https://user-images.githubusercontent.com/705880/63391976-4d311700-c37a-11e9-91a8-4fb0c454413d.png",
|
||||
"tintColor": "8A28F7",
|
||||
"size": 18456630,
|
||||
"size": 18558635,
|
||||
"beta": true,
|
||||
"permissions": [
|
||||
{
|
||||
@@ -274,6 +274,6 @@
|
||||
}
|
||||
],
|
||||
"userInfo": {
|
||||
"patreonAccessToken": "vfuSOfSYTUC8pEq6TM9r6uoL-T-uTJEI85hFescT4AM"
|
||||
"patreonAccessToken": "dRUfeocZhNq6K3LWfu2poRE9aVkSTcFp2xBviRoOlcY"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,13 +16,10 @@ public extension StoreApp
|
||||
{
|
||||
#if ALPHA
|
||||
static let altstoreAppID = "com.rileytestut.AltStore.Alpha"
|
||||
static let alternativeAltStoreAppIDs: Set<String> = ["com.rileytestut.AltStore", "com.rileytestut.AltStore.Beta"]
|
||||
#elseif BETA
|
||||
static let altstoreAppID = "com.rileytestut.AltStore.Beta"
|
||||
static let alternativeAltStoreAppIDs: Set<String> = ["com.rileytestut.AltStore", "com.rileytestut.AltStore.Alpha"]
|
||||
#else
|
||||
static let altstoreAppID = "com.rileytestut.AltStore"
|
||||
static let alternativeAltStoreAppIDs: Set<String> = ["com.rileytestut.AltStore.Beta", "com.rileytestut.AltStore.Alpha"]
|
||||
#endif
|
||||
|
||||
static let dolphinAppID = "me.oatmealdome.dolphinios-njb"
|
||||
|
||||
2
Dependencies/AltSign
vendored
2
Dependencies/AltSign
vendored
Submodule Dependencies/AltSign updated: 2856e479c3...b027505dcc
19
Shared/Extensions/ALTApplication+AltStoreApp.swift
Normal file
19
Shared/Extensions/ALTApplication+AltStoreApp.swift
Normal file
@@ -0,0 +1,19 @@
|
||||
//
|
||||
// ALTApplication+AltStoreApp.swift
|
||||
// AltStore
|
||||
//
|
||||
// Created by Riley Testut on 11/11/20.
|
||||
// Copyright © 2020 Riley Testut. All rights reserved.
|
||||
//
|
||||
|
||||
import AltSign
|
||||
|
||||
extension ALTApplication
|
||||
{
|
||||
static let altstoreBundleID = "com.rileytestut.AltStore"
|
||||
|
||||
var isAltStoreApp: Bool {
|
||||
let isAltStoreApp = self.bundleIdentifier.contains(ALTApplication.altstoreBundleID)
|
||||
return isAltStoreApp
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user