Skip to content

Commit

Permalink
Target macOS 13
Browse files Browse the repository at this point in the history
  • Loading branch information
sindresorhus committed Aug 23, 2023
1 parent c645f80 commit a7b0936
Show file tree
Hide file tree
Showing 15 changed files with 128 additions and 103 deletions.
4 changes: 2 additions & 2 deletions .swiftlint.yml
Expand Up @@ -119,6 +119,7 @@ only_rules:
- static_operator
- strong_iboutlet
- superfluous_disable_command
- superfluous_else
- switch_case_alignment
- switch_case_on_newline
- syntactic_sugar
Expand Down Expand Up @@ -165,7 +166,6 @@ identifier_name:
min_length:
warning: 2
error: 2
validates_start_with_lowercase: false
allowed_symbols:
- '_'
excluded:
Expand All @@ -180,7 +180,7 @@ identifier_name:
- 'y2'
- 'z2'
deployment_target:
macOS_deployment_target: '12'
macOS_deployment_target: '13'
custom_rules:
no_nsrect:
regex: '\bNSRect\b'
Expand Down
6 changes: 3 additions & 3 deletions Gifski.xcodeproj/project.pbxproj
Expand Up @@ -638,7 +638,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 12.4;
MACOSX_DEPLOYMENT_TARGET = 13.3;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
Expand Down Expand Up @@ -695,7 +695,7 @@
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
MACOSX_DEPLOYMENT_TARGET = 12.4;
MACOSX_DEPLOYMENT_TARGET = 13.3;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SDKROOT = macosx;
Expand Down Expand Up @@ -833,7 +833,7 @@
repositoryURL = "https://github.com/firebase/firebase-ios-sdk";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 10.9.0;
minimumVersion = 10.14.0;
};
};
/* End XCRemoteSwiftPackageReference section */
Expand Down
7 changes: 6 additions & 1 deletion Gifski/App.swift
Expand Up @@ -5,11 +5,12 @@ import FirebaseCrashlytics
import DockProgress

/**
TODO when targeting macOS 13:
TODO when targeting macOS 14:
- Rewrite everything to use async/await, AsyncSequence, and actors.
- Rewrite `CheckerboardView` to use `SwiftUI.Canvas`.
- Make `final class Gifski` an actor.
- Use `@MainActor`
- Add button in the editor to preview the final GIF.
*/

@main
Expand Down Expand Up @@ -113,6 +114,10 @@ final class AppDelegate: NSObject, NSApplicationDelegate {
return .terminateNow
}

func applicationWillTerminate(_ notification: Notification) {
UNUserNotificationCenter.current().removeAllDeliveredNotifications()
}

