diff --git a/Timelane.xcodeproj/project.pbxproj b/Timelane.xcodeproj/project.pbxproj
index 11cd8f3..8ca5010 100644
--- a/Timelane.xcodeproj/project.pbxproj
+++ b/Timelane.xcodeproj/project.pbxproj
@@ -26,8 +26,11 @@
9C25F022241E5CD0004AAA35 /* Worker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C25F021241E5CD0004AAA35 /* Worker.swift */; };
9C25F029241E5D6A004AAA35 /* TimelaneCombine in Frameworks */ = {isa = PBXBuildFile; productRef = 9C25F028241E5D6A004AAA35 /* TimelaneCombine */; };
9C3C6AFF23DE2F1400F3AAA8 /* about.txt in Resources */ = {isa = PBXBuildFile; fileRef = 9C3C6AFE23DE2F1400F3AAA8 /* about.txt */; };
- 9C3C6B0123DE301A00F3AAA8 /* Releases.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C3C6B0023DE301A00F3AAA8 /* Releases.swift */; };
+ 9C4FA1B52441E8CE00064D08 /* AppMover in Frameworks */ = {isa = PBXBuildFile; productRef = 9C4FA1B42441E8CE00064D08 /* AppMover */; };
+ 9C83711A245D5322000C1EDF /* ClickableImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C837119245D5322000C1EDF /* ClickableImageView.swift */; };
9CAA292523DE2B870029FCD2 /* AboutViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9CAA292423DE2B870029FCD2 /* AboutViewController.swift */; };
+ 9CED3DDD2444E00200719205 /* Sparkle.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9CED3DDC2444E00200719205 /* Sparkle.framework */; };
+ 9CED3DDF2444E09000719205 /* Sparkle.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = 9CED3DDC2444E00200719205 /* Sparkle.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
9CFD3B2D2409186300AABADC /* gcdbook.png in Resources */ = {isa = PBXBuildFile; fileRef = 9CFD3B2C2409186300AABADC /* gcdbook.png */; };
/* End PBXBuildFile section */
@@ -41,6 +44,19 @@
};
/* End PBXContainerItemProxy section */
+/* Begin PBXCopyFilesBuildPhase section */
+ 9CED3DDE2444E07F00719205 /* CopyFiles */ = {
+ isa = PBXCopyFilesBuildPhase;
+ buildActionMask = 2147483647;
+ dstPath = "";
+ dstSubfolderSpec = 10;
+ files = (
+ 9CED3DDF2444E09000719205 /* Sparkle.framework in CopyFiles */,
+ );
+ runOnlyForDeploymentPostprocessing = 0;
+ };
+/* End PBXCopyFilesBuildPhase section */
+
/* Begin PBXFileReference section */
9C1C26D623DDCC0600673570 /* Timelane.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Timelane.app; sourceTree = BUILT_PRODUCTS_DIR; };
9C1C26D923DDCC0600673570 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
@@ -64,8 +80,9 @@
9C25F01D241E5CA7004AAA35 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
9C25F021241E5CD0004AAA35 /* Worker.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Worker.swift; sourceTree = ""; };
9C3C6AFE23DE2F1400F3AAA8 /* about.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = about.txt; sourceTree = ""; };
- 9C3C6B0023DE301A00F3AAA8 /* Releases.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Releases.swift; sourceTree = ""; };
+ 9C837119245D5322000C1EDF /* ClickableImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ClickableImageView.swift; sourceTree = ""; };
9CAA292423DE2B870029FCD2 /* AboutViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AboutViewController.swift; sourceTree = ""; };
+ 9CED3DDC2444E00200719205 /* Sparkle.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Sparkle.framework; path = Timelane/Vendor/Sparkle/Sparkle.framework; sourceTree = ""; };
9CFD3B2C2409186300AABADC /* gcdbook.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = gcdbook.png; sourceTree = ""; };
/* End PBXFileReference section */
@@ -74,6 +91,8 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
+ 9C4FA1B52441E8CE00064D08 /* AppMover in Frameworks */,
+ 9CED3DDD2444E00200719205 /* Sparkle.framework in Frameworks */,
9C1C26F823DDEB1B00673570 /* Down in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -92,6 +111,7 @@
9C0E61B923F5ECCC00A9E063 /* Frameworks */ = {
isa = PBXGroup;
children = (
+ 9CED3DDC2444E00200719205 /* Sparkle.framework */,
);
name = Frameworks;
sourceTree = "";
@@ -120,11 +140,11 @@
9C1C26D823DDCC0600673570 /* Timelane */ = {
isa = PBXGroup;
children = (
+ 9C837118245D5306000C1EDF /* Components */,
9CAA292123DE27DA0029FCD2 /* Controllers */,
9C1C26E923DDD94D00673570 /* Resources */,
9C1C26D923DDCC0600673570 /* AppDelegate.swift */,
9C1C26DB23DDCC0600673570 /* ViewController.swift */,
- 9C3C6B0023DE301A00F3AAA8 /* Releases.swift */,
9C1C26E223DDCC0700673570 /* Info.plist */,
9C1C26E323DDCC0700673570 /* Timelane.entitlements */,
);
@@ -183,6 +203,14 @@
path = Assets;
sourceTree = "";
};
+ 9C837118245D5306000C1EDF /* Components */ = {
+ isa = PBXGroup;
+ children = (
+ 9C837119245D5322000C1EDF /* ClickableImageView.swift */,
+ );
+ path = Components;
+ sourceTree = "";
+ };
9CAA292123DE27DA0029FCD2 /* Controllers */ = {
isa = PBXGroup;
children = (
@@ -202,6 +230,7 @@
9C1C26D223DDCC0600673570 /* Sources */,
9C1C26D323DDCC0600673570 /* Frameworks */,
9C1C26D423DDCC0600673570 /* Resources */,
+ 9CED3DDE2444E07F00719205 /* CopyFiles */,
);
buildRules = (
);
@@ -211,6 +240,7 @@
name = Timelane;
packageProductDependencies = (
9C1C26F723DDEB1B00673570 /* Down */,
+ 9C4FA1B42441E8CE00064D08 /* AppMover */,
);
productName = Timelane;
productReference = 9C1C26D623DDCC0600673570 /* Timelane.app */;
@@ -284,6 +314,7 @@
packageReferences = (
9C1C26F623DDEB1B00673570 /* XCRemoteSwiftPackageReference "Down" */,
9C25F027241E5D6A004AAA35 /* XCRemoteSwiftPackageReference "TimelaneCombine" */,
+ 9C4FA1B32441E8CE00064D08 /* XCRemoteSwiftPackageReference "AppMover" */,
);
productRefGroup = 9C1C26D723DDCC0600673570 /* Products */;
projectDirPath = "";
@@ -329,8 +360,8 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
- 9C3C6B0123DE301A00F3AAA8 /* Releases.swift in Sources */,
9C1C26DC23DDCC0600673570 /* ViewController.swift in Sources */,
+ 9C83711A245D5322000C1EDF /* ClickableImageView.swift in Sources */,
9C1C26EE23DDDC8B00673570 /* AdController.swift in Sources */,
9CAA292523DE2B870029FCD2 /* AboutViewController.swift in Sources */,
9C1C26DA23DDCC0600673570 /* AppDelegate.swift in Sources */,
@@ -513,16 +544,21 @@
CODE_SIGN_ENTITLEMENTS = Timelane/Timelane.entitlements;
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
- CURRENT_PROJECT_VERSION = 1.1;
+ CURRENT_PROJECT_VERSION = 1.3;
DEVELOPMENT_TEAM = 9MF8G8D9Y5;
ENABLE_HARDENED_RUNTIME = YES;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)/Timelane/Vendor/Sparkle",
+ );
INFOPLIST_FILE = Timelane/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
+ "@loader_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.14;
- MARKETING_VERSION = 1.1;
+ MARKETING_VERSION = 1.3;
PRODUCT_BUNDLE_IDENTIFIER = com.underplot.Timelane;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
@@ -536,16 +572,21 @@
CODE_SIGN_ENTITLEMENTS = Timelane/Timelane.entitlements;
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
- CURRENT_PROJECT_VERSION = 1.1;
+ CURRENT_PROJECT_VERSION = 1.3;
DEVELOPMENT_TEAM = 9MF8G8D9Y5;
ENABLE_HARDENED_RUNTIME = YES;
+ FRAMEWORK_SEARCH_PATHS = (
+ "$(inherited)",
+ "$(PROJECT_DIR)/Timelane/Vendor/Sparkle",
+ );
INFOPLIST_FILE = Timelane/Info.plist;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
+ "@loader_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 10.14;
- MARKETING_VERSION = 1.1;
+ MARKETING_VERSION = 1.3;
PRODUCT_BUNDLE_IDENTIFIER = com.underplot.Timelane;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_VERSION = 5.0;
@@ -679,6 +720,14 @@
minimumVersion = 1.0.4;
};
};
+ 9C4FA1B32441E8CE00064D08 /* XCRemoteSwiftPackageReference "AppMover" */ = {
+ isa = XCRemoteSwiftPackageReference;
+ repositoryURL = "https://github.com/icanzilb/AppMover";
+ requirement = {
+ kind = upToNextMajorVersion;
+ minimumVersion = 1.1.0;
+ };
+ };
/* End XCRemoteSwiftPackageReference section */
/* Begin XCSwiftPackageProductDependency section */
@@ -692,6 +741,11 @@
package = 9C25F027241E5D6A004AAA35 /* XCRemoteSwiftPackageReference "TimelaneCombine" */;
productName = TimelaneCombine;
};
+ 9C4FA1B42441E8CE00064D08 /* AppMover */ = {
+ isa = XCSwiftPackageProductDependency;
+ package = 9C4FA1B32441E8CE00064D08 /* XCRemoteSwiftPackageReference "AppMover" */;
+ productName = AppMover;
+ };
/* End XCSwiftPackageProductDependency section */
};
rootObject = 9C1C26CE23DDCC0600673570 /* Project object */;
diff --git a/Timelane.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Timelane.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
index c3f37fe..5a5ef1b 100644
--- a/Timelane.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
+++ b/Timelane.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
@@ -1,13 +1,22 @@
{
"object": {
"pins": [
+ {
+ "package": "AppMover",
+ "repositoryURL": "https://github.com/icanzilb/AppMover",
+ "state": {
+ "branch": null,
+ "revision": "dc90ac087844ab05b6bd0f12ed10317372f40458",
+ "version": "1.1.0"
+ }
+ },
{
"package": "Down",
"repositoryURL": "https://github.com/iwasrobbed/Down",
"state": {
"branch": null,
- "revision": "940b173fc1328b0539d0e3681fdfa679501d2bdb",
- "version": "0.9.0"
+ "revision": "2aada56e6a0a5593a58e1ad583bd605b6d0b58dd",
+ "version": "0.9.2"
}
},
{
@@ -15,8 +24,8 @@
"repositoryURL": "https://github.com/icanzilb/TimelaneCombine",
"state": {
"branch": null,
- "revision": "585553bc60cd4c9f29cebffdea384d7a27089cad",
- "version": "1.0.4"
+ "revision": "12c6aa6c3e0d5358f80651f2e7307bde01e6858b",
+ "version": "1.0.5"
}
},
{
@@ -24,8 +33,8 @@
"repositoryURL": "https://github.com/icanzilb/TimelaneCore",
"state": {
"branch": null,
- "revision": "eed561862b81852b9b0b0ea3a6ba3774b3a7b99f",
- "version": "1.0.3"
+ "revision": "29d9c8c56575819c025b87c836fb1ce320e405df",
+ "version": "1.0.8"
}
}
]
diff --git a/Timelane.xcodeproj/xcshareddata/xcschemes/Timelane.xcscheme b/Timelane.xcodeproj/xcshareddata/xcschemes/Timelane.xcscheme
new file mode 100644
index 0000000..94c5340
--- /dev/null
+++ b/Timelane.xcodeproj/xcshareddata/xcschemes/Timelane.xcscheme
@@ -0,0 +1,85 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Timelane/AppDelegate.swift b/Timelane/AppDelegate.swift
index 59cde73..4bb1a56 100644
--- a/Timelane/AppDelegate.swift
+++ b/Timelane/AppDelegate.swift
@@ -7,14 +7,36 @@
//
import Cocoa
+import AppMover
+import Sparkle
@NSApplicationMain
class AppDelegate: NSObject, NSApplicationDelegate {
+ let updater = SUUpdater()
+
func applicationDidFinishLaunching(_ aNotification: Notification) {
+ if let _ = ProcessInfo.processInfo.environment["RESET_USERDEFAULTS"] {
+ UserDefaults.standard.removePersistentDomain(forName: Bundle.main.bundleIdentifier!)
+ }
+
// Insert code here to initialize your application
+ #if !DEBUG
+ AppMover.moveIfNecessary()
+ #endif
+ updater.delegate = self
}
func applicationWillTerminate(_ aNotification: Notification) {
// Insert code here to tear down your application
}
+
+ @IBAction func checkForUpdates(_ sender: Any) {
+ updater.checkForUpdates(sender)
+ }
+}
+
+extension AppDelegate: SUUpdaterDelegate {
+ func updaterShouldPromptForPermissionToCheck(forUpdates updater: SUUpdater) -> Bool {
+ return false
+ }
}
diff --git a/Timelane/Components/ClickableImageView.swift b/Timelane/Components/ClickableImageView.swift
new file mode 100644
index 0000000..741a9e3
--- /dev/null
+++ b/Timelane/Components/ClickableImageView.swift
@@ -0,0 +1,43 @@
+//
+// ClickableImageView.swift
+// Timelane
+//
+// Created by Marin Todorov on 5/2/20.
+// Copyright © 2020 Underplot ltd. All rights reserved.
+//
+
+import Cocoa
+
+class ClickableImageView: NSImageView {
+
+ var trackingArea: NSTrackingArea?
+ var onClick: (() -> Void)?
+
+ // MARK: Mouse events
+ override func mouseEntered(with event: NSEvent) {
+ super.mouseEntered(with: event)
+ NSCursor.pointingHand.set()
+ }
+
+ override func mouseExited(with event: NSEvent) {
+ super.mouseExited(with: event)
+ NSCursor.arrow.set()
+ }
+
+ override func mouseUp(with event: NSEvent) {
+ super.mouseUp(with: event)
+ NSCursor.arrow.set()
+ onClick?()
+ }
+
+ override func updateTrackingAreas() {
+ if let ta = trackingArea {
+ removeTrackingArea(ta)
+ }
+ trackingArea = NSTrackingArea(rect: bounds,
+ options: [.activeInKeyWindow, .mouseEnteredAndExited],
+ owner: self,
+ userInfo: nil)
+ addTrackingArea(trackingArea!)
+ }
+}
diff --git a/Timelane/Info.plist b/Timelane/Info.plist
index 0fafc84..451ec09 100644
--- a/Timelane/Info.plist
+++ b/Timelane/Info.plist
@@ -34,6 +34,12 @@
NSSupportsSuddenTermination
+ SUAllowsAutomaticUpdates
+
+ SUEnableAutomaticChecks
+
+ SUFeedURL
+ https://raw.githubusercontent.com/icanzilb/Timelane/master/appcast/updates.xml
SUPublicEDKey
HoSWsdnW/bCm2U+7dS1TvxY9zxbasfHV1hMDni/XI3Q=
diff --git a/Timelane/Releases.swift b/Timelane/Releases.swift
deleted file mode 100644
index 17159e3..0000000
--- a/Timelane/Releases.swift
+++ /dev/null
@@ -1,84 +0,0 @@
-//
-// Releases.swift
-// Timelane
-//
-// Created by Marin Todorov on 1/26/20.
-// Copyright © 2020 Underplot ltd. All rights reserved.
-//
-
-import Foundation
-import AppKit
-
-struct Release: Codable {
- struct Version: Codable, Comparable, CustomStringConvertible {
- var description: String {
- return "\(major).\(minor).\(patch)"
- }
-
- static func < (lhs: Release.Version, rhs: Release.Version) -> Bool {
- guard lhs.major == rhs.major else { return lhs.major < rhs.major }
- guard lhs.minor == rhs.minor else { return lhs.minor < rhs.minor }
- return lhs.patch < rhs.patch
- }
-
- let major: Int
- let minor: Int
- let patch: Int
-
- enum DecodingError: Error {
- case invalidVersion
- }
-
- init(string: String) throws {
- let versionComponents = string.components(separatedBy: ".")
- guard versionComponents.count >= 2 else {
- throw DecodingError.invalidVersion
- }
- let patch = versionComponents.count >= 3 ? Int(versionComponents[2]) : 0
- self.init(major: Int(versionComponents[0]) ?? 0, minor: Int(versionComponents[1]) ?? 0, patch: patch ?? 0)
- }
-
- init(major: Int, minor: Int, patch: Int) {
- self.major = major
- self.minor = minor
- self.patch = patch
- }
- }
-
- let version: Version
- let name: String
- let isPrerelease: Bool
- let url: URL
-
- enum CodingKeys: String, CodingKey {
- case version = "tag_name"
- case name
- case isPrerelease = "prerelease"
- case url = "html_url"
- }
-
- init(from decoder: Decoder) throws {
- let values = try decoder.container(keyedBy: CodingKeys.self)
- let versionString = try values.decode(String.self, forKey: .version)
- version = try Version(string: versionString)
- name = try values.decode(String.self, forKey: .name)
- isPrerelease = try values.decode(Bool.self, forKey: .isPrerelease)
- url = try values.decode(URL.self, forKey: .url)
- }
-}
-
-struct Releases {
- func fetch(callback: @escaping (Release)->Void) {
- let currentVersionString = Bundle.main.object(forInfoDictionaryKey: "CFBundleShortVersionString") as! String
- let currentVersion = try! Release.Version(string: currentVersionString)
-
- URLSession.shared.dataTask(with: URL(string: "https://api.github.com/repos/icanzilb/Timelane/releases")!) { (data, response, error) in
- guard let data = data,
- let releases = try? JSONDecoder().decode([Release].self, from: data),
- let latestRelease = releases.first(where: { release -> Bool in
- return !release.isPrerelease && currentVersion < release.version
- }) else { return }
- callback(latestRelease)
- }.resume()
- }
-}
diff --git a/Timelane/Resources/Base.lproj/Main.storyboard b/Timelane/Resources/Base.lproj/Main.storyboard
index 53f899b..4bd71dc 100644
--- a/Timelane/Resources/Base.lproj/Main.storyboard
+++ b/Timelane/Resources/Base.lproj/Main.storyboard
@@ -26,6 +26,13 @@
+
+
Bug fixes
-
Fixed small typos
+ Improved installation instructions
]]>
- Mon, 13 Apr 2020 19:20:11 +0000
- Sat, 02 May 2020 09:20:11 +0000
+
10.14