Merge branch 'updates' of https://github.com/exelban/stats into dev

This commit is contained in:
Serhiy Mytrovtsiy
2019-06-25 23:54:52 +02:00
6 changed files with 526 additions and 5 deletions

View File

@@ -81,3 +81,65 @@ class AboutVC: NSViewController {
}
}
}
class UpdatesVC: NSViewController {
@IBOutlet weak var mainView: NSStackView!
@IBOutlet weak var spinnerView: NSView!
@IBOutlet weak var noInternetView: NSView!
@IBOutlet weak var mainTextLabel: NSTextFieldCell!
@IBOutlet weak var currentVersionLabel: NSTextField!
@IBOutlet weak var latestVersionLabel: NSTextField!
@IBOutlet weak var downloadButton: NSButton!
@IBOutlet weak var spinner: NSProgressIndicator!
let updater = macAppUpdater(user: "exelban", repo: "stats")
var url: String?
override func viewDidLoad() {
super.viewDidLoad()
self.view.wantsLayer = true
self.spinner.startAnimation(self)
updater.check() { result, error in
if error != nil && error as! String == "No internet connection" {
DispatchQueue.main.async(execute: {
self.spinnerView.isHidden = true
self.noInternetView.isHidden = false
})
return
}
guard error == nil, let version: version = result else {
print("Error: \(error ?? "check error")")
return
}
DispatchQueue.main.async(execute: {
self.spinner.stopAnimation(self)
self.spinnerView.isHidden = true
self.mainView.isHidden = false
self.currentVersionLabel.stringValue = version.current
self.latestVersionLabel.stringValue = version.latest
self.url = version.url
if !version.newest {
self.mainTextLabel.stringValue = "No new version available"
self.downloadButton.isEnabled = false
}
})
}
}
@IBAction func download(_ sender: Any) {
guard let urlString = self.url, let url = URL(string: urlString) else {
return
}
NSWorkspace.shared.open(url)
self.view.window?.close()
}
@IBAction func exit(_ sender: Any) {
self.view.window?.close()
}
}

View File

