mirror of
https://github.com/XcodesOrg/XcodesApp.git
synced 2026-03-28 09:25:47 +00:00
Merge pull request #290 from RobotsAndPencils/NoLoginDownload
Add ability to download Xcode without logging in using XcodeReleases
This commit is contained in:
commit
c27b28868b
5 changed files with 64 additions and 15 deletions
|
|
@ -40,15 +40,10 @@ extension AppState {
|
|||
}
|
||||
|
||||
private func install(_ installationType: InstallationType, downloader: Downloader, attemptNumber: Int) -> AnyPublisher<InstalledXcode, Error> {
|
||||
// We need to check if the Apple ID that is logged in is an Apple Developer
|
||||
// Since users can use xcodereleases, we don't check for Apple ID on a xcode list refresh
|
||||
// If the Apple Id is not a developer, the download action will try and download a xip that is invalid, causing a `xcode13.0.xip is damaged and can't be expanded.`
|
||||
|
||||
Logger.appState.info("Using \(downloader) downloader")
|
||||
|
||||
return validateSession()
|
||||
.flatMap { _ in
|
||||
self.getXcodeArchive(installationType, downloader: downloader)
|
||||
}
|
||||
return self.getXcodeArchive(installationType, downloader: downloader)
|
||||
.flatMap { xcode, url -> AnyPublisher<InstalledXcode, Swift.Error> in
|
||||
self.installArchivedXcode(xcode, at: url)
|
||||
}
|
||||
|
|
@ -98,13 +93,16 @@ extension AppState {
|
|||
}
|
||||
|
||||
private func downloadXcode(availableXcode: AvailableXcode, downloader: Downloader) -> AnyPublisher<(AvailableXcode, URL), Error> {
|
||||
downloadOrUseExistingArchive(for: availableXcode, downloader: downloader, progressChanged: { [unowned self] progress in
|
||||
DispatchQueue.main.async {
|
||||
self.setInstallationStep(of: availableXcode.version, to: .downloading(progress: progress))
|
||||
return validateADCSession(path: availableXcode.downloadPath)
|
||||
.flatMap { _ in
|
||||
return self.downloadOrUseExistingArchive(for: availableXcode, downloader: downloader, progressChanged: { [unowned self] progress in
|
||||
DispatchQueue.main.async {
|
||||
self.setInstallationStep(of: availableXcode.version, to: .downloading(progress: progress))
|
||||
}
|
||||
})
|
||||
.map { return (availableXcode, $0) }
|
||||
}
|
||||
})
|
||||
.map { return (availableXcode, $0) }
|
||||
.eraseToAnyPublisher()
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
public func downloadOrUseExistingArchive(for availableXcode: AvailableXcode, downloader: Downloader, progressChanged: @escaping (Progress) -> Void) -> AnyPublisher<URL, Error> {
|
||||
|
|
|
|||
|
|
@ -157,7 +157,16 @@ class AppState: ObservableObject {
|
|||
}
|
||||
// MARK: - Authentication
|
||||
|
||||
func validateADCSession(path: String) -> AnyPublisher<Void, Error> {
|
||||
return Current.network.dataTask(with: URLRequest.downloadADCAuth(path: path))
|
||||
.receive(on: DispatchQueue.main)
|
||||
.tryMap { _ in
|
||||
}
|
||||
.eraseToAnyPublisher()
|
||||
}
|
||||
|
||||
func validateSession() -> AnyPublisher<Void, Error> {
|
||||
|
||||
return Current.network.validateSession()
|
||||
.receive(on: DispatchQueue.main)
|
||||
.handleEvents(receiveCompletion: { completion in
|
||||
|
|
@ -368,7 +377,12 @@ class AppState: ObservableObject {
|
|||
}
|
||||
}
|
||||
|
||||
install(id: id)
|
||||
switch self.dataSource {
|
||||
case .apple:
|
||||
install(id: id)
|
||||
case .xcodeReleases:
|
||||
installWithoutLogin(id: id)
|
||||
}
|
||||
}
|
||||
|
||||
func install(id: Xcode.ID) {
|
||||
|
|
@ -439,6 +453,30 @@ class AppState: ObservableObject {
|
|||
)
|
||||
}
|
||||
|
||||
/// Skips using the username/password to log in to Apple, and simply gets a Auth Cookie used in downloading
|
||||
func installWithoutLogin(id: Xcode.ID) {
|
||||
guard let availableXcode = availableXcodes.first(where: { $0.version == id }) else { return }
|
||||
|
||||
installationPublishers[id] = self.install(.version(availableXcode), downloader: Downloader(rawValue: UserDefaults.standard.string(forKey: "downloader") ?? "aria2") ?? .aria2)
|
||||
.receive(on: DispatchQueue.main)
|
||||
.sink(
|
||||
receiveCompletion: { [unowned self] completion in
|
||||
self.installationPublishers[id] = nil
|
||||
if case let .failure(error) = completion {
|
||||
// Prevent setting the app state error if it is an invalid session, we will present the sign in view instead
|
||||
if error as? AuthenticationError != .invalidSession {
|
||||
self.error = error
|
||||
self.presentedAlert = .generic(title: localizeString("Alert.Install.Error.Title"), message: error.legibleLocalizedDescription)
|
||||
}
|
||||
if let index = self.allXcodes.firstIndex(where: { $0.id == id }) {
|
||||
self.allXcodes[index].installState = .notInstalled
|
||||
}
|
||||
}
|
||||
},
|
||||
receiveValue: { _ in }
|
||||
)
|
||||
}
|
||||
|
||||
func cancelInstall(id: Xcode.ID) {
|
||||
guard let availableXcode = availableXcodes.first(where: { $0.version == id }) else { return }
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,9 @@ public struct AvailableXcode: Codable {
|
|||
public let sdks: SDKs?
|
||||
public let compilers: Compilers?
|
||||
public let fileSize: Int64?
|
||||
public var downloadPath: String {
|
||||
return url.path
|
||||
}
|
||||
|
||||
public init(
|
||||
version: Version,
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ public struct Shell {
|
|||
"--max-connection-per-server=16",
|
||||
"--split=16",
|
||||
"--summary-interval=1",
|
||||
"--stop-with-process=\(ProcessInfo.processInfo.processIdentifier)",
|
||||
"--stop-with-process=\(ProcessInfo.processInfo.processIdentifier)", // if xcodes quits, stop aria2 process
|
||||
"--dir=\(destination.parent.string)",
|
||||
"--out=\(destination.basename())",
|
||||
"--human-readable=false", // sets the output to use bytes instead of formatting
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ extension URL {
|
|||
static let download = URL(string: "https://developer.apple.com/download")!
|
||||
static let downloads = URL(string: "https://developer.apple.com/services-account/QH65B2/downloadws/listDownloads.action")!
|
||||
static let downloadXcode = URL(string: "https://developer.apple.com/devcenter/download.action")!
|
||||
static let downloadADCAuth = URL(string: "https://developerservices2.apple.com/services/download")!
|
||||
}
|
||||
|
||||
extension URLRequest {
|
||||
|
|
@ -25,4 +26,13 @@ extension URLRequest {
|
|||
request.allHTTPHeaderFields?["Accept"] = "*/*"
|
||||
return request
|
||||
}
|
||||
|
||||
static func downloadADCAuth(path: String) -> URLRequest {
|
||||
var components = URLComponents(url: .downloadADCAuth, resolvingAgainstBaseURL: false)!
|
||||
components.queryItems = [URLQueryItem(name: "path", value: path)]
|
||||
var request = URLRequest(url: components.url!)
|
||||
request.allHTTPHeaderFields = request.allHTTPHeaderFields ?? [:]
|
||||
request.allHTTPHeaderFields?["Accept"] = "*/*"
|
||||
return request
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue