Skip to content

Commit 3bcaa50

Browse files
committed
Add Sparkle for auto update
Signed-off-by: Alex Mazanov <alexandr.mazanov@gmail.com>
1 parent c407321 commit 3bcaa50

File tree

9 files changed

+120
-3
lines changed

9 files changed

+120
-3
lines changed

Packages/TRexCore/Sources/TRexCore/Preferences.swift

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ public class Preferences: ObservableObject {
2525
case TesseractEnabled
2626
case TesseractLanguages
2727
case TesseractPath
28+
case IncludeBetaUpdates
2829
}
2930

3031
public enum MenuBarIcon: String, CaseIterable {
@@ -228,6 +229,12 @@ public class Preferences: ObservableObject {
228229
Preferences.setValue(value: tesseractPath, key: .TesseractPath)
229230
}
230231
}
232+
233+
@Published public var includeBetaUpdates: Bool {
234+
didSet {
235+
Preferences.setValue(value: includeBetaUpdates, key: .IncludeBetaUpdates)
236+
}
237+
}
231238

232239
init() {
233240
needsOnboarding = Preferences.getValue(key: .NeedsOnboarding) as? Bool ?? true
@@ -257,6 +264,7 @@ public class Preferences: ObservableObject {
257264
tesseractEnabled = Preferences.getValue(key: .TesseractEnabled) as? Bool ?? false
258265
tesseractLanguages = Preferences.getValue(key: .TesseractLanguages) as? [String] ?? ["eng"]
259266
tesseractPath = Preferences.getValue(key: .TesseractPath) as? String ?? ""
267+
includeBetaUpdates = Preferences.getValue(key: .IncludeBetaUpdates) as? Bool ?? false
260268
}
261269

262270
static func removeAll() {

TRex.xcodeproj/project.pbxproj

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
FA1EE63D2E1F2F5F009D86D8 /* QLImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = FA1EE63C2E1F2F5F009D86D8 /* QLImage.swift */; };
3636
FA1EE6412E1F32C0009D86D8 /* step2.gif in Resources */ = {isa = PBXBuildFile; fileRef = FA1EE63E2E1F32C0009D86D8 /* step2.gif */; };
3737
FA1EE6422E1F32C0009D86D8 /* step3.gif in Resources */ = {isa = PBXBuildFile; fileRef = FA1EE63F2E1F32C0009D86D8 /* step3.gif */; };
38+
FA1EE6452E254E54009D86D8 /* Sparkle in Frameworks */ = {isa = PBXBuildFile; productRef = FA1EE6442E254E54009D86D8 /* Sparkle */; };
3839
FA2B835B25DEBF8200B23F25 /* Credits.rtf in Resources */ = {isa = PBXBuildFile; fileRef = FA2B835A25DEBF8200B23F25 /* Credits.rtf */; };
3940
FA3AEAA82C9B85710096CCA5 /* TRexCore in Frameworks */ = {isa = PBXBuildFile; productRef = FA3AEAA72C9B85710096CCA5 /* TRexCore */; };
4041
FA3AEAAA2C9B85790096CCA5 /* TRexCore in Frameworks */ = {isa = PBXBuildFile; productRef = FA3AEAA92C9B85790096CCA5 /* TRexCore */; };
@@ -172,6 +173,7 @@
172173
buildActionMask = 2147483647;
173174
files = (
174175
FA3AEAA82C9B85710096CCA5 /* TRexCore in Frameworks */,
176+
FA1EE6452E254E54009D86D8 /* Sparkle in Frameworks */,
175177
FA5F875F25DD637300B1AF77 /* KeyboardShortcuts in Frameworks */,
176178
FA6648712E18469400EF497A /* TesseractSwift in Frameworks */,
177179
);
@@ -457,6 +459,7 @@
457459
FA5F875E25DD637300B1AF77 /* KeyboardShortcuts */,
458460
FA3AEAA72C9B85710096CCA5 /* TRexCore */,
459461
FA6648702E18469400EF497A /* TesseractSwift */,
462+
FA1EE6442E254E54009D86D8 /* Sparkle */,
460463
);
461464
productName = TRex;
462465
productReference = FA5F872E25DC243200B1AF77 /* TRex.app */;
@@ -493,6 +496,7 @@
493496
packageReferences = (
494497
FA5F875D25DD637300B1AF77 /* XCRemoteSwiftPackageReference "KeyboardShortcuts" */,
495498
FA58E3DF27AE31DF0034837F /* XCRemoteSwiftPackageReference "swift-argument-parser" */,
499+
FA1EE6432E254E54009D86D8 /* XCRemoteSwiftPackageReference "Sparkle" */,
496500
);
497501
productRefGroup = FA5F872F25DC243200B1AF77 /* Products */;
498502
projectDirPath = "";
@@ -835,7 +839,9 @@
835839
"@executable_path/../Frameworks",
836840
);
837841
MACOSX_DEPLOYMENT_TARGET = 13.5;
842+
MAC_APP_STORE = NO;
838843
MARKETING_VERSION = 1.9.0;
844+
MTL_ENABLE_DEBUG_INFO = "";
839845
OTHER_LDFLAGS = "-lc++";
840846
PRODUCT_BUNDLE_IDENTIFIER = com.ameba.TRex;
841847
PRODUCT_NAME = "$(TARGET_NAME)";
@@ -870,6 +876,7 @@
870876
"@executable_path/../Frameworks",
871877
);
872878
MACOSX_DEPLOYMENT_TARGET = 13.5;
879+
MAC_APP_STORE = NO;
873880
MARKETING_VERSION = 1.9.0;
874881
OTHER_LDFLAGS = "-lc++";
875882
PRODUCT_BUNDLE_IDENTIFIER = com.ameba.TRex;
@@ -911,6 +918,14 @@
911918
/* End XCConfigurationList section */
912919

