Skip to content

Commit ad6aca2

Browse files
authored
Merge pull request #215 from RobotsAndPencils/noLoginDownload
Add ability to download Xcode without logging in using XcodeRelease
2 parents 3ddb27c + fadab3c commit ad6aca2

File tree

3 files changed

+38
-15
lines changed

3 files changed

+38
-15
lines changed

Sources/XcodesKit/Models.swift

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ public struct Xcode: Codable, Equatable {
5050
public let filename: String
5151
public let releaseDate: Date?
5252

53+
public var downloadPath: String {
54+
return url.path
55+
}
56+
5357
public init(version: Version, url: URL, filename: String, releaseDate: Date?) {
5458
self.version = version
5559
self.url = url

Sources/XcodesKit/URLRequest+Apple.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ extension URL {
44
static let download = URL(string: "https://developer.apple.com/download")!
55
static let downloads = URL(string: "https://developer.apple.com/services-account/QH65B2/downloadws/listDownloads.action")!
66
static let downloadXcode = URL(string: "https://developer.apple.com/devcenter/download.action")!
7+
static let downloadADCAuth = URL(string: "https://developerservices2.apple.com/services/download")!
78
}
89

910
extension URLRequest {
@@ -25,4 +26,14 @@ extension URLRequest {
2526
request.allHTTPHeaderFields?["Accept"] = "*/*"
2627
return request
2728
}
29+
30+
// default to a known download path if none passed in
31+
static func downloadADCAuth(path: String? = "/Developer_Tools/Xcode_14/Xcode_14.xip") -> URLRequest {
32+
var components = URLComponents(url: .downloadADCAuth, resolvingAgainstBaseURL: false)!
33+
components.queryItems = [URLQueryItem(name: "path", value: path)]
34+
var request = URLRequest(url: components.url!)
35+
request.allHTTPHeaderFields = request.allHTTPHeaderFields ?? [:]
36+
request.allHTTPHeaderFields?["Accept"] = "*/*"
37+
return request
38+
}
2839
}

Sources/XcodesKit/XcodeInstaller.swift

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,15 @@ public final class XcodeInstaller {
290290

291291
private func downloadXcode(version: Version, dataSource: DataSource, downloader: Downloader, willInstall: Bool) -> Promise<(Xcode, URL)> {
292292
return firstly { () -> Promise<Version> in
293-
loginIfNeeded().map { version }
293+
if dataSource == .apple {
294+
return loginIfNeeded().map { version }
295+
} else {
296+
guard let xcode = self.xcodeList.availableXcodes.first(withVersion: version) else {
297+
throw Error.unavailableVersion(version)
298+
}
299+
300+
return validateADCSession(path: xcode.downloadPath).map { version }
301+
}
294302
}
295303
.then { version -> Promise<Version> in
296304
if self.xcodeList.shouldUpdate {
@@ -300,14 +308,6 @@ public final class XcodeInstaller {
300308
return Promise.value(version)
301309
}
302310
}
303-
.then { version -> Promise<Version> in
304-
// This request would've already been made if the Apple data source were being used.
305-
// That's not the case for the Xcode Releases data source.
306-
// We need the cookies from its response in order to download Xcodes though,
307-
// so perform it here first just to be sure.
308-
Current.network.dataTask(with: URLRequest.downloads)
309-
.map { _ in version }
310-
}
311311
.then { version -> Promise<(Xcode, URL)> in
312312
guard let xcode = self.xcodeList.availableXcodes.first(withVersion: version) else {
313313
throw Error.unavailableVersion(version)
@@ -337,7 +337,11 @@ public final class XcodeInstaller {
337337
.map { return (xcode, $0) }
338338
}
339339
}
340-
340+
341+
func validateADCSession(path: String) -> Promise<Void> {
342+
return Current.network.dataTask(with: URLRequest.downloadADCAuth(path: path)).asVoid()
343+
}
344+
341345
func loginIfNeeded(withUsername providedUsername: String? = nil, shouldPromptForPassword: Bool = false) -> Promise<Void> {
342346
return firstly { () -> Promise<Void> in
343347
return Current.network.validateSession()
@@ -645,11 +649,15 @@ public final class XcodeInstaller {
645649
}
646650

647651
func update(dataSource: DataSource) -> Promise<[Xcode]> {
648-
return firstly { () -> Promise<Void> in
649-
loginIfNeeded()
650-
}
651-
.then { () -> Promise<[Xcode]> in
652-
self.xcodeList.update(dataSource: dataSource)
652+
if dataSource == .apple {
653+
return firstly { () -> Promise<Void> in
654+
loginIfNeeded()
655+
}
656+
.then { () -> Promise<[Xcode]> in
657+
self.xcodeList.update(dataSource: dataSource)
658+
}
659+
} else {
660+
return self.xcodeList.update(dataSource: dataSource)
653661
}
654662
}
655663

0 commit comments

Comments
 (0)