Skip to content

Commit 69c96ea

Browse files
committed
Tweak update process
1 parent e82900c commit 69c96ea

File tree

8 files changed

+113
-12
lines changed

8 files changed

+113
-12
lines changed

Copilot for Xcode/App.swift

Lines changed: 25 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,22 @@ import UpdateChecker
66
import XPCShared
77

88
struct VisualEffect: NSViewRepresentable {
9-
func makeNSView(context: Self.Context) -> NSView { return NSVisualEffectView() }
10-
func updateNSView(_ nsView: NSView, context: Context) { }
9+
func makeNSView(context: Self.Context) -> NSView { return NSVisualEffectView() }
10+
func updateNSView(_ nsView: NSView, context: Context) {}
1111
}
1212

13+
class TheUpdateCheckerDelegate: UpdateCheckerDelegate {
14+
func prepareForRelaunch(finish: @escaping () -> Void) {
15+
Task {
16+
let service = try? getService()
17+
try? await service?.quitService()
18+
finish()
19+
}
20+
}
21+
}
22+
23+
let updateCheckerDelegate = TheUpdateCheckerDelegate()
24+
1325
@main
1426
struct CopilotForXcodeApp: App {
1527
var body: some Scene {
@@ -20,7 +32,17 @@ struct CopilotForXcodeApp: App {
2032
.onAppear {
2133
UserDefaults.setupDefaultSettings()
2234
}
23-
.environment(\.updateChecker, UpdateChecker(hostBundle: Bundle.main))
35+
.environment(
36+
\.updateChecker,
37+
{
38+
let checker = UpdateChecker(
39+
hostBundle: Bundle.main,
40+
shouldAutomaticallyCheckForUpdate: false
41+
)
42+
checker.updateCheckerDelegate = updateCheckerDelegate
43+
return checker
44+
}()
45+
)
2446
}
2547
}
2648
}

Core/Sources/HostApp/TabContainer.swift

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,10 @@ private extension EnvironmentValues {
215215
}
216216

217217
struct UpdateCheckerKey: EnvironmentKey {
218-
static var defaultValue: UpdateChecker = .init(hostBundle: nil)
218+
static var defaultValue: UpdateChecker = .init(
219+
hostBundle: nil,
220+
shouldAutomaticallyCheckForUpdate: false
221+
)
219222
}
220223