913920
/* Begin XCRemoteSwiftPackageReference section */
921+
FA1EE6432E254E54009D86D8 /* XCRemoteSwiftPackageReference "Sparkle" */ = {
922+
isa = XCRemoteSwiftPackageReference;
923+
repositoryURL = "https://github.com/sparkle-project/Sparkle";
924+
requirement = {
925+
kind = upToNextMajorVersion;
926+
minimumVersion = 2.7.1;
927+
};
928+
};
914929
FA58E3DF27AE31DF0034837F /* XCRemoteSwiftPackageReference "swift-argument-parser" */ = {
915930
isa = XCRemoteSwiftPackageReference;
916931
repositoryURL = "https://github.com/apple/swift-argument-parser.git";
@@ -930,6 +945,11 @@
930945
/* End XCRemoteSwiftPackageReference section */
931946

932947
/* Begin XCSwiftPackageProductDependency section */
948+
FA1EE6442E254E54009D86D8 /* Sparkle */ = {
949+
isa = XCSwiftPackageProductDependency;
950+
package = FA1EE6432E254E54009D86D8 /* XCRemoteSwiftPackageReference "Sparkle" */;
951+
productName = Sparkle;
952+
};
933953
FA3AEAA72C9B85710096CCA5 /* TRexCore */ = {
934954
isa = XCSwiftPackageProductDependency;
935955
productName = TRexCore;
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import SwiftUI
2+
3+
struct BrandBackground: View {
4+
var body: some View {
5+
LinearGradient(
6+
gradient: Gradient(colors: Color.brandBackgroundGradientColors),
7+
startPoint: .topLeading,
8+
endPoint: .bottomTrailing
9+
)
10+
.ignoresSafeArea()
11+
}
12+
}

TRex/App/UI/Settings/AboutSettingsView.swift

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
import SwiftUI
2+
import TRexCore
23

34
struct AboutSettingsView: View {
5+
@EnvironmentObject var preferences: Preferences
6+
47
var body: some View {
58
VStack {
69
HStack {

TRex/App/UI/Settings/GeneralSettingsView.swift

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,10 +64,37 @@ struct GeneralSettingsView: View {
6464
.foregroundColor(.secondary)
6565
}
6666
}
67+
68+
#if !MAC_APP_STORE
69+
Spacer()
70+
Divider()
71+
Section(header: Text("Updates")) {
72+
HStack {
73+
Button("Check for Updates", action: {
74+
checkForUpdates()
75+
})
76+
Spacer()
77+
Toggle("Include beta updates", isOn: $preferences.includeBetaUpdates)
78+
.toggleStyle(.checkbox)
79+
}
80+
}
81+
#endif
6782
}
6883
.padding(20)
84+
#if MAC_APP_STORE
6985
.frame(width: 410, height: preferences.showMenuBarIcon ? (preferences.tesseractEnabled ? 220 : 280) : (preferences.tesseractEnabled ? 140 : 200))
86+
#else
87+
.frame(width: 410, height: preferences.showMenuBarIcon ? (preferences.tesseractEnabled ? 280 : 340) : (preferences.tesseractEnabled ? 200 : 260))
88+
#endif
89+
}
90+
91+
#if !MAC_APP_STORE
92+
func checkForUpdates() {
93+
if let appDelegate = NSApp.delegate as? AppDelegate {
94+
appDelegate.softwareUpdater.checkForUpdates()
95+
}
7096
}
97+
#endif
7198
}
7299

73100
struct MenuBarIconView: View {

TRex/App/UI/Settings/TesseractSettingsView.swift

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ struct TesseractSettingsView: View {
5050
.toggleStyle(SwitchToggleStyle())
5151
}
5252

53-
Text("Enable additional OCR languages beyond Apple Vision's built-in support")
53+
Text("Additional OCR languages beyond Apple Vision's built-in support")
5454
.font(.system(size: 12))
5555
.foregroundColor(.secondary)
5656
.fixedSize(horizontal: false, vertical: true)
@@ -149,7 +149,7 @@ struct TesseractSettingsView: View {
149149
.padding(.bottom, 16)
150150
}
151151
}
152-
.frame(width: 550, height: preferences.tesseractEnabled ? 430 : 140)
152+
.frame(width: 410, height: preferences.tesseractEnabled ? 430 : 140)
153153
.onAppear {
154154
refreshLanguageList()
155155
selectedLanguages = Set(preferences.tesseractLanguages)
@@ -321,4 +321,4 @@ struct LanguageRow: View {
321321
.fill(Color.clear)
322322
)
323323
}
324-
}
324+
}

