Skip to content
Merged
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
Original file line number Diff line number Diff line change
@@ -1,23 +1,19 @@
//
// ApiService.swift
// MediaCCCApiClient.swift
// CCCApi
//
// Created by Mathijs Bernson on 29/07/2022.
//

import Foundation

@MainActor
@Observable
public class ApiService {
public final class MediaCCCApiClient {
private let session: URLSession
private let baseURL = URL(string: "https://api.media.ccc.de/public")!
private let decoder = JSONDecoder()

public static let shared = ApiService()

public init() {
session = URLSession(configuration: .default)
public init(urlSession: URLSession = .shared) {
session = urlSession
decoder.dateDecodingStrategy = .formatted(CustomISO8601DateFormatter())
}

Expand Down
30 changes: 12 additions & 18 deletions HackerTube.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,6 @@
SKIP_INSTALL = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
TARGETED_DEVICE_FAMILY = 3;
TVOS_DEPLOYMENT_TARGET = 18.6;
};
name = Debug;
};
Expand All @@ -425,7 +424,6 @@
SKIP_INSTALL = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
TARGETED_DEVICE_FAMILY = 3;
TVOS_DEPLOYMENT_TARGET = 18.6;
};
name = Release;
};
Expand All @@ -436,15 +434,13 @@
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 18.6;
PRODUCT_BUNDLE_IDENTIFIER = nl.bernson.CCCTubeTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SWIFT_EMIT_LOC_STRINGS = NO;
TARGETED_DEVICE_FAMILY = "1,2,3";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/HackerTube.app/HackerTube";
TVOS_DEPLOYMENT_TARGET = 18.6;
};
name = Debug;
};
Expand All @@ -455,15 +451,13 @@
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 18.6;
PRODUCT_BUNDLE_IDENTIFIER = nl.bernson.CCCTubeTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SUPPORTED_PLATFORMS = "appletvos appletvsimulator iphoneos iphonesimulator";
SUPPORTS_MACCATALYST = NO;
SWIFT_EMIT_LOC_STRINGS = NO;
TARGETED_DEVICE_FAMILY = "1,2,3";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/HackerTube.app/HackerTube";
TVOS_DEPLOYMENT_TARGET = 18.6;
};
name = Release;
};
Expand Down Expand Up @@ -522,6 +516,7 @@
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
INFOPLIST_KEY_UIRequiredDeviceCapabilities = arm64;
IPHONEOS_DEPLOYMENT_TARGET = 18.6;
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MARKETING_VERSION = 1.5.0;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
Expand All @@ -530,8 +525,12 @@
SDKROOT = appletvos;
STRING_CATALOG_GENERATE_SYMBOLS = YES;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
SWIFT_APPROACHABLE_CONCURRENCY = YES;
SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_VERSION = 6.0;
SWIFT_VERSION = 5.0;
TVOS_DEPLOYMENT_TARGET = 18.6;
XROS_DEPLOYMENT_TARGET = 26.0;
};
name = Debug;
};
Expand Down Expand Up @@ -584,16 +583,21 @@
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
INFOPLIST_KEY_UIRequiredDeviceCapabilities = arm64;
IPHONEOS_DEPLOYMENT_TARGET = 18.6;
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MARKETING_VERSION = 1.5.0;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SDKROOT = appletvos;
STRING_CATALOG_GENERATE_SYMBOLS = YES;
SWIFT_APPROACHABLE_CONCURRENCY = YES;
SWIFT_COMPILATION_MODE = wholemodule;
SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor;
SWIFT_OPTIMIZATION_LEVEL = "-O";
SWIFT_VERSION = 6.0;
SWIFT_VERSION = 5.0;
TVOS_DEPLOYMENT_TARGET = 18.6;
VALIDATE_PRODUCT = YES;
XROS_DEPLOYMENT_TARGET = 26.0;
};
name = Release;
};
Expand Down Expand Up @@ -623,7 +627,6 @@
INFOPLIST_KEY_UIRequiredDeviceCapabilities = arm64;
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
INFOPLIST_KEY_UIUserInterfaceStyle = Automatic;
IPHONEOS_DEPLOYMENT_TARGET = 18.6;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
Expand All @@ -637,8 +640,6 @@
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_EMIT_LOC_STRINGS = YES;
TARGETED_DEVICE_FAMILY = "1,2,3,6,7";
TVOS_DEPLOYMENT_TARGET = 18.6;
XROS_DEPLOYMENT_TARGET = 26.0;
};
name = Debug;
};
Expand Down Expand Up @@ -668,7 +669,6 @@
INFOPLIST_KEY_UIRequiredDeviceCapabilities = arm64;
INFOPLIST_KEY_UISupportedInterfaceOrientations = "UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown";
INFOPLIST_KEY_UIUserInterfaceStyle = Automatic;
IPHONEOS_DEPLOYMENT_TARGET = 18.6;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
Expand All @@ -682,8 +682,6 @@
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_EMIT_LOC_STRINGS = YES;
TARGETED_DEVICE_FAMILY = "1,2,3,6,7";
TVOS_DEPLOYMENT_TARGET = 18.6;
XROS_DEPLOYMENT_TARGET = 26.0;
};
name = Release;
};
Expand All @@ -696,7 +694,6 @@
DEAD_CODE_STRIPPING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 18.6;
PRODUCT_BUNDLE_IDENTIFIER = nl.bernson.CCCTubeUITests;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = auto;
Expand All @@ -706,7 +703,6 @@
SWIFT_EMIT_LOC_STRINGS = NO;
TARGETED_DEVICE_FAMILY = "1,2,3";
TEST_TARGET_NAME = HackerTube;
TVOS_DEPLOYMENT_TARGET = 18.6;
};
name = Debug;
};
Expand All @@ -719,7 +715,6 @@
DEAD_CODE_STRIPPING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 18.6;
PRODUCT_BUNDLE_IDENTIFIER = nl.bernson.CCCTubeUITests;
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = auto;
Expand All @@ -728,7 +723,6 @@
SWIFT_EMIT_LOC_STRINGS = NO;
TARGETED_DEVICE_FAMILY = "1,2,3";
TEST_TARGET_NAME = HackerTube;
TVOS_DEPLOYMENT_TARGET = 18.6;
};
name = Release;
};
Expand Down
56 changes: 56 additions & 0 deletions HackerTube/Domain/ApiService.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
//
// ApiService.swift
// HackerTube
//
// Created by Mathijs Bernson on 13/01/2026.
//