func application(_ application: NSApplication, willPresentError error: Error) -> Error {
Crashlytics.recordNonFatalError(error: error)
return error
Expand Down
5 changes: 2 additions & 3 deletions Gifski/Base.lproj/MainMenu.xib
@@ -1,8 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="21507" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<document type="com.apple.InterfaceBuilder3.Cocoa.XIB" version="3.0" toolsVersion="21701" targetRuntime="MacOSX.Cocoa" propertyAccessControl="none" useAutolayout="YES" customObjectInstantitationMethod="direct">
<dependencies>
<deployment identifier="macosx"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="21507"/>
<plugIn identifier="com.apple.InterfaceBuilder.CocoaPlugin" version="21701"/>
</dependencies>
<objects>
<customObject id="-2" userLabel="File's Owner" customClass="NSApplication">
Expand Down
2 changes: 1 addition & 1 deletion Gifski/CheckerboardView.swift
Expand Up @@ -20,7 +20,7 @@ final class CheckerboardView: NSView {
super.draw(dirtyRect)

NSColor.Checkerboard.first.setFill()
dirtyRect.fill()
bounds.fill()

NSColor.Checkerboard.second.setFill()

Expand Down
1 change: 0 additions & 1 deletion Gifski/Constants.swift
Expand Up @@ -8,7 +8,6 @@ enum Constants {
}

extension NSColor {
static let themeColor = NSColor.controlAccentColor
static let progressCircleColor = NSColor(named: "ProgressCircleColor")!

enum Checkerboard {
Expand Down
6 changes: 3 additions & 3 deletions Gifski/ConversionCompletedViewController.swift
Expand Up @@ -65,10 +65,10 @@ final class ConversionCompletedViewController: NSViewController {
)
}

wrapperView.fadeIn(duration: 0.5, delay: 0.15, completion: nil)
wrapperView.fadeIn(duration: .seconds(0.5), delay: .seconds(0.15), completion: nil)
}

delay(seconds: 1) { [weak self] in
delay(.seconds(1)) { [weak self] in
guard let self else {
return
}
Expand Down Expand Up @@ -153,7 +153,7 @@ final class ConversionCompletedViewController: NSViewController {
)
}

delay(seconds: 1) { [weak self] in
delay(.seconds(1)) { [weak self] in
guard let self else {
return
}
Expand Down
2 changes: 1 addition & 1 deletion Gifski/ConversionViewController.swift
Expand Up @@ -140,7 +140,7 @@ final class ConversionViewController: NSViewController {
isRunning = false
DockProgress.resetProgress()

circularProgress.fadeOut(delay: 0.5) {
circularProgress.fadeOut(delay: .seconds(0.5)) {
completion?()
}
}
Expand Down
7 changes: 7 additions & 0 deletions Gifski/EditVideoViewController.swift
Expand Up @@ -331,6 +331,13 @@ final class EditVideoViewController: NSViewController {
estimatedFileSizeModel.updateEstimate()
}

Defaults.publisher(.outputSpeed)
.receive(on: DispatchQueue.main)
.sink { [weak self] _ in
self?.estimatedFileSizeModel.updateEstimate()
}
.store(in: &cancellables)

// We round it so that `29.970` becomes `30` for practical reasons.
let frameRate = videoMetadata.frameRate.rounded()

Expand Down
15 changes: 4 additions & 11 deletions Gifski/EstimatedFileSize.swift
Expand Up @@ -6,7 +6,7 @@ final class EstimatedFileSizeModel: ObservableObject {
@Published var error: Error?

// This is outside the scope of "file estimate", but it was easier to add this here than doing a separate SwiftUI view. This should be refactored out into a separate view when all of Gifski is SwiftUI.
@Published var duration: TimeInterval = 0
@Published var duration = Duration.zero

var estimatedFileSizeNaive: String {
Int(getNaiveEstimate()).formatted(.byteCount(style: .file))
Expand Down Expand Up @@ -72,7 +72,7 @@ final class EstimatedFileSizeModel: ObservableObject {
}

func updateEstimate() {
Debouncer.debounce(delay: 0.5, action: _estimateFileSize)
Debouncer.debounce(delay: .seconds(0.5), action: _estimateFileSize)
duration = getConversionSettings().gifDuration
}
}
Expand Down Expand Up @@ -112,15 +112,8 @@ struct EstimatedFileSizeView: View {
.overlay {
if model.error == nil {
HStack {
// TODO: Use the below instead when targeting macOS 13.
/*
Duration.seconds(duration)
.formatted(
.time(pattern: .minuteSecond(padMinuteToLength: 2, fractionalSecondsLength: 2))
.locale(locale)
)
*/
Text(DateComponentsFormatter.localizedStringPositionalWithFractionalSeconds(model.duration))
let formattedDuration = model.duration.formatted(.time(pattern: .minuteSecond(padMinuteToLength: 2, fractionalSecondsLength: 2)))
Text(formattedDuration)
.monospacedDigit()
.padding(.horizontal, 6)
.padding(.vertical, 3)
Expand Down
14 changes: 7 additions & 7 deletions Gifski/GIFGenerator.swift
Expand Up @@ -125,7 +125,7 @@ final class GIFGenerator {

let frameResult = processFrame(
for: imageResult,
at: startTime,
at: .seconds(startTime),
frameRate: fps,
conversion: conversion,
isEstimation: isEstimation,
Expand Down Expand Up @@ -292,7 +292,7 @@ final class GIFGenerator {
*/
private func processFrame(
for result: Result<AVAssetImageGenerator.CompletionHandlerResult, Swift.Error>,
at startTime: TimeInterval,
at startTime: Duration,
frameRate: Int,
conversion: Conversion,
isEstimation: Bool,
Expand All @@ -319,7 +319,7 @@ final class GIFGenerator {
// TODO: This is just a workaround. Look into the cause of this.
// https://github.com/sindresorhus/Gifski/pull/262
// Skip incorrect out-of-range frames.
if result.actualTime.seconds < startTime {
if result.actualTime.seconds < startTime.toTimeInterval {
return .success(true)
}

Expand All @@ -329,7 +329,7 @@ final class GIFGenerator {

try gifski?.addFrame(
result.image,
presentationTimestamp: max(0, result.actualTime.seconds - startTime)
presentationTimestamp: max(0, result.actualTime.seconds - startTime.toTimeInterval)
)

if conversion.bounce, !result.isFinished {
Expand Down Expand Up @@ -411,12 +411,12 @@ extension GIFGenerator {
var loop: Gifski.Loop
var bounce: Bool

var gifDuration: TimeInterval {
var gifDuration: Duration {
guard let duration = (timeRange ?? asset.firstVideoTrack?.timeRange.range)?.length else {
return 0
return .zero
}

return bounce ? (duration * 2) : duration
return .seconds(bounce ? (duration * 2) : duration)
}
}
}
Expand Down
24 changes: 12 additions & 12 deletions Gifski/TimeRemainingEstimator.swift
Expand Up @@ -7,17 +7,17 @@ final class TimeRemainingEstimator {
/**
The delay before revealing the estimated time remaining, allowing the estimation to stabilize.
*/
let bufferDuration: TimeInterval = 3
let bufferDuration = Duration.seconds(3)

/**
Don't show the estimate at all if the total time estimate (after it stabilizes) is less than this amount.
*/
let skipThreshold: TimeInterval = 10
let skipThreshold = Duration.seconds(10)

/**
Begin fade out when remaining time reaches this amount.
*/
let fadeOutThreshold: TimeInterval = 1
let fadeOutThreshold = Duration.seconds(1)

weak var progress: Progress? {
didSet {
Expand Down Expand Up @@ -82,22 +82,22 @@ final class TimeRemainingEstimator {
case .buffering:
if finishedBuffering {
return shouldShowEstimation ? .running : .done
} else {
return .buffering
}

return .buffering
case .running:
return secondsRemaining < fadeOutThreshold ? .done : .running
return remaining < fadeOutThreshold ? .done : .running
case .done:
return .done
}
}

private var finishedBuffering: Bool { secondsElapsed > bufferDuration }
private var shouldShowEstimation: Bool { secondsRemaining > skipThreshold }
private var secondsElapsed: TimeInterval { Date().timeIntervalSince(startTime) }
private var finishedBuffering: Bool { elapsed > bufferDuration }
private var shouldShowEstimation: Bool { remaining > skipThreshold }
private var elapsed: Duration { .seconds(Date.now.timeIntervalSince(startTime)) }

private var secondsRemaining: TimeInterval {
(secondsElapsed / percentComplete) * (1 - percentComplete)
private var remaining: Duration {
(elapsed / percentComplete) * (1 - percentComplete)
}

private let label: Label
Expand All @@ -110,7 +110,7 @@ final class TimeRemainingEstimator {
}

private var formattedTimeRemaining: String? {
let seconds = secondsRemaining.clamped(to: 1...)
let seconds = remaining.toTimeInterval.clamped(to: 1...)
elapsedTimeFormatter.allowedUnits = seconds < 60 ? .second : [.hour, .minute]
return elapsedTimeFormatter.string(from: seconds)
}
Expand Down

0 comments on commit a7b0936

Please sign in to comment.