mirror of
https://github.com/SideStore/SideStore.git
synced 2026-02-20 20:23:25 +01:00
[AltServer] Supports sideloading .ipa files directly to iOS devices
This commit is contained in:
@@ -13,6 +13,12 @@ import AltSign
|
|||||||
|
|
||||||
import LaunchAtLogin
|
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
|
@NSApplicationMain
|
||||||
class AppDelegate: NSObject, NSApplicationDelegate {
|
class AppDelegate: NSObject, NSApplicationDelegate {
|
||||||
|
|
||||||
@@ -26,6 +32,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||||||
|
|
||||||
@IBOutlet private var appMenu: NSMenu!
|
@IBOutlet private var appMenu: NSMenu!
|
||||||
@IBOutlet private var connectedDevicesMenu: NSMenu!
|
@IBOutlet private var connectedDevicesMenu: NSMenu!
|
||||||
|
@IBOutlet private var sideloadIPAConnectedDevicesMenu: NSMenu!
|
||||||
@IBOutlet private var launchAtLoginMenuItem: NSMenuItem!
|
@IBOutlet private var launchAtLoginMenuItem: NSMenuItem!
|
||||||
@IBOutlet private var installMailPluginMenuItem: NSMenuItem!
|
@IBOutlet private var installMailPluginMenuItem: NSMenuItem!
|
||||||
|
|
||||||
@@ -48,6 +55,7 @@ class AppDelegate: NSObject, NSApplicationDelegate {
|
|||||||
|
|
||||||
self.appMenu.delegate = self
|
self.appMenu.delegate = self
|
||||||
self.connectedDevicesMenu.delegate = self
|
self.connectedDevicesMenu.delegate = self
|
||||||
|
self.sideloadIPAConnectedDevicesMenu.delegate = self
|
||||||
|
|
||||||
UNUserNotificationCenter.current().requestAuthorization(options: [.alert]) { (success, error) in
|
UNUserNotificationCenter.current().requestAuthorization(options: [.alert]) { (success, error) in
|
||||||
guard success else { return }
|
guard success else { return }
|
||||||
@@ -81,8 +89,30 @@ private extension AppDelegate
|
|||||||
{
|
{
|
||||||
@objc func installAltStore(_ item: NSMenuItem)
|
@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()
|
let alert = NSAlert()
|
||||||
alert.messageText = NSLocalizedString("Please enter your Apple ID and password.", comment: "")
|
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: "")
|
alert.informativeText = NSLocalizedString("Your Apple ID and password are not saved and are only sent to Apple for authentication.", comment: "")
|
||||||
@@ -126,17 +156,15 @@ private extension AppDelegate
|
|||||||
let username = appleIDTextField.stringValue
|
let username = appleIDTextField.stringValue
|
||||||
let password = passwordTextField.stringValue
|
let password = passwordTextField.stringValue
|
||||||
|
|
||||||
let device = self.connectedDevices[index]
|
|
||||||
|
|
||||||
func install()
|
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
|
switch result
|
||||||
{
|
{
|
||||||
case .success:
|
case .success(let application):
|
||||||
let content = UNMutableNotificationContent()
|
let content = UNMutableNotificationContent()
|
||||||
content.title = NSLocalizedString("Installation Succeeded", comment: "")
|
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)
|
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: nil)
|
||||||
UNUserNotificationCenter.current().add(request)
|
UNUserNotificationCenter.current().add(request)
|
||||||
@@ -283,14 +311,14 @@ extension AppDelegate: NSMenuDelegate
|
|||||||
|
|
||||||
func numberOfItems(in menu: NSMenu) -> Int
|
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
|
return self.connectedDevices.isEmpty ? 1 : self.connectedDevices.count
|
||||||
}
|
}
|
||||||
|
|
||||||
func menu(_ menu: NSMenu, update item: NSMenuItem, at index: Int, shouldCancel: Bool) -> Bool
|
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
|
if self.connectedDevices.isEmpty
|
||||||
{
|
{
|
||||||
@@ -305,7 +333,7 @@ extension AppDelegate: NSMenuDelegate
|
|||||||
item.title = device.name
|
item.title = device.name
|
||||||
item.isEnabled = true
|
item.isEnabled = true
|
||||||
item.target = self
|
item.target = self
|
||||||
item.action = #selector(AppDelegate.installAltStore)
|
item.action = (menu == self.connectedDevicesMenu) ? #selector(AppDelegate.installAltStore(_:)) : #selector(AppDelegate.sideloadIPA(_:))
|
||||||
item.tag = index
|
item.tag = index
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -64,6 +64,7 @@
|
|||||||
<outlet property="connectedDevicesMenu" destination="KJ9-WY-pW1" id="Mcv-64-iFU"/>
|
<outlet property="connectedDevicesMenu" destination="KJ9-WY-pW1" id="Mcv-64-iFU"/>
|
||||||
<outlet property="installMailPluginMenuItem" destination="3CM-gV-X2G" id="lio-ha-z0S"/>
|
<outlet property="installMailPluginMenuItem" destination="3CM-gV-X2G" id="lio-ha-z0S"/>
|
||||||
<outlet property="launchAtLoginMenuItem" destination="IyR-FQ-upe" id="Fxn-EP-hwH"/>
|
<outlet property="launchAtLoginMenuItem" destination="IyR-FQ-upe" id="Fxn-EP-hwH"/>
|
||||||
|
<outlet property="sideloadIPAConnectedDevicesMenu" destination="IuI-bV-fTY" id="QQw-St-HfG"/>
|
||||||
</connections>
|
</connections>
|
||||||
</customObject>
|
</customObject>
|
||||||
<customObject id="Arf-IC-5eb" customClass="SUUpdater"/>
|
<customObject id="Arf-IC-5eb" customClass="SUUpdater"/>
|
||||||
@@ -97,6 +98,22 @@
|
|||||||
</connections>
|
</connections>
|
||||||
</menu>
|
</menu>
|
||||||
</menuItem>
|
</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 isSeparatorItem="YES" id="1ZZ-BB-xHy"/>
|
||||||
<menuItem title="Launch at Login" id="IyR-FQ-upe" userLabel="Launch At Login">
|
<menuItem title="Launch at Login" id="IyR-FQ-upe" userLabel="Launch At Login">
|
||||||
<modifierMask key="keyEquivalentModifierMask"/>
|
<modifierMask key="keyEquivalentModifierMask"/>
|
||||||
|
|||||||
@@ -10,12 +10,6 @@ import Cocoa
|
|||||||
import UserNotifications
|
import UserNotifications
|
||||||
import ObjectiveC
|
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()
|
private let appGroupsLock = NSLock()
|
||||||
|
|
||||||
enum InstallError: LocalizedError
|
enum InstallError: LocalizedError
|
||||||
@@ -38,21 +32,14 @@ enum InstallError: LocalizedError
|
|||||||
|
|
||||||
extension ALTDeviceManager
|
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)
|
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 {
|
DispatchQueue.main.async {
|
||||||
if let error = error
|
completion(result)
|
||||||
{
|
|
||||||
completion(.failure(error))
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
completion(.success(()))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try? FileManager.default.removeItem(at: destinationDirectoryURL)
|
try? FileManager.default.removeItem(at: destinationDirectoryURL)
|
||||||
@@ -83,14 +70,13 @@ extension ALTDeviceManager
|
|||||||
{
|
{
|
||||||
let certificate = try result.get()
|
let certificate = try result.get()
|
||||||
|
|
||||||
let content = UNMutableNotificationContent()
|
if !url.isFileURL
|
||||||
content.title = String(format: NSLocalizedString("Installing AltStore to %@...", comment: ""), device.name)
|
{
|
||||||
content.body = NSLocalizedString("This may take a few seconds.", comment: "")
|
// Show alert before downloading remote .ipa.
|
||||||
|
self.showInstallationAlert(appName: NSLocalizedString("AltStore", comment: ""), deviceName: device.name)
|
||||||
|
}
|
||||||
|
|
||||||
let request = UNNotificationRequest(identifier: UUID().uuidString, content: content, trigger: nil)
|
self.downloadApp(from: url) { (result) in
|
||||||
UNUserNotificationCenter.current().add(request)
|
|
||||||
|
|
||||||
self.downloadApp { (result) in
|
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
let fileURL = try result.get()
|
let fileURL = try result.get()
|
||||||
@@ -98,18 +84,14 @@ extension ALTDeviceManager
|
|||||||
try FileManager.default.createDirectory(at: destinationDirectoryURL, withIntermediateDirectories: true, attributes: nil)
|
try FileManager.default.createDirectory(at: destinationDirectoryURL, withIntermediateDirectories: true, attributes: nil)
|
||||||
|
|
||||||
let appBundleURL = try FileManager.default.unzipAppBundle(at: fileURL, toDirectory: destinationDirectoryURL)
|
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) }
|
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.
|
// Refresh anisette data to prevent session timeouts.
|
||||||
AnisetteDataManager.shared.requestAnisetteData { (result) in
|
AnisetteDataManager.shared.requestAnisetteData { (result) in
|
||||||
do
|
do
|
||||||
@@ -123,65 +105,73 @@ extension ALTDeviceManager
|
|||||||
let profiles = try result.get()
|
let profiles = try result.get()
|
||||||
|
|
||||||
self.install(application, to: device, team: team, certificate: certificate, profiles: profiles) { (result) in
|
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
|
catch
|
||||||
{
|
{
|
||||||
finish(error, title: "Failed to Fetch Provisioning Profiles")
|
finish(.failure(error), title: "Failed to Fetch Provisioning Profiles")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
finish(error, title: "Failed to Refresh Anisette Data")
|
finish(.failure(error), title: "Failed to Refresh Anisette Data")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
finish(error, title: "Failed to Download AltStore")
|
finish(.failure(error), title: "Failed to Download AltStore")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
finish(error, title: "Failed to Fetch Certificate")
|
finish(.failure(error), title: "Failed to Fetch Certificate")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
finish(error, title: "Failed to Register Device")
|
finish(.failure(error), title: "Failed to Register Device")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
finish(error, title: "Failed to Fetch Team")
|
finish(.failure(error), title: "Failed to Fetch Team")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
finish(error, title: "Failed to Authenticate")
|
finish(.failure(error), title: "Failed to Authenticate")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch
|
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
|
do
|
||||||
{
|
{
|
||||||
let (fileURL, _) = try Result((fileURL, response), error).get()
|
let (fileURL, _) = try Result((fileURL, response), error).get()
|
||||||
completionHandler(.success(fileURL))
|
completionHandler(.success(fileURL))
|
||||||
|
|
||||||
|
do { try FileManager.default.removeItem(at: fileURL) }
|
||||||
|
catch { print("Failed to remove downloaded .ipa.", error) }
|
||||||
}
|
}
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
@@ -259,11 +249,11 @@ extension ALTDeviceManager
|
|||||||
{
|
{
|
||||||
DispatchQueue.main.sync {
|
DispatchQueue.main.sync {
|
||||||
let alert = NSAlert()
|
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("""
|
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.
|
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: "")
|
""", comment: "")
|
||||||
|
|
||||||
alert.addButton(withTitle: NSLocalizedString("Continue", comment: ""))
|
alert.addButton(withTitle: NSLocalizedString("Continue", comment: ""))
|
||||||
@@ -410,7 +400,7 @@ 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, team: ALTTeam, session: ALTAppleAPISession,
|
||||||
completion: @escaping (Result<[String: ALTProvisioningProfile], Error>) -> Void)
|
completion: @escaping (Result<[String: ALTProvisioningProfile], Error>) -> Void)
|
||||||
{
|
{
|
||||||
self.prepareProvisioningProfile(for: application, team: team, session: session) { (result) in
|
self.prepareProvisioningProfile(for: application, parentApp: nil, team: team, session: session) { (result) in
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
let profile = try result.get()
|
let profile = try result.get()
|
||||||
@@ -424,7 +414,7 @@ To prevent this from happening, feel free to try again with another Apple ID to
|
|||||||
{
|
{
|
||||||
dispatchGroup.enter()
|
dispatchGroup.enter()
|
||||||
|
|
||||||
self.prepareProvisioningProfile(for: appExtension, team: team, session: session) { (result) in
|
self.prepareProvisioningProfile(for: appExtension, parentApp: application, team: team, session: session) { (result) in
|
||||||
switch result
|
switch result
|
||||||
{
|
{
|
||||||
case .failure(let e): error = e
|
case .failure(let e): error = e
|
||||||
@@ -453,9 +443,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?, 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
|
do
|
||||||
{
|
{
|
||||||
let appID = try result.get()
|
let appID = try result.get()
|
||||||
@@ -493,10 +509,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
|
ALTAppleAPI.shared.fetchAppIDs(for: team, session: session) { (appIDs, error) in
|
||||||
do
|
do
|
||||||
{
|
{
|
||||||
@@ -723,17 +737,21 @@ To prevent this from happening, feel free to try again with another Apple ID to
|
|||||||
allURLSchemes.append(altstoreURLScheme)
|
allURLSchemes.append(altstoreURLScheme)
|
||||||
|
|
||||||
var additionalValues: [String: Any] = [Bundle.Info.urlTypes: allURLSchemes]
|
var additionalValues: [String: Any] = [Bundle.Info.urlTypes: allURLSchemes]
|
||||||
additionalValues[Bundle.Info.deviceID] = device.identifier
|
|
||||||
additionalValues[Bundle.Info.serverID] = UserDefaults.standard.serverID
|
|
||||||
|
|
||||||
if
|
if application.isAltStoreApp
|
||||||
let machineIdentifier = certificate.machineIdentifier,
|
|
||||||
let encryptedData = certificate.encryptedP12Data(withPassword: machineIdentifier)
|
|
||||||
{
|
{
|
||||||
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")
|
if
|
||||||
try encryptedData.write(to: certificateURL, options: .atomic)
|
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)
|
try prepare(appBundle, additionalInfoDictionaryValues: additionalValues)
|
||||||
@@ -750,7 +768,7 @@ To prevent this from happening, feel free to try again with another Apple ID to
|
|||||||
{
|
{
|
||||||
try Result(success, error).get()
|
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
|
ALTDeviceManager.shared.installApp(at: application.fileURL, toDeviceWithUDID: device.identifier, activeProvisioningProfiles: activeProfiles) { (success, error) in
|
||||||
completionHandler(Result(success, error))
|
completionHandler(Result(success, error))
|
||||||
}
|
}
|
||||||
@@ -769,6 +787,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
|
private var securityCodeAlertKey = 0
|
||||||
|
|||||||
@@ -333,6 +333,8 @@
|
|||||||
BFF0B696232242D3007A79E1 /* LicensesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF0B695232242D3007A79E1 /* LicensesViewController.swift */; };
|
BFF0B696232242D3007A79E1 /* LicensesViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF0B695232242D3007A79E1 /* LicensesViewController.swift */; };
|
||||||
BFF0B6982322CAB8007A79E1 /* InstructionsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF0B6972322CAB8007A79E1 /* InstructionsViewController.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 */; };
|
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 */; };
|
BFF615A82510042B00484D3B /* AltStoreCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = BF66EE7E2501AE50007EE018 /* AltStoreCore.framework */; };
|
||||||
BFF767C82489A74E0097E58C /* WirelessConnectionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF767C72489A74E0097E58C /* WirelessConnectionHandler.swift */; };
|
BFF767C82489A74E0097E58C /* WirelessConnectionHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = BFF767C72489A74E0097E58C /* WirelessConnectionHandler.swift */; };
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
@@ -750,6 +752,7 @@
|
|||||||
BFF0B695232242D3007A79E1 /* LicensesViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LicensesViewController.swift; sourceTree = "<group>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
BFF767CD2489ABE90097E58C /* NetworkConnection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkConnection.swift; sourceTree = "<group>"; };
|
||||||
@@ -1596,6 +1599,7 @@
|
|||||||
BF1E314122A05D4C00370A3C /* Bundle+AltStore.swift */,
|
BF1E314122A05D4C00370A3C /* Bundle+AltStore.swift */,
|
||||||
BFF767CB2489AB5C0097E58C /* ALTServerError+Conveniences.swift */,
|
BFF767CB2489AB5C0097E58C /* ALTServerError+Conveniences.swift */,
|
||||||
BF1FE357251A9FB000C3CE09 /* NSXPCConnection+MachServices.swift */,
|
BF1FE357251A9FB000C3CE09 /* NSXPCConnection+MachServices.swift */,
|
||||||
|
BFF435D7255CBDAB00DD724F /* ALTApplication+AltStoreApp.swift */,
|
||||||
);
|
);
|
||||||
path = Extensions;
|
path = Extensions;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -2177,6 +2181,7 @@
|
|||||||
BFECAC8124FD950B0077C41F /* ALTServerError+Conveniences.swift in Sources */,
|
BFECAC8124FD950B0077C41F /* ALTServerError+Conveniences.swift in Sources */,
|
||||||
BFECAC7F24FD950B0077C41F /* CodableServerError.swift in Sources */,
|
BFECAC7F24FD950B0077C41F /* CodableServerError.swift in Sources */,
|
||||||
BFECAC8624FD950B0077C41F /* Result+Conveniences.swift in Sources */,
|
BFECAC8624FD950B0077C41F /* Result+Conveniences.swift in Sources */,
|
||||||
|
BFF435D9255CBDAB00DD724F /* ALTApplication+AltStoreApp.swift in Sources */,
|
||||||
BF718BD523C928A300A89F2D /* ALTNotificationConnection.mm in Sources */,
|
BF718BD523C928A300A89F2D /* ALTNotificationConnection.mm in Sources */,
|
||||||
BF1E312B229F474900370A3C /* RequestHandler.swift in Sources */,
|
BF1E312B229F474900370A3C /* RequestHandler.swift in Sources */,
|
||||||
BF718BD123C91BD300A89F2D /* ALTWiredConnection.mm in Sources */,
|
BF718BD123C91BD300A89F2D /* ALTWiredConnection.mm in Sources */,
|
||||||
@@ -2418,6 +2423,7 @@
|
|||||||
BF9ABA4922DD0742008935CF /* ScreenshotCollectionViewCell.swift in Sources */,
|
BF9ABA4922DD0742008935CF /* ScreenshotCollectionViewCell.swift in Sources */,
|
||||||
BF9ABA4D22DD16DE008935CF /* PillButton.swift in Sources */,
|
BF9ABA4D22DD16DE008935CF /* PillButton.swift in Sources */,
|
||||||
BFE6326C22A86FF300F30809 /* AuthenticationOperation.swift in Sources */,
|
BFE6326C22A86FF300F30809 /* AuthenticationOperation.swift in Sources */,
|
||||||
|
BFF435D8255CBDAB00DD724F /* ALTApplication+AltStoreApp.swift in Sources */,
|
||||||
BF4B78FE24B3D1DB008AB4AC /* SceneDelegate.swift in Sources */,
|
BF4B78FE24B3D1DB008AB4AC /* SceneDelegate.swift in Sources */,
|
||||||
BF6C8FB02429599900125131 /* TextCollectionReusableView.swift in Sources */,
|
BF6C8FB02429599900125131 /* TextCollectionReusableView.swift in Sources */,
|
||||||
BF663C4F2433ED8200DAA738 /* FileManager+DirectorySize.swift in Sources */,
|
BF663C4F2433ED8200DAA738 /* FileManager+DirectorySize.swift in Sources */,
|
||||||
|
|||||||
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