import CCCApi
import Foundation

@Observable
class ApiService {
private let client: MediaCCCApiClient

init() {
client = .init()
}

// MARK: Conferences

func conferences() async throws -> [Conference] {
try await client.conferences()
}

func conference(acronym: String) async throws -> Conference {
try await client.conference(acronym: acronym)
}

// MARK: Talks

func talk(id: String) async throws -> Talk {
try await client.talk(id: id)
}

// func talks() async throws -> [Talk] {
// try await client.talks()
// }

func recentTalks() async throws -> [Talk] {
try await client.recentTalks()
}

func popularTalks(in year: Int) async throws -> [Talk] {
try await client.popularTalks(in: year)
}

func searchTalks(query: String) async throws -> [Talk] {
try await client.searchTalks(query: query)
}

// MARK: Recordings

func recordings(for talk: Talk) async throws -> [Recording] {
try await client.recordings(for: talk)
}
}
3 changes: 1 addition & 2 deletions HackerTube/Features/Talk/TalkPlayerViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,7 @@ import Foundation
import os.log

@Observable
@MainActor
final class TalkPlayerViewModel {
class TalkPlayerViewModel {
var player = AVPlayer()

var currentRecording: Recording?
Expand Down
13 changes: 9 additions & 4 deletions HackerTube/Features/Talk/TalkViewModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,17 +16,22 @@ enum CopyrightState: Equatable {
}

@Observable
@MainActor
final class TalkViewModel {
class TalkViewModel {
var currentTalk: Talk?
var recordings: [Recording] = []
var preferredRecording: Recording?
var copyright: CopyrightState = .loading

private let mediaAnalyzer = MediaAnalyzer()
private let client: MediaCCCApiClient
private let mediaAnalyzer: MediaAnalyzer

init() {
client = .init()
mediaAnalyzer = .init()
}

func loadRecordings(for talk: Talk) async throws {
let recordings = try await ApiService.shared.recordings(for: talk)
let recordings = try await client.recordings(for: talk)
currentTalk = talk
self.recordings = recordings
let hdRecording = recordings.first(where: { $0.isHighQuality && $0.isVideo })
Expand Down
2 changes: 1 addition & 1 deletion HackerTube/HackerTubeApp.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ struct HackerTubeApp: App {
var body: some Scene {
WindowGroup {
ContentView()
.environment(ApiService.shared)
.environment(ApiService())
.onAppear {
do {
try AVAudioSession.sharedInstance().setCategory(.playback)
Expand Down
3 changes: 1 addition & 2 deletions HackerTubeUITests/HackerTubeUIScreenshotTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@

import XCTest

@MainActor
final class HackerTubeUIScreenshotTests: XCTestCase {
class HackerTubeUIScreenshotTests: XCTestCase {
var app: XCUIApplication!

override func setUpWithError() throws {
Expand Down
2 changes: 1 addition & 1 deletion TopShelf/ContentProvider.swift
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ class ContentProvider: TVTopShelfContentProvider {

override func loadTopShelfContent() async -> TVTopShelfContent? {
do {
let api = await ApiService.shared
let api = MediaCCCApiClient()
async let recentTalks = api.recentTalks()
let currentYear = Calendar.current.component(.year, from: .now)
async let popularTalks = api.popularTalks(in: currentYear)
Expand Down