221224
public extension EnvironmentValues {

Core/Sources/Service/XPCService.swift

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -195,6 +195,13 @@ public class XPCService: NSObject, XPCServiceProtocol {
195195
reply()
196196
NotificationCenter.default.post(name: .init(name), object: nil)
197197
}
198+
199+
public func quit(reply: @escaping () -> Void) {
200+
Task {
201+
await Service.shared.prepareForExit()
202+
reply()
203+
}
204+
}
198205

199206
// MARK: - Requests
200207

Core/Sources/UpdateChecker/UpdateChecker.swift

Lines changed: 37 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,15 +5,25 @@ import Sparkle
55
public final class UpdateChecker {
66
let updater: SPUUpdater
77
let hostBundleFound: Bool
8-
let delegate = UpdaterDelegate()
8+
let delegate: UpdaterDelegate
9+
public weak var updateCheckerDelegate: UpdateCheckerDelegate? {
10+
get { delegate.updateCheckerDelegate }
11+
set { delegate.updateCheckerDelegate = newValue }
12+
}
913

10-
public init(hostBundle: Bundle?) {
14+
public init(
15+
hostBundle: Bundle?,
16+
shouldAutomaticallyCheckForUpdate: Bool
17+
) {
1118
if hostBundle == nil {
1219
hostBundleFound = false
1320
Logger.updateChecker.error("Host bundle not found")
1421
} else {
1522
hostBundleFound = true
1623
}
24+
delegate = .init(
25+
shouldAutomaticallyCheckForUpdate: shouldAutomaticallyCheckForUpdate
26+
)
1727
updater = SPUUpdater(
1828
hostBundle: hostBundle ?? Bundle.main,
1929
applicationBundle: Bundle.main,
@@ -37,7 +47,32 @@ public final class UpdateChecker {
3747
}
3848
}
3949

50+
public protocol UpdateCheckerDelegate: AnyObject {
51+
func prepareForRelaunch(finish: @escaping () -> Void)
52+
}
53+
4054
class UpdaterDelegate: NSObject, SPUUpdaterDelegate {
55+
let shouldAutomaticallyCheckForUpdate: Bool
56+
weak var updateCheckerDelegate: UpdateCheckerDelegate?
57+
58+
init(shouldAutomaticallyCheckForUpdate: Bool) {
59+
self.shouldAutomaticallyCheckForUpdate = shouldAutomaticallyCheckForUpdate
60+
}
61+
62+
func updater(_ updater: SPUUpdater, mayPerform updateCheck: SPUUpdateCheck) throws {
63+
if !shouldAutomaticallyCheckForUpdate, updateCheck == .updatesInBackground {
64+
throw CancellationError()
65+
}
66+
}
67+
68+
func updater(_ updater: SPUUpdater, shouldPostponeRelaunchForUpdate item: SUAppcastItem, untilInvokingBlock installHandler: @escaping () -> Void) -> Bool {
69+
if let updateCheckerDelegate {
70+
updateCheckerDelegate.prepareForRelaunch(finish: installHandler)
71+
return true
72+
}
73+
return false
74+
}
75+
4176
func allowedChannels(for updater: SPUUpdater) -> Set<String> {
4277
if UserDefaults.shared.value(for: \.installBetaBuilds) {
4378
Set(["beta"])

ExtensionService/AppDelegate.swift

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,15 +21,16 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {
2121
let service = Service.shared
2222
var statusBarItem: NSStatusItem!
2323
var xpcController: XPCController?
24-
let updateChecker =
25-
UpdateChecker(
26-
hostBundle: locateHostBundleURL(url: Bundle.main.bundleURL)
27-
.flatMap(Bundle.init(url:))
28-
)
24+
let updateChecker = UpdateChecker(
25+
hostBundle: locateHostBundleURL(url: Bundle.main.bundleURL)
26+
.flatMap(Bundle.init(url:)),
27+
shouldAutomaticallyCheckForUpdate: true
28+
)
2929

3030
func applicationDidFinishLaunching(_: Notification) {
3131
if ProcessInfo.processInfo.environment["IS_UNIT_TEST"] == "YES" { return }
3232
_ = XcodeInspector.shared
33+
updateChecker.updateCheckerDelegate = self
3334
service.start()
3435
AXIsProcessTrustedWithOptions([
3536
kAXTrustedCheckOptionPrompt.takeRetainedValue() as NSString: true,
@@ -138,6 +139,15 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {
138139
}
139140
}
140141

142+
extension AppDelegate: UpdateCheckerDelegate {
143+
func prepareForRelaunch(finish: @escaping () -> Void) {
144+
Task {
145+
await service.prepareForExit()
146+
finish()
147+
}
148+
}
149+
}
150+
141151
extension NSRunningApplication {
142152
var isUserOfService: Bool {
143153
[

Pro

Submodule Pro updated from f056f7c to d58fa18

Tool/Sources/XPCShared/XPCExtensionService.swift

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -161,6 +161,15 @@ public class XPCExtensionService {
161161
{ service in { service.customCommand(id: id, editorContent: $0, withReply: $1) } }
162162
)
163163
}
164+
165+
public func quitService() async throws {
166+
try await withXPCServiceConnectedWithoutLaunching {
167+
service, continuation in
168+
service.quit {
169+
continuation.resume(())
170+
}
171+
}
172+
}
164173

165174
public func postNotification(name: String) async throws {
166175
try await withXPCServiceConnected {
@@ -254,6 +263,20 @@ extension XPCExtensionService {
254263
}
255264
}
256265
}
266+
267+
@XPCServiceActor
268+
private func withXPCServiceConnectedWithoutLaunching<T>(
269+
_ fn: @escaping (XPCServiceProtocol, AutoFinishContinuation<T>) -> Void
270+
) async throws -> T {
271+
if let service, let connection = service.connection {
272+
do {
273+
return try await XPCShared.withXPCServiceConnected(connection: connection, fn)
274+
} catch {
275+
throw XPCExtensionServiceError.xpcServiceError(error)
276+
}
277+
}
278+
throw XPCExtensionServiceError.failedToCreateXPCConnection
279+
}
257280

258281
@XPCServiceActor
259282
private func suggestionRequest(

Tool/Sources/XPCShared/XPCServiceProtocol.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ public protocol XPCServiceProtocol {
5656
func getXPCServiceAccessibilityPermission(withReply reply: @escaping (Bool) -> Void)
5757
func postNotification(name: String, withReply reply: @escaping () -> Void)
5858
func send(endpoint: String, requestBody: Data, reply: @escaping (Data?, Error?) -> Void)
59+
func quit(reply: @escaping () -> Void)
5960
}
6061

6162
public struct NoResponse: Codable {

0 commit comments

Comments
 (0)