@@ -74,14 +74,27 @@ class MenuBar {
menu.addItem(preferences)
menu.addItem(NSMenuItem.separator())
let updateMenu = NSMenuItem(title: "Check for updates", action: #selector(checkUpdate), keyEquivalent: "")
updateMenu.target = self
let aboutMenu = NSMenuItem(title: "About Stats", action: #selector(openAbout), keyEquivalent: "")
aboutMenu.target = self
menu.addItem(updateMenu)
menu.addItem(aboutMenu)
menu.addItem(NSMenuItem(title: "Quit Stats", action: #selector(NSApplication.terminate(_:)), keyEquivalent: ""))
return menu
}
@objc func checkUpdate(_ sender : NSMenuItem) {
let updatesVC: NSWindowController? = NSStoryboard(name: "Updates", bundle: nil).instantiateController(withIdentifier: "UpdatesVC") as? NSWindowController
updatesVC?.window?.center()
updatesVC?.window?.level = .floating
updatesVC!.showWindow(self)
}
@objc func openAbout(_ sender : NSMenuItem) {
let aboutVC: NSWindowController? = NSStoryboard(name: "About", bundle: nil).instantiateController(withIdentifier: "AboutVC") as? NSWindowController
aboutVC?.window?.center()

View File

@@ -2,9 +2,11 @@
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.files.user-selected.read-only</key>
<true/>
<key>com.apple.security.app-sandbox</key>
<true/>
<key>com.apple.security.files.user-selected.read-only</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
</dict>
</plist>

View File

@@ -0,0 +1,287 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.Storyboard.XIB" version="3.0" toolsVersion="14460.31" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="14460.31"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Window Controller-->
<scene sceneID="Qxa-eF-fGn">
<objects>
<windowController storyboardIdentifier="UpdatesVC" id="eTp-5e-KuD" sceneMemberID="viewController">
<window key="window" title="Window" allowsToolTipsWhenApplicationIsInactive="NO" autorecalculatesKeyViewLoop="NO" restorable="NO" releasedWhenClosed="NO" visibleAtLaunch="NO" frameAutosaveName="" animationBehavior="default" titleVisibility="hidden" id="MdT-C2-Xn6">
<windowStyleMask key="styleMask" titled="YES"/>
<windowPositionMask key="initialPositionMask" leftStrut="YES" rightStrut="YES" topStrut="YES" bottomStrut="YES"/>
<rect key="contentRect" x="425" y="313" width="440" height="140"/>
<rect key="screenRect" x="0.0" y="0.0" width="2560" height="1057"/>
<connections>
<outlet property="delegate" destination="eTp-5e-KuD" id="Kaj-pD-86v"/>
</connections>
</window>
<connections>
<segue destination="Jvz-IB-V0r" kind="relationship" relationship="window.shadowedContentViewController" id="xGi-4Z-y1y"/>
</connections>
</windowController>
<customObject id="cb1-RP-9mi" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="4" y="48"/>
</scene>
<!--UpdatesVC-->
<scene sceneID="dHW-OY-NO5">
<objects>
<viewController id="Jvz-IB-V0r" customClass="UpdatesVC" customModule="Stats" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" id="c5r-hq-Fd6">
<rect key="frame" x="0.0" y="0.0" width="440" height="140"/>
<autoresizingMask key="autoresizingMask"/>
<subviews>
<stackView hidden="YES" distribution="fill" orientation="horizontal" alignment="top" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="gbc-x4-ULo">
<rect key="frame" x="20" y="20" width="400" height="100"/>
<subviews>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="lil-l7-SfL">
<rect key="frame" x="0.0" y="0.0" width="100" height="100"/>
<subviews>
<imageView horizontalHuggingPriority="251" verticalHuggingPriority="251" translatesAutoresizingMaskIntoConstraints="NO" id="dW0-Eg-dcV">
<rect key="frame" x="5" y="20" width="80" height="80"/>
<constraints>
<constraint firstAttribute="height" constant="80" id="M09-H6-mdV"/>
<constraint firstAttribute="width" constant="80" id="OXL-r3-XPM"/>
</constraints>
<imageCell key="cell" refusesFirstResponder="YES" alignment="left" imageScaling="proportionallyDown" image="AppIcon" id="qay-dJ-vRD"/>
</imageView>
</subviews>
<constraints>
<constraint firstAttribute="height" constant="100" id="426-Mn-c7c"/>
<constraint firstAttribute="width" constant="100" id="J6X-3U-Tq1"/>
<constraint firstItem="dW0-Eg-dcV" firstAttribute="top" secondItem="lil-l7-SfL" secondAttribute="top" id="faU-OY-TeM"/>
<constraint firstItem="dW0-Eg-dcV" firstAttribute="leading" secondItem="lil-l7-SfL" secondAttribute="leading" constant="5" id="hQN-bI-dc4"/>
</constraints>
</customView>
<stackView distribution="fill" orientation="vertical" alignment="trailing" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="a2t-Ca-JuB">
<rect key="frame" x="108" y="0.0" width="292" height="100"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="ouZ-jq-tRu">
<rect key="frame" x="-2" y="82" width="296" height="18"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="New Version Available" id="ifE-Um-dTa">
<font key="font" metaFont="systemBold" size="14"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<stackView distribution="fill" orientation="vertical" alignment="leading" spacing="4" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="ePi-7H-tuZ">
<rect key="frame" x="0.0" y="36" width="292" height="38"/>
<subviews>
<stackView distribution="fillEqually" orientation="horizontal" alignment="top" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="Ekt-ux-mcR">
<rect key="frame" x="0.0" y="21" width="208" height="17"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="fOg-zl-1M0">
<rect key="frame" x="-2" y="0.0" width="104" height="17"/>
<constraints>
<constraint firstAttribute="width" constant="100" id="xw9-2q-BDa"/>
</constraints>
<textFieldCell key="cell" lineBreakMode="clipping" title="Latest version:" id="ETQ-A6-cne">
<font key="font" metaFont="systemLight" size="13"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="zPp-kk-s1Y">
<rect key="frame" x="106" y="0.0" width="104" height="17"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="0.0.0" id="beD-Uf-T9l">
<font key="font" metaFont="systemMedium" size="13"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<visibilityPriorities>
<integer value="1000"/>
<integer value="1000"/>
</visibilityPriorities>
<customSpacing>
<real value="3.4028234663852886e+38"/>
<real value="3.4028234663852886e+38"/>
</customSpacing>
</stackView>
<stackView distribution="fillEqually" orientation="horizontal" alignment="top" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="wYM-Rg-iB8">
<rect key="frame" x="0.0" y="0.0" width="208" height="17"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="gxk-NW-8MC">
<rect key="frame" x="-2" y="0.0" width="104" height="17"/>
<constraints>
<constraint firstAttribute="width" constant="100" id="3hv-Qv-72l"/>
</constraints>
<textFieldCell key="cell" lineBreakMode="clipping" title="Current version:" id="pkM-KV-LXp">
<font key="font" metaFont="systemLight" size="13"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="gob-ke-eHN">
<rect key="frame" x="106" y="0.0" width="104" height="17"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="0.0.0" id="w43-pL-4IU">
<font key="font" metaFont="systemMedium" size="13"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<visibilityPriorities>
<integer value="1000"/>
<integer value="1000"/>
</visibilityPriorities>
<customSpacing>
<real value="3.4028234663852886e+38"/>
<real value="3.4028234663852886e+38"/>
</customSpacing>
</stackView>
</subviews>
<constraints>
<constraint firstAttribute="height" constant="38" id="hTG-ET-Q9C"/>
</constraints>
<visibilityPriorities>
<integer value="1000"/>
<integer value="1000"/>
</visibilityPriorities>
<customSpacing>
<real value="3.4028234663852886e+38"/>
<real value="3.4028234663852886e+38"/>
</customSpacing>
</stackView>
<stackView distribution="fill" orientation="horizontal" alignment="bottom" horizontalStackHuggingPriority="249.99998474121094" verticalStackHuggingPriority="249.99998474121094" detachesHiddenViews="YES" translatesAutoresizingMaskIntoConstraints="NO" id="sXv-6U-z9c">
<rect key="frame" x="132" y="0.0" width="160" height="28"/>
<subviews>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="4Ft-uA-xyo">
<rect key="frame" x="-6" y="-7" width="101" height="32"/>
<buttonCell key="cell" type="push" title="Download" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="lYz-7d-p0I">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="download:" target="Jvz-IB-V0r" id="CaY-3w-sQl"/>
</connections>
</button>
<button verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="9JO-Ob-lhu">
<rect key="frame" x="91" y="-7" width="75" height="32"/>
<buttonCell key="cell" type="push" title="Close" bezelStyle="rounded" alignment="center" borderStyle="border" imageScaling="proportionallyDown" inset="2" id="kc0-rN-ti2">
<behavior key="behavior" pushIn="YES" lightByBackground="YES" lightByGray="YES"/>
<font key="font" metaFont="system"/>
</buttonCell>
<connections>
<action selector="exit:" target="Jvz-IB-V0r" id="8ae-Af-YpV"/>
</connections>
</button>
</subviews>
<visibilityPriorities>
<integer value="1000"/>
<integer value="1000"/>
</visibilityPriorities>
<customSpacing>
<real value="3.4028234663852886e+38"/>
<real value="3.4028234663852886e+38"/>
</customSpacing>
</stackView>
</subviews>
<constraints>
<constraint firstAttribute="bottom" secondItem="sXv-6U-z9c" secondAttribute="bottom" id="0wF-S8-fdh"/>
<constraint firstItem="ouZ-jq-tRu" firstAttribute="leading" secondItem="a2t-Ca-JuB" secondAttribute="leading" id="EHv-7M-gMc"/>
<constraint firstItem="ePi-7H-tuZ" firstAttribute="leading" secondItem="a2t-Ca-JuB" secondAttribute="leading" id="OwE-N0-YYv"/>
<constraint firstAttribute="trailing" secondItem="ouZ-jq-tRu" secondAttribute="trailing" id="rVg-xV-PZ3"/>
<constraint firstAttribute="trailing" secondItem="ePi-7H-tuZ" secondAttribute="trailing" id="sxv-7o-0SH"/>
</constraints>
<visibilityPriorities>
<integer value="1000"/>
<integer value="1000"/>
<integer value="1000"/>
</visibilityPriorities>
<customSpacing>
<real value="3.4028234663852886e+38"/>
<real value="3.4028234663852886e+38"/>
<real value="3.4028234663852886e+38"/>
</customSpacing>
</stackView>
</subviews>
<constraints>
<constraint firstAttribute="width" constant="400" id="Acb-hj-G5c"/>
<constraint firstAttribute="bottom" secondItem="a2t-Ca-JuB" secondAttribute="bottom" id="Ari-ig-V9M"/>
<constraint firstAttribute="trailing" secondItem="a2t-Ca-JuB" secondAttribute="trailing" id="M7p-NR-yqx"/>
<constraint firstItem="a2t-Ca-JuB" firstAttribute="top" secondItem="gbc-x4-ULo" secondAttribute="top" id="dyF-7z-8sN"/>
<constraint firstAttribute="height" constant="100" id="f6V-S5-nxa"/>
<constraint firstItem="a2t-Ca-JuB" firstAttribute="leading" secondItem="lil-l7-SfL" secondAttribute="trailing" constant="8" id="xeZ-6k-w1Q"/>
</constraints>
<visibilityPriorities>
<integer value="1000"/>
<integer value="1000"/>
</visibilityPriorities>
<customSpacing>
<real value="3.4028234663852886e+38"/>
<real value="3.4028234663852886e+38"/>
</customSpacing>
</stackView>
<customView translatesAutoresizingMaskIntoConstraints="NO" id="TaT-Bx-z9f">
<rect key="frame" x="0.0" y="0.0" width="440" height="140"/>
<subviews>
<progressIndicator wantsLayer="YES" horizontalHuggingPriority="750" verticalHuggingPriority="750" maxValue="100" bezeled="NO" indeterminate="YES" style="spinning" translatesAutoresizingMaskIntoConstraints="NO" id="4lV-OW-XAy">
<rect key="frame" x="204" y="54" width="32" height="32"/>
</progressIndicator>
</subviews>
<constraints>
<constraint firstItem="4lV-OW-XAy" firstAttribute="centerX" secondItem="TaT-Bx-z9f" secondAttribute="centerX" id="RSU-P4-ZMI"/>
<constraint firstItem="4lV-OW-XAy" firstAttribute="centerY" secondItem="TaT-Bx-z9f" secondAttribute="centerY" id="yJ2-V9-z1C"/>
</constraints>
</customView>
<customView hidden="YES" translatesAutoresizingMaskIntoConstraints="NO" id="VuR-f2-eNB">
<rect key="frame" x="0.0" y="0.0" width="440" height="140"/>
<subviews>
<textField horizontalHuggingPriority="251" verticalHuggingPriority="750" translatesAutoresizingMaskIntoConstraints="NO" id="jbE-84-Rxv">
<rect key="frame" x="120" y="61" width="200" height="17"/>
<textFieldCell key="cell" lineBreakMode="clipping" title="Internet connection not available" id="zbB-5V-Hny">
<font key="font" metaFont="systemLight" size="13"/>
<color key="textColor" name="labelColor" catalog="System" colorSpace="catalog"/>
<color key="backgroundColor" name="textBackgroundColor" catalog="System" colorSpace="catalog"/>
</textFieldCell>
</textField>
</subviews>
<constraints>
<constraint firstItem="jbE-84-Rxv" firstAttribute="centerX" secondItem="VuR-f2-eNB" secondAttribute="centerX" id="8Qe-nE-B8p"/>
<constraint firstItem="jbE-84-Rxv" firstAttribute="centerY" secondItem="VuR-f2-eNB" secondAttribute="centerY" id="QGV-Gz-g7v"/>
</constraints>
</customView>
</subviews>
<constraints>
<constraint firstItem="VuR-f2-eNB" firstAttribute="leading" secondItem="c5r-hq-Fd6" secondAttribute="leading" id="84G-yg-hMa"/>
<constraint firstItem="gbc-x4-ULo" firstAttribute="centerX" secondItem="c5r-hq-Fd6" secondAttribute="centerX" id="Dpi-rK-AzM"/>
<constraint firstItem="TaT-Bx-z9f" firstAttribute="leading" secondItem="c5r-hq-Fd6" secondAttribute="leading" id="E51-Oc-2V4"/>
<constraint firstAttribute="bottom" secondItem="VuR-f2-eNB" secondAttribute="bottom" id="F2o-tO-1Cw"/>
<constraint firstAttribute="trailing" secondItem="VuR-f2-eNB" secondAttribute="trailing" id="IpB-2O-18u"/>
<constraint firstAttribute="bottom" secondItem="gbc-x4-ULo" secondAttribute="bottom" constant="20" id="LMa-In-o74"/>
<constraint firstItem="gbc-x4-ULo" firstAttribute="top" secondItem="c5r-hq-Fd6" secondAttribute="top" constant="20" id="LRc-d3-GD7"/>
<constraint firstItem="TaT-Bx-z9f" firstAttribute="top" secondItem="c5r-hq-Fd6" secondAttribute="top" id="R4W-Se-evk"/>
<constraint firstAttribute="trailing" secondItem="TaT-Bx-z9f" secondAttribute="trailing" id="WwE-PV-CRA"/>
<constraint firstItem="gbc-x4-ULo" firstAttribute="leading" secondItem="c5r-hq-Fd6" secondAttribute="leading" constant="20" id="dFH-7N-tFv"/>
<constraint firstItem="VuR-f2-eNB" firstAttribute="top" secondItem="c5r-hq-Fd6" secondAttribute="top" id="dMy-6f-EzA"/>
<constraint firstAttribute="bottom" secondItem="TaT-Bx-z9f" secondAttribute="bottom" id="eSI-Yp-nsb"/>
<constraint firstAttribute="trailing" secondItem="gbc-x4-ULo" secondAttribute="trailing" constant="20" id="fVu-MD-LPi"/>
<constraint firstItem="gbc-x4-ULo" firstAttribute="centerY" secondItem="c5r-hq-Fd6" secondAttribute="centerY" id="m0O-DZ-N1Z"/>
</constraints>
</view>
<connections>
<outlet property="currentVersionLabel" destination="gob-ke-eHN" id="CEW-EL-VoZ"/>
<outlet property="downloadButton" destination="4Ft-uA-xyo" id="gIo-OP-jHo"/>
<outlet property="latestVersionLabel" destination="zPp-kk-s1Y" id="Rw8-5R-dgM"/>
<outlet property="mainTextLabel" destination="ifE-Um-dTa" id="srK-S1-Icw"/>
<outlet property="mainView" destination="gbc-x4-ULo" id="8kc-ng-29G"/>
<outlet property="noInternetView" destination="VuR-f2-eNB" id="LrM-sz-2Ej"/>
<outlet property="spinner" destination="4lV-OW-XAy" id="a0Q-hW-TxG"/>
<outlet property="spinnerView" destination="TaT-Bx-z9f" id="bsd-xe-n0O"/>
</connections>
</viewController>
<customObject id="bP2-Gx-7ZJ" userLabel="First Responder" customClass="NSResponder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="4" y="386"/>
</scene>
</scenes>
<resources>
<image name="AppIcon" width="128" height="128"/>
</resources>
</document>

View File

@@ -0,0 +1,136 @@
//
// macAppUpdater.swift
// Stats
//
// Created by Serhiy Mytrovtsiy on 25.06.2019.
// Copyright © 2019 Serhiy Mytrovtsiy. All rights reserved.
//
import Foundation
import SystemConfiguration
extension String: Error {}
struct version {
let current: String
let latest: String
let newest: Bool
let url: String
}
public class macAppUpdater {
let user: String
let repo: String
let appName: String = Bundle.main.object(forInfoDictionaryKey: "CFBundleName") as! String
let currentVersion: String = "v\(Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as! String)"
var url: String {
return "https://api.github.com/repos/\(user)/\(repo)/releases/latest"
}
init(user: String, repo: String) {
self.user = user
self.repo = repo
}
func fetchLastVersion(completionHandler: @escaping (_ result: [String]?, _ error: Error?) -> Void) {
let task = URLSession.shared.dataTask(with: URL(string: self.url)!) { data, response, error in
guard let data = data, error == nil else { return }
do {
let jsonResponse = try JSONSerialization.jsonObject(with: data, options: [])
guard let jsonArray = jsonResponse as? [String: Any] else {
completionHandler(nil, "parse json")
return
}
let lastVersion = jsonArray["tag_name"] as? String
guard let assets = jsonArray["assets"] as? [[String: Any]] else {
completionHandler(nil, "parse assets")
return
}
if let asset = assets.first(where: {$0["name"] as! String == "\(self.appName).dmg"}) {
let downloadURL = asset["browser_download_url"] as? String
completionHandler([lastVersion!, downloadURL!], nil)
}
} catch let parsingError {
completionHandler(nil, parsingError)
}
}
task.resume()
}
func checkIfNewer(current: String, latest: String) -> Bool {
guard let currentNumber: Int64 = Int64(current.replacingOccurrences(of: "[v.]", with: "", options: [.regularExpression])) else {
print("Error: wrong version tag \(current)")
return false
}
guard let latestNumber: Int64 = Int64(latest.replacingOccurrences(of: "[v.]", with: "", options: [.regularExpression])) else {
print("Error: wrong version tag \(latest)")
return false
}
return latestNumber>currentNumber
}
func check(completionHandler: @escaping (_ result: version?, _ error: Error?) -> Void) {
if !Reachability.isConnectedToNetwork() {
completionHandler(nil, "No internet connection")
return
}
fetchLastVersion() { result, error in
guard error == nil else {
completionHandler(nil, error)
return
}
guard let results = result, results.count > 1 else {
completionHandler(nil, "wrong results")
return
}
let downloadURL: String = result![1]
let lastVersion: String = result![0]
let newVersion: Bool = self.checkIfNewer(current: self.currentVersion, latest: lastVersion)
completionHandler(version(current: self.currentVersion, latest: lastVersion, newest: newVersion, url: downloadURL), nil)
}
}
}
// https://stackoverflow.com/questions/30743408/check-for-internet-connection-with-swift
public class Reachability {
class func isConnectedToNetwork() -> Bool {
var zeroAddress = sockaddr_in(sin_len: 0, sin_family: 0, sin_port: 0, sin_addr: in_addr(s_addr: 0), sin_zero: (0, 0, 0, 0, 0, 0, 0, 0))
zeroAddress.sin_len = UInt8(MemoryLayout.size(ofValue: zeroAddress))
zeroAddress.sin_family = sa_family_t(AF_INET)
let defaultRouteReachability = withUnsafePointer(to: &zeroAddress) {
$0.withMemoryRebound(to: sockaddr.self, capacity: 1) {zeroSockAddress in
SCNetworkReachabilityCreateWithAddress(nil, zeroSockAddress)
}
}
var flags: SCNetworkReachabilityFlags = SCNetworkReachabilityFlags(rawValue: 0)
if SCNetworkReachabilityGetFlags(defaultRouteReachability!, &flags) == false {
return false
}
/* Only Working for WIFI
let isReachable = flags == .reachable
let needsConnection = flags == .connectionRequired
return isReachable && !needsConnection
*/
// Working for Cellular and WIFI
let isReachable = (flags.rawValue & UInt32(kSCNetworkFlagsReachable)) != 0
let needsConnection = (flags.rawValue & UInt32(kSCNetworkFlagsConnectionRequired)) != 0
let ret = (isReachable && !needsConnection)
return ret
}
}