├── 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 |
Detail ScreenGrid View List Screen
Simple View List ScreenFilter Bottom Sheet
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 | --------------------------------------------------------------------------------