├── adntmdbapp
├── MovieStore.swift
├── Assets.xcassets
│ ├── Contents.json
│ ├── AccentColor.colorset
│ │ └── Contents.json
│ ├── AppIcon.appiconset
│ │ └── Contents.json
│ └── PrimaryColor.colorset
│ │ └── Contents.json
├── Preview Content
│ └── Preview Assets.xcassets
│ │ └── Contents.json
├── Localizable.strings
├── FavoritesView.swift
├── Info.plist
├── adntmdbappApp.swift
├── ThemeManager.swift
├── MovieCategory.swift
├── GenreMapping.swift
├── ThemeSettingsView.swift
├── SettingsView.swift
├── SortView.swift
├── Constants.swift
├── FavoritesManager.swift
├── FavoriteView.swift
├── Movie.swift
├── FilterView.swift
├── MovieDetailView.swift
├── NetworkManager.swift
└── ContentView.swift
├── docs
└── assets
│ ├── p1.png
│ ├── p2.png
│ ├── p3.png
│ └── p4.png
├── adntmdbapp.xcodeproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ └── xcshareddata
│ │ ├── IDEWorkspaceChecks.plist
│ │ └── swiftpm
│ │ └── Package.resolved
├── xcuserdata
│ └── ask.xcuserdatad
│ │ ├── xcschemes
│ │ └── xcschememanagement.plist
│ │ └── xcdebugger
│ │ └── Breakpoints_v2.xcbkptlist
├── xcshareddata
│ └── xcschemes
│ │ └── adntmdbapp.xcscheme
└── project.pbxproj
├── .gitignore
└── README.md
/adntmdbapp/MovieStore.swift:
--------------------------------------------------------------------------------
1 |
2 |
--------------------------------------------------------------------------------
/docs/assets/p1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/androiddevnotes/ios-modern-movie-app/HEAD/docs/assets/p1.png
--------------------------------------------------------------------------------
/docs/assets/p2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/androiddevnotes/ios-modern-movie-app/HEAD/docs/assets/p2.png
--------------------------------------------------------------------------------
/docs/assets/p3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/androiddevnotes/ios-modern-movie-app/HEAD/docs/assets/p3.png
--------------------------------------------------------------------------------
/docs/assets/p4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/androiddevnotes/ios-modern-movie-app/HEAD/docs/assets/p4.png
--------------------------------------------------------------------------------
/adntmdbapp/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/adntmdbapp/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/adntmdbapp.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/adntmdbapp/Localizable.strings:
--------------------------------------------------------------------------------
1 | "overview" = "Overview";
2 | "releaseDate" = "Release Date";
3 | "genre" = "Genre";
4 | "director" = "Director";
5 | "addToFavorites" = "Add to Favorites";
6 | "removeFromFavorites" = "Remove from Favorites";
--------------------------------------------------------------------------------
/adntmdbapp/Assets.xcassets/AccentColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "idiom" : "universal"
5 | }
6 | ],
7 | "info" : {
8 | "author" : "xcode",
9 | "version" : 1
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/adntmdbapp/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "idiom" : "universal",
5 | "platform" : "ios",
6 | "size" : "1024x1024"
7 | }
8 | ],
9 | "info" : {
10 | "author" : "xcode",
11 | "version" : 1
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/adntmdbapp/FavoritesView.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 |
3 | struct FavoriteView: View {
4 | @ObservedObject var favoritesManager: FavoritesManager
5 |
6 | var body: some View {
7 | ScrollView {
8 |
9 | }
10 |
11 | .navigationTitle("Favorites")
12 | .overlay(emptyStateView)
13 | }
14 |
15 | }
16 |
--------------------------------------------------------------------------------
/adntmdbapp/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | NSBonjourServices
6 |
7 | _Proxyman._tcp
8 |
9 |
10 |
11 |
--------------------------------------------------------------------------------
/adntmdbapp.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/adntmdbapp/adntmdbappApp.swift:
--------------------------------------------------------------------------------
1 | import Atlantis
2 | import SwiftUI
3 |
4 | @main
5 | struct adntmdbappApp: App {
6 | @StateObject private var themeManager = ThemeManager()
7 |
8 | init() {
9 | Atlantis.start(hostName: "asks-macbook-pro.local.")
10 | }
11 |
12 | var body: some Scene {
13 | WindowGroup {
14 | ContentView()
15 | .environmentObject(themeManager)
16 | .preferredColorScheme(themeManager.selectedTheme.colorScheme)
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/adntmdbapp/ThemeManager.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 |
3 | class ThemeManager: ObservableObject {
4 | @AppStorage("selectedTheme") var selectedTheme: Theme = .system
5 |
6 | enum Theme: String, CaseIterable, Identifiable {
7 | case light, dark, system
8 | var id: Self { self }
9 |
10 | var colorScheme: ColorScheme? {
11 | switch self {
12 | case .light: return .light
13 | case .dark: return .dark
14 | case .system: return nil
15 | }
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/adntmdbapp/MovieCategory.swift:
--------------------------------------------------------------------------------
1 | enum MovieCategory: String, CaseIterable {
2 | case popular = "popularity.desc"
3 | case upcoming = "primary_release_date.asc"
4 | case nowPlaying = "primary_release_date.desc"
5 | case topRated = "vote_average.desc"
6 |
7 | var displayName: String {
8 | switch self {
9 | case .popular: return "Popular"
10 | case .upcoming: return "Upcoming"
11 | case .nowPlaying: return "Now Playing"
12 | case .topRated: return "Top Rated"
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/adntmdbapp.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved:
--------------------------------------------------------------------------------
1 | {
2 | "originHash" : "d5450604dd0dd5af62103ae6a1fa6788bea0ed9c04ed6e9bb7f276c1aebbf022",
3 | "pins" : [
4 | {
5 | "identity" : "atlantis",
6 | "kind" : "remoteSourceControl",
7 | "location" : "https://github.com/ProxymanApp/atlantis",
8 | "state" : {
9 | "revision" : "523dd773538e1e20036cb2d28f8b9947448c2d20",
10 | "version" : "1.25.1"
11 | }
12 | }
13 | ],
14 | "version" : 3
15 | }
16 |
--------------------------------------------------------------------------------
/adntmdbapp.xcodeproj/xcuserdata/ask.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | adntmdbapp.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 |
13 | SuppressBuildableAutocreation
14 |
15 | 6A4A38242C9C7308001FA9A9
16 |
17 | primary
18 |
19 |
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/adntmdbapp/GenreMapping.swift:
--------------------------------------------------------------------------------
1 | struct GenreMapping {
2 | static let genreNameToId = [
3 | "Action": "28",
4 | "Adventure": "12",
5 | "Animation": "16",
6 | "Comedy": "35",
7 | "Crime": "80",
8 | "Documentary": "99",
9 | "Drama": "18",
10 | "Family": "10751",
11 | "Fantasy": "14",
12 | "History": "36",
13 | "Horror": "27",
14 | "Music": "10402",
15 | "Mystery": "9648",
16 | "Romance": "10749",
17 | "Science Fiction": "878",
18 | "TV Movie": "10770",
19 | "Thriller": "53",
20 | "War": "10752",
21 | "Western": "37",
22 | ]
23 |
24 | static let idToName = Dictionary(uniqueKeysWithValues: genreNameToId.map { ($1, $0) })
25 | }
26 |
--------------------------------------------------------------------------------
/adntmdbapp/ThemeSettingsView.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 |
3 | struct ThemeSettingsView: View {
4 | @EnvironmentObject var themeManager: ThemeManager
5 | @Environment(\.presentationMode) var presentationMode
6 |
7 | var body: some View {
8 | NavigationView {
9 | Form {
10 | Picker("App Theme", selection: $themeManager.selectedTheme) {
11 | ForEach(ThemeManager.Theme.allCases) { theme in
12 | Text(theme.rawValue.capitalized)
13 | }
14 | }
15 | .pickerStyle(SegmentedPickerStyle())
16 | }
17 | .navigationTitle("Theme Settings")
18 | .navigationBarItems(
19 | trailing: Button("Done") {
20 | presentationMode.wrappedValue.dismiss()
21 | })
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/adntmdbapp/SettingsView.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 |
3 | struct SettingsView: View {
4 | @EnvironmentObject var themeManager: ThemeManager
5 | @Environment(\.presentationMode) var presentationMode
6 |
7 | var body: some View {
8 | NavigationView {
9 | Form {
10 | Section(header: Text("Appearance")) {
11 | Picker("App Theme", selection: $themeManager.selectedTheme) {
12 | ForEach(ThemeManager.Theme.allCases) { theme in
13 | Text(theme.rawValue.capitalized)
14 | }
15 | }
16 | .pickerStyle(SegmentedPickerStyle())
17 | }
18 | }
19 | .navigationTitle("Settings")
20 | .navigationBarItems(
21 | trailing: Button("Done") {
22 | presentationMode.wrappedValue.dismiss()
23 | })
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/adntmdbapp/Assets.xcassets/PrimaryColor.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0x90",
9 | "green" : "0x56",
10 | "red" : "0x61"
11 | }
12 | },
13 | "idiom" : "universal"
14 | },
15 | {
16 | "appearances" : [
17 | {
18 | "appearance" : "luminosity",
19 | "value" : "dark"
20 | }
21 | ],
22 | "color" : {
23 | "color-space" : "srgb",
24 | "components" : {
25 | "alpha" : "1.000",
26 | "blue" : "0xB0",
27 | "green" : "0x76",
28 | "red" : "0x81"
29 | }
30 | },
31 | "idiom" : "universal"
32 | }
33 | ],
34 | "info" : {
35 | "author" : "xcode",
36 | "version" : 1
37 | }
38 | }
--------------------------------------------------------------------------------
/adntmdbapp.xcodeproj/xcuserdata/ask.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
9 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/adntmdbapp/SortView.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 |
3 | struct SortView: View {
4 | @ObservedObject var networkManager: NetworkManager
5 | @Binding var isPresented: Bool
6 |
7 | var body: some View {
8 | NavigationView {
9 | List {
10 | ForEach(MovieCategory.allCases, id: \.self) { category in
11 | Button(action: {
12 | networkManager.fetchMovies(for: category)
13 | isPresented = false
14 | }) {
15 | HStack {
16 | Text(category.displayName)
17 | Spacer()
18 | if networkManager.currentCategory == category {
19 | Image(systemName: "checkmark")
20 | .foregroundColor(Constants.Colors.primary)
21 | }
22 | }
23 | }
24 | }
25 | }
26 | .navigationTitle("Sort By")
27 | .navigationBarItems(
28 | trailing: Button("Done") {
29 | isPresented = false
30 | })
31 | }
32 | .accentColor(Constants.Colors.primary)
33 |
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/adntmdbapp/Constants.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 |
3 | struct Constants {
4 | struct API {
5 | static let baseURL = "https://api.themoviedb.org/3"
6 | }
7 |
8 | struct Image {
9 | static let baseURL = "https://image.tmdb.org/t/p/w500"
10 | }
11 |
12 | struct Secrets {
13 | static let plistName = "Secrets"
14 | static let apiKeyKey = "API_KEY"
15 | }
16 |
17 | struct UI {
18 | static let appTitle = "ADN"
19 | static let placeholderImage = "placeholder"
20 | }
21 |
22 | struct Colors {
23 | static let primary = Color("PrimaryColor")
24 | }
25 |
26 | struct Strings {
27 | static let overview = NSLocalizedString("overview", comment: "")
28 | static let releaseDate = NSLocalizedString("releaseDate", comment: "")
29 | static let genre = NSLocalizedString("genre", comment: "")
30 | static let director = NSLocalizedString("director", comment: "")
31 | static let addToFavorites = NSLocalizedString("addToFavorites", comment: "")
32 | static let removeFromFavorites = NSLocalizedString("removeFromFavorites", comment: "")
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/adntmdbapp/FavoritesManager.swift:
--------------------------------------------------------------------------------
1 | import Foundation
2 |
3 | class FavoritesManager: ObservableObject {
4 | @Published var favoriteMovies: [Movie] = []
5 | private var favoriteIds: Set = []
6 |
7 | init() {
8 | loadFavorites()
9 | }
10 |
11 | func toggleFavorite(for movie: Movie) {
12 | if favoriteIds.contains(movie.id) {
13 | favoriteIds.remove(movie.id)
14 | favoriteMovies.removeAll { $0.id == movie.id }
15 | } else {
16 | favoriteIds.insert(movie.id)
17 | favoriteMovies.append(movie)
18 | }
19 | saveFavorites()
20 | }
21 |
22 | func isFavorite(_ movie: Movie) -> Bool {
23 | favoriteIds.contains(movie.id)
24 | }
25 |
26 | func removeFromFavorites(_ movie: Movie) {
27 | favoriteMovies.removeAll { $0.id == movie.id }
28 | favoriteIds.remove(movie.id)
29 | saveFavorites()
30 | }
31 |
32 | private func saveFavorites() {
33 | let encoder = JSONEncoder()
34 | if let encoded = try? encoder.encode(favoriteMovies) {
35 | UserDefaults.standard.set(encoded, forKey: "FavoriteMovies")
36 | }
37 | }
38 |
39 | private func loadFavorites() {
40 | if let savedFavorites = UserDefaults.standard.data(forKey: "FavoriteMovies") {
41 | let decoder = JSONDecoder()
42 | if let loadedFavorites = try? decoder.decode([Movie].self, from: savedFavorites) {
43 | favoriteMovies = loadedFavorites
44 | favoriteIds = Set(loadedFavorites.map { $0.id })
45 | }
46 | }
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/adntmdbapp/FavoriteView.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 |
3 | struct FavoriteView: View {
4 | @ObservedObject var favoritesManager: FavoritesManager
5 |
6 | var body: some View {
7 | ScrollView {
8 | LazyVStack(spacing: 16) {
9 | ForEach(favoritesManager.favoriteMovies) { movie in
10 | NavigationLink(
11 | destination: MovieDetailView(
12 | networkManager: NetworkManager(favoritesManager: favoritesManager),
13 | favoritesManager: favoritesManager,
14 | movie: .constant(movie)
15 | )
16 | ) {
17 | MovieRowView(
18 | movie: movie,
19 | networkManager: NetworkManager(favoritesManager: favoritesManager),
20 | favoritesManager: favoritesManager
21 | )
22 | }
23 | .buttonStyle(PlainButtonStyle())
24 | }
25 | }
26 | .padding()
27 | }
28 | .background(Color(UIColor.systemBackground))
29 | .navigationTitle("Favorites")
30 | .overlay(emptyStateView)
31 | }
32 |
33 | @ViewBuilder
34 | private var emptyStateView: some View {
35 | if favoritesManager.favoriteMovies.isEmpty {
36 | VStack(spacing: 16) {
37 | Image(systemName: "heart.slash")
38 | .font(.system(size: 60))
39 | .foregroundColor(.gray)
40 | Text("No favorites yet")
41 | .font(.title2)
42 | .foregroundColor(.gray)
43 | Text("Add movies to your favorites list")
44 | .font(.subheadline)
45 | .foregroundColor(.gray)
46 | }
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | Secrets.plist
2 | # Xcode
3 | #
4 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore
5 |
6 | ## User settings
7 | xcuserdata/
8 |
9 | ## Obj-C/Swift specific
10 | *.hmap
11 |
12 | ## App packaging
13 | *.ipa
14 | *.dSYM.zip
15 | *.dSYM
16 |
17 | ## Playgrounds
18 | timeline.xctimeline
19 | playground.xcworkspace
20 |
21 | # Swift Package Manager
22 | #
23 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies.
24 | # Packages/
25 | # Package.pins
26 | # Package.resolved
27 | # *.xcodeproj
28 | #
29 | # Xcode automatically generates this directory with a .xcworkspacedata file and xcuserdata
30 | # hence it is not needed unless you have added a package configuration file to your project
31 | # .swiftpm
32 |
33 | .build/
34 |
35 | # CocoaPods
36 | #
37 | # We recommend against adding the Pods directory to your .gitignore. However
38 | # you should judge for yourself, the pros and cons are mentioned at:
39 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
40 | #
41 | # Pods/
42 | #
43 | # Add this line if you want to avoid checking in source code from the Xcode workspace
44 | # *.xcworkspace
45 |
46 | # Carthage
47 | #
48 | # Add this line if you want to avoid checking in source code from Carthage dependencies.
49 | # Carthage/Checkouts
50 |
51 | Carthage/Build/
52 |
53 | # fastlane
54 | #
55 | # It is recommended to not store the screenshots in the git repo.
56 | # Instead, use fastlane to re-generate the screenshots whenever they are needed.
57 | # For more information about the recommended setup visit:
58 | # https://docs.fastlane.tools/best-practices/source-control/#source-control
59 |
60 | fastlane/report.xml
61 | fastlane/Preview.html
62 | fastlane/screenshots/**/*.png
63 | fastlane/test_output
--------------------------------------------------------------------------------
/adntmdbapp/Movie.swift:
--------------------------------------------------------------------------------
1 | struct Movie: Identifiable, Codable {
2 | let id: Int
3 | let title: String
4 | let overview: String
5 | let posterPath: String?
6 | let rating: Double
7 | let releaseDate: String
8 | let genres: [String]
9 | var isFavorite: Bool = false
10 | var categoryId: String?
11 |
12 | enum CodingKeys: String, CodingKey {
13 | case id
14 | case title
15 | case overview
16 | case posterPath = "poster_path"
17 | case rating = "vote_average"
18 | case releaseDate = "release_date"
19 | case genreIds = "genre_ids"
20 | }
21 |
22 | init(from decoder: Decoder) throws {
23 | let container = try decoder.container(keyedBy: CodingKeys.self)
24 | id = try container.decode(Int.self, forKey: .id)
25 | title = try container.decode(String.self, forKey: .title)
26 | overview = try container.decode(String.self, forKey: .overview)
27 | posterPath = try container.decodeIfPresent(String.self, forKey: .posterPath)
28 | rating = try container.decode(Double.self, forKey: .rating)
29 | releaseDate = try container.decode(String.self, forKey: .releaseDate)
30 |
31 | let genreIds = try container.decodeIfPresent([Int].self, forKey: .genreIds) ?? []
32 | genres = genreIds.map { String($0) }.compactMap { GenreMapping.idToName[$0] }
33 |
34 | isFavorite = false
35 | categoryId = nil
36 | }
37 |
38 | init(
39 | id: Int, title: String, overview: String, posterPath: String?, rating: Double,
40 | releaseDate: String, genres: [String], isFavorite: Bool = false, categoryId: String? = nil
41 | ) {
42 | self.id = id
43 | self.title = title
44 | self.overview = overview
45 | self.posterPath = posterPath
46 | self.rating = rating
47 | self.releaseDate = releaseDate
48 | self.genres = genres
49 | self.isFavorite = isFavorite
50 | self.categoryId = categoryId
51 | }
52 |
53 | func encode(to encoder: Encoder) throws {
54 | var container = encoder.container(keyedBy: CodingKeys.self)
55 | try container.encode(id, forKey: .id)
56 | try container.encode(title, forKey: .title)
57 | try container.encode(overview, forKey: .overview)
58 | try container.encode(posterPath, forKey: .posterPath)
59 | try container.encode(rating, forKey: .rating)
60 | try container.encode(releaseDate, forKey: .releaseDate)
61 | // We don't encode genres back to genre IDs, as we don't need to send this data back to the API
62 | }
63 | }
64 |
65 | struct MovieResponse: Codable {
66 | let page: Int
67 | let results: [Movie]
68 | let totalPages: Int
69 |
70 | enum CodingKeys: String, CodingKey {
71 | case page
72 | case results
73 | case totalPages = "total_pages"
74 | }
75 | }
76 |
77 | struct MovieResult: Codable {
78 | let id: Int
79 | let title: String
80 | let overview: String
81 | let posterPath: String?
82 | let voteAverage: Double
83 |
84 | enum CodingKeys: String, CodingKey {
85 | case id
86 | case title
87 | case overview
88 | case posterPath = "poster_path"
89 | case voteAverage = "vote_average"
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/adntmdbapp.xcodeproj/xcshareddata/xcschemes/adntmdbapp.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
9 |
10 |
16 |
22 |
23 |
24 |
25 |
26 |
32 |
33 |
43 |
45 |
51 |
52 |
53 |
54 |
60 |
62 |
68 |
69 |
70 |
71 |
73 |
74 |
77 |
78 |
79 |
--------------------------------------------------------------------------------
/adntmdbapp/FilterView.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 |
3 | struct FilterView: View {
4 | @Binding var isPresented: Bool
5 | @Binding var selectedGenres: Set
6 | @Binding var selectedYear: Int?
7 | @Binding var minRating: Double
8 |
9 | let genres = [
10 | "Action", "Adventure", "Animation", "Comedy", "Crime", "Documentary", "Drama", "Family",
11 | "Fantasy", "History", "Horror", "Music", "Mystery", "Romance", "Science Fiction",
12 | "TV Movie", "Thriller", "War", "Western",
13 | ].sorted()
14 |
15 | let years = Array(1900...Calendar.current.component(.year, from: Date())).reversed()
16 |
17 | var body: some View {
18 | NavigationView {
19 | Form {
20 | Section(header: Text("Genres")) {
21 | ScrollView(.horizontal, showsIndicators: false) {
22 | LazyHStack(spacing: 8) {
23 | ForEach(genres, id: \.self) { genre in
24 | GenreTagView(genre: genre, isSelected: selectedGenres.contains(genre)) {
25 | toggleGenre(genre)
26 | }
27 | }
28 | }
29 | .padding(.vertical, 8)
30 | }
31 | }
32 |
33 | Section(header: Text("Release Year")) {
34 | Picker("Select Year", selection: $selectedYear) {
35 | Text("Any").tag(nil as Int?)
36 | ForEach(years, id: \.self) { year in
37 | Text(String(year)).tag(year as Int?)
38 | }
39 | }
40 | .pickerStyle(WheelPickerStyle())
41 | }
42 |
43 | Section(header: Text("Minimum Rating")) {
44 | VStack(alignment: .leading, spacing: 10) {
45 | HStack {
46 | Text(String(format: "%.1f", minRating))
47 | .font(.title2)
48 | .fontWeight(.bold)
49 | .foregroundColor(Constants.Colors.primary)
50 | Spacer()
51 | Image(systemName: "star.fill")
52 | .foregroundColor(.yellow)
53 | }
54 | Slider(value: $minRating, in: 0...10, step: 0.5)
55 | .accentColor(Constants.Colors.primary)
56 | HStack {
57 | Text("0")
58 | .font(.caption)
59 | .foregroundColor(.secondary)
60 | Spacer()
61 | Text("10")
62 | .font(.caption)
63 | .foregroundColor(.secondary)
64 | }
65 | }
66 | .padding(.vertical, 8)
67 | }
68 | }
69 | .navigationTitle("Filter Movies")
70 | .navigationBarItems(
71 | trailing: Button("Apply") {
72 | isPresented = false
73 | })
74 | }
75 | }
76 |
77 | private func toggleGenre(_ genre: String) {
78 | if selectedGenres.contains(genre) {
79 | selectedGenres.remove(genre)
80 | } else {
81 | selectedGenres.insert(genre)
82 | }
83 | }
84 | }
85 |
86 | struct GenreTagView: View {
87 | let genre: String
88 | let isSelected: Bool
89 | let action: () -> Void
90 |
91 | var body: some View {
92 | Button(action: action) {
93 | Text(genre)
94 | .padding(.horizontal, 12)
95 | .padding(.vertical, 6)
96 | .background(isSelected ? Constants.Colors.primary : Color.gray.opacity(0.2))
97 | .foregroundColor(isSelected ? .white : .primary)
98 | .cornerRadius(20)
99 | .overlay(
100 | RoundedRectangle(cornerRadius: 20)
101 | .stroke(isSelected ? Constants.Colors.primary : Color.gray, lineWidth: 1)
102 | )
103 | }
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Modern Movie App (iOS)
2 |
3 | ## Overview
4 |
5 | This iOS app interacts with The Movie Database (TMDB) API. Every line of code has been generated
6 | or suggested by AI using a large language model.
7 |
8 | ## Current state
9 |
10 | The codebase is currently under development, with core features implemented and more being added.
11 |
12 |
13 |
14 |  |
15 |  |
16 |
17 |
18 |  |
19 |  |
20 |
21 |
22 |
23 | ## Setup
24 |
25 | 1. Get a TMDB API key from [themoviedb.org](https://www.themoviedb.org/).
26 | 2. Create a `Secrets.plist` file in the project root with the following content:
27 | ```xml
28 |
29 |
30 |
31 |
32 | API_KEY
33 | your_api_key_here
34 |
35 |
36 | ```
37 | 3. Open the project in Xcode and build.
38 |
39 | ## Tech Stack
40 |
41 | - **Language**: Swift
42 | - **UI Framework**: SwiftUI
43 | - **Architecture**: MVVM (Model-View-ViewModel)
44 | - **Networking**: URLSession
45 | - **JSON Parsing**: Codable
46 | - **Image Loading**: AsyncImage
47 | - **Asynchronous Programming**: Swift Concurrency
48 | - **State Management**: @Published, @State, @Binding
49 | - **Dependency Injection**: Manual (via init and @EnvironmentObject)
50 | - **UI Components**: SwiftUI native components
51 | - **Build System**: Xcode
52 | - **Minimum iOS Version**: iOS 17.4 (as per current project settings)
53 |
54 | ## Key Features
55 |
56 | 1. Browse movies by different categories (popular, upcoming, now playing, top-rated)
57 | 2. Search for movies
58 | 3. View detailed movie information
59 | 4. Mark movies as favorites
60 | 5. Apply filters (genre, release year, minimum rating)
61 | 6. Toggle between light and dark themes
62 | 7. Localization support
63 |
64 | ## Architecture
65 |
66 | The app follows the MVVM (Model-View-ViewModel) architecture:
67 |
68 | - Model: Represented by the `Movie` and `MovieResponse` structs
69 | - View: SwiftUI views in various view files
70 | - ViewModel: `NetworkManager` manages the app's state and business logic
71 |
72 | ## Data Flow
73 |
74 | 1. The `NetworkManager` fetches data from the TMDB API
75 | 2. Data is then exposed to the UI components via @Published properties
76 | 3. UI components observe these properties and update when the data changes
77 |
78 | ## Networking
79 |
80 | The app uses `URLSession` for network requests. The `NetworkManager` class handles all API calls and data parsing.
81 |
82 | ## State Management
83 |
84 | The app uses SwiftUI's state management tools:
85 | - @Published for observable object properties
86 | - @State for view-local state
87 | - @Binding for passing mutable state to child views
88 | - @EnvironmentObject for dependency injection
89 |
90 | ## Theming
91 |
92 | The app supports dynamic theming with light and dark mode options. The `ThemeManager` class handles theme selection and application.
93 |
94 | ## Localization
95 |
96 | The app supports localization with strings defined in `Localizable.strings`.
97 |
98 | ## Third-Party Libraries
99 |
100 | - Atlantis: Used for network debugging
--------------------------------------------------------------------------------
/adntmdbapp/MovieDetailView.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 |
3 | struct MovieDetailView: View {
4 | @ObservedObject var networkManager: NetworkManager
5 | @ObservedObject var favoritesManager: FavoritesManager
6 | @Binding var movie: Movie
7 | @Environment(\.presentationMode) var presentationMode
8 | @State private var showingFilterView = false
9 | @State private var selectedGenres: Set = []
10 | @State private var selectedYear: Int?
11 | @State private var minRating: Double = 0.0
12 |
13 | var body: some View {
14 | ScrollView {
15 | VStack(alignment: .leading, spacing: 16) {
16 |
17 | ZStack(alignment: .bottomLeading) {
18 | networkManager.posterImage(for: movie)
19 | .aspectRatio(contentMode: .fill)
20 | .frame(height: UIScreen.main.bounds.height * 0.6)
21 | .frame(maxWidth: .infinity)
22 | .clipped()
23 |
24 | LinearGradient(
25 | gradient: Gradient(colors: [Color.clear, Color.black.opacity(0.8)]), startPoint: .top,
26 | endPoint: .bottom)
27 |
28 | VStack(alignment: .leading, spacing: 8) {
29 | Text(movie.title)
30 | .font(.largeTitle)
31 | .fontWeight(.bold)
32 | .foregroundColor(.white)
33 |
34 | HStack(spacing: 4) {
35 | Image(systemName: "star.fill")
36 | .foregroundColor(.yellow)
37 | Text(String(format: "%.1f", movie.rating))
38 | .font(.title2)
39 | .fontWeight(.semibold)
40 | .foregroundColor(.white)
41 | }
42 | }
43 | .padding()
44 | }
45 | .frame(height: UIScreen.main.bounds.height * 0.6)
46 |
47 | VStack(alignment: .leading, spacing: 30) {
48 |
49 | VStack(alignment: .leading, spacing: 15) {
50 | Text(Constants.Strings.overview)
51 | .font(.title2)
52 | .fontWeight(.bold)
53 | .foregroundColor(.primary)
54 | Text(movie.overview)
55 | .padding()
56 | .background(Color(UIColor.secondarySystemBackground))
57 | .cornerRadius(8)
58 | }
59 | .padding(.horizontal)
60 |
61 | VStack(alignment: .leading, spacing: 15) {
62 | detailRow(title: Constants.Strings.releaseDate, value: movie.releaseDate)
63 | detailRow(title: Constants.Strings.genre, value: movie.genres.joined(separator: ", "))
64 | }
65 | .padding(.horizontal)
66 | }
67 | .padding(.vertical, 30)
68 | }
69 | }
70 | .safeAreaInset(edge: .bottom) {
71 | Button(action: {
72 | favoritesManager.toggleFavorite(for: movie)
73 | }) {
74 | HStack {
75 | Image(systemName: favoritesManager.isFavorite(movie) ? "heart.fill" : "heart")
76 | Text(
77 | favoritesManager.isFavorite(movie)
78 | ? Constants.Strings.removeFromFavorites : Constants.Strings.addToFavorites)
79 | }
80 | .frame(maxWidth: .infinity)
81 | .padding()
82 | .background(favoritesManager.isFavorite(movie) ? Color.red : Constants.Colors.primary)
83 | .foregroundColor(.white)
84 | .cornerRadius(10)
85 | }
86 | .padding(.horizontal)
87 | .padding(.bottom, 8)
88 | .background(Color(UIColor.systemBackground).opacity(0.8))
89 | }
90 | .edgesIgnoringSafeArea(.top)
91 | }
92 |
93 | private var favoriteButton: some View {
94 | Button(action: {
95 | favoritesManager.toggleFavorite(for: movie)
96 | }) {
97 | HStack {
98 | Image(systemName: favoritesManager.isFavorite(movie) ? "heart.fill" : "heart")
99 | Text(favoritesManager.isFavorite(movie) ? "Remove from Favorites" : "Add to Favorites")
100 | }
101 | .foregroundColor(favoritesManager.isFavorite(movie) ? .red : .blue)
102 | }
103 | }
104 |
105 | private func detailRow(title: String, value: String) -> some View {
106 | HStack {
107 | Text(title)
108 | .font(.headline)
109 | .foregroundColor(.primary)
110 | Spacer()
111 | Text(value)
112 | .font(.subheadline)
113 | .foregroundColor(.secondary)
114 | }
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/adntmdbapp/NetworkManager.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 |
3 | class NetworkManager: ObservableObject {
4 | @Published var movies: [Movie] = []
5 | @Published var currentCategory: MovieCategory = .popular
6 | var currentPage = 1
7 | var totalPages = 1
8 | @Published var selectedGenres: Set = []
9 | @Published var selectedYear: Int?
10 | @Published var minRating: Double = 0.0
11 | @Published var searchQuery = ""
12 |
13 | let favoritesManager: FavoritesManager
14 |
15 | init(favoritesManager: FavoritesManager) {
16 | self.favoritesManager = favoritesManager
17 | }
18 |
19 | private var apiKey: String {
20 | if let path = Bundle.main.path(forResource: Constants.Secrets.plistName, ofType: "plist"),
21 | let dict = NSDictionary(contentsOfFile: path),
22 | let apiKey = dict[Constants.Secrets.apiKeyKey] as? String
23 | {
24 | return apiKey
25 | }
26 | return ""
27 | }
28 |
29 | private let baseURL = Constants.API.baseURL
30 | private let imageBaseURL = Constants.Image.baseURL
31 |
32 | func fetchMovies(for category: MovieCategory) {
33 | currentCategory = category
34 | currentPage = 1
35 | movies = []
36 | fetchMoviesPage()
37 | }
38 |
39 | func applyFilters() {
40 | currentPage = 1
41 | movies = []
42 | fetchMoviesPage()
43 | }
44 |
45 | func fetchMoviesPage() {
46 | guard currentPage <= totalPages else { return }
47 | var urlString =
48 | "\(baseURL)/discover/movie?api_key=\(apiKey)&page=\(currentPage)&sort_by=\(currentCategory.rawValue)"
49 |
50 | if !selectedGenres.isEmpty {
51 | let genreIds = selectedGenres.compactMap { GenreMapping.genreNameToId[$0] }.joined(
52 | separator: ",")
53 | urlString += "&with_genres=\(genreIds)"
54 | }
55 |
56 | if let year = selectedYear {
57 | urlString += "&primary_release_year=\(year)"
58 | }
59 |
60 | urlString += "&vote_average.gte=\(minRating)"
61 |
62 | guard let url = URL(string: urlString) else { return }
63 |
64 | URLSession.shared.dataTask(with: url) { data, response, error in
65 | if let data = data {
66 | do {
67 | let movieResponse = try JSONDecoder().decode(MovieResponse.self, from: data)
68 | DispatchQueue.main.async {
69 | let newMovies = movieResponse.results.map { movie in
70 | var updatedMovie = movie
71 | updatedMovie.isFavorite = self.favoritesManager.isFavorite(movie)
72 | updatedMovie.categoryId = self.currentCategory.rawValue
73 | return updatedMovie
74 | }
75 | self.movies.append(contentsOf: newMovies)
76 | self.currentPage += 1
77 | self.totalPages = movieResponse.totalPages
78 | }
79 | } catch {
80 | print("Error decoding JSON: \(error)")
81 | }
82 | }
83 | }.resume()
84 | }
85 |
86 | func searchMovies() {
87 | guard !searchQuery.isEmpty else {
88 | fetchMovies(for: currentCategory)
89 | return
90 | }
91 |
92 | currentPage = 1
93 | movies = []
94 |
95 | let urlString =
96 | "\(baseURL)/search/movie?api_key=\(apiKey)&query=\(searchQuery.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) ?? "")"
97 |
98 | guard let url = URL(string: urlString) else { return }
99 |
100 | URLSession.shared.dataTask(with: url) { data, response, error in
101 | if let data = data {
102 | do {
103 | let movieResponse = try JSONDecoder().decode(MovieResponse.self, from: data)
104 | DispatchQueue.main.async {
105 | self.movies = movieResponse.results.map { movie in
106 | var updatedMovie = movie
107 | updatedMovie.isFavorite = self.favoritesManager.isFavorite(movie)
108 | return updatedMovie
109 | }
110 | self.currentPage = 2
111 | self.totalPages = movieResponse.totalPages
112 | }
113 | } catch {
114 | print("Error decoding JSON: \(error)")
115 | }
116 | }
117 | }.resume()
118 | }
119 |
120 | func posterImage(for movie: Movie) -> some View {
121 | Group {
122 | if let posterPath = movie.posterPath {
123 | AsyncImage(url: URL(string: "\(imageBaseURL)\(posterPath)")) { phase in
124 | switch phase {
125 | case .empty:
126 | ProgressView()
127 | case .success(let image):
128 | image
129 | .resizable()
130 | .aspectRatio(contentMode: .fit)
131 | case .failure:
132 | Image(systemName: "photo")
133 | .foregroundColor(.gray)
134 | @unknown default:
135 | EmptyView()
136 | }
137 | }
138 | } else {
139 | Image(systemName: "photo")
140 | .foregroundColor(.gray)
141 | }
142 | }
143 | }
144 | }
145 |
--------------------------------------------------------------------------------
/adntmdbapp/ContentView.swift:
--------------------------------------------------------------------------------
1 | import SwiftUI
2 |
3 | struct ContentView: View {
4 | @StateObject private var favoritesManager = FavoritesManager()
5 | @StateObject private var networkManager: NetworkManager
6 | @EnvironmentObject var themeManager: ThemeManager
7 | @State private var showingSettings = false
8 | @State private var showingSortView = false
9 | @State private var showingFilterView = false
10 | @State private var selectedTab = 0
11 |
12 | init() {
13 | let favoritesManager = FavoritesManager()
14 | _networkManager = StateObject(wrappedValue: NetworkManager(favoritesManager: favoritesManager))
15 | }
16 |
17 | var body: some View {
18 | TabView(selection: $selectedTab) {
19 | NavigationView {
20 | MovieListView(networkManager: networkManager, favoritesManager: favoritesManager)
21 | .navigationBarItems(leading: leadingBarItems, trailing: trailingBarItems)
22 | .navigationBarTitleDisplayMode(.inline)
23 | }
24 | .tabItem {
25 | Label("Movies", systemImage: "film")
26 | }
27 | .tag(0)
28 |
29 | NavigationView {
30 | FavoriteView(favoritesManager: favoritesManager)
31 | .navigationBarItems(trailing: settingsButton)
32 | .navigationBarTitleDisplayMode(.inline)
33 | }
34 | .tabItem {
35 | Label("Favorites", systemImage: "heart.fill")
36 | }
37 | .tag(1)
38 | }
39 | .accentColor(Constants.Colors.primary)
40 | .sheet(isPresented: $showingSettings) {
41 | SettingsView()
42 | .environmentObject(themeManager)
43 | }
44 | .sheet(isPresented: $showingSortView) {
45 | SortView(networkManager: networkManager, isPresented: $showingSortView)
46 | }
47 | .sheet(isPresented: $showingFilterView) {
48 | FilterView(
49 | isPresented: $showingFilterView,
50 | selectedGenres: $networkManager.selectedGenres,
51 | selectedYear: $networkManager.selectedYear,
52 | minRating: $networkManager.minRating
53 | )
54 | .onDisappear {
55 | networkManager.applyFilters()
56 | }
57 | }
58 | }
59 |
60 | private var leadingBarItems: some View {
61 | Text(selectedTab == 0 ? "Movies" : "Favorites")
62 | .font(.title2)
63 | .fontWeight(.bold)
64 | .foregroundColor(Constants.Colors.primary)
65 | }
66 |
67 | private var trailingBarItems: some View {
68 | HStack(spacing: 16) {
69 | Button(action: { showingSortView = true }) {
70 | Image(systemName: "arrow.up.arrow.down")
71 | .imageScale(.large)
72 | }
73 | Button(action: { showingFilterView = true }) {
74 | Image(systemName: "line.3.horizontal.decrease.circle")
75 | .imageScale(.large)
76 | }
77 | settingsButton
78 | }
79 | .foregroundColor(Constants.Colors.primary)
80 | }
81 |
82 | private var settingsButton: some View {
83 | Button(action: { showingSettings = true }) {
84 | Image(systemName: "gearshape.fill")
85 | .imageScale(.large)
86 | }
87 | }
88 | }
89 |
90 | struct MovieListView: View {
91 | @ObservedObject var networkManager: NetworkManager
92 | @ObservedObject var favoritesManager: FavoritesManager
93 | @State private var showingSortView = false
94 | @State private var showingFilterView = false
95 |
96 | var body: some View {
97 | VStack(spacing: 0) {
98 | SearchBar(
99 | text: $networkManager.searchQuery,
100 | onCommit: {
101 | networkManager.searchMovies()
102 | }
103 | )
104 | .padding()
105 | .background(Color(UIColor.secondarySystemBackground))
106 |
107 | ScrollView {
108 | LazyVStack(spacing: 16) {
109 | ForEach(networkManager.movies) { movie in
110 | NavigationLink(
111 | destination: MovieDetailView(
112 | networkManager: networkManager,
113 | favoritesManager: favoritesManager,
114 | movie: .constant(movie)
115 | )
116 | ) {
117 | MovieRowView(
118 | movie: movie,
119 | networkManager: networkManager,
120 | favoritesManager: favoritesManager
121 | )
122 | }
123 | .buttonStyle(PlainButtonStyle())
124 | }
125 | if networkManager.currentPage <= networkManager.totalPages {
126 | ProgressView()
127 | .frame(maxWidth: .infinity, alignment: .center)
128 | .onAppear {
129 | networkManager.fetchMoviesPage()
130 | }
131 | }
132 | }
133 | .padding()
134 | }
135 | }
136 | .refreshable {
137 | await refreshMovies()
138 | }
139 | }
140 |
141 | func refreshMovies() async {
142 | networkManager.currentPage = 1
143 | networkManager.movies = []
144 | networkManager.fetchMoviesPage()
145 | }
146 | }
147 |
148 | struct MovieRowView: View {
149 | let movie: Movie
150 | @ObservedObject var networkManager: NetworkManager
151 | @ObservedObject var favoritesManager: FavoritesManager
152 | @State private var isAnimating = false
153 |
154 | var body: some View {
155 | HStack(spacing: 16) {
156 | networkManager.posterImage(for: movie)
157 | .frame(width: 80, height: 120)
158 | .cornerRadius(8)
159 | .shadow(radius: 4)
160 |
161 | VStack(alignment: .leading, spacing: 8) {
162 | Text(movie.title)
163 | .font(.headline)
164 | .lineLimit(2)
165 |
166 | HStack {
167 | Image(systemName: "star.fill")
168 | .foregroundColor(.yellow)
169 | Text(String(format: "%.1f", movie.rating))
170 | .font(.subheadline)
171 | Spacer()
172 | Text(formattedReleaseDate)
173 | .font(.caption)
174 | .foregroundColor(.secondary)
175 | }
176 |
177 | Text(movie.overview)
178 | .font(.caption)
179 | .foregroundColor(.secondary)
180 | .lineLimit(3)
181 | }
182 |
183 | VStack {
184 | Spacer()
185 | favoriteButton
186 | Spacer()
187 | }
188 | }
189 | .padding()
190 | .background(Color(UIColor.secondarySystemBackground))
191 | .cornerRadius(12)
192 | .shadow(color: Color.black.opacity(0.1), radius: 5, x: 0, y: 2)
193 | }
194 |
195 | private var favoriteButton: some View {
196 | Button(action: {
197 | withAnimation(.spring(response: 0.3, dampingFraction: 0.6)) {
198 | favoritesManager.toggleFavorite(for: movie)
199 | isAnimating = true
200 | }
201 | DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) {
202 | isAnimating = false
203 | }
204 | }) {
205 | Image(systemName: favoritesManager.isFavorite(movie) ? "heart.fill" : "heart")
206 | .foregroundColor(favoritesManager.isFavorite(movie) ? .red : .gray)
207 | .scaleEffect(isAnimating ? 1.3 : 1.0)
208 | .frame(width: 44, height: 44)
209 | .background(Color(UIColor.systemBackground))
210 | .clipShape(Circle())
211 | .shadow(color: Color.black.opacity(0.1), radius: 3, x: 0, y: 2)
212 | }
213 | .buttonStyle(PlainButtonStyle())
214 | }
215 |
216 | private var formattedReleaseDate: String {
217 | let dateFormatter = DateFormatter()
218 | dateFormatter.dateFormat = "yyyy-MM-dd"
219 | if let date = dateFormatter.date(from: movie.releaseDate) {
220 | dateFormatter.dateFormat = "MMM d, yyyy"
221 | return dateFormatter.string(from: date)
222 | }
223 | return movie.releaseDate
224 | }
225 | }
226 |
227 | struct SearchBar: View {
228 | @Binding var text: String
229 | var onCommit: () -> Void
230 |
231 | var body: some View {
232 | HStack {
233 | Image(systemName: "magnifyingglass")
234 | .foregroundColor(.gray)
235 | TextField("Search movies...", text: $text, onCommit: onCommit)
236 | .textFieldStyle(PlainTextFieldStyle())
237 | .autocapitalization(.none)
238 | .disableAutocorrection(true)
239 | if !text.isEmpty {
240 | Button(action: {
241 | text = ""
242 | onCommit()
243 | }) {
244 | Image(systemName: "xmark.circle.fill")
245 | .foregroundColor(.gray)
246 | }
247 | }
248 | }
249 | .padding(12)
250 | .background(Color(UIColor.systemBackground))
251 | .cornerRadius(10)
252 | .overlay(
253 | RoundedRectangle(cornerRadius: 10)
254 | .stroke(Color.gray.opacity(0.3), lineWidth: 1)
255 | )
256 | .shadow(color: Color.black.opacity(0.1), radius: 3, x: 0, y: 2)
257 | }
258 | }
259 |
--------------------------------------------------------------------------------
/adntmdbapp.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 56;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 6A4A38292C9C7308001FA9A9 /* adntmdbappApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A4A38282C9C7308001FA9A9 /* adntmdbappApp.swift */; };
11 | 6A4A382B2C9C7308001FA9A9 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A4A382A2C9C7308001FA9A9 /* ContentView.swift */; };
12 | 6A4A382D2C9C7309001FA9A9 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6A4A382C2C9C7309001FA9A9 /* Assets.xcassets */; };
13 | 6A4A38302C9C7309001FA9A9 /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6A4A382F2C9C7309001FA9A9 /* Preview Assets.xcassets */; };
14 | 6A57BD022C9CD24200EE13AA /* GenreMapping.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A57BD002C9CD24200EE13AA /* GenreMapping.swift */; };
15 | 6A57BD032C9CD24200EE13AA /* MovieCategory.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A57BD012C9CD24200EE13AA /* MovieCategory.swift */; };
16 | 6A57BD062C9CD34D00EE13AA /* MovieStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A57BD042C9CD34D00EE13AA /* MovieStore.swift */; };
17 | 6A57BD072C9CD34D00EE13AA /* FavoritesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6A57BD052C9CD34D00EE13AA /* FavoritesManager.swift */; };
18 | 6AE98CD12C9C7F2900E96343 /* Movie.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AE98CCF2C9C7F2900E96343 /* Movie.swift */; };
19 | 6AE98CD22C9C7F2900E96343 /* NetworkManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AE98CD02C9C7F2900E96343 /* NetworkManager.swift */; };
20 | 6AE98CD42C9C81BB00E96343 /* Secrets.plist in Resources */ = {isa = PBXBuildFile; fileRef = 6AE98CD32C9C81BB00E96343 /* Secrets.plist */; };
21 | 6AE98CF02C9C88D200E96343 /* MovieDetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AE98CEF2C9C88D200E96343 /* MovieDetailView.swift */; };
22 | 6AE98CF22C9C89ED00E96343 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AE98CF12C9C89ED00E96343 /* Constants.swift */; };
23 | 6AE98CF92C9C8D5700E96343 /* Atlantis in Frameworks */ = {isa = PBXBuildFile; productRef = 6AE98CF82C9C8D5700E96343 /* Atlantis */; };
24 | 6AE98CFE2C9CA04E00E96343 /* FavoriteView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AE98CFD2C9CA04E00E96343 /* FavoriteView.swift */; };
25 | 6AE98D002C9CA14100E96343 /* SortView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AE98CFF2C9CA14100E96343 /* SortView.swift */; };
26 | 6AE98D052C9CB95600E96343 /* FilterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AE98D032C9CB95600E96343 /* FilterView.swift */; };
27 | 6AE98D062C9CB95600E96343 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 6AE98D042C9CB95600E96343 /* Localizable.strings */; };
28 | 6AE98D0B2C9CC3E600E96343 /* ThemeManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AE98D092C9CC3E600E96343 /* ThemeManager.swift */; };
29 | 6AE98D0C2C9CC3E600E96343 /* ThemeSettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AE98D0A2C9CC3E600E96343 /* ThemeSettingsView.swift */; };
30 | 6AE98D0E2C9CC42B00E96343 /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6AE98D0D2C9CC42B00E96343 /* SettingsView.swift */; };
31 | /* End PBXBuildFile section */
32 |
33 | /* Begin PBXFileReference section */
34 | 6A4A38252C9C7308001FA9A9 /* adntmdbapp.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = adntmdbapp.app; sourceTree = BUILT_PRODUCTS_DIR; };
35 | 6A4A38282C9C7308001FA9A9 /* adntmdbappApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = adntmdbappApp.swift; sourceTree = ""; };
36 | 6A4A382A2C9C7308001FA9A9 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; };
37 | 6A4A382C2C9C7309001FA9A9 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
38 | 6A4A382F2C9C7309001FA9A9 /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; };
39 | 6A57BD002C9CD24200EE13AA /* GenreMapping.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GenreMapping.swift; sourceTree = ""; };
40 | 6A57BD012C9CD24200EE13AA /* MovieCategory.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MovieCategory.swift; sourceTree = ""; };
41 | 6A57BD042C9CD34D00EE13AA /* MovieStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MovieStore.swift; sourceTree = ""; };
42 | 6A57BD052C9CD34D00EE13AA /* FavoritesManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FavoritesManager.swift; sourceTree = ""; };
43 | 6AE98CCF2C9C7F2900E96343 /* Movie.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Movie.swift; sourceTree = ""; };
44 | 6AE98CD02C9C7F2900E96343 /* NetworkManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NetworkManager.swift; sourceTree = ""; };
45 | 6AE98CD32C9C81BB00E96343 /* Secrets.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Secrets.plist; sourceTree = ""; };
46 | 6AE98CEF2C9C88D200E96343 /* MovieDetailView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MovieDetailView.swift; sourceTree = ""; };
47 | 6AE98CF12C9C89ED00E96343 /* Constants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; };
48 | 6AE98CFA2C9C8D8A00E96343 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = Info.plist; sourceTree = ""; };
49 | 6AE98CFD2C9CA04E00E96343 /* FavoriteView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FavoriteView.swift; sourceTree = ""; };
50 | 6AE98CFF2C9CA14100E96343 /* SortView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SortView.swift; sourceTree = ""; };
51 | 6AE98D032C9CB95600E96343 /* FilterView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FilterView.swift; sourceTree = ""; };
52 | 6AE98D042C9CB95600E96343 /* Localizable.strings */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; path = Localizable.strings; sourceTree = ""; };
53 | 6AE98D092C9CC3E600E96343 /* ThemeManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThemeManager.swift; sourceTree = ""; };
54 | 6AE98D0A2C9CC3E600E96343 /* ThemeSettingsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ThemeSettingsView.swift; sourceTree = ""; };
55 | 6AE98D0D2C9CC42B00E96343 /* SettingsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = ""; };
56 | /* End PBXFileReference section */
57 |
58 | /* Begin PBXFrameworksBuildPhase section */
59 | 6A4A38222C9C7308001FA9A9 /* Frameworks */ = {
60 | isa = PBXFrameworksBuildPhase;
61 | buildActionMask = 2147483647;
62 | files = (
63 | 6AE98CF92C9C8D5700E96343 /* Atlantis in Frameworks */,
64 | );
65 | runOnlyForDeploymentPostprocessing = 0;
66 | };
67 | /* End PBXFrameworksBuildPhase section */
68 |
69 | /* Begin PBXGroup section */
70 | 6A4A381C2C9C7308001FA9A9 = {
71 | isa = PBXGroup;
72 | children = (
73 | 6AE98CD32C9C81BB00E96343 /* Secrets.plist */,
74 | 6A4A38272C9C7308001FA9A9 /* adntmdbapp */,
75 | 6A4A38262C9C7308001FA9A9 /* Products */,
76 | );
77 | sourceTree = "";
78 | };
79 | 6A4A38262C9C7308001FA9A9 /* Products */ = {
80 | isa = PBXGroup;
81 | children = (
82 | 6A4A38252C9C7308001FA9A9 /* adntmdbapp.app */,
83 | );
84 | name = Products;
85 | sourceTree = "";
86 | };
87 | 6A4A38272C9C7308001FA9A9 /* adntmdbapp */ = {
88 | isa = PBXGroup;
89 | children = (
90 | 6A57BD052C9CD34D00EE13AA /* FavoritesManager.swift */,
91 | 6A57BD042C9CD34D00EE13AA /* MovieStore.swift */,
92 | 6A57BD002C9CD24200EE13AA /* GenreMapping.swift */,
93 | 6A57BD012C9CD24200EE13AA /* MovieCategory.swift */,
94 | 6AE98D0D2C9CC42B00E96343 /* SettingsView.swift */,
95 | 6AE98D092C9CC3E600E96343 /* ThemeManager.swift */,
96 | 6AE98D0A2C9CC3E600E96343 /* ThemeSettingsView.swift */,
97 | 6AE98D032C9CB95600E96343 /* FilterView.swift */,
98 | 6AE98D042C9CB95600E96343 /* Localizable.strings */,
99 | 6AE98CFF2C9CA14100E96343 /* SortView.swift */,
100 | 6AE98CFD2C9CA04E00E96343 /* FavoriteView.swift */,
101 | 6AE98CFA2C9C8D8A00E96343 /* Info.plist */,
102 | 6AE98CF12C9C89ED00E96343 /* Constants.swift */,
103 | 6AE98CEF2C9C88D200E96343 /* MovieDetailView.swift */,
104 | 6AE98CCF2C9C7F2900E96343 /* Movie.swift */,
105 | 6AE98CD02C9C7F2900E96343 /* NetworkManager.swift */,
106 | 6A4A38282C9C7308001FA9A9 /* adntmdbappApp.swift */,
107 | 6A4A382A2C9C7308001FA9A9 /* ContentView.swift */,
108 | 6A4A382C2C9C7309001FA9A9 /* Assets.xcassets */,
109 | 6A4A382E2C9C7309001FA9A9 /* Preview Content */,
110 | );
111 | path = adntmdbapp;
112 | sourceTree = "";
113 | };
114 | 6A4A382E2C9C7309001FA9A9 /* Preview Content */ = {
115 | isa = PBXGroup;
116 | children = (
117 | 6A4A382F2C9C7309001FA9A9 /* Preview Assets.xcassets */,
118 | );
119 | path = "Preview Content";
120 | sourceTree = "";
121 | };
122 | /* End PBXGroup section */
123 |
124 | /* Begin PBXNativeTarget section */
125 | 6A4A38242C9C7308001FA9A9 /* adntmdbapp */ = {
126 | isa = PBXNativeTarget;
127 | buildConfigurationList = 6A4A38332C9C7309001FA9A9 /* Build configuration list for PBXNativeTarget "adntmdbapp" */;
128 | buildPhases = (
129 | 6A4A38212C9C7308001FA9A9 /* Sources */,
130 | 6A4A38222C9C7308001FA9A9 /* Frameworks */,
131 | 6A4A38232C9C7308001FA9A9 /* Resources */,
132 | );
133 | buildRules = (
134 | );
135 | dependencies = (
136 | );
137 | name = adntmdbapp;
138 | packageProductDependencies = (
139 | 6AE98CF82C9C8D5700E96343 /* Atlantis */,
140 | );
141 | productName = adntmdbapp;
142 | productReference = 6A4A38252C9C7308001FA9A9 /* adntmdbapp.app */;
143 | productType = "com.apple.product-type.application";
144 | };
145 | /* End PBXNativeTarget section */
146 |
147 | /* Begin PBXProject section */
148 | 6A4A381D2C9C7308001FA9A9 /* Project object */ = {
149 | isa = PBXProject;
150 | attributes = {
151 | BuildIndependentTargetsInParallel = 1;
152 | LastSwiftUpdateCheck = 1530;
153 | LastUpgradeCheck = 1530;
154 | TargetAttributes = {
155 | 6A4A38242C9C7308001FA9A9 = {
156 | CreatedOnToolsVersion = 15.3;
157 | };
158 | };
159 | };
160 | buildConfigurationList = 6A4A38202C9C7308001FA9A9 /* Build configuration list for PBXProject "adntmdbapp" */;
161 | compatibilityVersion = "Xcode 14.0";
162 | developmentRegion = en;
163 | hasScannedForEncodings = 0;
164 | knownRegions = (
165 | en,
166 | Base,
167 | );
168 | mainGroup = 6A4A381C2C9C7308001FA9A9;
169 | packageReferences = (
170 | 6AE98CF72C9C8D5700E96343 /* XCRemoteSwiftPackageReference "atlantis" */,
171 | );
172 | productRefGroup = 6A4A38262C9C7308001FA9A9 /* Products */;
173 | projectDirPath = "";
174 | projectRoot = "";
175 | targets = (
176 | 6A4A38242C9C7308001FA9A9 /* adntmdbapp */,
177 | );
178 | };
179 | /* End PBXProject section */
180 |
181 | /* Begin PBXResourcesBuildPhase section */
182 | 6A4A38232C9C7308001FA9A9 /* Resources */ = {
183 | isa = PBXResourcesBuildPhase;
184 | buildActionMask = 2147483647;
185 | files = (
186 | 6A4A38302C9C7309001FA9A9 /* Preview Assets.xcassets in Resources */,
187 | 6AE98D062C9CB95600E96343 /* Localizable.strings in Resources */,
188 | 6AE98CD42C9C81BB00E96343 /* Secrets.plist in Resources */,
189 | 6A4A382D2C9C7309001FA9A9 /* Assets.xcassets in Resources */,
190 | );
191 | runOnlyForDeploymentPostprocessing = 0;
192 | };
193 | /* End PBXResourcesBuildPhase section */
194 |
195 | /* Begin PBXSourcesBuildPhase section */
196 | 6A4A38212C9C7308001FA9A9 /* Sources */ = {
197 | isa = PBXSourcesBuildPhase;
198 | buildActionMask = 2147483647;
199 | files = (
200 | 6AE98CF02C9C88D200E96343 /* MovieDetailView.swift in Sources */,
201 | 6AE98D0C2C9CC3E600E96343 /* ThemeSettingsView.swift in Sources */,
202 | 6AE98CD22C9C7F2900E96343 /* NetworkManager.swift in Sources */,
203 | 6AE98CFE2C9CA04E00E96343 /* FavoriteView.swift in Sources */,
204 | 6A57BD062C9CD34D00EE13AA /* MovieStore.swift in Sources */,
205 | 6A57BD022C9CD24200EE13AA /* GenreMapping.swift in Sources */,
206 | 6A4A382B2C9C7308001FA9A9 /* ContentView.swift in Sources */,
207 | 6AE98D052C9CB95600E96343 /* FilterView.swift in Sources */,
208 | 6AE98D002C9CA14100E96343 /* SortView.swift in Sources */,
209 | 6A57BD032C9CD24200EE13AA /* MovieCategory.swift in Sources */,
210 | 6A4A38292C9C7308001FA9A9 /* adntmdbappApp.swift in Sources */,
211 | 6AE98D0B2C9CC3E600E96343 /* ThemeManager.swift in Sources */,
212 | 6AE98D0E2C9CC42B00E96343 /* SettingsView.swift in Sources */,
213 | 6AE98CD12C9C7F2900E96343 /* Movie.swift in Sources */,
214 | 6AE98CF22C9C89ED00E96343 /* Constants.swift in Sources */,
215 | 6A57BD072C9CD34D00EE13AA /* FavoritesManager.swift in Sources */,
216 | );
217 | runOnlyForDeploymentPostprocessing = 0;
218 | };
219 | /* End PBXSourcesBuildPhase section */
220 |
221 | /* Begin XCBuildConfiguration section */
222 | 6A4A38312C9C7309001FA9A9 /* Debug */ = {
223 | isa = XCBuildConfiguration;
224 | buildSettings = {
225 | ALWAYS_SEARCH_USER_PATHS = NO;
226 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
227 | CLANG_ANALYZER_NONNULL = YES;
228 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
229 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
230 | CLANG_ENABLE_MODULES = YES;
231 | CLANG_ENABLE_OBJC_ARC = YES;
232 | CLANG_ENABLE_OBJC_WEAK = YES;
233 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
234 | CLANG_WARN_BOOL_CONVERSION = YES;
235 | CLANG_WARN_COMMA = YES;
236 | CLANG_WARN_CONSTANT_CONVERSION = YES;
237 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
238 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
239 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
240 | CLANG_WARN_EMPTY_BODY = YES;
241 | CLANG_WARN_ENUM_CONVERSION = YES;
242 | CLANG_WARN_INFINITE_RECURSION = YES;
243 | CLANG_WARN_INT_CONVERSION = YES;
244 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
245 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
246 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
247 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
248 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
249 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
250 | CLANG_WARN_STRICT_PROTOTYPES = YES;
251 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
252 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
253 | CLANG_WARN_UNREACHABLE_CODE = YES;
254 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
255 | COPY_PHASE_STRIP = NO;
256 | DEBUG_INFORMATION_FORMAT = dwarf;
257 | ENABLE_STRICT_OBJC_MSGSEND = YES;
258 | ENABLE_TESTABILITY = YES;
259 | ENABLE_USER_SCRIPT_SANDBOXING = YES;
260 | GCC_C_LANGUAGE_STANDARD = gnu17;
261 | GCC_DYNAMIC_NO_PIC = NO;
262 | GCC_NO_COMMON_BLOCKS = YES;
263 | GCC_OPTIMIZATION_LEVEL = 0;
264 | GCC_PREPROCESSOR_DEFINITIONS = (
265 | "DEBUG=1",
266 | "$(inherited)",
267 | );
268 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
269 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
270 | GCC_WARN_UNDECLARED_SELECTOR = YES;
271 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
272 | GCC_WARN_UNUSED_FUNCTION = YES;
273 | GCC_WARN_UNUSED_VARIABLE = YES;
274 | IPHONEOS_DEPLOYMENT_TARGET = 17.4;
275 | LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
276 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
277 | MTL_FAST_MATH = YES;
278 | ONLY_ACTIVE_ARCH = YES;
279 | SDKROOT = iphoneos;
280 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
281 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
282 | };
283 | name = Debug;
284 | };
285 | 6A4A38322C9C7309001FA9A9 /* Release */ = {
286 | isa = XCBuildConfiguration;
287 | buildSettings = {
288 | ALWAYS_SEARCH_USER_PATHS = NO;
289 | ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
290 | CLANG_ANALYZER_NONNULL = YES;
291 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
292 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
293 | CLANG_ENABLE_MODULES = YES;
294 | CLANG_ENABLE_OBJC_ARC = YES;
295 | CLANG_ENABLE_OBJC_WEAK = YES;
296 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
297 | CLANG_WARN_BOOL_CONVERSION = YES;
298 | CLANG_WARN_COMMA = YES;
299 | CLANG_WARN_CONSTANT_CONVERSION = YES;
300 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
301 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
302 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
303 | CLANG_WARN_EMPTY_BODY = YES;
304 | CLANG_WARN_ENUM_CONVERSION = YES;
305 | CLANG_WARN_INFINITE_RECURSION = YES;
306 | CLANG_WARN_INT_CONVERSION = YES;
307 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
308 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
309 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
310 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
311 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
312 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
313 | CLANG_WARN_STRICT_PROTOTYPES = YES;
314 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
315 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
316 | CLANG_WARN_UNREACHABLE_CODE = YES;
317 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
318 | COPY_PHASE_STRIP = NO;
319 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
320 | ENABLE_NS_ASSERTIONS = NO;
321 | ENABLE_STRICT_OBJC_MSGSEND = YES;
322 | ENABLE_USER_SCRIPT_SANDBOXING = YES;
323 | GCC_C_LANGUAGE_STANDARD = gnu17;
324 | GCC_NO_COMMON_BLOCKS = YES;
325 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
326 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
327 | GCC_WARN_UNDECLARED_SELECTOR = YES;
328 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
329 | GCC_WARN_UNUSED_FUNCTION = YES;
330 | GCC_WARN_UNUSED_VARIABLE = YES;
331 | IPHONEOS_DEPLOYMENT_TARGET = 17.4;
332 | LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
333 | MTL_ENABLE_DEBUG_INFO = NO;
334 | MTL_FAST_MATH = YES;
335 | SDKROOT = iphoneos;
336 | SWIFT_COMPILATION_MODE = wholemodule;
337 | VALIDATE_PRODUCT = YES;
338 | };
339 | name = Release;
340 | };
341 | 6A4A38342C9C7309001FA9A9 /* Debug */ = {
342 | isa = XCBuildConfiguration;
343 | buildSettings = {
344 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
345 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
346 | CODE_SIGN_STYLE = Automatic;
347 | CURRENT_PROJECT_VERSION = 1;
348 | DEVELOPMENT_ASSET_PATHS = "\"adntmdbapp/Preview Content\"";
349 | DEVELOPMENT_TEAM = MDNUUAHTT6;
350 | ENABLE_PREVIEWS = YES;
351 | GENERATE_INFOPLIST_FILE = YES;
352 | INFOPLIST_FILE = adntmdbapp/Info.plist;
353 | INFOPLIST_KEY_NSLocalNetworkUsageDescription = "Atlantis uses Bonjour Service to send your recorded traffic to Proxyman app.";
354 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
355 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
356 | INFOPLIST_KEY_UILaunchScreen_Generation = YES;
357 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
358 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
359 | LD_RUNPATH_SEARCH_PATHS = (
360 | "$(inherited)",
361 | "@executable_path/Frameworks",
362 | );
363 | MARKETING_VERSION = 1.0;
364 | PRODUCT_BUNDLE_IDENTIFIER = example.adntmdbapp;
365 | PRODUCT_NAME = "$(TARGET_NAME)";
366 | SWIFT_EMIT_LOC_STRINGS = YES;
367 | SWIFT_VERSION = 5.0;
368 | TARGETED_DEVICE_FAMILY = "1,2";
369 | };
370 | name = Debug;
371 | };
372 | 6A4A38352C9C7309001FA9A9 /* Release */ = {
373 | isa = XCBuildConfiguration;
374 | buildSettings = {
375 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
376 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
377 | CODE_SIGN_STYLE = Automatic;
378 | CURRENT_PROJECT_VERSION = 1;
379 | DEVELOPMENT_ASSET_PATHS = "\"adntmdbapp/Preview Content\"";
380 | DEVELOPMENT_TEAM = MDNUUAHTT6;
381 | ENABLE_PREVIEWS = YES;
382 | GENERATE_INFOPLIST_FILE = YES;
383 | INFOPLIST_FILE = adntmdbapp/Info.plist;
384 | INFOPLIST_KEY_NSLocalNetworkUsageDescription = "Atlantis uses Bonjour Service to send your recorded traffic to Proxyman app.";
385 | INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
386 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
387 | INFOPLIST_KEY_UILaunchScreen_Generation = YES;
388 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
389 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
390 | LD_RUNPATH_SEARCH_PATHS = (
391 | "$(inherited)",
392 | "@executable_path/Frameworks",
393 | );
394 | MARKETING_VERSION = 1.0;
395 | PRODUCT_BUNDLE_IDENTIFIER = example.adntmdbapp;
396 | PRODUCT_NAME = "$(TARGET_NAME)";
397 | SWIFT_EMIT_LOC_STRINGS = YES;
398 | SWIFT_VERSION = 5.0;
399 | TARGETED_DEVICE_FAMILY = "1,2";
400 | };
401 | name = Release;
402 | };
403 | /* End XCBuildConfiguration section */
404 |
405 | /* Begin XCConfigurationList section */
406 | 6A4A38202C9C7308001FA9A9 /* Build configuration list for PBXProject "adntmdbapp" */ = {
407 | isa = XCConfigurationList;
408 | buildConfigurations = (
409 | 6A4A38312C9C7309001FA9A9 /* Debug */,
410 | 6A4A38322C9C7309001FA9A9 /* Release */,
411 | );
412 | defaultConfigurationIsVisible = 0;
413 | defaultConfigurationName = Release;
414 | };
415 | 6A4A38332C9C7309001FA9A9 /* Build configuration list for PBXNativeTarget "adntmdbapp" */ = {
416 | isa = XCConfigurationList;
417 | buildConfigurations = (
418 | 6A4A38342C9C7309001FA9A9 /* Debug */,
419 | 6A4A38352C9C7309001FA9A9 /* Release */,
420 | );
421 | defaultConfigurationIsVisible = 0;
422 | defaultConfigurationName = Release;
423 | };
424 | /* End XCConfigurationList section */
425 |
426 | /* Begin XCRemoteSwiftPackageReference section */
427 | 6AE98CF72C9C8D5700E96343 /* XCRemoteSwiftPackageReference "atlantis" */ = {
428 | isa = XCRemoteSwiftPackageReference;
429 | repositoryURL = "https://github.com/ProxymanApp/atlantis";
430 | requirement = {
431 | kind = upToNextMajorVersion;
432 | minimumVersion = 1.25.1;
433 | };
434 | };
435 | /* End XCRemoteSwiftPackageReference section */
436 |
437 | /* Begin XCSwiftPackageProductDependency section */
438 | 6AE98CF82C9C8D5700E96343 /* Atlantis */ = {
439 | isa = XCSwiftPackageProductDependency;
440 | package = 6AE98CF72C9C8D5700E96343 /* XCRemoteSwiftPackageReference "atlantis" */;
441 | productName = Atlantis;
442 | };
443 | /* End XCSwiftPackageProductDependency section */
444 | };
445 | rootObject = 6A4A381D2C9C7308001FA9A9 /* Project object */;
446 | }
447 |
--------------------------------------------------------------------------------