diff --git a/MacTorn-v1.2.zip b/MacTorn-v1.2.zip
new file mode 100644
index 0000000..9d5a4ba
Binary files /dev/null and b/MacTorn-v1.2.zip differ
diff --git a/MacTorn-v1.0.zip b/MacTorn/Archive/MacTorn-v1.0.zip
similarity index 100%
rename from MacTorn-v1.0.zip
rename to MacTorn/Archive/MacTorn-v1.0.zip
diff --git a/MacTorn/MacTorn/Info.plist b/MacTorn/MacTorn/Info.plist
index bc929d5..7e734d3 100644
--- a/MacTorn/MacTorn/Info.plist
+++ b/MacTorn/MacTorn/Info.plist
@@ -17,8 +17,8 @@
CFBundlePackageType
$(PRODUCT_BUNDLE_PACKAGE_TYPE)
CFBundleShortVersionString
- 1.0
+ 1.2
CFBundleVersion
- 1
+ 1.2
diff --git a/MacTorn/MacTorn/Models/TornModels.swift b/MacTorn/MacTorn/Models/TornModels.swift
index f795376..335e02d 100644
--- a/MacTorn/MacTorn/Models/TornModels.swift
+++ b/MacTorn/MacTorn/Models/TornModels.swift
@@ -440,6 +440,75 @@ struct WatchlistItem: Codable, Identifiable {
}
}
+// MARK: - Update Manager
+struct GitHubRelease: Codable {
+ let tagName: String
+ let htmlUrl: String
+ let body: String
+
+ enum CodingKeys: String, CodingKey {
+ case tagName = "tag_name"
+ case htmlUrl = "html_url"
+ case body
+ }
+}
+
+class UpdateManager {
+ static let shared = UpdateManager()
+
+ // Configure your repository here
+ private let githubOwner = "pawelorzech"
+ private let githubRepo = "MacTorn"
+
+ func checkForUpdates(currentVersion: String) async -> GitHubRelease? {
+ let urlString = "https://api.github.com/repos/\(githubOwner)/\(githubRepo)/releases/latest"
+ guard let url = URL(string: urlString) else { return nil }
+
+ do {
+ let (data, response) = try await URLSession.shared.data(from: url)
+
+ guard let httpResponse = response as? HTTPURLResponse,
+ httpResponse.statusCode == 200 else {
+ return nil
+ }
+
+ let release = try JSONDecoder().decode(GitHubRelease.self, from: data)
+
+ // Compare versions
+ let versionString = release.tagName.replacingOccurrences(of: "v", with: "")
+
+ if isVersion(versionString, greaterThan: currentVersion) {
+ return release
+ }
+
+ } catch {
+ print("Update check failed: \(error)")
+ }
+
+ return nil
+ }
+
+ private func isVersion(_ newVersion: String, greaterThan currentVersion: String) -> Bool {
+ let newComponents = newVersion.split(separator: ".").compactMap { Int($0) }
+ let currentComponents = currentVersion.split(separator: ".").compactMap { Int($0) }
+
+ let maxLength = max(newComponents.count, currentComponents.count)
+
+ for i in 0.. current {
+ return true
+ } else if new < current {
+ return false
+ }
+ }
+
+ return false
+ }
+}
+
// MARK: - Error
struct TornError: Codable {
let code: Int
diff --git a/MacTorn/MacTorn/ViewModels/AppState.swift b/MacTorn/MacTorn/ViewModels/AppState.swift
index d6a5614..0e1ee54 100644
--- a/MacTorn/MacTorn/ViewModels/AppState.swift
+++ b/MacTorn/MacTorn/ViewModels/AppState.swift
@@ -23,9 +23,13 @@ class AppState: ObservableObject {
@Published var propertiesData: [PropertyInfo]?
@Published var watchlistItems: [WatchlistItem] = []
+ // MARK: - Update State
+ @Published var updateAvailable: GitHubRelease?
+
// MARK: - Managers
let launchAtLogin = LaunchAtLoginManager()
let shortcutsManager = ShortcutsManager()
+ let updateManager = UpdateManager.shared
// MARK: - State Comparison
private var previousBars: Bars?
@@ -458,6 +462,19 @@ class AppState: ObservableObject {
}
}
}
+
+ // MARK: - Updates
+ func checkForAppUpdates() {
+ guard let currentVersion = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String else { return }
+
+ Task {
+ if let release = await updateManager.checkForUpdates(currentVersion: currentVersion) {
+ await MainActor.run {
+ self.updateAvailable = release
+ }
+ }
+ }
+ }
}
// MARK: - Errors
diff --git a/MacTorn/MacTorn/Views/ContentView.swift b/MacTorn/MacTorn/Views/ContentView.swift
index 0e7bbed..974cf31 100644
--- a/MacTorn/MacTorn/Views/ContentView.swift
+++ b/MacTorn/MacTorn/Views/ContentView.swift
@@ -71,6 +71,7 @@ struct ContentView: View {
}
.task {
await NotificationManager.shared.requestPermission()
+ appState.checkForAppUpdates()
}
}
diff --git a/MacTorn/MacTorn/Views/SettingsView.swift b/MacTorn/MacTorn/Views/SettingsView.swift
index edf0a48..d6e75c1 100644
--- a/MacTorn/MacTorn/Views/SettingsView.swift
+++ b/MacTorn/MacTorn/Views/SettingsView.swift
@@ -114,14 +114,47 @@ struct SettingsView: View {
.background(Color.purple.opacity(0.05))
.cornerRadius(8)
- // GitHub
- HStack(spacing: 4) {
- Image(systemName: "chevron.left.forwardslash.chevron.right")
- .font(.caption2)
- .foregroundColor(.gray)
- Link("View on GitHub",
- destination: URL(string: "https://github.com/pawelorzech/MacTorn")!)
- .font(.caption)
+ // Update Section
+ if let update = appState.updateAvailable {
+ VStack(spacing: 8) {
+ HStack {
+ Image(systemName: "arrow.triangle.2.circlepath")
+ .foregroundColor(.green)
+ Text("New version available: \(update.tagName)")
+ .font(.caption.bold())
+ }
+
+ Button("Download Update") {
+ if let url = URL(string: update.htmlUrl) {
+ NSWorkspace.shared.open(url)
+ }
+ }
+ .buttonStyle(.borderedProminent)
+ .controlSize(.small)
+ }
+ .padding(10)
+ .frame(maxWidth: .infinity)
+ .background(Color.green.opacity(0.1))
+ .cornerRadius(8)
+ }
+
+ // GitHub & Version
+ VStack(spacing: 4) {
+ HStack(spacing: 4) {
+ Image(systemName: "chevron.left.forwardslash.chevron.right")
+ .font(.caption2)
+ .foregroundColor(.gray)
+ Link("View on GitHub",
+ destination: URL(string: "https://github.com/pawelorzech/MacTorn")!)
+ .font(.caption)
+ }
+
+ if let version = Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String {
+ Text("v\(version)")
+ .font(.caption2)
+ .foregroundColor(.secondary)
+ .opacity(0.5)
+ }
}
}
.padding()
diff --git a/README.md b/README.md
index 8858f83..ffb36b5 100644
--- a/README.md
+++ b/README.md
@@ -6,7 +6,9 @@ A native macOS menu bar app for monitoring your **Torn** game status.


-
+
+
+
## Features
@@ -35,18 +37,17 @@ A native macOS menu bar app for monitoring your **Torn** game status.
- Armory quick-use buttons
### 📈 Watchlist Tab
-- Track item prices
+- Track item prices (Latest API v2 support)
+- Displays lowest market price AND quantity (e.g., `$4.2M x12`)
- Price change indicators
- Add/remove items from watchlist
-### 🏠 Properties Tab
-- Property info and vault contents
-- Upkeep status and countdown
-
### ⚙️ General
-- 🔔 Smart notifications for bars, cooldowns, landing, chain
-- 🕒 Configurable refresh intervals (15s/30s/60s/2m)
-- 🚀 Launch at Login
+- **🔄 Update Checker**: Automatically notifies you when a new version is available on GitHub.
+- **🔔 Smart Notifications**: Alerts for bar thresholds, cooldown ready, landing, chain expiring.
+- **🕒 Configurable Refresh**: Intervals (15s/30s/60s/2m).
+- **🚀 Launch at Login**: Start seamlessly with macOS.
+- **⚡️ Optimized Startup**: Non-blocking data fetching for instant UI responsiveness.
## Installation
@@ -55,12 +56,12 @@ A native macOS menu bar app for monitoring your **Torn** game status.
3. Open MacTorn from Applications
4. Enter your [Torn API Key](https://www.torn.com/preferences.php#tab=api)
-> **Note**: If you download an unsigned build, macOS Gatekeeper will block it. Right-click the app and select "Open", or go to System Settings → Privacy & Security → Open Anyway.
+> **Note**: If you download an unsigned build, macOS Gatekeeper may block it. Right-click the app and select "Open", or go to System Settings → Privacy & Security → Open Anyway.
## Requirements
- macOS 13.0 (Ventura) or later
-- Torn API Key with access to: basic, bars, cooldowns, travel, profile, events, messages
+- Torn API Key with access to: basic, bars, cooldowns, travel, profile, events, messages, market
## Configuration
@@ -70,8 +71,8 @@ Choose polling frequency: 15s, 30s, 60s, or 120s
### Notifications
MacTorn sends notifications for bar thresholds, cooldown ready, landing, chain expiring, and release. Notification defaults are stored locally.
-### Quick Links
-8 preset shortcuts to common Torn pages (fully editable)
+### Updates
+The app checks for updates automatically on startup. If a new version is available, you'll see a notification in the **Settings** tab.
## Building from Source