Fix all SwiftLint violations across codebase

Resolves 130+ violations including deployment target checks, code style issues, and formatting inconsistencies. Adds SwiftLint disable comments for compiler-required availability checks while maintaining deployment target compliance.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Arkadiusz Fal
2025-11-09 17:56:15 +01:00
parent 4840c9a05f
commit be4e1adb9b
58 changed files with 257 additions and 303 deletions

View File

@@ -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),

View File

@@ -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)
}
}

View File

@@ -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],

View File

@@ -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 {

View File

@@ -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)")

View File

@@ -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 {

View File

@@ -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,

View File

@@ -321,7 +321,7 @@ extension PlayerModel {
}
restoredQueue.append(contentsOf: Defaults[.queue])
queue = restoredQueue.compactMap { $0 }
queue = restoredQueue.compactMap(\.self)
queue.forEach { loadQueueVideoDetails($0) }
}

View File

@@ -100,7 +100,7 @@ extension PlayerModel {
streamsMenu,
playbackModeMenu,
switchToMPVAction
].compactMap { $0 }
].compactMap(\.self)
#endif
}
}

View File

@@ -4,7 +4,7 @@ import Siesta
final class Store<Data>: 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) {

View File

@@ -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
}

View File

@@ -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
}

View File

@@ -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,