mirror of
https://github.com/morgan9e/macos-stats
synced 2026-04-14 00:04:15 +09:00
- update a download view
- add a progress bar to download new version view - small refactoring in upgrader - remove Sensors config from Stats target
This commit is contained in:
@@ -104,9 +104,7 @@
|
||||
9AE29AE2249A50640071B02D /* ModuleKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9AABEADD243FB13500668CB0 /* ModuleKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
9AE29AE5249A50640071B02D /* StatsKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9A0C82DA24460F7200FAE3D4 /* StatsKit.framework */; };
|
||||
9AE29AE6249A50640071B02D /* StatsKit.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 9A0C82DA24460F7200FAE3D4 /* StatsKit.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
9AE29AEE249A50960071B02D /* Info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 9AE29AEC249A50960071B02D /* Info.plist */; };
|
||||
9AE29AF3249A51D70071B02D /* main.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AE29AF1249A50CD0071B02D /* main.swift */; };
|
||||
9AE29AF5249A52870071B02D /* config.plist in Resources */ = {isa = PBXBuildFile; fileRef = 9AE29AF4249A52870071B02D /* config.plist */; };
|
||||
9AE29AF6249A52B00071B02D /* config.plist in Resources */ = {isa = PBXBuildFile; fileRef = 9AE29AF4249A52870071B02D /* config.plist */; };
|
||||
9AE29AFB249A53DC0071B02D /* readers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AE29AF9249A53780071B02D /* readers.swift */; };
|
||||
9AE29AFC249A53DC0071B02D /* values.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9AE29AF7249A53420071B02D /* values.swift */; };
|
||||
@@ -1187,9 +1185,7 @@
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
9AE29AEE249A50960071B02D /* Info.plist in Resources */,
|
||||
9A6CFC0122A1C9F5001E782D /* Assets.xcassets in Resources */,
|
||||
9AE29AF5249A52870071B02D /* config.plist in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
|
||||
@@ -25,10 +25,9 @@ var modules: [Module] = [Battery(&store), Network(&store), Sensors(&store, &smc)
|
||||
var log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "Stats")
|
||||
|
||||
class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDelegate {
|
||||
private let settingsWindow: SettingsWindow = SettingsWindow()
|
||||
private let updateWindow: UpdateWindow = UpdateWindow()
|
||||
internal let settingsWindow: SettingsWindow = SettingsWindow()
|
||||
internal let updateNotification = NSUserNotification()
|
||||
|
||||
private let updateNotification = NSUserNotification()
|
||||
private let updateActivity = NSBackgroundActivityScheduler(identifier: "eu.exelban.Stats.updateCheck")
|
||||
|
||||
func applicationDidFinishLaunching(_ aNotification: Notification) {
|
||||
@@ -37,7 +36,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
|
||||
self.parseArguments()
|
||||
|
||||
NSUserNotificationCenter.default.removeAllDeliveredNotifications()
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(checkForNewVersion), name: .checkForUpdates, object: nil)
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(updateCron), name: .changeCronInterval, object: nil)
|
||||
|
||||
modules.forEach{ $0.mount() }
|
||||
@@ -54,7 +52,9 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
|
||||
if let uri = notification.userInfo?["url"] as? String {
|
||||
os_log(.debug, log: log, "Downloading new version of app...")
|
||||
if let url = URL(string: uri) {
|
||||
updater.download(url)
|
||||
updater.download(url, doneHandler: { path in
|
||||
updater.install(path: path)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,44 +76,6 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
|
||||
return true
|
||||
}
|
||||
|
||||
@objc internal func checkForNewVersion(_ window: Bool = false) {
|
||||
updater.check() { result, error in
|
||||
if error != nil {
|
||||
os_log(.error, log: log, "error updater.check(): %s", "\(error!.localizedDescription)")
|
||||
return
|
||||
}
|
||||
|
||||
guard error == nil, let version: version = result else {
|
||||
os_log(.error, log: log, "download error(): %s", "\(error!.localizedDescription)")
|
||||
return
|
||||
}
|
||||
|
||||
DispatchQueue.main.async(execute: {
|
||||
if window {
|
||||
os_log(.debug, log: log, "open update window: %s", "\(version.latest)")
|
||||
self.updateWindow.open(version)
|
||||
return
|
||||
}
|
||||
|
||||
if version.newest {
|
||||
os_log(.debug, log: log, "show update window because new version of app found: %s", "\(version.latest)")
|
||||
|
||||
self.updateNotification.identifier = "new-version-\(version.latest)"
|
||||
self.updateNotification.title = "New version available"
|
||||
self.updateNotification.subtitle = "Click to install the new version of Stats"
|
||||
self.updateNotification.soundName = NSUserNotificationDefaultSoundName
|
||||
|
||||
self.updateNotification.hasActionButton = true
|
||||
self.updateNotification.actionButtonTitle = "Install"
|
||||
self.updateNotification.userInfo = ["url": version.url]
|
||||
|
||||
NSUserNotificationCenter.default.delegate = self
|
||||
NSUserNotificationCenter.default.deliver(self.updateNotification)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func updateCron() {
|
||||
self.updateActivity.invalidate()
|
||||
self.updateActivity.repeats = true
|
||||
@@ -132,7 +94,7 @@ class AppDelegate: NSObject, NSApplicationDelegate, NSUserNotificationCenterDele
|
||||
}
|
||||
|
||||
self.updateActivity.schedule { (completion: @escaping NSBackgroundActivityScheduler.CompletionHandler) in
|
||||
self.checkForNewVersion(false)
|
||||
self.checkForNewVersion()
|
||||
completion(NSBackgroundActivityScheduler.Result.finished)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
import Cocoa
|
||||
import StatsKit
|
||||
import os.log
|
||||
|
||||
class ApplicationSettings: NSView {
|
||||
private let width: CGFloat = 540
|
||||
@@ -23,6 +24,9 @@ class ApplicationSettings: NSView {
|
||||
}
|
||||
}
|
||||
|
||||
private var updateButton: NSButton? = nil
|
||||
private let updateWindow: UpdateWindow = UpdateWindow()
|
||||
|
||||
init() {
|
||||
super.init(frame: NSRect(x: 0, y: 0, width: width, height: height))
|
||||
self.wantsLayer = true
|
||||
@@ -36,6 +40,16 @@ class ApplicationSettings: NSView {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
public override func viewDidMoveToWindow() {
|
||||
if let button = self.updateButton, let version = updater.latest {
|
||||
if version.newest {
|
||||
button.title = "Update application"
|
||||
} else {
|
||||
button.title = "Check for updates"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func addSettings() {
|
||||
let view: NSView = NSView(frame: NSRect(x: 0, y: 1, width: self.width-1, height: self.height - self.deviceInfoHeight))
|
||||
let rowHeight: CGFloat = 40
|
||||
@@ -262,7 +276,8 @@ class ApplicationSettings: NSView {
|
||||
button.title = "Check for updates"
|
||||
button.bezelStyle = .rounded
|
||||
button.target = self
|
||||
button.action = #selector(checkNewVersion)
|
||||
button.action = #selector(updateAction)
|
||||
self.updateButton = button
|
||||
|
||||
rightPanel.addSubview(iconView)
|
||||
rightPanel.addSubview(infoView)
|
||||
@@ -274,8 +289,23 @@ class ApplicationSettings: NSView {
|
||||
self.addSubview(view)
|
||||
}
|
||||
|
||||
@objc func checkNewVersion(_ sender: NSObject) {
|
||||
NotificationCenter.default.post(name: .checkForUpdates, object: nil, userInfo: nil)
|
||||
@objc func updateAction(_ sender: NSObject) {
|
||||
updater.check() { result, error in
|
||||
if error != nil {
|
||||
os_log(.error, log: log, "error updater.check(): %s", "\(error!.localizedDescription)")
|
||||
return
|
||||
}
|
||||
|
||||
guard error == nil, let version: version_s = result else {
|
||||
os_log(.error, log: log, "download error(): %s", "\(error!.localizedDescription)")
|
||||
return
|
||||
}
|
||||
|
||||
DispatchQueue.main.async(execute: {
|
||||
self.updateWindow.open(version)
|
||||
return
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func toggleUpdateInterval(_ sender: NSMenuItem) {
|
||||
|
||||
@@ -20,7 +20,12 @@ class UpdateWindow: NSWindow, NSWindowDelegate {
|
||||
let w = NSScreen.main!.frame.width
|
||||
let h = NSScreen.main!.frame.height
|
||||
super.init(
|
||||
contentRect: NSMakeRect(w - self.viewController.view.frame.width, h - self.viewController.view.frame.height, self.viewController.view.frame.width, self.viewController.view.frame.height),
|
||||
contentRect: NSMakeRect(
|
||||
w - self.viewController.view.frame.width,
|
||||
h - self.viewController.view.frame.height,
|
||||
self.viewController.view.frame.width,
|
||||
self.viewController.view.frame.height
|
||||
),
|
||||
styleMask: [.closable, .titled],
|
||||
backing: .buffered,
|
||||
defer: true
|
||||
@@ -39,7 +44,7 @@ class UpdateWindow: NSWindow, NSWindowDelegate {
|
||||
windowController.loadWindow()
|
||||
}
|
||||
|
||||
public func open(_ v: version) {
|
||||
public func open(_ v: version_s) {
|
||||
if !self.isVisible {
|
||||
self.setIsVisible(true)
|
||||
self.makeKeyAndOrderFront(nil)
|
||||
@@ -61,72 +66,80 @@ private class UpdateViewController: NSViewController {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
public func open(_ v: version) {
|
||||
self.update.setVersions(v)
|
||||
public func open(_ v: version_s) {
|
||||
self.update.clear()
|
||||
|
||||
if v.newest {
|
||||
self.update.newVersion(v)
|
||||
return
|
||||
}
|
||||
self.update.noUpdates()
|
||||
}
|
||||
}
|
||||
|
||||
private class UpdateView: NSView {
|
||||
private let progressBar: NSProgressIndicator = NSProgressIndicator()
|
||||
private var version: version? = nil
|
||||
private var informationView: NSView? = nil
|
||||
private var noNew: NSView? = nil
|
||||
private var currentVersion: NSTextField? = nil
|
||||
private var latestVersion: NSTextField? = nil
|
||||
private var version: version_s? = nil
|
||||
private var path: String = ""
|
||||
|
||||
override init(frame: NSRect) {
|
||||
super.init(frame: CGRect(x: frame.origin.x, y: frame.origin.y, width: frame.width, height: frame.height))
|
||||
self.wantsLayer = true
|
||||
|
||||
self.addProgressBar()
|
||||
self.addInformation()
|
||||
self.addNoNew()
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
private func addProgressBar() {
|
||||
self.progressBar.isDisplayedWhenStopped = false
|
||||
self.progressBar.frame = NSRect(x: (self.frame.width - 22)/2, y: (self.frame.height - 22)/2, width: 22, height: 22)
|
||||
self.progressBar.style = .spinning
|
||||
|
||||
self.addSubview(self.progressBar)
|
||||
}
|
||||
|
||||
private func addInformation() {
|
||||
public func newVersion(_ version: version_s) {
|
||||
self.version = version
|
||||
let view: NSView = NSView(frame: NSRect(x: 10, y: 10, width: self.frame.width - 20, height: self.frame.height - 20))
|
||||
|
||||
let title: NSTextField = TextView(frame: NSRect(x: 0, y: view.frame.height - 18, width: view.frame.width, height: 18))
|
||||
title.font = NSFont.systemFont(ofSize: 14, weight: .bold)
|
||||
let title: NSTextField = TextView(frame: NSRect(x: 0, y: view.frame.height - 20, width: view.frame.width, height: 18))
|
||||
title.font = NSFont.systemFont(ofSize: 14, weight: .semibold)
|
||||
title.alignment = .center
|
||||
title.stringValue = "New version available"
|
||||
|
||||
let currentVersion: NSTextField = TextView(frame: NSRect(x: 0, y: title.frame.origin.y - 40, width: view.frame.width, height: 16))
|
||||
currentVersion.stringValue = "Current version: 0.0.0"
|
||||
let currentVersionString = "Current version: \(version.current)"
|
||||
let currentVersionWidth = currentVersionString.widthOfString(usingFont: .systemFont(ofSize: 13, weight: .light))
|
||||
let currentVersion: NSTextField = TextView(frame: NSRect(
|
||||
x: (view.frame.width-currentVersionWidth)/2,
|
||||
y: title.frame.origin.y - 40,
|
||||
width: currentVersionWidth,
|
||||
height: 16
|
||||
))
|
||||
currentVersion.stringValue = currentVersionString
|
||||
|
||||
let latestVersion: NSTextField = TextView(frame: NSRect(x: 0, y: currentVersion.frame.origin.y - 22, width: view.frame.width, height: 16))
|
||||
latestVersion.stringValue = "Latest version: 0.0.0"
|
||||
let latestVersionString = "Latest version: \(version.latest)"
|
||||
let latestVersionWidth = latestVersionString.widthOfString(usingFont: .systemFont(ofSize: 13, weight: .light))
|
||||
let latestVersion: NSTextField = TextView(frame: NSRect(
|
||||
x: (view.frame.width-currentVersionWidth)/2,
|
||||
y: currentVersion.frame.origin.y - 22,
|
||||
width: latestVersionWidth,
|
||||
height: 16
|
||||
))
|
||||
latestVersion.stringValue = latestVersionString
|
||||
|
||||
let button: NSButton = NSButton(frame: NSRect(x: 0, y: 0, width: view.frame.width, height: 26))
|
||||
button.title = "Download"
|
||||
button.bezelStyle = .rounded
|
||||
button.action = #selector(self.download)
|
||||
button.target = self
|
||||
let closeButton: NSButton = NSButton(frame: NSRect(x: 0, y: 0, width: view.frame.width/2, height: 26))
|
||||
closeButton.title = "Close"
|
||||
closeButton.bezelStyle = .rounded
|
||||
closeButton.action = #selector(self.close)
|
||||
closeButton.target = self
|
||||
|
||||
let downloadButton: NSButton = NSButton(frame: NSRect(x: view.frame.width/2, y: 0, width: view.frame.width/2, height: 26))
|
||||
downloadButton.title = "Download"
|
||||
downloadButton.bezelStyle = .rounded
|
||||
downloadButton.action = #selector(self.download)
|
||||
downloadButton.target = self
|
||||
|
||||
view.addSubview(title)
|
||||
view.addSubview(currentVersion)
|
||||
view.addSubview(latestVersion)
|
||||
view.addSubview(button)
|
||||
view.isHidden = true
|
||||
view.addSubview(closeButton)
|
||||
view.addSubview(downloadButton)
|
||||
self.addSubview(view)
|
||||
self.informationView = view
|
||||
self.currentVersion = currentVersion
|
||||
self.latestVersion = latestVersion
|
||||
}
|
||||
|
||||
private func addNoNew() {
|
||||
public func noUpdates() {
|
||||
let view: NSView = NSView(frame: NSRect(x: 10, y: 10, width: self.frame.width - 20, height: self.frame.height - 20))
|
||||
|
||||
let title: NSTextField = TextView(frame: NSRect(x: 0, y: ((view.frame.height - 18)/2)+20, width: view.frame.width, height: 18))
|
||||
@@ -143,37 +156,77 @@ private class UpdateView: NSView {
|
||||
view.addSubview(button)
|
||||
view.addSubview(title)
|
||||
self.addSubview(view)
|
||||
self.noNew = view
|
||||
}
|
||||
|
||||
public func setVersions(_ v: version) {
|
||||
self.progressBar.stopAnimation(self)
|
||||
self.noNew?.isHidden = true
|
||||
self.informationView?.isHidden = true
|
||||
|
||||
if v.newest {
|
||||
self.informationView?.isHidden = false
|
||||
self.version = v
|
||||
|
||||
currentVersion?.stringValue = "Current version: \(v.current)"
|
||||
latestVersion?.stringValue = "Latest version: \(v.latest)"
|
||||
return
|
||||
}
|
||||
|
||||
self.noNew?.isHidden = false
|
||||
public func clear() {
|
||||
self.subviews.forEach{ $0.removeFromSuperview() }
|
||||
}
|
||||
|
||||
@objc func close(_ sender: Any) {
|
||||
self.window?.setIsVisible(false)
|
||||
}
|
||||
|
||||
@objc func download(_ sender: Any) {
|
||||
@objc private func download(_ sender: Any) {
|
||||
guard let urlString = self.version?.url, let url = URL(string: urlString) else {
|
||||
return
|
||||
}
|
||||
os_log(.debug, log: log, "start downloading new version of app from: %s", "\(url.absoluteString)")
|
||||
updater.download(url)
|
||||
self.progressBar.startAnimation(self)
|
||||
self.informationView?.isHidden = true
|
||||
|
||||
self.clear()
|
||||
|
||||
let view: NSView = NSView(frame: NSRect(x: 10, y: 10, width: self.frame.width - 20, height: self.frame.height - 20))
|
||||
|
||||
let title: NSTextField = TextView(frame: NSRect(x: 0, y: view.frame.height - 28, width: view.frame.width, height: 18))
|
||||
title.font = NSFont.systemFont(ofSize: 14, weight: .semibold)
|
||||
title.alignment = .center
|
||||
title.stringValue = "Downloading..."
|
||||
|
||||
let progressBar: NSProgressIndicator = NSProgressIndicator()
|
||||
progressBar.frame = NSRect(x: 20, y: 64, width: view.frame.width - 40, height: 22)
|
||||
progressBar.minValue = 0
|
||||
progressBar.maxValue = 1
|
||||
progressBar.isIndeterminate = false
|
||||
|
||||
let state: NSTextField = TextView(frame: NSRect(x: 0, y: 48, width: view.frame.width, height: 18))
|
||||
state.font = NSFont.systemFont(ofSize: 12, weight: .light)
|
||||
state.alignment = .center
|
||||
state.textColor = .secondaryLabelColor
|
||||
state.stringValue = "0%"
|
||||
|
||||
let closeButton: NSButton = NSButton(frame: NSRect(x: 0, y: 0, width: view.frame.width, height: 26))
|
||||
closeButton.title = "Close"
|
||||
closeButton.bezelStyle = .rounded
|
||||
closeButton.action = #selector(self.close)
|
||||
closeButton.target = self
|
||||
|
||||
let installButton: NSButton = NSButton(frame: NSRect(x: view.frame.width/2, y: 0, width: view.frame.width/2, height: 26))
|
||||
installButton.title = "Install"
|
||||
installButton.bezelStyle = .rounded
|
||||
installButton.action = #selector(self.install)
|
||||
installButton.target = self
|
||||
installButton.isHidden = true
|
||||
|
||||
updater.download(url, progressHandler: { progress in
|
||||
DispatchQueue.main.async {
|
||||
progressBar.doubleValue = progress.fractionCompleted
|
||||
state.stringValue = "\(Int(progress.fractionCompleted*100))%"
|
||||
}
|
||||
}, doneHandler: { path in
|
||||
self.path = path
|
||||
DispatchQueue.main.async {
|
||||
closeButton.setFrameSize(NSSize(width: view.frame.width/2, height: closeButton.frame.height))
|
||||
installButton.isHidden = false
|
||||
}
|
||||
})
|
||||
|
||||
view.addSubview(title)
|
||||
view.addSubview(progressBar)
|
||||
view.addSubview(state)
|
||||
view.addSubview(closeButton)
|
||||
view.addSubview(installButton)
|
||||
self.addSubview(view)
|
||||
}
|
||||
|
||||
@objc private func close(_ sender: Any) {
|
||||
self.window?.close()
|
||||
}
|
||||
|
||||
@objc private func install(_ sender: Any) {
|
||||
updater.install(path: self.path)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -89,7 +89,39 @@ extension AppDelegate {
|
||||
}
|
||||
|
||||
if updateIntervals(rawValue: store.string(key: "update-interval", defaultValue: updateIntervals.atStart.rawValue)) != .never {
|
||||
self.checkForNewVersion(false)
|
||||
self.checkForNewVersion()
|
||||
}
|
||||
}
|
||||
|
||||
internal func checkForNewVersion() {
|
||||
updater.check() { result, error in
|
||||
if error != nil {
|
||||
os_log(.error, log: log, "error updater.check(): %s", "\(error!.localizedDescription)")
|
||||
return
|
||||
}
|
||||
|
||||
guard error == nil, let version: version_s = result else {
|
||||
os_log(.error, log: log, "download error(): %s", "\(error!.localizedDescription)")
|
||||
return
|
||||
}
|
||||
|
||||
DispatchQueue.main.async(execute: {
|
||||
if version.newest {
|
||||
os_log(.debug, log: log, "show update window because new version of app found: %s", "\(version.latest)")
|
||||
|
||||
self.updateNotification.identifier = "new-version-\(version.latest)"
|
||||
self.updateNotification.title = "New version available"
|
||||
self.updateNotification.subtitle = "Click to install the new version of Stats"
|
||||
self.updateNotification.soundName = NSUserNotificationDefaultSoundName
|
||||
|
||||
self.updateNotification.hasActionButton = true
|
||||
self.updateNotification.actionButtonTitle = "Install"
|
||||
self.updateNotification.userInfo = ["url": version.url]
|
||||
|
||||
NSUserNotificationCenter.default.delegate = self
|
||||
NSUserNotificationCenter.default.deliver(self.updateNotification)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,11 +12,18 @@
|
||||
import Cocoa
|
||||
import SystemConfiguration
|
||||
|
||||
public struct version {
|
||||
public struct version_s {
|
||||
public let current: String
|
||||
public let latest: String
|
||||
public let newest: Bool
|
||||
public let url: String
|
||||
|
||||
public init(current: String, latest: String, newest: Bool, url: String) {
|
||||
self.current = current
|
||||
self.latest = latest
|
||||
self.newest = newest
|
||||
self.url = url
|
||||
}
|
||||
}
|
||||
|
||||
public struct Version {
|
||||
@@ -32,6 +39,9 @@ public class macAppUpdater {
|
||||
private let appName: String = Bundle.main.object(forInfoDictionaryKey: "CFBundleName") as! String
|
||||
private let currentVersion: String = "v\(Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as! String)"
|
||||
|
||||
public var latest: version_s? = nil
|
||||
private var observation: NSKeyValueObservation?
|
||||
|
||||
private var url: String {
|
||||
return "https://api.github.com/repos/\(user)/\(repo)/releases/latest"
|
||||
}
|
||||
@@ -41,7 +51,11 @@ public class macAppUpdater {
|
||||
self.repo = repo
|
||||
}
|
||||
|
||||
public func check(completionHandler: @escaping (_ result: version?, _ error: Error?) -> Void) {
|
||||
deinit {
|
||||
observation?.invalidate()
|
||||
}
|
||||
|
||||
public func check(completionHandler: @escaping (_ result: version_s?, _ error: Error?) -> Void) {
|
||||
if !isConnectedToNetwork() {
|
||||
completionHandler(nil, "No internet connection")
|
||||
return
|
||||
@@ -62,7 +76,8 @@ public class macAppUpdater {
|
||||
let lastVersion: String = result![0]
|
||||
let newVersion: Bool = IsNewestVersion(currentVersion: self.currentVersion, latestVersion: lastVersion)
|
||||
|
||||
completionHandler(version(current: self.currentVersion, latest: lastVersion, newest: newVersion, url: downloadURL), nil)
|
||||
self.latest = version_s(current: self.currentVersion, latest: lastVersion, newest: newVersion, url: downloadURL)
|
||||
completionHandler(self.latest, nil)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,13 +108,8 @@ public class macAppUpdater {
|
||||
task.resume()
|
||||
}
|
||||
|
||||
public func download(_ url: URL) {
|
||||
let downloadTask = URLSession.shared.downloadTask(with: url) {
|
||||
urlOrNil, responseOrNil, errorOrNil in
|
||||
// check for and handle errors:
|
||||
// * errorOrNil should be nil
|
||||
// * responseOrNil should be an HTTPURLResponse with statusCode in 200..<299
|
||||
|
||||
public func download(_ url: URL, progressHandler: @escaping (_ progress: Progress) -> Void = {_ in }, doneHandler: @escaping (_ path: String) -> Void = {_ in }) {
|
||||
let downloadTask = URLSession.shared.downloadTask(with: url) { urlOrNil, responseOrNil, errorOrNil in
|
||||
guard let fileURL = urlOrNil else { return }
|
||||
do {
|
||||
let downloadsURL = try FileManager.default.url(for: .downloadsDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
|
||||
@@ -111,27 +121,47 @@ public class macAppUpdater {
|
||||
return
|
||||
}
|
||||
|
||||
_ = syncShell("mkdir /tmp/Stats") // make sure that directory exist
|
||||
let res = syncShell("/usr/bin/hdiutil attach \(path) -mountpoint /tmp/Stats -noverify -nobrowse -noautoopen") // mount the dmg
|
||||
|
||||
if res.contains("is busy") { // dmg can be busy, if yes, unmount it and mount again
|
||||
_ = syncShell("/usr/bin/hdiutil detach $TMPDIR/Stats")
|
||||
_ = syncShell("/usr/bin/hdiutil attach \(path) -mountpoint /tmp/Stats -noverify -nobrowse -noautoopen")
|
||||
}
|
||||
|
||||
_ = syncShell("cp /tmp/Stats/Stats.app/Contents/Resources/Scripts/updater.sh $TMPDIR/updater.sh") // copy updater script to tmp folder
|
||||
|
||||
let pwd = Bundle.main.bundleURL.absoluteString.replacingOccurrences(of: "file://", with: "").replacingOccurrences(of: "Stats.app/", with: "")
|
||||
_ = asyncShell("sh $TMPDIR/updater.sh --app \(pwd) --dmg \(path) >/dev/null &") // run updater script in in background
|
||||
exit(0)
|
||||
doneHandler(path)
|
||||
}
|
||||
} catch {
|
||||
print ("file error: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
self.observation = downloadTask.progress.observe(\.fractionCompleted) { progress, _ in
|
||||
progressHandler(progress)
|
||||
}
|
||||
|
||||
downloadTask.resume()
|
||||
}
|
||||
|
||||
public func install(path: String) {
|
||||
print("Started new version installation...")
|
||||
|
||||
_ = syncShell("mkdir /tmp/Stats") // make sure that directory exist
|
||||
let res = syncShell("/usr/bin/hdiutil attach \(path) -mountpoint /tmp/Stats -noverify -nobrowse -noautoopen") // mount the dmg
|
||||
|
||||
print("DMG is mounted")
|
||||
|
||||
if res.contains("is busy") { // dmg can be busy, if yes, unmount it and mount again
|
||||
print("DMG is busy, remounting")
|
||||
|
||||
_ = syncShell("/usr/bin/hdiutil detach $TMPDIR/Stats")
|
||||
_ = syncShell("/usr/bin/hdiutil attach \(path) -mountpoint /tmp/Stats -noverify -nobrowse -noautoopen")
|
||||
}
|
||||
|
||||
_ = syncShell("cp -rf /tmp/Stats/Stats.app/Contents/Resources/Scripts/updater.sh $TMPDIR/updater.sh") // copy updater script to tmp folder
|
||||
|
||||
print("Script is copied to $TMPDIR/updater.sh")
|
||||
|
||||
let pwd = Bundle.main.bundleURL.absoluteString.replacingOccurrences(of: "file://", with: "").replacingOccurrences(of: "Stats.app/", with: "")
|
||||
_ = asyncShell("sh $TMPDIR/updater.sh --app \(pwd) --dmg \(path) >/dev/null &") // run updater script in in background
|
||||
|
||||
print("Run updater.sh with app: \(pwd) and dmg: \(path)")
|
||||
|
||||
exit(0)
|
||||
}
|
||||
|
||||
private func copyFile(from: URL, to: URL, completionHandler: @escaping (_ path: String, _ error: Error?) -> Void) {
|
||||
var toPath = to
|
||||
let fileName = (URL(fileURLWithPath: to.absoluteString)).lastPathComponent
|
||||
|
||||
Reference in New Issue
Block a user