-
Notifications
You must be signed in to change notification settings - Fork 0
/
MovieService.swift
96 lines (79 loc) · 3.26 KB
/
MovieService.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
//
// MovieService.swift
// GrappaMovie
//
// Created by Kristian Emil on 27/11/2024.
//
import Foundation
// MovieService handles all API communication with TMDB
actor MovieService {
// Using actor ensures thread-safety for our shared instance
static let shared = MovieService()
// Your API key should ideally be in a configuration file or secure storage
private let apiKey = "9d702fdc5b6e0206e3e9d491593621d3"
private let baseURL = "https://api.themoviedb.org/3"
// Custom error types for better error handling
enum MovieError: Error {
case invalidURL
case invalidResponse
case networkError(Error)
case decodingError(Error)
}
// Fetch popular movies with pagination support
func fetchPopularMovies(page: Int = 1) async throws -> MovieResponse {
// Construct the URL with query parameters
guard var urlComponents = URLComponents(string: "\(baseURL)/movie/popular") else {
throw MovieError.invalidURL
}
urlComponents.queryItems = [
URLQueryItem(name: "api_key", value: apiKey),
URLQueryItem(name: "page", value: String(page))
]
guard let url = urlComponents.url else {
throw MovieError.invalidURL
}
do {
// Make the network request
let (data, response) = try await URLSession.shared.data(from: url)
// Verify we got a valid HTTP response
guard let httpResponse = response as? HTTPURLResponse,
(200...299).contains(httpResponse.statusCode) else {
throw MovieError.invalidResponse
}
// Decode the JSON response into our MovieResponse model
let decoder = JSONDecoder()
return try decoder.decode(MovieResponse.self, from: data)
} catch let decodingError as DecodingError {
throw MovieError.decodingError(decodingError)
} catch {
throw MovieError.networkError(error)
}
}
// Search for movies
func searchMovies(query: String, page: Int = 1) async throws -> MovieResponse {
guard var urlComponents = URLComponents(string: "\(baseURL)/search/movie") else {
throw MovieError.invalidURL
}
urlComponents.queryItems = [
URLQueryItem(name: "api_key", value: apiKey),
URLQueryItem(name: "query", value: query),
URLQueryItem(name: "page", value: String(page))
]
guard let url = urlComponents.url else {
throw MovieError.invalidURL
}
do {
let (data, response) = try await URLSession.shared.data(from: url)
guard let httpResponse = response as? HTTPURLResponse,
(200...299).contains(httpResponse.statusCode) else {
throw MovieError.invalidResponse
}
let decoder = JSONDecoder()
return try decoder.decode(MovieResponse.self, from: data)
} catch let decodingError as DecodingError {
throw MovieError.decodingError(decodingError)
} catch {
throw MovieError.networkError(error)
}
}
}