TRex/AppDelegate.swift

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@ import Combine
22
import KeyboardShortcuts
33
import SwiftUI
44
import TRexCore
5+
#if !MAC_APP_STORE
6+
import Sparkle
7+
#endif
58

69
// TesseractWrapper bridge removed - now using TesseractSwift directly in TRexCore
710

@@ -12,6 +15,9 @@ class AppDelegate: NSObject, NSApplicationDelegate {
1215
var cancellable: Set<AnyCancellable> = []
1316
var onboardingWindowController: NSWindowController?
1417
let bundleID = Bundle.main.bundleIdentifier!
18+
#if !MAC_APP_STORE
19+
var softwareUpdater: SPUUpdater!
20+
#endif
1521

1622
func applicationDidFinishLaunching(_: Notification) {
1723
NSApp.servicesProvider = self
@@ -20,6 +26,10 @@ class AppDelegate: NSObject, NSApplicationDelegate {
2026
NSWorkspace.shared.open(URL(string: "trex://showPreferences")!)
2127
NSApp.terminate(nil)
2228
}
29+
30+
#if !MAC_APP_STORE
31+
setupSparkle()
32+
#endif
2333

2434
preferences.$showMenuBarIcon.sink(receiveValue: { [weak self] show in
2535
guard let self = self else { return }
@@ -143,3 +153,26 @@ extension AppDelegate {
143153
}
144154
}
145155
}
156+
157+
#if !MAC_APP_STORE
158+
extension AppDelegate: SPUUpdaterDelegate, SPUStandardUserDriverDelegate {
159+
func setupSparkle() {
160+
let hostBundle = Bundle.main
161+
let updateDriver = SPUStandardUserDriver(hostBundle: hostBundle, delegate: self)
162+
softwareUpdater = SPUUpdater(hostBundle: hostBundle, applicationBundle: hostBundle, userDriver: updateDriver, delegate: self)
163+
164+
do {
165+
try softwareUpdater.start()
166+
} catch {
167+
print("Failed to start software updater with error: \(error)")
168+
}
169+
}
170+
171+
func feedURLString(for updater: SPUUpdater) -> String? {
172+
if preferences.includeBetaUpdates {
173+
return "https://ameba.github.io/TRex/appcast_beta.xml"
174+
}
175+
return "https://ameba.github.io/TRex/appcast.xml"
176+
}
177+
}
178+
#endif

TRex/Resources/Info.plist

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,10 @@
3333
<string>$(CURRENT_PROJECT_VERSION)</string>
3434
<key>LSApplicationCategoryType</key>
3535
<string>public.app-category.utilities</string>
36+
<key>SUPublicEDKey</key>
37+
<string>c1vnuzkyrl2y6GNfNk/N91wC0S2w0PYxfbBonj+QrT0=</string>
38+
<key>SUEnableAutomaticChecks</key>
39+
<true/>
3640
<key>LSMinimumSystemVersion</key>
3741
<string>$(MACOSX_DEPLOYMENT_TARGET)</string>
3842
<key>LSUIElement</key>
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# TRex v1.9.0-BETA-4 Release Notes
2+
3+
## ✨ What's New
4+
5+
### Auto-Update Support
6+
- **Sparkle Integration**: TRex now supports automatic updates! The app will check for new versions on launch and notify you when updates are available.
7+
- **Update Preferences**: Control update behavior in Settings → General:
8+
- Check for updates manually with the "Check for Updates" button
9+
- Opt into beta updates with the "Include beta updates" checkbox
10+
- **Menu Bar Access**: Quickly check for updates from the TRex menu bar icon

0 commit comments

Comments
 (0)