Convert XcodeList to Combine

This commit is contained in:
Brandon Evans 2020-12-24 11:08:13 -07:00
parent e3687eacc6
commit 4ef8428151
No known key found for this signature in database
GPG key ID: D58A4B8DB64F8E93
3 changed files with 45 additions and 52 deletions

View file

@ -2,7 +2,6 @@ import AppKit
import AppleAPI
import Combine
import Path
import PromiseKit
import LegibleError
import KeychainAccess
@ -173,22 +172,15 @@ class AppState: ObservableObject {
public func update() -> AnyPublisher<[Xcode], Never> {
signInIfNeeded()
.flatMap {
// Wrap the Promise API in a Publisher for now
Deferred {
Future { promise in
self.list.update()
.done { promise(.success($0)) }
.catch { promise(.failure($0)) }
self.list.update()
}
.handleEvents(
receiveCompletion: { completion in
if case let .failure(error) = completion {
self.error = AlertContent(title: "Update Error", message: error.legibleLocalizedDescription)
}
}
.handleEvents(
receiveCompletion: { completion in
if case let .failure(error) = completion {
self.error = AlertContent(title: "Update Error", message: error.legibleLocalizedDescription)
}
}
)
}
)
.catch { _ in
Just(self.list.availableXcodes)
}

View file

@ -111,10 +111,10 @@ private func _installedXcodes(destination: Path) -> [InstalledXcode] {
public struct Network {
private static let client = AppleAPI.Client()
public var dataTask: (URLRequestConvertible) -> Promise<(data: Data, response: URLResponse)> = { AppleAPI.Current.network.session.dataTask(.promise, with: $0) }
public func dataTask(with convertible: URLRequestConvertible) -> Promise<(data: Data, response: URLResponse)> {
dataTask(convertible)
public var dataTask: (URLRequest) -> URLSession.DataTaskPublisher = { AppleAPI.Current.network.session.dataTaskPublisher(for: $0) }
public func dataTask(with request: URLRequest) -> URLSession.DataTaskPublisher {
dataTask(request)
}
public var downloadTask: (URLRequestConvertible, URL, Data?) -> (Progress, Promise<(saveLocation: URL, response: URLResponse)>) = { AppleAPI.Current.network.session.downloadTask(with: $0, to: $1, resumingWith: $2) }

View file

@ -1,10 +1,10 @@
import Combine
import Foundation
import Path
import Version
import PromiseKit
import SwiftSoup
/// Provides lists of available and installed Xcodes
/// Provides lists of available Xcodes
public final class XcodeList {
public init() {
try? loadCachedAvailableXcodes()
@ -16,8 +16,9 @@ public final class XcodeList {
return availableXcodes.isEmpty
}
public func update() -> Promise<[Xcode]> {
return when(fulfilled: releasedXcodes(), prereleaseXcodes())
public func update() -> AnyPublisher<[Xcode], Error> {
releasedXcodes().combineLatest(prereleaseXcodes())
.receive(on: DispatchQueue.main)
.map { releasedXcodes, prereleaseXcodes in
// Starting with Xcode 11 beta 6, developer.apple.com/download and developer.apple.com/download/more both list some pre-release versions of Xcode.
// Previously pre-release versions only appeared on developer.apple.com/download.
@ -30,6 +31,7 @@ public final class XcodeList {
try? self.cacheAvailableXcodes(xcodes)
return xcodes
}
.eraseToAnyPublisher()
}
}
@ -49,38 +51,37 @@ extension XcodeList {
}
extension XcodeList {
private func releasedXcodes() -> Promise<[Xcode]> {
return firstly { () -> Promise<(data: Data, response: URLResponse)> in
Current.network.dataTask(with: URLRequest.downloads)
}
.map { (data, response) -> [Xcode] in
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .formatted(.downloadsDateModified)
let downloads = try decoder.decode(Downloads.self, from: data)
let xcodes = downloads
.downloads
.filter { $0.name.range(of: "^Xcode [0-9]", options: .regularExpression) != nil }
.compactMap { download -> Xcode? in
let urlPrefix = URL(string: "https://download.developer.apple.com/")!
guard
let xcodeFile = download.files.first(where: { $0.remotePath.hasSuffix("dmg") || $0.remotePath.hasSuffix("xip") }),
let version = Version(xcodeVersion: download.name)
else { return nil }
private func releasedXcodes() -> AnyPublisher<[Xcode], Error> {
Current.network.dataTask(with: URLRequest.downloads)
.map(\.data)
.decode(type: Downloads.self, decoder: configure(JSONDecoder()) {
$0.dateDecodingStrategy = .formatted(.downloadsDateModified)
})
.map { downloads -> [Xcode] in
let xcodes = downloads
.downloads
.filter { $0.name.range(of: "^Xcode [0-9]", options: .regularExpression) != nil }
.compactMap { download -> Xcode? in
let urlPrefix = URL(string: "https://download.developer.apple.com/")!
guard
let xcodeFile = download.files.first(where: { $0.remotePath.hasSuffix("dmg") || $0.remotePath.hasSuffix("xip") }),
let version = Version(xcodeVersion: download.name)
else { return nil }
let url = urlPrefix.appendingPathComponent(xcodeFile.remotePath)
return Xcode(version: version, url: url, filename: String(xcodeFile.remotePath.suffix(fromLast: "/")), releaseDate: download.dateModified)
}
return xcodes
}
let url = urlPrefix.appendingPathComponent(xcodeFile.remotePath)
return Xcode(version: version, url: url, filename: String(xcodeFile.remotePath.suffix(fromLast: "/")), releaseDate: download.dateModified)
}
return xcodes
}
.eraseToAnyPublisher()
}
private func prereleaseXcodes() -> Promise<[Xcode]> {
return firstly { () -> Promise<(data: Data, response: URLResponse)> in
Current.network.dataTask(with: URLRequest.download)
}
.map { (data, _) -> [Xcode] in
try self.parsePrereleaseXcodes(from: data)
}
private func prereleaseXcodes() -> AnyPublisher<[Xcode], Error> {
Current.network.dataTask(with: URLRequest.download)
.tryMap { (data, _) -> [Xcode] in
try self.parsePrereleaseXcodes(from: data)
}
.eraseToAnyPublisher()
}
func parsePrereleaseXcodes(from data: Data) throws -> [Xcode] {