Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 11 additions & 15 deletions Shared/Configuration/CronicaApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -228,23 +228,19 @@ struct CronicaApp: App {
private func handleAppRefresh(task: BGAppRefreshTask?) {
if let task {
scheduleAppRefresh()
let queue = OperationQueue()
queue.maxConcurrentOperationCount = 1
task.expirationHandler = {
// After all operations are cancelled, the completion block below is called to set the task to complete.
queue.cancelAllOperations()
let workItem = Task {
await BackgroundManager.shared.handleWatchingContentRefresh()
BackgroundManager.shared.lastWatchingRefresh = Date()
await BackgroundManager.shared.handleUpcomingContentRefresh()
BackgroundManager.shared.lastUpcomingRefresh = Date()
await BackgroundManager.shared.handleAppRefreshMaintenance()
BackgroundManager.shared.lastMaintenance = Date()
task.setTaskCompleted(success: true)
}
queue.addOperation {
Task {
await BackgroundManager.shared.handleWatchingContentRefresh()
BackgroundManager.shared.lastWatchingRefresh = Date()
await BackgroundManager.shared.handleUpcomingContentRefresh()
BackgroundManager.shared.lastUpcomingRefresh = Date()
await BackgroundManager.shared.handleAppRefreshMaintenance()
BackgroundManager.shared.lastMaintenance = Date()
}
task.expirationHandler = {
workItem.cancel()
task.setTaskCompleted(success: false)
}
task.setTaskCompleted(success: true)
}
}
#elseif os(macOS)
Expand Down
11 changes: 10 additions & 1 deletion Shared/Controllers/PersistenceController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,10 +7,15 @@

import CoreData
import CloudKit
import os

/// An environment singleton responsible for managing Watchlist Core Data stack, including handling saving,
/// tracking watchlists, and dealing with sample data.
struct PersistenceController {
private static let logger = Logger(
subsystem: Bundle.main.bundleIdentifier ?? "com.cronica",
category: "PersistenceController"
)
static let shared = PersistenceController()
// MARK: Preview sample
static var preview: PersistenceController = {
Expand Down Expand Up @@ -64,7 +69,11 @@ struct PersistenceController {

func save() {
if container.viewContext.hasChanges {
try? container.viewContext.save()
do {
try container.viewContext.save()
} catch {
Self.logger.error("Core Data save failed: \(error.localizedDescription)")
}
}
}
}
26 changes: 13 additions & 13 deletions Shared/Extensions/ItemContent-Extensions.swift
Original file line number Diff line number Diff line change
Expand Up @@ -101,8 +101,8 @@ extension ItemContent {
return nil
}
var itemInfo: String {
if itemTheatricalString != nil && shortItemRuntime != nil {
return "\(itemGenre) • \(itemTheatricalString!) • \(shortItemRuntime!)"
if let theatricalString = itemTheatricalString, let runtime = shortItemRuntime {
return "\(itemGenre) • \(theatricalString) • \(runtime)"
}
if let itemTheatricalString {
return "\(itemGenres) • \(itemTheatricalString)"
Expand All @@ -123,8 +123,8 @@ extension ItemContent {
return ""
}
var itemQuickInfo: String {
if itemTheatricalString != nil && shortItemRuntime != nil {
return "\(itemTheatricalString!) • \(shortItemRuntime!)"
if let theatricalString = itemTheatricalString, let runtime = shortItemRuntime {
return "\(theatricalString) • \(runtime)"
}
if let itemTheatricalString {
return "\(itemTheatricalString)"
Expand Down Expand Up @@ -245,7 +245,10 @@ extension ItemContent {
#endif
}
var itemURL: URL {
return URL(string: "https://www.themoviedb.org/\(itemContentMedia.rawValue)/\(id)")!
guard let url = URL(string: "https://www.themoviedb.org/\(itemContentMedia.rawValue)/\(id)") else {
return URL(string: "https://www.themoviedb.org")!
}
return url
}

// MARK: Bool
Expand Down Expand Up @@ -279,11 +282,8 @@ extension ItemContent {
if media == .person {
return media.title
}
if itemTheatricalString != nil && shortItemRuntime != nil {
return "\(itemContentMedia.title) • \(itemTheatricalString!)"
}
if let itemTheatricalString {
return "\(itemContentMedia.title) • \(itemTheatricalString)"
if let theatricalString = itemTheatricalString {
return "\(itemContentMedia.title) • \(theatricalString)"
}
if let date = nextEpisodeDate {
return "\(itemContentMedia.title) • \(DatesManager.dateString.string(from: date))"
Expand All @@ -304,8 +304,8 @@ extension ItemContent {
}
var itemNotificationDescription: String {
if itemContentMedia == .movie {
if itemTheatricalString != nil && shortItemRuntime != nil {
return "\(itemContentMedia.title) • \(itemTheatricalString!)"
if let theatricalString = itemTheatricalString {
return "\(itemContentMedia.title) • \(theatricalString)"
}
if let itemTheatricalString {
return "\(itemContentMedia.title) • \(itemTheatricalString)"
Expand Down Expand Up @@ -401,7 +401,7 @@ extension ItemContent {
// MARK: Preview
static var examples: [ItemContent] {
let data: ItemContentResponse? = try? Bundle.main.decode(from: "content")
return data!.results
return data?.results ?? []
}
static var example: ItemContent {
examples[0]
Expand Down
5 changes: 4 additions & 1 deletion Shared/Extensions/WatchlistItem-Extension.swift
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,10 @@ extension WatchlistItem {
}
}
var itemLink: URL {
return URL(string: "https://www.themoviedb.org/\(itemMedia.rawValue)/\(itemId)")!
guard let url = URL(string: "https://www.themoviedb.org/\(itemMedia.rawValue)/\(itemId)") else {
return URL(string: "https://www.themoviedb.org")!
}
return url
}
var itemDateForNextSeason: String {
guard let date else { return String() }
Expand Down
18 changes: 16 additions & 2 deletions Shared/Helpers/KeychainHelper.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,22 @@ final class KeychainHelper {
kSecAttrService: service,
kSecAttrAccount: account,
] as [CFString : Any] as CFDictionary

_ = SecItemAdd(query, nil)

let status = SecItemAdd(query, nil)

if status == errSecDuplicateItem {
let searchQuery = [
kSecClass: kSecClassGenericPassword,
kSecAttrService: service,
kSecAttrAccount: account,
] as [CFString : Any] as CFDictionary

let attributesToUpdate = [
kSecValueData: data
] as [CFString : Any] as CFDictionary

SecItemUpdate(searchQuery, attributesToUpdate)
}
}

func read(service: String, account: String) -> Data? {
Expand Down
3 changes: 2 additions & 1 deletion Shared/Manager/AccountManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,8 @@ class AccountManager: ObservableObject {
do {
let parameters = ["session_id": "\(self.userSessionId)"]
let postData = try JSONSerialization.data(withJSONObject: parameters)
var request = URLRequest(url: URL(string: "https://api.themoviedb.org/3/authentication/session?api_key=\(Key.tmdbApi)")!,
guard let deleteUrl = URL(string: "https://api.themoviedb.org/3/authentication/session?api_key=\(Key.tmdbApi)") else { return }
var request = URLRequest(url: deleteUrl,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval: 10.0)
request.httpMethod = "DELETE"
Expand Down
3 changes: 2 additions & 1 deletion Shared/Manager/ExternalWatchlistManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ class ExternalWatchlistManager: ObservableObject {
"content-type": contentTypeHeader,
"authorization": "Bearer \(userAccessToken)"
]
var request = URLRequest(url: URL(string: "https://api.themoviedb.org/4/account/\(userAccessId)/\(type.rawValue)/watchlist?page=\(page)&sort_by=created_at.asc")!,
guard let watchlistUrl = URL(string: "https://api.themoviedb.org/4/account/\(userAccessId)/\(type.rawValue)/watchlist?page=\(page)&sort_by=created_at.asc") else { return nil }
var request = URLRequest(url: watchlistUrl,
cachePolicy: .useProtocolCachePolicy,
timeoutInterval: 10.0)
request.httpMethod = "GET"
Expand Down
12 changes: 5 additions & 7 deletions Shared/Manager/NotificationManager.swift
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,9 @@ class NotificationManager: ObservableObject {
}
}

func isNotificationAllowed() -> Bool {
var isAllowed = false
UNUserNotificationCenter.current().getNotificationSettings { settings in
if settings.authorizationStatus == .authorized { isAllowed.toggle() }
}
return isAllowed
func isNotificationAllowed() async -> Bool {
let settings = await UNUserNotificationCenter.current().notificationSettings()
return settings.authorizationStatus == .authorized
}

func schedule(_ content: ItemContent) {
Expand Down Expand Up @@ -197,7 +194,8 @@ class NotificationManager: ObservableObject {
}
let id = identifier.dropLast(2)
do {
let item = try await service.fetchItem(id: Int(id)!, type: media)
guard let contentID = Int(id) else { continue }
let item = try await service.fetchItem(id: contentID, type: media)
items.append(item)
} catch {
return nil
Expand Down
3 changes: 2 additions & 1 deletion Shared/View/Buttons/WatchlistButton.swift
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,8 @@ struct WatchlistButton: View {
media = .tvShow
}
let contentID = identifier.dropLast(2)
let content = try? await NetworkService.shared.fetchItem(id: Int(contentID)!, type: media)
guard let contentIDNumber = Int(contentID) else { return }
let content = try? await NetworkService.shared.fetchItem(id: contentIDNumber, type: media)
guard let content else { return }
persistence.save(content)
registerNotification(content)
Expand Down