Skip to content

Commit

Permalink
Merge pull request #10 from Kyome22/auto-update
Browse files Browse the repository at this point in the history
Auto update using Sparkle
  • Loading branch information
Kyome22 authored Nov 7, 2024
2 parents 45e0678 + c0ae9a8 commit f5d191b
Show file tree
Hide file tree
Showing 18 changed files with 283 additions and 13 deletions.
12 changes: 8 additions & 4 deletions ShiftWindow.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
1C0D29D626AEFBDA00DD0690 /* ShiftWindow.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ShiftWindow.app; sourceTree = BUILT_PRODUCTS_DIR; };
1C0D29DD26AEFBDC00DD0690 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
1C0D29E326AEFBDC00DD0690 /* ShiftWindow.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = ShiftWindow.entitlements; sourceTree = "<group>"; };
1C90D5CA2CDCABD800C1DD2A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = "<group>"; };
1CC92CF72869E51C00D46424 /* ShiftWindowApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShiftWindowApp.swift; sourceTree = "<group>"; };
1CEF19DB2CD3CDC60069E419 /* ShiftWindowPackages */ = {isa = PBXFileReference; lastKnownFileType = wrapper; path = ShiftWindowPackages; sourceTree = "<group>"; };
1CEF19E32CD523DE0069E419 /* InfoPlist.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = InfoPlist.xcstrings; sourceTree = "<group>"; };
Expand Down Expand Up @@ -67,6 +68,7 @@
1C0D29D826AEFBDA00DD0690 /* ShiftWindow */ = {
isa = PBXGroup;
children = (
1C90D5CA2CDCABD800C1DD2A /* Info.plist */,
1C0D29E326AEFBDC00DD0690 /* ShiftWindow.entitlements */,
1C0D29DD26AEFBDC00DD0690 /* Assets.xcassets */,
1CEF19E32CD523DE0069E419 /* InfoPlist.xcstrings */,
Expand Down Expand Up @@ -309,19 +311,20 @@
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 2.9.0;
CURRENT_PROJECT_VERSION = 3.0.0;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = VJ5N2X84K8;
ENABLE_HARDENED_RUNTIME = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = ShiftWindow/Info.plist;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
INFOPLIST_KEY_LSUIElement = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = "© 2021-2024 Takuto Nakamura";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
MARKETING_VERSION = 2.9;
MARKETING_VERSION = 3.0;
PRODUCT_BUNDLE_IDENTIFIER = com.kyome.ShiftWindow;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
Expand All @@ -338,19 +341,20 @@
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "Apple Development";
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 2.9.0;
CURRENT_PROJECT_VERSION = 3.0.0;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = VJ5N2X84K8;
ENABLE_HARDENED_RUNTIME = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = ShiftWindow/Info.plist;
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.utilities";
INFOPLIST_KEY_LSUIElement = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = "© 2021-2024 Takuto Nakamura";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
MARKETING_VERSION = 2.9;
MARKETING_VERSION = 3.0;
PRODUCT_BUNDLE_IDENTIFIER = com.kyome.ShiftWindow;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"originHash" : "b690a697b63b51f14aecd0af5063c13c473cb780745cb8ba8e6a6f4094c54925",
"originHash" : "d111412ccccc94ca5fe4a846eb78f4ab2f7d2da62b8999fb29a8850d087e284f",
"pins" : [
{
"identity" : "panelscenekit",
Expand All @@ -10,6 +10,15 @@
"version" : "1.2.0"
}
},
{
"identity" : "sparkle",
"kind" : "remoteSourceControl",
"location" : "https://github.com/sparkle-project/Sparkle.git",
"state" : {
"revision" : "0ef1ee0220239b3776f433314515fd849025673f",
"version" : "2.6.4"
}
},
{
"identity" : "spicekey",
"kind" : "remoteSourceControl",
Expand Down
10 changes: 10 additions & 0 deletions ShiftWindow/Info.plist
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>SUFeedURL</key>
<string>https://raw.githubusercontent.com/Kyome22/ShiftWindow/main/appcast.xml</string>
<key>SUPublicEDKey</key>
<string>gfW6qX5a5TkPRUrz3AK+pPNb+wr/SxdN4zPTgJjPj28=</string>
</dict>
</plist>
2 changes: 2 additions & 0 deletions ShiftWindowPackages/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,14 @@ let package = Package(
.package(url: "https://github.com/apple/swift-log.git", exact: "1.6.1"),
.package(url: "https://github.com/Kyome22/PanelSceneKit.git", exact: "1.2.0"),
.package(url: "https://github.com/Kyome22/SpiceKey.git", exact: "5.4.1"),
.package(url: "https://github.com/sparkle-project/Sparkle.git", exact: "2.6.4"),
],
targets: [
.target(
name: "DataLayer",
dependencies: [
.product(name: "Logging", package: "swift-log"),
.product(name: "Sparkle", package: "Sparkle"),
.product(name: "SpiceKey", package: "SpiceKey"),
],
swiftSettings: swiftSettings
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
/*
SPUUpdaterClient.swift
DataLayer

Created by Takuto Nakamura on 2024/11/06.
Copyright 2022 Takuto Nakamura (Kyome22)

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import Combine
@preconcurrency import Sparkle

public struct SPUUpdaterClient: DependencyClient {
var automaticallyChecksForUpdates: @Sendable () -> Bool
var setAutomaticallyChecksForUpdates: @Sendable (Bool) -> Void
public var canCheckForUpdatesPublisher: @Sendable () -> AnyPublisher<Bool, Never>
public var checkForUpdates: @Sendable () -> Void

public static let liveValue: Self = {
let updaterController = SPUStandardUpdaterController(
startingUpdater: true,
updaterDelegate: nil,
userDriverDelegate: nil
)
return Self(
automaticallyChecksForUpdates: {
updaterController.updater.automaticallyChecksForUpdates
},
setAutomaticallyChecksForUpdates: {
updaterController.updater.automaticallyChecksForUpdates = $0
},
canCheckForUpdatesPublisher: {
updaterController.updater.publisher(for: \.canCheckForUpdates).eraseToAnyPublisher()
},
checkForUpdates: {
updaterController.updater.checkForUpdates()
}
)
}()

public static let testValue = Self(
automaticallyChecksForUpdates: { false },
setAutomaticallyChecksForUpdates: { _ in },
canCheckForUpdatesPublisher: { Just(false).eraseToAnyPublisher() },
checkForUpdates: {}
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/*
CheckForUpdatesRepository.swift
DataLayer

Created by Takuto Nakamura on 2024/11/07.
Copyright 2022 Takuto Nakamura (Kyome22)

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import Foundation

public struct CheckForUpdatesRepository: Sendable {
private let spuUpdaterClient: SPUUpdaterClient

public var isEnabled: Bool {
spuUpdaterClient.automaticallyChecksForUpdates()
}

public init(_ spuUpdaterClient: SPUUpdaterClient) {
self.spuUpdaterClient = spuUpdaterClient
}

public func switchStatus(_ isEnabled: Bool) {
spuUpdaterClient.setAutomaticallyChecksForUpdates(isEnabled)
}
}
6 changes: 6 additions & 0 deletions ShiftWindowPackages/Sources/Domain/AppDependency.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,13 @@ public final class AppDependency: Sendable {
public let hiServicesClient: HIServicesClient
public let nsAppClient: NSAppClient
public let nsWorkspaceClient: NSWorkspaceClient
public let checkForUpdatesRepository: CheckForUpdatesRepository
public let userDefaultsRepository: UserDefaultsRepository
public let launchAtLoginRepository: LaunchAtLoginRepository
public let logService: LogService
public let shiftService: ShiftService
public let shortcutService: ShortcutService
public let updateService: UpdateService

public nonisolated init(
cgDirectDisplayClient: CGDirectDisplayClient = .liveValue,
Expand All @@ -42,13 +44,15 @@ public final class AppDependency: Sendable {
nsScreenClient: NSScreenClient = .liveValue,
nsWorkspaceClient: NSWorkspaceClient = .liveValue,
smAppServiceClient: SMAppServiceClient = .liveValue,
spuUpdaterClient: SPUUpdaterClient = .liveValue,
userDefaultsClient: UserDefaultsClient = .liveValue,
needsResetUserDefaults: Bool = false
) {
self.executeClient = executeClient
self.hiServicesClient = hiServicesClient
self.nsAppClient = nsAppClient
self.nsWorkspaceClient = nsWorkspaceClient
checkForUpdatesRepository = .init(spuUpdaterClient)
userDefaultsRepository = .init(userDefaultsClient, reset: needsResetUserDefaults)
launchAtLoginRepository = .init(smAppServiceClient)
logService = .init(loggingSystemClient)
Expand All @@ -58,6 +62,7 @@ public final class AppDependency: Sendable {
nsScreenClient,
nsWorkspaceClient)
shortcutService = .init(userDefaultsRepository, shiftService)
updateService = .init(spuUpdaterClient)
}
}

Expand All @@ -71,6 +76,7 @@ struct AppDependencyKey: EnvironmentKey {
nsScreenClient: .testValue,
nsWorkspaceClient: .testValue,
smAppServiceClient: .testValue,
spuUpdaterClient: .testValue,
userDefaultsClient: .testValue
)
}
Expand Down
47 changes: 47 additions & 0 deletions ShiftWindowPackages/Sources/Domain/Service/UpdateService.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
/*
UpdateService.swift
Domain

Created by Takuto Nakamura on 2024/11/07.
Copyright 2022 Takuto Nakamura (Kyome22)

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

import Combine
import DataLayer

public actor UpdateService {
private let spuUpdaterClient: SPUUpdaterClient

public init(_ spuUpdaterClient: SPUUpdaterClient) {
self.spuUpdaterClient = spuUpdaterClient
}

public func canChecksForUpdatesStream() -> AsyncStream<Bool> {
AsyncStream { continuation in
let cancellable = spuUpdaterClient
.canCheckForUpdatesPublisher()
.sink { value in
continuation.yield(value)
}
continuation.onTermination = { _ in
cancellable.cancel()
}
}
}

public func checkForUpdates() {
spuUpdaterClient.checkForUpdates()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,22 +24,30 @@ import Observation

@MainActor @Observable public final class GeneralSettingsViewModel {
private let nsWorkspaceClient: NSWorkspaceClient
private let checkForUpdatesRepository: CheckForUpdatesRepository
private let launchAtLoginRepository: LaunchAtLoginRepository
private let logService: LogService

public var launchAtLoginIsEnabled: Bool {
didSet { launchAtLoginSwitched(launchAtLoginIsEnabled) }
}

public var checkForUpdatesIsEnabled: Bool {
didSet { checkForUpdatesRepository.switchStatus(checkForUpdatesIsEnabled) }
}

public init(
_ nsWorkspaceClient: NSWorkspaceClient,
_ checkForUpdatesRepository: CheckForUpdatesRepository,
_ launchAtLoginRepository: LaunchAtLoginRepository,
_ logService: LogService
) {
self.nsWorkspaceClient = nsWorkspaceClient
self.checkForUpdatesRepository = checkForUpdatesRepository
self.launchAtLoginRepository = launchAtLoginRepository
self.logService = logService
launchAtLoginIsEnabled = launchAtLoginRepository.isEnabled
checkForUpdatesIsEnabled = checkForUpdatesRepository.isEnabled
}

public func onAppear(screenName: String) {
Expand Down
29 changes: 26 additions & 3 deletions ShiftWindowPackages/Sources/Domain/ViewModel/MenuViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -27,29 +27,46 @@ import Observation
private let nsAppClient: NSAppClient
private let logService: LogService
private let shiftService: ShiftService
private let updateService: UpdateService

@ObservationIgnored private var task: Task<Void, Never>?

public var patterns = [ShiftPattern]()
public var hideIcons: Bool {
didSet { toggleIconsVisible(hideIcons) }
}
public var canChecksForUpdates: Bool = false

public init(
_ executeClient: ExecuteClient,
_ nsAppClient: NSAppClient,
_ logService: LogService,
_ shiftService: ShiftService,
_ shortcutService: ShortcutService
_ shortcutService: ShortcutService,
_ updateService: UpdateService
) {
self.executeClient = executeClient
self.nsAppClient = nsAppClient
self.logService = logService
self.shiftService = shiftService
self.updateService = updateService
hideIcons = (try? executeClient.checkIconsVisible()) ?? false
task = Task {
for await patterns in await shortcutService.patternsStream() {
self.patterns = patterns
await withTaskGroup(of: Void.self) { group in
group.addTask {
for await patterns in await shortcutService.patternsStream() {
await MainActor.run {
self.patterns = patterns
}
}
}
group.addTask {
for await value in await updateService.canChecksForUpdatesStream() {
await MainActor.run {
self.canChecksForUpdates = value
}
}
}
}
}
}
Expand All @@ -68,6 +85,12 @@ import Observation
nsAppClient.activate(true)
}

public func checkForUpdates() {
Task {
await updateService.checkForUpdates()
}
}

public func openAbout() {
nsAppClient.activate(true)
nsAppClient.orderFrontStandardAboutPanel(nil)
Expand Down
Loading

0 comments on commit f5d191b

Please sign in to comment.