diff --git a/Backports/Badge+Backport.swift b/Backports/Badge+Backport.swift index 4c12db48..e23ca5ad 100644 --- a/Backports/Badge+Backport.swift +++ b/Backports/Badge+Backport.swift @@ -5,7 +5,8 @@ extension Backport where Content: View { #if os(tvOS) content #else - if #available(iOS 15.0, macOS 12.0, tvOS 15.0, *) { + // swiftlint:disable:next deployment_target + if #available(iOS 15.0, macOS 12.0, *) { content.badge(count) } else { content diff --git a/Backports/ListRowSeparator+Backport.swift b/Backports/ListRowSeparator+Backport.swift index 78d17bc3..1ca100ee 100644 --- a/Backports/ListRowSeparator+Backport.swift +++ b/Backports/ListRowSeparator+Backport.swift @@ -3,13 +3,16 @@ import SwiftUI extension Backport where Content: View { @ViewBuilder func listRowSeparator(_ visible: Bool) -> some View { - if #available(iOS 15, macOS 13, *) { + #if !os(tvOS) + // swiftlint:disable:next deployment_target + if #available(iOS 15.0, macOS 12.0, *) { + content + .listRowSeparator(visible ? .visible : .hidden) + } else { + content + } + #else content - #if !os(tvOS) - .listRowSeparator(visible ? .visible : .hidden) - #endif - } else { - content - } + #endif } } diff --git a/Backports/PersistentSystemOverlays+Backport.swift b/Backports/PersistentSystemOverlays+Backport.swift index 8a07ef61..4872caf1 100644 --- a/Backports/PersistentSystemOverlays+Backport.swift +++ b/Backports/PersistentSystemOverlays+Backport.swift @@ -2,6 +2,7 @@ import SwiftUI extension Backport where Content: View { @ViewBuilder func persistentSystemOverlays(_ visible: Bool) -> some View { + // swiftlint:disable:next deployment_target if #available(iOS 16.0, macOS 13.0, tvOS 16.0, *) { content.persistentSystemOverlays(visible ? .visible : .hidden) } else { diff --git a/Backports/Refreshable+Backport.swift b/Backports/Refreshable+Backport.swift index 9f6da1c8..40bcb37c 100644 --- a/Backports/Refreshable+Backport.swift +++ b/Backports/Refreshable+Backport.swift @@ -2,6 +2,7 @@ import SwiftUI extension Backport where Content: View { @ViewBuilder func refreshable(action: @Sendable @escaping () async -> Void) -> some View { + // swiftlint:disable:next deployment_target if #available(iOS 15.0, macOS 12.0, tvOS 15.0, *) { content.refreshable(action: action) } else { diff --git a/Backports/ScrollContentBackground+Backport.swift b/Backports/ScrollContentBackground+Backport.swift index 9a26c6fe..f34fabf6 100644 --- a/Backports/ScrollContentBackground+Backport.swift +++ b/Backports/ScrollContentBackground+Backport.swift @@ -3,6 +3,7 @@ import SwiftUI extension Backport where Content: View { @ViewBuilder func scrollContentBackground(_ visibility: Bool) -> some View { + // swiftlint:disable:next deployment_target if #available(iOS 16.0, macOS 13.0, tvOS 16.0, *) { content.scrollContentBackground(visibility ? .visible : .hidden) } else { diff --git a/Backports/ScrollDismissesKeyboard+Backport.swift b/Backports/ScrollDismissesKeyboard+Backport.swift index adb8363b..00fd2a7d 100644 --- a/Backports/ScrollDismissesKeyboard+Backport.swift +++ b/Backports/ScrollDismissesKeyboard+Backport.swift @@ -3,6 +3,7 @@ import SwiftUI extension Backport where Content: View { @ViewBuilder func scrollDismissesKeyboardImmediately() -> some View { + // swiftlint:disable:next deployment_target if #available(iOS 16.0, macOS 13.0, tvOS 16.0, *) { content.scrollDismissesKeyboard(.immediately) } else { @@ -11,6 +12,7 @@ extension Backport where Content: View { } @ViewBuilder func scrollDismissesKeyboardInteractively() -> some View { + // swiftlint:disable:next deployment_target if #available(iOS 16.0, macOS 13.0, tvOS 16.0, *) { content.scrollDismissesKeyboard(.interactively) } else { diff --git a/Backports/Tint+Backport.swift b/Backports/Tint+Backport.swift index 05683362..438b40ea 100644 --- a/Backports/Tint+Backport.swift +++ b/Backports/Tint+Backport.swift @@ -2,7 +2,8 @@ import SwiftUI extension Backport where Content: View { @ViewBuilder func tint(_ color: Color?) -> some View { - if #available(iOS 15.0, macOS 12.0, tvOS 15.0, *) { + // swiftlint:disable:next deployment_target + if #available(iOS 16.0, macOS 13.0, tvOS 16.0, *) { content.tint(color) } else { content.foregroundColor(color) diff --git a/Backports/ToolbarBackground+Backport.swift b/Backports/ToolbarBackground+Backport.swift index f5cbfa07..ca04dad4 100644 --- a/Backports/ToolbarBackground+Backport.swift +++ b/Backports/ToolbarBackground+Backport.swift @@ -2,7 +2,8 @@ import SwiftUI extension Backport where Content: View { @ViewBuilder func toolbarBackground(_ color: Color) -> some View { - if #available(iOS 16, *) { + // swiftlint:disable:next deployment_target + if #available(iOS 16.0, macOS 13.0, tvOS 16.0, *) { content .toolbarBackground(color, for: .navigationBar) } else { @@ -11,7 +12,8 @@ extension Backport where Content: View { } @ViewBuilder func toolbarBackgroundVisibility(_ visible: Bool) -> some View { - if #available(iOS 16, *) { + // swiftlint:disable:next deployment_target + if #available(iOS 16.0, macOS 13.0, tvOS 16.0, *) { content .toolbarBackground(visible ? .visible : .hidden, for: .navigationBar) } else { diff --git a/Backports/ToolbarColorScheme+Backport.swift b/Backports/ToolbarColorScheme+Backport.swift index 59eef496..2d860c5a 100644 --- a/Backports/ToolbarColorScheme+Backport.swift +++ b/Backports/ToolbarColorScheme+Backport.swift @@ -2,7 +2,8 @@ import SwiftUI extension Backport where Content: View { @ViewBuilder func toolbarColorScheme(_ colorScheme: ColorScheme) -> some View { - if #available(iOS 16, *) { + // swiftlint:disable:next deployment_target + if #available(iOS 16.0, macOS 13.0, tvOS 16.0, *) { content .toolbarColorScheme(colorScheme, for: .navigationBar) } else { diff --git a/Model/Applications/InvidiousAPI.swift b/Model/Applications/InvidiousAPI.swift index 483822dc..67d1bc1e 100644 --- a/Model/Applications/InvidiousAPI.swift +++ b/Model/Applications/InvidiousAPI.swift @@ -502,7 +502,7 @@ final class InvidiousAPI: Service, ObservableObject, VideosAPI { publishedAt: publishedAt, likes: json["likeCount"].int, dislikes: json["dislikeCount"].int, - keywords: json["keywords"].arrayValue.compactMap { $0.string }, + keywords: json["keywords"].arrayValue.compactMap(\.string), streams: extractStreams(from: json), related: extractRelated(from: json), chapters: createChapters(from: description, thumbnails: json), diff --git a/Model/Applications/PipedAPI.swift b/Model/Applications/PipedAPI.swift index 4a0c5def..38e77c3d 100644 --- a/Model/Applications/PipedAPI.swift +++ b/Model/Applications/PipedAPI.swift @@ -695,10 +695,10 @@ final class PipedAPI: Service, ObservableObject, VideosAPI { for audioStream in allAudioStreams { let trackType = audioStream.dictionaryValue["audioTrackType"]?.string let trackLocale = audioStream.dictionaryValue["audioTrackLocale"]?.string - + // Create a unique key for this audio track combination let key = "\(trackType ?? "ORIGINAL")_\(trackLocale ?? "")" - + // Only keep the first (highest bitrate) stream for each unique track type/locale combination if audioTracksByType[key] == nil { audioTracksByType[key] = audioStream @@ -710,25 +710,26 @@ final class PipedAPI: Service, ObservableObject, VideosAPI { guard let url = audioStream.dictionaryValue["url"]?.url else { return nil } - + let trackType = audioStream.dictionaryValue["audioTrackType"]?.string let trackLocale = audioStream.dictionaryValue["audioTrackLocale"]?.string - + return Stream.AudioTrack( url: url, content: trackType, language: trackLocale ) - }.sorted { track1, track2 in + } + .sorted { track1, track2 in // Sort: ORIGINAL first, then DUBBED, then others if track1.content == "ORIGINAL" && track2.content != "ORIGINAL" { return true - } else if track1.content != "ORIGINAL" && track2.content == "ORIGINAL" { - return false - } else { - // If both are same type, sort by language - return (track1.language ?? "") < (track2.language ?? "") } + if track1.content != "ORIGINAL" && track2.content == "ORIGINAL" { + return false + } + // If both are same type, sort by language + return (track1.language ?? "") < (track2.language ?? "") } // Fallback to first audio stream if no tracks were extracted @@ -859,7 +860,7 @@ final class PipedAPI: Service, ObservableObject, VideosAPI { return Chapter(title: title, image: image, start: start) } } - + private func extractCaptions(from content: JSON) -> [Captions] { content["subtitles"].arrayValue.compactMap { details in guard let url = details["url"].url, @@ -867,13 +868,13 @@ final class PipedAPI: Service, ObservableObject, VideosAPI { let label = details["name"].string, var components = URLComponents(url: url, resolvingAgainstBaseURL: false) else { return nil } - + components.queryItems = components.queryItems?.map { item in item.name == "fmt" ? URLQueryItem(name: "fmt", value: "srt") : item } - + guard let newUrl = components.url else { return nil } - + return Captions(label: label, code: code, url: newUrl) } } diff --git a/Model/Import Export Settings/Exporters/BrowsingSettingsGroupExporter.swift b/Model/Import Export Settings/Exporters/BrowsingSettingsGroupExporter.swift index 1b2284c4..eb79f3ac 100644 --- a/Model/Import Export Settings/Exporters/BrowsingSettingsGroupExporter.swift +++ b/Model/Import Export Settings/Exporters/BrowsingSettingsGroupExporter.swift @@ -12,7 +12,7 @@ final class BrowsingSettingsGroupExporter: SettingsGroupExporter { "widgetsSettings": Defaults[.widgetsSettings].compactMap { widgetSettingsJSON($0) }, "startupSection": Defaults[.startupSection].rawValue, "showSearchSuggestions": Defaults[.showSearchSuggestions], - "visibleSections": Defaults[.visibleSections].compactMap { $0.rawValue }, + "visibleSections": Defaults[.visibleSections].compactMap(\.rawValue), "showOpenActionsToolbarItem": Defaults[.showOpenActionsToolbarItem], "accountPickerDisplaysAnonymousAccounts": Defaults[.accountPickerDisplaysAnonymousAccounts], "showUnwatchedFeedBadges": Defaults[.showUnwatchedFeedBadges], diff --git a/Model/Import Export Settings/Importers/SponsorBlockSettingsGroupImporter.swift b/Model/Import Export Settings/Importers/SponsorBlockSettingsGroupImporter.swift index 98e09b34..1321bc76 100644 --- a/Model/Import Export Settings/Importers/SponsorBlockSettingsGroupImporter.swift +++ b/Model/Import Export Settings/Importers/SponsorBlockSettingsGroupImporter.swift @@ -10,7 +10,7 @@ struct SponsorBlockSettingsGroupImporter { } if let sponsorBlockCategories = json["sponsorBlockCategories"].array { - Defaults[.sponsorBlockCategories] = Set(sponsorBlockCategories.compactMap { $0.string }) + Defaults[.sponsorBlockCategories] = Set(sponsorBlockCategories.compactMap(\.string)) } if let sponsorBlockColors = json["sponsorBlockColors"].dictionary { diff --git a/Model/Player/Backends/MPVBackend.swift b/Model/Player/Backends/MPVBackend.swift index d2aae712..8db27161 100644 --- a/Model/Player/Backends/MPVBackend.swift +++ b/Model/Player/Backends/MPVBackend.swift @@ -185,7 +185,7 @@ final class MPVBackend: PlayerBackend { var audioSampleRate: String { client?.audioSampleRate ?? "unknown" } - + var availableAudioTracks: [Stream.AudioTrack] { stream?.audioTracks ?? [] } @@ -331,7 +331,7 @@ final class MPVBackend: PlayerBackend { if stream.selectedAudioTrackIndex >= stream.audioTracks.count { stream.selectedAudioTrackIndex = 0 } - + stream.audioAsset = AVURLAsset(url: stream.audioTracks[stream.selectedAudioTrackIndex].url) let fileToLoad = self.model.musicMode ? stream.audioAsset.url : stream.videoAsset.url let audioTrack = self.model.musicMode ? nil : stream.audioAsset.url @@ -343,7 +343,7 @@ final class MPVBackend: PlayerBackend { } else { // Fallback for streams without separate audio tracks (e.g., single asset streams) let fileToLoad = stream.videoAsset.url - + client.loadFile(fileToLoad, bitrate: stream.bitrate, kind: stream.kind, sub: captions?.url, time: time, forceSeekable: stream.kind == .hls) { [weak self] _ in self?.isLoadingVideo = true self?.pause() @@ -754,7 +754,7 @@ final class MPVBackend: PlayerBackend { func switchAudioTrack(to index: Int) { guard let stream, let video else { return } - + // Validate the index is within bounds guard index >= 0 && index < stream.audioTracks.count else { logger.error("Invalid audio track index: \(index), available tracks: \(stream.audioTracks.count)") diff --git a/Model/Player/Backends/MPVClient.swift b/Model/Player/Backends/MPVClient.swift index 4afaf502..afda9c8e 100644 --- a/Model/Player/Backends/MPVClient.swift +++ b/Model/Player/Backends/MPVClient.swift @@ -354,7 +354,7 @@ final class MPVClient: ObservableObject { func areSubtitlesAdded() async -> Bool { guard !mpv.isNil else { return false } - let trackCount = await Task(operation: { getInt("track-list/count") }).value + let trackCount = await Task { getInt("track-list/count") }.value guard trackCount > 0 else { return false } for index in 0 ..< trackCount { diff --git a/Model/Player/PlayerModel.swift b/Model/Player/PlayerModel.swift index 284b8c27..30c5b24d 100644 --- a/Model/Player/PlayerModel.swift +++ b/Model/Player/PlayerModel.swift @@ -257,6 +257,7 @@ final class PlayerModel: ObservableObject { pipController = .init(playerLayer: avPlayerBackend.playerLayer) pipController?.delegate = pipDelegate #if os(iOS) + // swiftlint:disable:next deployment_target if #available(iOS 14.2, *) { pipController?.canStartPictureInPictureAutomaticallyFromInline = true } @@ -1037,7 +1038,7 @@ final class PlayerModel: ObservableObject { #else func handleEnterForeground() { DispatchQueue.global(qos: .userInteractive).async { [weak self] in - guard let self = self else { return } + guard let self else { return } if !self.musicMode, self.activeBackend == .mpv { self.mpvBackend.addVideoTrackFromStream() @@ -1329,6 +1330,7 @@ final class PlayerModel: ObservableObject { // Check availability for iOS 14.5 or newer to handle interruption reason // Currently only for debugging purpose #if os(iOS) + // swiftlint:disable:next deployment_target if #available(iOS 14.5, *) { // Extract the interruption reason, if available if let reasonValue = info[AVAudioSessionInterruptionReasonKey] as? UInt, diff --git a/Model/Player/PlayerQueue.swift b/Model/Player/PlayerQueue.swift index 9609a2f9..e75bf1fe 100644 --- a/Model/Player/PlayerQueue.swift +++ b/Model/Player/PlayerQueue.swift @@ -321,7 +321,7 @@ extension PlayerModel { } restoredQueue.append(contentsOf: Defaults[.queue]) - queue = restoredQueue.compactMap { $0 } + queue = restoredQueue.compactMap(\.self) queue.forEach { loadQueueVideoDetails($0) } } diff --git a/Model/Player/PlayerTVMenu.swift b/Model/Player/PlayerTVMenu.swift index 72366951..d9739eb6 100644 --- a/Model/Player/PlayerTVMenu.swift +++ b/Model/Player/PlayerTVMenu.swift @@ -100,7 +100,7 @@ extension PlayerModel { streamsMenu, playbackModeMenu, switchToMPVAction - ].compactMap { $0 } + ].compactMap(\.self) #endif } } diff --git a/Model/Store.swift b/Model/Store.swift index 67e0eee8..52c9b4d4 100644 --- a/Model/Store.swift +++ b/Model/Store.swift @@ -4,7 +4,7 @@ import Siesta final class Store: ResourceObserver, ObservableObject { @Published private var all: Data? - var collection: Data { all ?? ([item].compactMap { $0 } as! Data) } + var collection: Data { all ?? ([item].compactMap(\.self) as! Data) } var item: Data? { all } init(_ data: Data? = nil) { diff --git a/Model/Stream.swift b/Model/Stream.swift index e52646e9..663fcf2c 100644 --- a/Model/Stream.swift +++ b/Model/Stream.swift @@ -191,21 +191,21 @@ class Stream: Equatable, Hashable, Identifiable { return .unknown } } - + struct AudioTrack: Hashable, Identifiable { let id = UUID().uuidString let url: URL let content: String? let language: String? - + var displayLanguage: String { LanguageCodes(rawValue: language ?? "")?.description.capitalized ?? language ?? "Unknown" } - + var description: String { "\(displayLanguage) (\(content ?? "Unknown"))" } - + var isDubbed: Bool { content?.lowercased().starts(with: "dubbed") ?? false } diff --git a/Model/UnwatchedFeedCountModel.swift b/Model/UnwatchedFeedCountModel.swift index 00575946..91595d5b 100644 --- a/Model/UnwatchedFeedCountModel.swift +++ b/Model/UnwatchedFeedCountModel.swift @@ -9,7 +9,6 @@ final class UnwatchedFeedCountModel: ObservableObject { private var accounts = AccountsModel.shared - // swiftlint:disable empty_count var unwatchedText: Text? { if let account = accounts.current, !account.anonymous, @@ -32,5 +31,4 @@ final class UnwatchedFeedCountModel: ObservableObject { } return nil } - // swiftlint:enable empty_count } diff --git a/Model/Video.swift b/Model/Video.swift index 2eb32c58..db8396c5 100644 --- a/Model/Video.swift +++ b/Model/Video.swift @@ -155,7 +155,7 @@ struct Video: Identifiable, Equatable, Hashable { "description": description ?? "", "genre": genre ?? "", "channel": channel.json.object, - "thumbnails": thumbnails.compactMap { $0.json.object }, + "thumbnails": thumbnails.compactMap(\.json.object), "indexID": indexID ?? "", "live": live, "upcoming": upcoming, diff --git a/Shared/Channels/ChannelVideosView.swift b/Shared/Channels/ChannelVideosView.swift index 50645ca0..d1d02f5c 100644 --- a/Shared/Channels/ChannelVideosView.swift +++ b/Shared/Channels/ChannelVideosView.swift @@ -182,17 +182,13 @@ struct ChannelVideosView: View { #endif return Group { - if #available(macOS 12.0, *) { - content - #if os(tvOS) - .background(Color.background(scheme: colorScheme)) - #endif - #if !os(iOS) - .focusScope(focusNamespace) - #endif - } else { - content - } + content + #if os(tvOS) + .background(Color.background(scheme: colorScheme)) + #endif + #if !os(iOS) + .focusScope(focusNamespace) + #endif } } diff --git a/Shared/Constants.swift b/Shared/Constants.swift index 64e89ab5..e21b41c8 100644 --- a/Shared/Constants.swift +++ b/Shared/Constants.swift @@ -150,10 +150,8 @@ enum Constants { let iOS15 = [5] let iconName = "go\(type).\(interval)" - if #available(iOS 15, macOS 12, *) { - if iOS15.contains(interval) { - return iconName - } + if iOS15.contains(interval) { + return iconName } if allVersions.contains(interval) { diff --git a/Shared/Defaults.swift b/Shared/Defaults.swift index 98d44db8..6b2b0098 100644 --- a/Shared/Defaults.swift +++ b/Shared/Defaults.swift @@ -713,7 +713,7 @@ enum SponsorBlockColors: String { case music_offtopic = "#FF9900" // Orange // Define all cases, can be used to iterate over the colors - static let allCases: [SponsorBlockColors] = [Self.sponsor, Self.selfpromo, Self.interaction, Self.intro, Self.outro, Self.preview, Self.filler, Self.music_offtopic] + static let allCases: [Self] = [Self.sponsor, Self.selfpromo, Self.interaction, Self.intro, Self.outro, Self.preview, Self.filler, Self.music_offtopic] // Create a dictionary with the category names as keys and colors as values static let dictionary: [String: String] = { diff --git a/Shared/Home/FavoriteItemView.swift b/Shared/Home/FavoriteItemView.swift index d2fa1861..9002c19b 100644 --- a/Shared/Home/FavoriteItemView.swift +++ b/Shared/Home/FavoriteItemView.swift @@ -200,19 +200,16 @@ struct FavoriteItemView: View { let limit = favoritesModel.limit(item) if item.section == .history { return Array(visibleWatches.prefix(limit).map { ContentItem(video: player.historyVideo($0.videoID) ?? $0.video) }) - } else { - var result = [ContentItem]() - result.reserveCapacity(min(store.contentItems.count, limit)) - for contentItem in store.contentItems { - if itemVisible(contentItem) { - result.append(contentItem) - if result.count >= limit { - break - } - } - } - return result } + var result = [ContentItem]() + result.reserveCapacity(min(store.contentItems.count, limit)) + for contentItem in store.contentItems where itemVisible(contentItem) { + result.append(contentItem) + if result.count >= limit { + break + } + } + return result } func itemVisible(_ item: ContentItem) -> Bool { diff --git a/Shared/Navigation/AccountViewButton.swift b/Shared/Navigation/AccountViewButton.swift index 4089c6a9..d626e7b5 100644 --- a/Shared/Navigation/AccountViewButton.swift +++ b/Shared/Navigation/AccountViewButton.swift @@ -15,14 +15,10 @@ struct AccountViewButton: View { } label: { HStack(spacing: 6) { if !accountPickerDisplaysUsername || !(model.current?.isPublic ?? true) { - if #available(iOS 15, macOS 12, *) { - if let name = model.current?.app?.rawValue.capitalized { - Image(name) - .resizable() - .frame(width: accountImageSize, height: accountImageSize) - } else { - Image(systemName: "globe") - } + if let name = model.current?.app?.rawValue.capitalized { + Image(name) + .resizable() + .frame(width: accountImageSize, height: accountImageSize) } else { Image(systemName: "globe") } diff --git a/Shared/Player/AppleAVPlayerView.swift b/Shared/Player/AppleAVPlayerView.swift index ac5d524d..cbd83be4 100644 --- a/Shared/Player/AppleAVPlayerView.swift +++ b/Shared/Player/AppleAVPlayerView.swift @@ -114,6 +114,7 @@ import SwiftUI func setupController() { controller.delegate = PlayerModel.shared.appleAVPlayerViewControllerDelegate controller.allowsPictureInPicturePlayback = true + // swiftlint:disable:next deployment_target if #available(iOS 14.2, *) { controller.canStartPictureInPictureAutomaticallyFromInline = true } diff --git a/Shared/Player/Controls/ControlBackgroundModifier.swift b/Shared/Player/Controls/ControlBackgroundModifier.swift index e2bbd315..fbebec97 100644 --- a/Shared/Player/Controls/ControlBackgroundModifier.swift +++ b/Shared/Player/Controls/ControlBackgroundModifier.swift @@ -7,6 +7,7 @@ struct ControlBackgroundModifier: ViewModifier { func body(content: Content) -> some View { if enabled { + // swiftlint:disable:next deployment_target if #available(iOS 15, macOS 12, *) { content .background(.thinMaterial) diff --git a/Shared/Player/PlaybackSettingsPresentationDetents+Backport.swift b/Shared/Player/PlaybackSettingsPresentationDetents+Backport.swift index 8688fa16..bf464f41 100644 --- a/Shared/Player/PlaybackSettingsPresentationDetents+Backport.swift +++ b/Shared/Player/PlaybackSettingsPresentationDetents+Backport.swift @@ -3,6 +3,7 @@ import SwiftUI extension Backport where Content: View { @ViewBuilder func playbackSettingsPresentationDetents() -> some View { + // swiftlint:disable:next deployment_target if #available(iOS 16.0, macOS 13.0, tvOS 16.0, *) { content .presentationDetents([.height(400), .large]) diff --git a/Shared/Player/PlayerLayerView.swift b/Shared/Player/PlayerLayerView.swift index ff2df0a9..ff2d865c 100644 --- a/Shared/Player/PlayerLayerView.swift +++ b/Shared/Player/PlayerLayerView.swift @@ -12,10 +12,6 @@ import Foundation wantsLayer = true }} - override init(frame frameRect: CGRect) { - super.init(frame: frameRect) - } - override func makeBackingLayer() -> CALayer { player.avPlayerBackend.playerLayer } @@ -28,12 +24,13 @@ import Foundation final class PlayerLayerView: UIView { var player: PlayerModel { .shared } + private var layerAdded = false + + // swiftlint:disable:next unneeded_override override init(frame: CGRect) { super.init(frame: frame) } - private var layerAdded = false - @available(*, unavailable) required init?(coder _: NSCoder) { fatalError("init(coder:) has not been implemented") diff --git a/Shared/Player/Video Details/CommentView.swift b/Shared/Player/Video Details/CommentView.swift index c017e432..514d51b4 100644 --- a/Shared/Player/Video Details/CommentView.swift +++ b/Shared/Player/Video Details/CommentView.swift @@ -7,7 +7,7 @@ import SwiftUI struct CommentView: View { let comment: Comment @Binding var repliesID: Comment.ID? - var availableWidth: CGFloat + var availableWidth: Double @State private var subscribed = false @@ -228,27 +228,20 @@ struct CommentView: View { private var commentText: some View { Group { let rawText = comment.text - if #available(iOS 15.0, macOS 12.0, *) { - #if os(iOS) - ActiveLabelCommentRepresentable( - text: rawText, - availableWidth: availableWidth - ) - #elseif os(macOS) - Text(rawText) - .font(.system(size: 14)) - .lineSpacing(3) - .fixedSize(horizontal: false, vertical: true) - .textSelection(.enabled) - #else - Text(comment.text) - #endif - } else { + #if os(iOS) + ActiveLabelCommentRepresentable( + text: rawText, + availableWidth: availableWidth + ) + #elseif os(macOS) Text(rawText) - .font(.system(size: 15)) + .font(.system(size: 14)) .lineSpacing(3) .fixedSize(horizontal: false, vertical: true) - } + .textSelection(.enabled) + #else + Text(comment.text) + #endif } } @@ -263,7 +256,7 @@ struct CommentView: View { #if os(iOS) struct ActiveLabelCommentRepresentable: UIViewRepresentable { var text: String - var availableWidth: CGFloat + var availableWidth: Double @State private var label = ActiveLabel() diff --git a/Shared/Player/Video Details/CommentsView.swift b/Shared/Player/Video Details/CommentsView.swift index 4a938182..b1cf41e4 100644 --- a/Shared/Player/Video Details/CommentsView.swift +++ b/Shared/Player/Video Details/CommentsView.swift @@ -38,6 +38,7 @@ struct CommentsView: View { struct CommentsView_Previews: PreviewProvider { static var previews: some View { + // swiftlint:disable:next deployment_target if #available(iOS 15.0, macOS 12.0, tvOS 15.0, *) { CommentsView() .previewInterfaceOrientation(.landscapeRight) diff --git a/Shared/Player/Video Details/InspectorView.swift b/Shared/Player/Video Details/InspectorView.swift index 997d27d7..ae1b6817 100644 --- a/Shared/Player/Video Details/InspectorView.swift +++ b/Shared/Player/Video Details/InspectorView.swift @@ -80,14 +80,16 @@ struct InspectorView: View { .foregroundColor(.secondary) Spacer() let value = Text(value).lineLimit(1) - if #available(iOS 15.0, macOS 12.0, *) { + #if !os(tvOS) + // swiftlint:disable:next deployment_target + if #available(iOS 15.0, macOS 12.0, *) { + value.textSelection(.enabled) + } else { + value + } + #else value - #if !os(tvOS) - .textSelection(.enabled) - #endif - } else { - value - } + #endif } .font(.caption) } diff --git a/Shared/Player/Video Details/VideoDescription.swift b/Shared/Player/Video Details/VideoDescription.swift index c0d5945b..737ee929 100644 --- a/Shared/Player/Video Details/VideoDescription.swift +++ b/Shared/Player/Video Details/VideoDescription.swift @@ -58,28 +58,19 @@ struct VideoDescription: View { @ViewBuilder var textDescription: some View { #if canImport(AppKit) - Group { - if #available(macOS 12, *) { - DescriptionWithLinks(description: description, detailsSize: detailsSize) - .frame(maxWidth: .infinity, alignment: .leading) - .lineLimit(expand ? 500 : collapsedLinesDescription) - .textSelection(.enabled) - } else { - Text(description) - .frame(maxWidth: .infinity, alignment: .leading) - .lineLimit(expand ? 500 : collapsedLinesDescription) - } - } - .multilineTextAlignment(.leading) - .font(.system(size: 14)) - .lineSpacing(3) - .allowsHitTesting(expand) + DescriptionWithLinks(description: description, detailsSize: detailsSize) + .frame(maxWidth: .infinity, alignment: .leading) + .lineLimit(expand ? 500 : collapsedLinesDescription) + .textSelection(.enabled) + .multilineTextAlignment(.leading) + .font(.system(size: 14)) + .lineSpacing(3) + .allowsHitTesting(expand) #endif } // If possibe convert URLs to clickable links #if canImport(AppKit) - @available(macOS 12, *) struct DescriptionWithLinks: View { let description: String let detailsSize: CGSize? diff --git a/Shared/Player/Video Details/VideoDetails.swift b/Shared/Player/Video Details/VideoDetails.swift index 33ee0927..131bb3b2 100644 --- a/Shared/Player/Video Details/VideoDetails.swift +++ b/Shared/Player/Video Details/VideoDetails.swift @@ -49,6 +49,7 @@ struct VideoDetails: View { .padding(.trailing, 5) // TODO: when setting tvOS minimum to 16, the platform modifier can be removed #if !os(tvOS) + .accessibilityAddTraits(.isButton) .simultaneousGesture( TapGesture() // Ensures the button tap is recognized ) @@ -63,11 +64,11 @@ struct VideoDetails: View { .lineLimit(1) // TODO: when setting tvOS minimum to 16, the platform modifier can be removed #if !os(tvOS) + .accessibilityAddTraits(.isButton) .onTapGesture { guard let channel = video?.channel else { return } NavigationModel.shared.openChannel(channel, navigationStyle: .sidebar) } - .accessibilityAddTraits(.isButton) #endif } else if model.videoBeingOpened != nil { Text("Yattee") diff --git a/Shared/Playlists/PlaylistsView.swift b/Shared/Playlists/PlaylistsView.swift index 9f927ee1..f093edf3 100644 --- a/Shared/Playlists/PlaylistsView.swift +++ b/Shared/Playlists/PlaylistsView.swift @@ -151,9 +151,9 @@ struct PlaylistsView: View { } #else .onReceive(NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification)) { _ in - model.load() - loadResource() - } + model.load() + loadResource() + } #endif #if !os(tvOS) .background( diff --git a/Shared/Search/FocusableSearchTextField.swift b/Shared/Search/FocusableSearchTextField.swift index 1fa32fe2..c2d2ab8f 100644 --- a/Shared/Search/FocusableSearchTextField.swift +++ b/Shared/Search/FocusableSearchTextField.swift @@ -2,7 +2,6 @@ import Repeat import SwiftUI import SwiftUIIntrospect -@available(iOS 15.0, macOS 12, *) struct FocusableSearchTextField: View { @ObservedObject private var state = SearchModel.shared diff --git a/Shared/Search/SearchView.swift b/Shared/Search/SearchView.swift index 0796eddf..65f43696 100644 --- a/Shared/Search/SearchView.swift +++ b/Shared/Search/SearchView.swift @@ -57,13 +57,8 @@ struct SearchView: View { .environment(\.listingStyle, searchListingStyle) .toolbar { ToolbarItem(placement: .principal) { - if #available(iOS 15, *) { - FocusableSearchTextField() - .frame(width: searchFieldWidth(geometry.size.width)) - } else { - SearchTextField() - .frame(width: searchFieldWidth(geometry.size.width)) - } + FocusableSearchTextField() + .frame(width: searchFieldWidth(geometry.size.width)) } ToolbarItem(placement: .navigationBarTrailing) { searchMenu @@ -227,11 +222,7 @@ struct SearchView: View { filtersMenu } - if #available(macOS 12, *) { - FocusableSearchTextField() - } else { - SearchTextField() - } + FocusableSearchTextField() } } .onAppear { @@ -650,21 +641,21 @@ struct SearchView: View { } #if os(iOS) - private func searchFieldWidth(_ viewWidth: CGFloat) -> CGFloat { + private func searchFieldWidth(_ viewWidth: Double) -> Double { // Base padding for internal SearchTextField padding (16pt each side = 32 total) - var totalDeduction: CGFloat = 32 - + var totalDeduction: Double = 32 + // Add space for trailing menu button totalDeduction += 44 - + // Add space for sidebar toggle button if in sidebar navigation style if navigationStyle == .sidebar { totalDeduction += 44 } - + // Minimum width to ensure usability - let minWidth: CGFloat = 200 - + let minWidth: Double = 200 + return max(minWidth, viewWidth - totalDeduction) } #endif diff --git a/Shared/Settings/AdvancedSettings.swift b/Shared/Settings/AdvancedSettings.swift index 9e374c2d..6d67ecb6 100644 --- a/Shared/Settings/AdvancedSettings.swift +++ b/Shared/Settings/AdvancedSettings.swift @@ -84,13 +84,14 @@ struct AdvancedSettings: View { Text("cache-pause-initial") #if !os(tvOS) Image(systemName: "link") - .accessibilityAddTraits([.isButton, .isLink]) .font(.footnote) #if os(iOS) + .accessibilityAddTraits([.isButton, .isLink]) .onTapGesture { UIApplication.shared.open(URL(string: "https://mpv.io/manual/stable/#options-cache-pause-initial")!) } #elseif os(macOS) + .accessibilityAddTraits([.isButton, .isLink]) .onTapGesture { NSWorkspace.shared.open(URL(string: "https://mpv.io/manual/stable/#options-cache-pause-initial")!) } @@ -104,13 +105,14 @@ struct AdvancedSettings: View { Text("cache-secs") #if !os(tvOS) Image(systemName: "link") - .accessibilityAddTraits([.isButton, .isLink]) .font(.footnote) #if os(iOS) + .accessibilityAddTraits([.isButton, .isLink]) .onTapGesture { UIApplication.shared.open(URL(string: "https://mpv.io/manual/stable/#options-cache-secs")!) } #elseif os(macOS) + .accessibilityAddTraits([.isButton, .isLink]) .onTapGesture { NSWorkspace.shared.open(URL(string: "https://mpv.io/manual/stable/#options-cache-secs")!) } @@ -130,13 +132,14 @@ struct AdvancedSettings: View { Text("cache-pause-wait") #if !os(tvOS) Image(systemName: "link") - .accessibilityAddTraits([.isButton, .isLink]) .font(.footnote) #if os(iOS) + .accessibilityAddTraits([.isButton, .isLink]) .onTapGesture { UIApplication.shared.open(URL(string: "https://mpv.io/manual/stable/#options-cache-pause-wait")!) } #elseif os(macOS) + .accessibilityAddTraits([.isButton, .isLink]) .onTapGesture { NSWorkspace.shared.open(URL(string: "https://mpv.io/manual/stable/#options-cache-pause-wait")!) } @@ -157,13 +160,14 @@ struct AdvancedSettings: View { Text("deinterlace") #if !os(tvOS) Image(systemName: "link") - .accessibilityAddTraits([.isButton, .isLink]) .font(.footnote) #if os(iOS) + .accessibilityAddTraits([.isButton, .isLink]) .onTapGesture { UIApplication.shared.open(URL(string: "https://mpv.io/manual/stable/#options-deinterlace")!) } #elseif os(macOS) + .accessibilityAddTraits([.isButton, .isLink]) .onTapGesture { NSWorkspace.shared.open(URL(string: "https://mpv.io/manual/stable/#options-deinterlace")!) } @@ -178,13 +182,14 @@ struct AdvancedSettings: View { Text("initial-audio-sync") #if !os(tvOS) Image(systemName: "link") - .accessibilityAddTraits([.isButton, .isLink]) .font(.footnote) #if os(iOS) + .accessibilityAddTraits([.isButton, .isLink]) .onTapGesture { UIApplication.shared.open(URL(string: "https://mpv.io/manual/stable/#options-initial-audio-sync")!) } #elseif os(macOS) + .accessibilityAddTraits([.isButton, .isLink]) .onTapGesture { NSWorkspace.shared.open(URL(string: "https://mpv.io/manual/stable/#options-initial-audio-sync")!) } @@ -199,13 +204,14 @@ struct AdvancedSettings: View { #if !os(tvOS) Image(systemName: "link") - .accessibilityAddTraits([.isButton, .isLink]) .font(.footnote) #if os(iOS) + .accessibilityAddTraits([.isButton, .isLink]) .onTapGesture { UIApplication.shared.open(URL(string: "https://mpv.io/manual/stable/#options-hwdec")!) } #elseif os(macOS) + .accessibilityAddTraits([.isButton, .isLink]) .onTapGesture { NSWorkspace.shared.open(URL(string: "https://mpv.io/manual/stable/#options-hwdec")!) } @@ -228,13 +234,14 @@ struct AdvancedSettings: View { #if !os(tvOS) Image(systemName: "link") - .accessibilityAddTraits([.isButton, .isLink]) .font(.footnote) #if os(iOS) + .accessibilityAddTraits([.isButton, .isLink]) .onTapGesture { UIApplication.shared.open(URL(string: "https://mpv.io/manual/stable/#options-demuxer-lavf-probe-info")!) } #elseif os(macOS) + .accessibilityAddTraits([.isButton, .isLink]) .onTapGesture { NSWorkspace.shared.open(URL(string: "https://mpv.io/manual/stable/#options-demuxer-lavf-probe-info")!) } diff --git a/Shared/Settings/HistorySettings.swift b/Shared/Settings/HistorySettings.swift index f6e5d8e4..c234cc00 100644 --- a/Shared/Settings/HistorySettings.swift +++ b/Shared/Settings/HistorySettings.swift @@ -221,12 +221,12 @@ struct HistorySettings: View { .labelStyle(.iconOnly) .padding(7) .foregroundColor(limitRecents ? .accentColor : .gray) - .accessibilityAddTraits(.isButton) #if os(iOS) .frame(minHeight: 35) .background(RoundedRectangle(cornerRadius: 4).strokeBorder(lineWidth: 1).foregroundColor(.accentColor)) #endif .contentShape(Rectangle()) + .accessibilityAddTraits(.isButton) .onTapGesture { value.wrappedValue -= 1 } @@ -253,11 +253,11 @@ struct HistorySettings: View { .labelStyle(.iconOnly) .padding(7) .foregroundColor(limitRecents ? .accentColor : .gray) - .accessibilityAddTraits(.isButton) #if os(iOS) .background(RoundedRectangle(cornerRadius: 4).strokeBorder(lineWidth: 1).foregroundColor(.accentColor)) #endif .contentShape(Rectangle()) + .accessibilityAddTraits(.isButton) .onTapGesture { value.wrappedValue += 1 } diff --git a/Shared/Settings/HomeSettings.swift b/Shared/Settings/HomeSettings.swift index 5472ee6c..ffd91b68 100644 --- a/Shared/Settings/HomeSettings.swift +++ b/Shared/Settings/HomeSettings.swift @@ -287,11 +287,11 @@ struct FavoriteItemEditorButton: View { .padding(7) .frame(minWidth: 40, minHeight: 40) .foregroundColor(color) - .accessibilityAddTraits(.isButton) #if os(iOS) .background(RoundedRectangle(cornerRadius: 4).strokeBorder(lineWidth: 1).foregroundColor(color)) #endif .contentShape(Rectangle()) + .accessibilityAddTraits(.isButton) .onTapGesture(perform: onTapGesture) #endif } diff --git a/Shared/Settings/PlayerControlsSettings.swift b/Shared/Settings/PlayerControlsSettings.swift index d0dbfbae..b800acf6 100644 --- a/Shared/Settings/PlayerControlsSettings.swift +++ b/Shared/Settings/PlayerControlsSettings.swift @@ -156,7 +156,7 @@ struct PlayerControlsSettings: View { Text("System controls buttons") .font(.headline) .padding(.vertical, 8) - + Button(action: { systemControlsCommands = .seek }) { HStack { Text(labelText("Seek".localized())) @@ -170,8 +170,8 @@ struct PlayerControlsSettings: View { } .buttonStyle(.plain) .padding(.vertical, 4) - - Button(action: { + + Button(action: { systemControlsCommands = .restartAndAdvanceToNext player.updateRemoteCommandCenter() }) { @@ -301,12 +301,12 @@ struct PlayerControlsSettings: View { .labelStyle(.iconOnly) .padding(7) .foregroundColor(.accentColor) - .accessibilityAddTraits(.isButton) #if os(iOS) .frame(minHeight: 35) .background(RoundedRectangle(cornerRadius: 4).strokeBorder(lineWidth: 1).foregroundColor(.accentColor)) #endif .contentShape(Rectangle()) + .accessibilityAddTraits(.isButton) .onTapGesture { var intValue = Int(value.wrappedValue) ?? 10 intValue -= 5 @@ -337,11 +337,11 @@ struct PlayerControlsSettings: View { .labelStyle(.iconOnly) .padding(7) .foregroundColor(.accentColor) - .accessibilityAddTraits(.isButton) #if os(iOS) .background(RoundedRectangle(cornerRadius: 4).strokeBorder(lineWidth: 1).foregroundColor(.accentColor)) #endif .contentShape(Rectangle()) + .accessibilityAddTraits(.isButton) .onTapGesture { var intValue = Int(value.wrappedValue) ?? 10 intValue += 5 diff --git a/Shared/Settings/QualityProfileForm.swift b/Shared/Settings/QualityProfileForm.swift index c04e153b..005d2965 100644 --- a/Shared/Settings/QualityProfileForm.swift +++ b/Shared/Settings/QualityProfileForm.swift @@ -136,19 +136,9 @@ struct QualityProfileForm: View { var formatsFooter: some View { VStack(alignment: .leading) { - if #available(iOS 16.0, *) { - Text("Formats can be reordered and will be selected in this order.") - .foregroundColor(.secondary) - .fixedSize(horizontal: false, vertical: true) - } else if #available(iOS 14.0, *) { - Text("Formats will be selected in the order they are listed.") - .foregroundColor(.secondary) - .fixedSize(horizontal: false, vertical: true) - } else { - Text("Formats will be selected in the order they are listed.") - .foregroundColor(.secondary) - .fixedSize(horizontal: false, vertical: true) - } + Text("Formats can be reordered and will be selected in this order.") + .foregroundColor(.secondary) + .fixedSize(horizontal: false, vertical: true) Text("**Note:** HLS is an adaptive format where specific resolution settings don't apply.") .foregroundColor(.secondary) @@ -252,15 +242,8 @@ struct QualityProfileForm: View { #if os(macOS) let list = filteredFormatList - Group { - if #available(macOS 12.0, *) { - list - .listStyle(.inset(alternatesRowBackgrounds: true)) - } else { - list - .listStyle(.inset) - } - } + list + .listStyle(.inset(alternatesRowBackgrounds: true)) Spacer() #else filteredFormatList diff --git a/Shared/Settings/QualitySettings.swift b/Shared/Settings/QualitySettings.swift index 68ee29a0..98715e17 100644 --- a/Shared/Settings/QualitySettings.swift +++ b/Shared/Settings/QualitySettings.swift @@ -182,24 +182,14 @@ struct QualitySettings: View { } } - if #available(macOS 12.0, *) { - #if os(macOS) - List { - list - } - .listStyle(.inset(alternatesRowBackgrounds: true)) - #else + #if os(macOS) + List { list - #endif - } else { - #if os(macOS) - List { - list - } - #else - list - #endif - } + } + .listStyle(.inset(alternatesRowBackgrounds: true)) + #else + list + #endif } } diff --git a/Shared/Subscriptions/ChannelsView.swift b/Shared/Subscriptions/ChannelsView.swift index b83164e7..b60172e3 100644 --- a/Shared/Subscriptions/ChannelsView.swift +++ b/Shared/Subscriptions/ChannelsView.swift @@ -190,6 +190,7 @@ struct ChannelsView: View { #if os(iOS) struct CompactListRowModifier: ViewModifier { func body(content: Content) -> some View { + // swiftlint:disable:next deployment_target if #available(iOS 15.0, *) { content .listRowSpacing(0) diff --git a/Shared/Subscriptions/FeedView.swift b/Shared/Subscriptions/FeedView.swift index bec4b1aa..ee69ccbd 100644 --- a/Shared/Subscriptions/FeedView.swift +++ b/Shared/Subscriptions/FeedView.swift @@ -110,8 +110,7 @@ struct FeedView: View { VStack { Text("Channels") .font(.subheadline) - if #available(tvOS 17.0, *) { - List(selection: $selectedChannel) { + List(selection: $selectedChannel) { Button(action: { self.selectedChannel = nil self.feedChannelsViewVisible = false @@ -156,8 +155,8 @@ struct FeedView: View { .buttonStyle(PlainButtonStyle()) .focused(self.$focusedChannel, equals: channel.id) } - } - .onChange(of: self.focusedChannel) { + } + .onChange(of: self.focusedChannel) { if self.focusedChannel == "all" { withAnimation { self.selectedChannel = nil @@ -171,18 +170,17 @@ struct FeedView: View { } } } - } - .onAppear { + } + .onAppear { guard let selectedChannel = self.selectedChannel else { return } proxy.scrollTo(selectedChannel, anchor: .top) - } - .onExitCommand { + } + .onExitCommand { withAnimation { self.feedChannelsViewVisible = false } - } } } } @@ -218,30 +216,28 @@ struct FeedView: View { #if !os(macOS) .onReceive(NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification)) { _ in feed.loadResources() - } + } #endif } var header: some View { HStack(spacing: 16) { #if os(tvOS) - if #available(tvOS 17.0, *) { - Menu { - accountsPicker - } label: { - Label("Channels", systemImage: "filemenu.and.selection") - .labelStyle(.iconOnly) - .imageScale(.small) - .font(.caption) - } primaryAction: { - withAnimation { - self.feedChannelsViewVisible = true - self.focusedChannel = selectedChannel?.id ?? "all" - } + Menu { + accountsPicker + } label: { + Label("Channels", systemImage: "filemenu.and.selection") + .labelStyle(.iconOnly) + .imageScale(.small) + .font(.caption) + } primaryAction: { + withAnimation { + self.feedChannelsViewVisible = true + self.focusedChannel = selectedChannel?.id ?? "all" } - .opacity(feedChannelsViewVisible ? 0 : 1) - .frame(minWidth: feedChannelsViewVisible ? 0 : nil, maxWidth: feedChannelsViewVisible ? 0 : nil) } + .opacity(feedChannelsViewVisible ? 0 : 1) + .frame(minWidth: feedChannelsViewVisible ? 0 : nil, maxWidth: feedChannelsViewVisible ? 0 : nil) channelHeaderView if selectedChannel == nil { Spacer() diff --git a/Shared/Trending/TrendingCountry.swift b/Shared/Trending/TrendingCountry.swift index 2f53db1c..f8bbd37b 100644 --- a/Shared/Trending/TrendingCountry.swift +++ b/Shared/Trending/TrendingCountry.swift @@ -16,10 +16,11 @@ struct TrendingCountry: View { VStack { #if !os(tvOS) HStack { + // swiftlint:disable:next deployment_target if #available(iOS 15.0, macOS 12.0, *) { TextField("Country", text: $query, prompt: Text(Self.prompt)) } else { - TextField(Self.prompt, text: $query) + TextField("Country", text: $query) } Button("Done") { selectCountryAndDismiss() } @@ -57,12 +58,8 @@ struct TrendingCountry: View { return Group { #if os(macOS) - if #available(macOS 12.0, *) { - list - .listStyle(.inset(alternatesRowBackgrounds: true)) - } else { - list - } + list + .listStyle(.inset(alternatesRowBackgrounds: true)) #else list #endif diff --git a/Shared/Trending/TrendingView.swift b/Shared/Trending/TrendingView.swift index 73d15135..afe2f2e8 100644 --- a/Shared/Trending/TrendingView.swift +++ b/Shared/Trending/TrendingView.swift @@ -78,12 +78,12 @@ struct TrendingView: View { } #else .sheet(isPresented: $presentingCountrySelection) { - TrendingCountry(selectedCountry: $country) - #if os(macOS) - .frame(minWidth: 400, minHeight: 400) - #endif - } - .background( + TrendingCountry(selectedCountry: $country) + #if os(macOS) + .frame(minWidth: 400, minHeight: 400) + #endif + } + .background( Button("Refresh") { resource.load() .onFailure { self.error = $0 } @@ -131,10 +131,10 @@ struct TrendingView: View { } #else .onReceive(NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification)) { _ in - resource.loadIfNeeded()? - .onFailure { self.error = $0 } - .onSuccess { _ in self.error = nil } - } + resource.loadIfNeeded()? + .onFailure { self.error = $0 } + .onSuccess { _ in self.error = nil } + } #endif } diff --git a/Shared/URLTester.swift b/Shared/URLTester.swift index 1dda64ca..e051968f 100644 --- a/Shared/URLTester.swift +++ b/Shared/URLTester.swift @@ -73,7 +73,6 @@ enum URLTester { return } - // swiftlint:disable:next non_optional_string_data_conversion guard let manifest = String(data: data, encoding: .utf8), !manifest.isEmpty else { Logger(label: "stream.yattee.httpRequest").error("Cannot read or empty HLS manifest") completion([]) diff --git a/Shared/Videos/ThumbnailView.swift b/Shared/Videos/ThumbnailView.swift index bfdf864c..26e77a4a 100644 --- a/Shared/Videos/ThumbnailView.swift +++ b/Shared/Videos/ThumbnailView.swift @@ -54,7 +54,8 @@ struct ThumbnailView: View { } @ViewBuilder var asyncImageIfAvailable: some View { - if #available(iOS 15, macOS 12, *) { + // swiftlint:disable:next deployment_target + if #available(iOS 15.0, macOS 12.0, tvOS 15.0, *) { CachedAsyncImage(url: url, urlCache: BaseCacheModel.imageCache) { phase in switch phase { case let .success(image): @@ -70,7 +71,7 @@ struct ThumbnailView: View { } } } else { - webImage + placeholder } } diff --git a/Shared/Views/ControlsBar.swift b/Shared/Views/ControlsBar.swift index 13bb69af..7eb6aa63 100644 --- a/Shared/Views/ControlsBar.swift +++ b/Shared/Views/ControlsBar.swift @@ -350,9 +350,9 @@ extension View { self } } - + @ViewBuilder - func applyControlsBackground(enabled: Bool, cornerRadius: CGFloat) -> some View { + func applyControlsBackground(enabled: Bool, cornerRadius: Double) -> some View { if enabled { if #available(iOS 26.0, macOS 26.0, tvOS 26.0, *) { // Use Liquid Glass on iOS 26+ @@ -360,29 +360,30 @@ extension View { .regular.interactive(), in: .rect(cornerRadius: cornerRadius) ) - } else if #available(iOS 15.0, macOS 12.0, tvOS 15.0, *) { - // Fallback to ultraThinMaterial for iOS 15+ - self - .background( - RoundedRectangle(cornerRadius: cornerRadius) - .fill(.ultraThinMaterial) - ) - .overlay( - RoundedRectangle(cornerRadius: cornerRadius) - .stroke(Color("ControlsBorderColor"), lineWidth: 0.5) - ) } else { - // Fallback for iOS 14 and earlier - self - .background( - RoundedRectangle(cornerRadius: cornerRadius) - .fill(Color.black.opacity(0.3)) - .blur(radius: 10) - ) - .overlay( - RoundedRectangle(cornerRadius: cornerRadius) - .stroke(Color("ControlsBorderColor"), lineWidth: 0.5) - ) + // Fallback to ultraThinMaterial + // swiftlint:disable:next deployment_target + if #available(iOS 15.0, macOS 12.0, tvOS 15.0, *) { + self + .background( + RoundedRectangle(cornerRadius: cornerRadius) + .fill(.ultraThinMaterial) + ) + .overlay( + RoundedRectangle(cornerRadius: cornerRadius) + .stroke(Color("ControlsBorderColor"), lineWidth: 0.5) + ) + } else { + self + .background( + RoundedRectangle(cornerRadius: cornerRadius) + .fill(Color.gray.opacity(0.3)) + ) + .overlay( + RoundedRectangle(cornerRadius: cornerRadius) + .stroke(Color("ControlsBorderColor"), lineWidth: 0.5) + ) + } } } else { self.background(Color.clear) diff --git a/Shared/Views/OpenSettingsButton.swift b/Shared/Views/OpenSettingsButton.swift index 4bb3f98a..65679cd1 100644 --- a/Shared/Views/OpenSettingsButton.swift +++ b/Shared/Views/OpenSettingsButton.swift @@ -21,6 +21,7 @@ struct OpenSettingsButton: View { } .buttonStyle(.plain) + // swiftlint:disable:next deployment_target if #available(iOS 15.0, macOS 12.0, tvOS 15.0, *) { button .buttonStyle(.borderedProminent) diff --git a/Shared/Views/PopularView.swift b/Shared/Views/PopularView.swift index 8ca31a6b..a1b427b4 100644 --- a/Shared/Views/PopularView.swift +++ b/Shared/Views/PopularView.swift @@ -48,11 +48,12 @@ struct PopularView: View { } .navigationBarTitleDisplayMode(.inline) .refreshControl { refreshControl in - resource?.load().onCompletion { _ in - refreshControl.endRefreshing() - } - .onFailure { self.error = $0 } - .onSuccess { _ in self.error = nil } + resource?.load() + .onCompletion { _ in + refreshControl.endRefreshing() + } + .onFailure { self.error = $0 } + .onSuccess { _ in self.error = nil } } .backport .refreshable { @@ -80,10 +81,10 @@ struct PopularView: View { } #else .onReceive(NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification)) { _ in - resource?.loadIfNeeded()? - .onFailure { self.error = $0 } - .onSuccess { _ in self.error = nil } - } + resource?.loadIfNeeded()? + .onFailure { self.error = $0 } + .onSuccess { _ in self.error = nil } + } #endif } diff --git a/Shared/Views/VideoContextMenuView.swift b/Shared/Views/VideoContextMenuView.swift index 83a3408b..2a50da77 100644 --- a/Shared/Views/VideoContextMenuView.swift +++ b/Shared/Views/VideoContextMenuView.swift @@ -309,7 +309,8 @@ struct VideoContextMenuView: View { let label = Label("Remove…", systemImage: "trash.fill") .foregroundColor(Color("AppRedColor")) - if #available(iOS 15, macOS 12, *) { + // swiftlint:disable:next deployment_target + if #available(iOS 15.0, macOS 12.0, tvOS 15.0, *) { Button(role: .destructive, action: action) { label } } else { Button(action: action) { label } diff --git a/Shared/YatteeApp.swift b/Shared/YatteeApp.swift index a072d909..a49aca3a 100644 --- a/Shared/YatteeApp.swift +++ b/Shared/YatteeApp.swift @@ -73,15 +73,15 @@ struct YatteeApp: App { ) #else .onReceive( - NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification) - ) { _ in - player.handleEnterForeground() - } - .onReceive( - NotificationCenter.default.publisher(for: UIApplication.didEnterBackgroundNotification) - ) { _ in - player.handleEnterBackground() - } + NotificationCenter.default.publisher(for: UIApplication.willEnterForegroundNotification) + ) { _ in + player.handleEnterForeground() + } + .onReceive( + NotificationCenter.default.publisher(for: UIApplication.didEnterBackgroundNotification) + ) { _ in + player.handleEnterBackground() + } #endif #if os(iOS) .handlesExternalEvents(preferring: Set(["*"]), allowing: Set(["*"])) diff --git a/iOS/Orientation.swift b/iOS/Orientation.swift index 23798e7f..d86a2401 100644 --- a/iOS/Orientation.swift +++ b/iOS/Orientation.swift @@ -29,6 +29,7 @@ enum Orientation { logger.info("rotating to \(orientationString)") + // swiftlint:disable:next deployment_target if #available(iOS 16, *) { guard let windowScene = Self.scene else { return } let rotateOrientationMask = rotateOrientation == .portrait ? UIInterfaceOrientationMask.portrait : diff --git a/macOS/InstancesSettings.swift b/macOS/InstancesSettings.swift index c46c0b22..806de418 100644 --- a/macOS/InstancesSettings.swift +++ b/macOS/InstancesSettings.swift @@ -71,12 +71,8 @@ struct InstancesSettings: View { } } - if #available(macOS 12.0, *) { - list - .listStyle(.inset(alternatesRowBackgrounds: true)) - } else { - list - } + list + .listStyle(.inset(alternatesRowBackgrounds: true)) } if selectedInstance != nil, selectedInstance.app.hasFrontendURL {