diff --git a/Darsey-MovieDB/Darsey-MovieDB.xcodeproj/project.pbxproj b/Darsey-MovieDB/Darsey-MovieDB.xcodeproj/project.pbxproj new file mode 100644 index 00000000..3386f3ea --- /dev/null +++ b/Darsey-MovieDB/Darsey-MovieDB.xcodeproj/project.pbxproj @@ -0,0 +1,839 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 50; + objects = { + +/* Begin PBXBuildFile section */ + 486A882F26166A9D00691E14 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 486A883126166A9D00691E14 /* Localizable.strings */; }; + 4872D16025F6B06A007DB11A /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4872D15F25F6B06A007DB11A /* AppDelegate.swift */; }; + 4872D16225F6B06A007DB11A /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4872D16125F6B06A007DB11A /* SceneDelegate.swift */; }; + 4872D16A25F6B06A007DB11A /* Darsey_MovieDB.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 4872D16825F6B06A007DB11A /* Darsey_MovieDB.xcdatamodeld */; }; + 4872D16C25F6B06D007DB11A /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 4872D16B25F6B06D007DB11A /* Assets.xcassets */; }; + 4872D16F25F6B06D007DB11A /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 4872D16D25F6B06D007DB11A /* LaunchScreen.storyboard */; }; + 4872D17A25F6B06E007DB11A /* Darsey_MovieDBTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4872D17925F6B06E007DB11A /* Darsey_MovieDBTests.swift */; }; + 4872D18525F6B06E007DB11A /* Darsey_MovieDBUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4872D18425F6B06E007DB11A /* Darsey_MovieDBUITests.swift */; }; + 4872D19925F6B197007DB11A /* Movies.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4872D19825F6B197007DB11A /* Movies.swift */; }; + 4872D1A125F6B223007DB11A /* SelectedMovie.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4872D1A025F6B223007DB11A /* SelectedMovie.swift */; }; + 4872D1A625F6B255007DB11A /* Trailers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4872D1A525F6B255007DB11A /* Trailers.swift */; }; + 4872D1AF25F7814E007DB11A /* MovieService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4872D1AE25F7814D007DB11A /* MovieService.swift */; }; + 4872D1B725F781A6007DB11A /* PlistService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4872D1B625F781A6007DB11A /* PlistService.swift */; }; + 4872D1BC25F7820C007DB11A /* key-info.plist in Resources */ = {isa = PBXBuildFile; fileRef = 4872D1BB25F7820C007DB11A /* key-info.plist */; }; + 4872D1C225F783C1007DB11A /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4872D1C125F783C1007DB11A /* Constants.swift */; }; + 4872D1CA25F78422007DB11A /* APIKey.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4872D1C925F78422007DB11A /* APIKey.swift */; }; + 4872D1CF25F7844E007DB11A /* APIError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4872D1CE25F7844E007DB11A /* APIError.swift */; }; + 4872D1D925F7867C007DB11A /* MoviesVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4872D1D725F7867C007DB11A /* MoviesVC.swift */; }; + 4872D1DA25F7867C007DB11A /* MoviesVC.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4872D1D825F7867C007DB11A /* MoviesVC.xib */; }; + 4872D1E325F788DC007DB11A /* Protocols.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4872D1E225F788DC007DB11A /* Protocols.swift */; }; + 4872D1ED25F78BF6007DB11A /* MovieCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4872D1EB25F78BF6007DB11A /* MovieCell.swift */; }; + 4872D1EE25F78BF6007DB11A /* MovieCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4872D1EC25F78BF6007DB11A /* MovieCell.xib */; }; + 4872D1F925F78E5F007DB11A /* MoviesVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4872D1F825F78E5F007DB11A /* MoviesVM.swift */; }; + 4872D20025F78F12007DB11A /* MoviesHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4872D1FE25F78F12007DB11A /* MoviesHeader.swift */; }; + 4872D20125F78F12007DB11A /* MoviesHeader.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4872D1FF25F78F12007DB11A /* MoviesHeader.xib */; }; + 4872D21D25F791EA007DB11A /* Languages.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4872D21C25F791EA007DB11A /* Languages.swift */; }; + 4872D27325F7A022007DB11A /* ActivityIndicator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4872D27225F7A022007DB11A /* ActivityIndicator.swift */; }; + 4872D27B25F7A06B007DB11A /* ViewProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4872D27A25F7A06B007DB11A /* ViewProperties.swift */; }; + 4872D28525F7A17F007DB11A /* SelectedMovieVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4872D28325F7A17F007DB11A /* SelectedMovieVC.swift */; }; + 4872D28625F7A17F007DB11A /* SelectedMovieVC.xib in Resources */ = {isa = PBXBuildFile; fileRef = 4872D28425F7A17F007DB11A /* SelectedMovieVC.xib */; }; + 4872D28B25F7A1D5007DB11A /* SelectedMovieVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = 4872D28A25F7A1D5007DB11A /* SelectedMovieVM.swift */; }; + 48759E2025F7BE9600239CAB /* TrailerVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 48759E1E25F7BE9600239CAB /* TrailerVC.swift */; }; + 48759E2125F7BE9600239CAB /* TrailerVC.xib in Resources */ = {isa = PBXBuildFile; fileRef = 48759E1F25F7BE9600239CAB /* TrailerVC.xib */; }; + 48759E3B25F7DD6C00239CAB /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 48759E3A25F7DD6C00239CAB /* Main.storyboard */; }; + 48759E4025F7E2B100239CAB /* SelectedMovieVMTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 48759E3F25F7E2B100239CAB /* SelectedMovieVMTests.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 4872D17625F6B06E007DB11A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4872D15425F6B06A007DB11A /* Project object */; + proxyType = 1; + remoteGlobalIDString = 4872D15B25F6B06A007DB11A; + remoteInfo = "Darsey-MovieDB"; + }; + 4872D18125F6B06E007DB11A /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 4872D15425F6B06A007DB11A /* Project object */; + proxyType = 1; + remoteGlobalIDString = 4872D15B25F6B06A007DB11A; + remoteInfo = "Darsey-MovieDB"; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 486A882726165BF500691E14 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/LaunchScreen.strings; sourceTree = ""; }; + 486A882B26165C3E00691E14 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/LaunchScreen.strings; sourceTree = ""; }; + 486A883026166A9D00691E14 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; + 486A883526166AA300691E14 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/Localizable.strings; sourceTree = ""; }; + 486A883626166AA500691E14 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Localizable.strings; sourceTree = ""; }; + 4872D15C25F6B06A007DB11A /* Darsey-MovieDB.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Darsey-MovieDB.app"; sourceTree = BUILT_PRODUCTS_DIR; }; + 4872D15F25F6B06A007DB11A /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 4872D16125F6B06A007DB11A /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; }; + 4872D16925F6B06A007DB11A /* Darsey_MovieDB.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Darsey_MovieDB.xcdatamodel; sourceTree = ""; }; + 4872D16B25F6B06D007DB11A /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 4872D16E25F6B06D007DB11A /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 4872D17025F6B06D007DB11A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 4872D17525F6B06E007DB11A /* Darsey-MovieDBTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Darsey-MovieDBTests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; + 4872D17925F6B06E007DB11A /* Darsey_MovieDBTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Darsey_MovieDBTests.swift; sourceTree = ""; }; + 4872D17B25F6B06E007DB11A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 4872D18025F6B06E007DB11A /* Darsey-MovieDBUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Darsey-MovieDBUITests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; + 4872D18425F6B06E007DB11A /* Darsey_MovieDBUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Darsey_MovieDBUITests.swift; sourceTree = ""; }; + 4872D18625F6B06E007DB11A /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 4872D19825F6B197007DB11A /* Movies.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Movies.swift; sourceTree = ""; }; + 4872D1A025F6B223007DB11A /* SelectedMovie.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectedMovie.swift; sourceTree = ""; }; + 4872D1A525F6B255007DB11A /* Trailers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Trailers.swift; sourceTree = ""; }; + 4872D1AE25F7814D007DB11A /* MovieService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MovieService.swift; sourceTree = ""; }; + 4872D1B625F781A6007DB11A /* PlistService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PlistService.swift; sourceTree = ""; }; + 4872D1BB25F7820C007DB11A /* key-info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = "key-info.plist"; sourceTree = ""; }; + 4872D1C125F783C1007DB11A /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; + 4872D1C925F78422007DB11A /* APIKey.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APIKey.swift; sourceTree = ""; }; + 4872D1CE25F7844E007DB11A /* APIError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = APIError.swift; sourceTree = ""; }; + 4872D1D725F7867C007DB11A /* MoviesVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoviesVC.swift; sourceTree = ""; }; + 4872D1D825F7867C007DB11A /* MoviesVC.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MoviesVC.xib; sourceTree = ""; }; + 4872D1E225F788DC007DB11A /* Protocols.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Protocols.swift; sourceTree = ""; }; + 4872D1EB25F78BF6007DB11A /* MovieCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MovieCell.swift; sourceTree = ""; }; + 4872D1EC25F78BF6007DB11A /* MovieCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MovieCell.xib; sourceTree = ""; }; + 4872D1F825F78E5F007DB11A /* MoviesVM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoviesVM.swift; sourceTree = ""; }; + 4872D1FE25F78F12007DB11A /* MoviesHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MoviesHeader.swift; sourceTree = ""; }; + 4872D1FF25F78F12007DB11A /* MoviesHeader.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = MoviesHeader.xib; sourceTree = ""; }; + 4872D21C25F791EA007DB11A /* Languages.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Languages.swift; sourceTree = ""; }; + 4872D27225F7A022007DB11A /* ActivityIndicator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ActivityIndicator.swift; sourceTree = ""; }; + 4872D27A25F7A06B007DB11A /* ViewProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewProperties.swift; sourceTree = ""; }; + 4872D28325F7A17F007DB11A /* SelectedMovieVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectedMovieVC.swift; sourceTree = ""; }; + 4872D28425F7A17F007DB11A /* SelectedMovieVC.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = SelectedMovieVC.xib; sourceTree = ""; }; + 4872D28A25F7A1D5007DB11A /* SelectedMovieVM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectedMovieVM.swift; sourceTree = ""; }; + 48759E1E25F7BE9600239CAB /* TrailerVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TrailerVC.swift; sourceTree = ""; }; + 48759E1F25F7BE9600239CAB /* TrailerVC.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = TrailerVC.xib; sourceTree = ""; }; + 48759E3A25F7DD6C00239CAB /* Main.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = Main.storyboard; sourceTree = ""; }; + 48759E3F25F7E2B100239CAB /* SelectedMovieVMTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectedMovieVMTests.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 4872D15925F6B06A007DB11A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4872D17225F6B06E007DB11A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4872D17D25F6B06E007DB11A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 4872D15325F6B06A007DB11A = { + isa = PBXGroup; + children = ( + 4872D15E25F6B06A007DB11A /* Darsey-MovieDB */, + 4872D17825F6B06E007DB11A /* Darsey-MovieDBTests */, + 4872D18325F6B06E007DB11A /* Darsey-MovieDBUITests */, + 4872D15D25F6B06A007DB11A /* Products */, + ); + sourceTree = ""; + }; + 4872D15D25F6B06A007DB11A /* Products */ = { + isa = PBXGroup; + children = ( + 4872D15C25F6B06A007DB11A /* Darsey-MovieDB.app */, + 4872D17525F6B06E007DB11A /* Darsey-MovieDBTests.xctest */, + 4872D18025F6B06E007DB11A /* Darsey-MovieDBUITests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 4872D15E25F6B06A007DB11A /* Darsey-MovieDB */ = { + isa = PBXGroup; + children = ( + 4872D1AA25F77DF1007DB11A /* AppDelegates */, + 4872D1E125F788C1007DB11A /* Protocols */, + 4872D20B25F790BE007DB11A /* Language */, + 4872D19725F6B12F007DB11A /* Configuration */, + 4872D19525F6B0F5007DB11A /* Models */, + 4872D1D325F785EB007DB11A /* Views */, + 4872D1C025F7838C007DB11A /* Utilities */, + 4872D19625F6B10D007DB11A /* Services */, + 4872D16B25F6B06D007DB11A /* Assets.xcassets */, + 4872D16D25F6B06D007DB11A /* LaunchScreen.storyboard */, + 4872D17025F6B06D007DB11A /* Info.plist */, + 4872D16825F6B06A007DB11A /* Darsey_MovieDB.xcdatamodeld */, + 48759E3A25F7DD6C00239CAB /* Main.storyboard */, + ); + path = "Darsey-MovieDB"; + sourceTree = ""; + }; + 4872D17825F6B06E007DB11A /* Darsey-MovieDBTests */ = { + isa = PBXGroup; + children = ( + 4872D17925F6B06E007DB11A /* Darsey_MovieDBTests.swift */, + 4872D17B25F6B06E007DB11A /* Info.plist */, + 48759E3F25F7E2B100239CAB /* SelectedMovieVMTests.swift */, + ); + path = "Darsey-MovieDBTests"; + sourceTree = ""; + }; + 4872D18325F6B06E007DB11A /* Darsey-MovieDBUITests */ = { + isa = PBXGroup; + children = ( + 4872D18425F6B06E007DB11A /* Darsey_MovieDBUITests.swift */, + 4872D18625F6B06E007DB11A /* Info.plist */, + ); + path = "Darsey-MovieDBUITests"; + sourceTree = ""; + }; + 4872D19525F6B0F5007DB11A /* Models */ = { + isa = PBXGroup; + children = ( + 4872D19825F6B197007DB11A /* Movies.swift */, + 4872D1A025F6B223007DB11A /* SelectedMovie.swift */, + 4872D1A525F6B255007DB11A /* Trailers.swift */, + ); + path = Models; + sourceTree = ""; + }; + 4872D19625F6B10D007DB11A /* Services */ = { + isa = PBXGroup; + children = ( + 4872D1AE25F7814D007DB11A /* MovieService.swift */, + 4872D1B625F781A6007DB11A /* PlistService.swift */, + 4872D1BB25F7820C007DB11A /* key-info.plist */, + ); + path = Services; + sourceTree = ""; + }; + 4872D19725F6B12F007DB11A /* Configuration */ = { + isa = PBXGroup; + children = ( + 4872D21C25F791EA007DB11A /* Languages.swift */, + ); + path = Configuration; + sourceTree = ""; + }; + 4872D1AA25F77DF1007DB11A /* AppDelegates */ = { + isa = PBXGroup; + children = ( + 4872D15F25F6B06A007DB11A /* AppDelegate.swift */, + 4872D16125F6B06A007DB11A /* SceneDelegate.swift */, + ); + path = AppDelegates; + sourceTree = ""; + }; + 4872D1C025F7838C007DB11A /* Utilities */ = { + isa = PBXGroup; + children = ( + 4872D1C125F783C1007DB11A /* Constants.swift */, + 4872D1C925F78422007DB11A /* APIKey.swift */, + 4872D1CE25F7844E007DB11A /* APIError.swift */, + 4872D27F25F7A094007DB11A /* ActivityIndicator */, + ); + path = Utilities; + sourceTree = ""; + }; + 4872D1D325F785EB007DB11A /* Views */ = { + isa = PBXGroup; + children = ( + 4872D1D625F7864E007DB11A /* SelectedMovie */, + 4872D1D525F78638007DB11A /* Trailer */, + 4872D1D425F78613007DB11A /* Movies */, + ); + path = Views; + sourceTree = ""; + }; + 4872D1D425F78613007DB11A /* Movies */ = { + isa = PBXGroup; + children = ( + 4872D1FD25F78EF0007DB11A /* MoviesHeader */, + 4872D1EA25F78BCB007DB11A /* MovieCell */, + 4872D1D725F7867C007DB11A /* MoviesVC.swift */, + 4872D1D825F7867C007DB11A /* MoviesVC.xib */, + 4872D1F825F78E5F007DB11A /* MoviesVM.swift */, + ); + path = Movies; + sourceTree = ""; + }; + 4872D1D525F78638007DB11A /* Trailer */ = { + isa = PBXGroup; + children = ( + 48759E1E25F7BE9600239CAB /* TrailerVC.swift */, + 48759E1F25F7BE9600239CAB /* TrailerVC.xib */, + ); + path = Trailer; + sourceTree = ""; + }; + 4872D1D625F7864E007DB11A /* SelectedMovie */ = { + isa = PBXGroup; + children = ( + 4872D28325F7A17F007DB11A /* SelectedMovieVC.swift */, + 4872D28425F7A17F007DB11A /* SelectedMovieVC.xib */, + 4872D28A25F7A1D5007DB11A /* SelectedMovieVM.swift */, + ); + path = SelectedMovie; + sourceTree = ""; + }; + 4872D1E125F788C1007DB11A /* Protocols */ = { + isa = PBXGroup; + children = ( + 4872D1E225F788DC007DB11A /* Protocols.swift */, + ); + path = Protocols; + sourceTree = ""; + }; + 4872D1EA25F78BCB007DB11A /* MovieCell */ = { + isa = PBXGroup; + children = ( + 4872D1EB25F78BF6007DB11A /* MovieCell.swift */, + 4872D1EC25F78BF6007DB11A /* MovieCell.xib */, + ); + path = MovieCell; + sourceTree = ""; + }; + 4872D1FD25F78EF0007DB11A /* MoviesHeader */ = { + isa = PBXGroup; + children = ( + 4872D1FE25F78F12007DB11A /* MoviesHeader.swift */, + 4872D1FF25F78F12007DB11A /* MoviesHeader.xib */, + ); + path = MoviesHeader; + sourceTree = ""; + }; + 4872D20B25F790BE007DB11A /* Language */ = { + isa = PBXGroup; + children = ( + 486A883126166A9D00691E14 /* Localizable.strings */, + ); + path = Language; + sourceTree = ""; + }; + 4872D27F25F7A094007DB11A /* ActivityIndicator */ = { + isa = PBXGroup; + children = ( + 4872D27225F7A022007DB11A /* ActivityIndicator.swift */, + 4872D27A25F7A06B007DB11A /* ViewProperties.swift */, + ); + path = ActivityIndicator; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 4872D15B25F6B06A007DB11A /* Darsey-MovieDB */ = { + isa = PBXNativeTarget; + buildConfigurationList = 4872D18925F6B06E007DB11A /* Build configuration list for PBXNativeTarget "Darsey-MovieDB" */; + buildPhases = ( + 4872D15825F6B06A007DB11A /* Sources */, + 4872D15925F6B06A007DB11A /* Frameworks */, + 4872D15A25F6B06A007DB11A /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "Darsey-MovieDB"; + productName = "Darsey-MovieDB"; + productReference = 4872D15C25F6B06A007DB11A /* Darsey-MovieDB.app */; + productType = "com.apple.product-type.application"; + }; + 4872D17425F6B06E007DB11A /* Darsey-MovieDBTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 4872D18C25F6B06E007DB11A /* Build configuration list for PBXNativeTarget "Darsey-MovieDBTests" */; + buildPhases = ( + 4872D17125F6B06E007DB11A /* Sources */, + 4872D17225F6B06E007DB11A /* Frameworks */, + 4872D17325F6B06E007DB11A /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 4872D17725F6B06E007DB11A /* PBXTargetDependency */, + ); + name = "Darsey-MovieDBTests"; + productName = "Darsey-MovieDBTests"; + productReference = 4872D17525F6B06E007DB11A /* Darsey-MovieDBTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 4872D17F25F6B06E007DB11A /* Darsey-MovieDBUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 4872D18F25F6B06E007DB11A /* Build configuration list for PBXNativeTarget "Darsey-MovieDBUITests" */; + buildPhases = ( + 4872D17C25F6B06E007DB11A /* Sources */, + 4872D17D25F6B06E007DB11A /* Frameworks */, + 4872D17E25F6B06E007DB11A /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 4872D18225F6B06E007DB11A /* PBXTargetDependency */, + ); + name = "Darsey-MovieDBUITests"; + productName = "Darsey-MovieDBUITests"; + productReference = 4872D18025F6B06E007DB11A /* Darsey-MovieDBUITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 4872D15425F6B06A007DB11A /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1240; + LastUpgradeCheck = 1240; + TargetAttributes = { + 4872D15B25F6B06A007DB11A = { + CreatedOnToolsVersion = 12.4; + }; + 4872D17425F6B06E007DB11A = { + CreatedOnToolsVersion = 12.4; + TestTargetID = 4872D15B25F6B06A007DB11A; + }; + 4872D17F25F6B06E007DB11A = { + CreatedOnToolsVersion = 12.4; + TestTargetID = 4872D15B25F6B06A007DB11A; + }; + }; + }; + buildConfigurationList = 4872D15725F6B06A007DB11A /* Build configuration list for PBXProject "Darsey-MovieDB" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + es, + sv, + ); + mainGroup = 4872D15325F6B06A007DB11A; + productRefGroup = 4872D15D25F6B06A007DB11A /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 4872D15B25F6B06A007DB11A /* Darsey-MovieDB */, + 4872D17425F6B06E007DB11A /* Darsey-MovieDBTests */, + 4872D17F25F6B06E007DB11A /* Darsey-MovieDBUITests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 4872D15A25F6B06A007DB11A /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4872D1EE25F78BF6007DB11A /* MovieCell.xib in Resources */, + 48759E3B25F7DD6C00239CAB /* Main.storyboard in Resources */, + 4872D16F25F6B06D007DB11A /* LaunchScreen.storyboard in Resources */, + 4872D16C25F6B06D007DB11A /* Assets.xcassets in Resources */, + 4872D1BC25F7820C007DB11A /* key-info.plist in Resources */, + 486A882F26166A9D00691E14 /* Localizable.strings in Resources */, + 48759E2125F7BE9600239CAB /* TrailerVC.xib in Resources */, + 4872D1DA25F7867C007DB11A /* MoviesVC.xib in Resources */, + 4872D20125F78F12007DB11A /* MoviesHeader.xib in Resources */, + 4872D28625F7A17F007DB11A /* SelectedMovieVC.xib in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4872D17325F6B06E007DB11A /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4872D17E25F6B06E007DB11A /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 4872D15825F6B06A007DB11A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 48759E2025F7BE9600239CAB /* TrailerVC.swift in Sources */, + 4872D19925F6B197007DB11A /* Movies.swift in Sources */, + 4872D16A25F6B06A007DB11A /* Darsey_MovieDB.xcdatamodeld in Sources */, + 4872D27325F7A022007DB11A /* ActivityIndicator.swift in Sources */, + 4872D1B725F781A6007DB11A /* PlistService.swift in Sources */, + 4872D1AF25F7814E007DB11A /* MovieService.swift in Sources */, + 4872D28B25F7A1D5007DB11A /* SelectedMovieVM.swift in Sources */, + 4872D1CF25F7844E007DB11A /* APIError.swift in Sources */, + 4872D20025F78F12007DB11A /* MoviesHeader.swift in Sources */, + 4872D1C225F783C1007DB11A /* Constants.swift in Sources */, + 4872D1CA25F78422007DB11A /* APIKey.swift in Sources */, + 4872D1ED25F78BF6007DB11A /* MovieCell.swift in Sources */, + 4872D1A125F6B223007DB11A /* SelectedMovie.swift in Sources */, + 4872D1E325F788DC007DB11A /* Protocols.swift in Sources */, + 4872D16025F6B06A007DB11A /* AppDelegate.swift in Sources */, + 4872D1A625F6B255007DB11A /* Trailers.swift in Sources */, + 4872D27B25F7A06B007DB11A /* ViewProperties.swift in Sources */, + 4872D16225F6B06A007DB11A /* SceneDelegate.swift in Sources */, + 4872D1D925F7867C007DB11A /* MoviesVC.swift in Sources */, + 4872D1F925F78E5F007DB11A /* MoviesVM.swift in Sources */, + 4872D28525F7A17F007DB11A /* SelectedMovieVC.swift in Sources */, + 4872D21D25F791EA007DB11A /* Languages.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4872D17125F6B06E007DB11A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 48759E4025F7E2B100239CAB /* SelectedMovieVMTests.swift in Sources */, + 4872D17A25F6B06E007DB11A /* Darsey_MovieDBTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 4872D17C25F6B06E007DB11A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 4872D18525F6B06E007DB11A /* Darsey_MovieDBUITests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 4872D17725F6B06E007DB11A /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4872D15B25F6B06A007DB11A /* Darsey-MovieDB */; + targetProxy = 4872D17625F6B06E007DB11A /* PBXContainerItemProxy */; + }; + 4872D18225F6B06E007DB11A /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 4872D15B25F6B06A007DB11A /* Darsey-MovieDB */; + targetProxy = 4872D18125F6B06E007DB11A /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 486A883126166A9D00691E14 /* Localizable.strings */ = { + isa = PBXVariantGroup; + children = ( + 486A883026166A9D00691E14 /* en */, + 486A883526166AA300691E14 /* es */, + 486A883626166AA500691E14 /* sv */, + ); + name = Localizable.strings; + sourceTree = ""; + }; + 4872D16D25F6B06D007DB11A /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 4872D16E25F6B06D007DB11A /* Base */, + 486A882726165BF500691E14 /* es */, + 486A882B26165C3E00691E14 /* sv */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 4872D18725F6B06E007DB11A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.4; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 4872D18825F6B06E007DB11A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 14.4; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 4872D18A25F6B06E007DB11A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = "Darsey-MovieDB/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 14.3; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.emil.Darsey-MovieDB"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 4872D18B25F6B06E007DB11A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = "Darsey-MovieDB/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 14.3; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.emil.Darsey-MovieDB"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 4872D18D25F6B06E007DB11A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = "Darsey-MovieDBTests/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 14.4; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.emil.Darsey-MovieDBTests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Darsey-MovieDB.app/Darsey-MovieDB"; + }; + name = Debug; + }; + 4872D18E25F6B06E007DB11A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = "Darsey-MovieDBTests/Info.plist"; + IPHONEOS_DEPLOYMENT_TARGET = 14.4; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.emil.Darsey-MovieDBTests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Darsey-MovieDB.app/Darsey-MovieDB"; + }; + name = Release; + }; + 4872D19025F6B06E007DB11A /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = "Darsey-MovieDBUITests/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.emil.Darsey-MovieDBUITests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = "Darsey-MovieDB"; + }; + name = Debug; + }; + 4872D19125F6B06E007DB11A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + INFOPLIST_FILE = "Darsey-MovieDBUITests/Info.plist"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = "com.emil.Darsey-MovieDBUITests"; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = "Darsey-MovieDB"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 4872D15725F6B06A007DB11A /* Build configuration list for PBXProject "Darsey-MovieDB" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4872D18725F6B06E007DB11A /* Debug */, + 4872D18825F6B06E007DB11A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 4872D18925F6B06E007DB11A /* Build configuration list for PBXNativeTarget "Darsey-MovieDB" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4872D18A25F6B06E007DB11A /* Debug */, + 4872D18B25F6B06E007DB11A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 4872D18C25F6B06E007DB11A /* Build configuration list for PBXNativeTarget "Darsey-MovieDBTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4872D18D25F6B06E007DB11A /* Debug */, + 4872D18E25F6B06E007DB11A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 4872D18F25F6B06E007DB11A /* Build configuration list for PBXNativeTarget "Darsey-MovieDBUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 4872D19025F6B06E007DB11A /* Debug */, + 4872D19125F6B06E007DB11A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCVersionGroup section */ + 4872D16825F6B06A007DB11A /* Darsey_MovieDB.xcdatamodeld */ = { + isa = XCVersionGroup; + children = ( + 4872D16925F6B06A007DB11A /* Darsey_MovieDB.xcdatamodel */, + ); + currentVersion = 4872D16925F6B06A007DB11A /* Darsey_MovieDB.xcdatamodel */; + path = Darsey_MovieDB.xcdatamodeld; + sourceTree = ""; + versionGroupType = wrapper.xcdatamodel; + }; +/* End XCVersionGroup section */ + }; + rootObject = 4872D15425F6B06A007DB11A /* Project object */; +} diff --git a/Darsey-MovieDB/Darsey-MovieDB.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Darsey-MovieDB/Darsey-MovieDB.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 00000000..919434a6 --- /dev/null +++ b/Darsey-MovieDB/Darsey-MovieDB.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Darsey-MovieDB/Darsey-MovieDB.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Darsey-MovieDB/Darsey-MovieDB.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 00000000..18d98100 --- /dev/null +++ b/Darsey-MovieDB/Darsey-MovieDB.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Darsey-MovieDB/Darsey-MovieDB.xcodeproj/project.xcworkspace/xcuserdata/emski.xcuserdatad/UserInterfaceState.xcuserstate b/Darsey-MovieDB/Darsey-MovieDB.xcodeproj/project.xcworkspace/xcuserdata/emski.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 00000000..9bf38775 Binary files /dev/null and b/Darsey-MovieDB/Darsey-MovieDB.xcodeproj/project.xcworkspace/xcuserdata/emski.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/Darsey-MovieDB/Darsey-MovieDB.xcodeproj/xcuserdata/emski.xcuserdatad/xcschemes/xcschememanagement.plist b/Darsey-MovieDB/Darsey-MovieDB.xcodeproj/xcuserdata/emski.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 00000000..2caa6296 --- /dev/null +++ b/Darsey-MovieDB/Darsey-MovieDB.xcodeproj/xcuserdata/emski.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,32 @@ + + + + + SchemeUserState + + Darsey-MovieDB.xcscheme_^#shared#^_ + + orderHint + 0 + + + SuppressBuildableAutocreation + + 4872D15B25F6B06A007DB11A + + primary + + + 4872D17425F6B06E007DB11A + + primary + + + 4872D17F25F6B06E007DB11A + + primary + + + + + diff --git a/Darsey-MovieDB/Darsey-MovieDB/AppDelegates/AppDelegate.swift b/Darsey-MovieDB/Darsey-MovieDB/AppDelegates/AppDelegate.swift new file mode 100644 index 00000000..4461d6e9 --- /dev/null +++ b/Darsey-MovieDB/Darsey-MovieDB/AppDelegates/AppDelegate.swift @@ -0,0 +1,88 @@ +// +// AppDelegate.swift +// Darsey-MovieDB +// +// Created by Emil Vaklinov on 08/03/2021. +// + +import UIKit +import CoreData + +@main +class AppDelegate: UIResponder, UIApplicationDelegate { + + var window: UIWindow? + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + // Override point for customization after application launch. + if #available(iOS 13, *) { // if device is running iOS 13 or later + + } else { // if device is running iOS 12 or lower + self.window = UIWindow(frame: UIScreen.main.bounds) + self.window?.rootViewController = UINavigationController(rootViewController: MoviesVC(nibName: "MoviesVC", bundle: nil)) + self.window?.makeKeyAndVisible() + } + return true + } + + // MARK: UISceneSession Lifecycle + + func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { + // Called when a new scene session is being created. + // Use this method to select a configuration to create the new scene with. + return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) + } + + func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) { + // Called when the user discards a scene session. + // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. + // Use this method to release any resources that were specific to the discarded scenes, as they will not return. + } + + // MARK: - Core Data stack + + lazy var persistentContainer: NSPersistentContainer = { + /* + The persistent container for the application. This implementation + creates and returns a container, having loaded the store for the + application to it. This property is optional since there are legitimate + error conditions that could cause the creation of the store to fail. + */ + let container = NSPersistentContainer(name: "Darsey_MovieDB") + container.loadPersistentStores(completionHandler: { (storeDescription, error) in + if let error = error as NSError? { + // Replace this implementation with code to handle the error appropriately. + // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. + + /* + Typical reasons for an error here include: + * The parent directory does not exist, cannot be created, or disallows writing. + * The persistent store is not accessible, due to permissions or data protection when the device is locked. + * The device is out of space. + * The store could not be migrated to the current model version. + Check the error message to determine what the actual problem was. + */ + fatalError("Unresolved error \(error), \(error.userInfo)") + } + }) + return container + }() + + // MARK: - Core Data Saving support + + func saveContext () { + let context = persistentContainer.viewContext + if context.hasChanges { + do { + try context.save() + } catch { + // Replace this implementation with code to handle the error appropriately. + // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. + let nserror = error as NSError + fatalError("Unresolved error \(nserror), \(nserror.userInfo)") + } + } + } + +} + diff --git a/Darsey-MovieDB/Darsey-MovieDB/AppDelegates/SceneDelegate.swift b/Darsey-MovieDB/Darsey-MovieDB/AppDelegates/SceneDelegate.swift new file mode 100644 index 00000000..d3c1a36b --- /dev/null +++ b/Darsey-MovieDB/Darsey-MovieDB/AppDelegates/SceneDelegate.swift @@ -0,0 +1,60 @@ +// +// SceneDelegate.swift +// Darsey-MovieDB +// +// Created by Emil Vaklinov on 08/03/2021. +// + +import UIKit + +class SceneDelegate: UIResponder, UIWindowSceneDelegate { + + var window: UIWindow? + + + func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { + // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. + // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. + // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). + guard let windowScene = (scene as? UIWindowScene) else { return } + + window = UIWindow(frame: UIScreen.main.bounds) + window?.windowScene = windowScene + self.window?.rootViewController = UINavigationController(rootViewController: MoviesVC(nibName: "MoviesVC", bundle: nil)) + window?.makeKeyAndVisible() + } + + func sceneDidDisconnect(_ scene: UIScene) { + // Called as the scene is being released by the system. + // This occurs shortly after the scene enters the background, or when its session is discarded. + // Release any resources associated with this scene that can be re-created the next time the scene connects. + // The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead). + } + + func sceneDidBecomeActive(_ scene: UIScene) { + // Called when the scene has moved from an inactive state to an active state. + // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. + } + + func sceneWillResignActive(_ scene: UIScene) { + // Called when the scene will move from an active state to an inactive state. + // This may occur due to temporary interruptions (ex. an incoming phone call). + } + + func sceneWillEnterForeground(_ scene: UIScene) { + // Called as the scene transitions from the background to the foreground. + // Use this method to undo the changes made on entering the background. + } + + func sceneDidEnterBackground(_ scene: UIScene) { + // Called as the scene transitions from the foreground to the background. + // Use this method to save data, release shared resources, and store enough scene-specific state information + // to restore the scene back to its current state. + + // Save changes in the application's managed object context when the application transitions to the background. + (UIApplication.shared.delegate as? AppDelegate)?.saveContext() + } + + +} + diff --git a/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/AccentColor.colorset/Contents.json b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 00000000..eb878970 --- /dev/null +++ b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/AppIcon.appiconset/Contents.json b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 00000000..1e2d7086 --- /dev/null +++ b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,122 @@ +{ + "images" : [ + { + "filename" : "Icon-App-20x20@2x.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "filename" : "Icon-App-20x20@3x.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "filename" : "Icon-App-29x29@1x.png", + "idiom" : "iphone", + "scale" : "1x", + "size" : "29x29" + }, + { + "filename" : "Icon-App-29x29@2x.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "filename" : "Icon-App-29x29@3x.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "filename" : "Icon-App-40x40@2x.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "filename" : "Icon-App-40x40@3x.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "filename" : "Icon-App-60x60@2x.png", + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "filename" : "Icon-App-60x60@3x.png", + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "filename" : "Icon-App-20x20@1x.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" + }, + { + "filename" : "Icon-App-20x20@2x.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" + }, + { + "filename" : "Icon-App-29x29@1x.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" + }, + { + "filename" : "Icon-App-29x29@2x.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "filename" : "Icon-App-40x40@1x.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" + }, + { + "filename" : "Icon-App-40x40@2x.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" + }, + { + "filename" : "Icon-App-76x76@1x.png", + "idiom" : "ipad", + "scale" : "1x", + "size" : "76x76" + }, + { + "filename" : "Icon-App-76x76@2x.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "filename" : "Icon-App-83.5x83.5@2x.png", + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "filename" : "ItunesArtwork@2x.png", + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png new file mode 100644 index 00000000..660937f7 Binary files /dev/null and b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ diff --git a/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png new file mode 100644 index 00000000..157ae809 Binary files /dev/null and b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ diff --git a/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png new file mode 100644 index 00000000..3b916f37 Binary files /dev/null and b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ diff --git a/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png new file mode 100644 index 00000000..248373d7 Binary files /dev/null and b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ diff --git a/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png new file mode 100644 index 00000000..5f30dbd5 Binary files /dev/null and b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ diff --git a/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png new file mode 100644 index 00000000..fe08a63e Binary files /dev/null and b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ diff --git a/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png new file mode 100644 index 00000000..157ae809 Binary files /dev/null and b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ diff --git a/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png new file mode 100644 index 00000000..1dbb041a Binary files /dev/null and b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ diff --git a/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png new file mode 100644 index 00000000..6fc535f4 Binary files /dev/null and b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ diff --git a/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png new file mode 100644 index 00000000..6fc535f4 Binary files /dev/null and b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ diff --git a/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png new file mode 100644 index 00000000..c710de62 Binary files /dev/null and b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ diff --git a/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png new file mode 100644 index 00000000..97efa90f Binary files /dev/null and b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ diff --git a/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png new file mode 100644 index 00000000..8001aa49 Binary files /dev/null and b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ diff --git a/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png new file mode 100644 index 00000000..5f475d17 Binary files /dev/null and b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ diff --git a/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png new file mode 100644 index 00000000..624be5e5 Binary files /dev/null and b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png differ diff --git a/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/Contents.json b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/Contents.json new file mode 100644 index 00000000..73c00596 --- /dev/null +++ b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/activity_indicator.imageset/Contents.json b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/activity_indicator.imageset/Contents.json new file mode 100644 index 00000000..c5dcb2bc --- /dev/null +++ b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/activity_indicator.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "activity_indicator.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/activity_indicator.imageset/activity_indicator.png b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/activity_indicator.imageset/activity_indicator.png new file mode 100644 index 00000000..dc5ca6cc Binary files /dev/null and b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/activity_indicator.imageset/activity_indicator.png differ diff --git a/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/back_white.imageset/Contents.json b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/back_white.imageset/Contents.json new file mode 100644 index 00000000..018c6e18 --- /dev/null +++ b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/back_white.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "back_white.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/back_white.imageset/back_white.png b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/back_white.imageset/back_white.png new file mode 100644 index 00000000..eff2fd84 Binary files /dev/null and b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/back_white.imageset/back_white.png differ diff --git a/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/close.imageset/Contents.json b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/close.imageset/Contents.json new file mode 100644 index 00000000..acb0efb2 --- /dev/null +++ b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/close.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "close.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/close.imageset/close.png b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/close.imageset/close.png new file mode 100644 index 00000000..67301efe Binary files /dev/null and b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/close.imageset/close.png differ diff --git a/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/empty_image.imageset/Contents.json b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/empty_image.imageset/Contents.json new file mode 100644 index 00000000..45f18cd3 --- /dev/null +++ b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/empty_image.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "empty_image.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/empty_image.imageset/empty_image.png b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/empty_image.imageset/empty_image.png new file mode 100644 index 00000000..de662737 Binary files /dev/null and b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/empty_image.imageset/empty_image.png differ diff --git a/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/language.imageset/Contents.json b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/language.imageset/Contents.json new file mode 100644 index 00000000..053acb11 --- /dev/null +++ b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/language.imageset/Contents.json @@ -0,0 +1,23 @@ +{ + "images" : [ + { + "filename" : "Language Settings.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "filename" : "Language Settings-1.png", + "idiom" : "universal", + "scale" : "2x" + }, + { + "filename" : "Language Settings-2.png", + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/language.imageset/Language Settings-1.png b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/language.imageset/Language Settings-1.png new file mode 100644 index 00000000..4601e546 Binary files /dev/null and b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/language.imageset/Language Settings-1.png differ diff --git a/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/language.imageset/Language Settings-2.png b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/language.imageset/Language Settings-2.png new file mode 100644 index 00000000..4601e546 Binary files /dev/null and b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/language.imageset/Language Settings-2.png differ diff --git a/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/language.imageset/Language Settings.png b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/language.imageset/Language Settings.png new file mode 100644 index 00000000..4601e546 Binary files /dev/null and b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/language.imageset/Language Settings.png differ diff --git a/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/logo.imageset/Contents.json b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/logo.imageset/Contents.json new file mode 100644 index 00000000..5f670ca8 --- /dev/null +++ b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/logo.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "logo.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/logo.imageset/logo.png b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/logo.imageset/logo.png new file mode 100644 index 00000000..64d10321 Binary files /dev/null and b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/logo.imageset/logo.png differ diff --git a/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/play.imageset/Contents.json b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/play.imageset/Contents.json new file mode 100644 index 00000000..222061f5 --- /dev/null +++ b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/play.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "play.png", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/play.imageset/play.png b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/play.imageset/play.png new file mode 100644 index 00000000..a89e4aca Binary files /dev/null and b/Darsey-MovieDB/Darsey-MovieDB/Assets.xcassets/play.imageset/play.png differ diff --git a/Darsey-MovieDB/Darsey-MovieDB/Base.lproj/LaunchScreen.storyboard b/Darsey-MovieDB/Darsey-MovieDB/Base.lproj/LaunchScreen.storyboard new file mode 100644 index 00000000..6642620f --- /dev/null +++ b/Darsey-MovieDB/Darsey-MovieDB/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Darsey-MovieDB/Darsey-MovieDB/Configuration/Languages.swift b/Darsey-MovieDB/Darsey-MovieDB/Configuration/Languages.swift new file mode 100644 index 00000000..abd03a87 --- /dev/null +++ b/Darsey-MovieDB/Darsey-MovieDB/Configuration/Languages.swift @@ -0,0 +1,27 @@ +// +// Languages.swift +// Darsey-MovieDB +// +// Created by Emil Vaklinov on 09/03/2021. +// + +import Foundation + +extension String { + var localized: String { + if let _ = UserDefaults.standard.string(forKey: Keys.APP_LANGUAGE) {} else { + // we set a default, just in case + UserDefaults.standard.set("en", forKey: Keys.APP_LANGUAGE) + UserDefaults.standard.synchronize() + } + + let lang = UserDefaults.standard.string(forKey: Keys.APP_LANGUAGE) + if let path = Bundle.main.path(forResource: lang, ofType: "lproj"), + let bundle = Bundle(path: path) { + return NSLocalizedString(self, tableName: nil, bundle: bundle, value: "", comment: "") + } + + return NSLocalizedString(self, tableName: nil, bundle: Bundle(), value: "", comment: "") + } + +} diff --git a/Darsey-MovieDB/Darsey-MovieDB/Darsey_MovieDB.xcdatamodeld/.xccurrentversion b/Darsey-MovieDB/Darsey-MovieDB/Darsey_MovieDB.xcdatamodeld/.xccurrentversion new file mode 100644 index 00000000..6c1462fc --- /dev/null +++ b/Darsey-MovieDB/Darsey-MovieDB/Darsey_MovieDB.xcdatamodeld/.xccurrentversion @@ -0,0 +1,8 @@ + + + + + _XCCurrentVersionName + Darsey_MovieDB.xcdatamodel + + diff --git a/Darsey-MovieDB/Darsey-MovieDB/Darsey_MovieDB.xcdatamodeld/Darsey_MovieDB.xcdatamodel/contents b/Darsey-MovieDB/Darsey-MovieDB/Darsey_MovieDB.xcdatamodeld/Darsey_MovieDB.xcdatamodel/contents new file mode 100644 index 00000000..50d2514e --- /dev/null +++ b/Darsey-MovieDB/Darsey-MovieDB/Darsey_MovieDB.xcdatamodeld/Darsey_MovieDB.xcdatamodel/contents @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/Darsey-MovieDB/Darsey-MovieDB/Info.plist b/Darsey-MovieDB/Darsey-MovieDB/Info.plist new file mode 100644 index 00000000..7762f87c --- /dev/null +++ b/Darsey-MovieDB/Darsey-MovieDB/Info.plist @@ -0,0 +1,65 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UIApplicationSceneManifest + + UIApplicationSupportsMultipleScenes + + UISceneConfigurations + + UIWindowSceneSessionRoleApplication + + + UISceneConfigurationName + Default Configuration + UISceneDelegateClassName + $(PRODUCT_MODULE_NAME).SceneDelegate + UISceneStoryboardFile + Main + + + + + UIApplicationSupportsIndirectInputEvents + + UILaunchStoryboardName + LaunchScreen + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + UIInterfaceOrientationPortraitUpsideDown + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/Darsey-MovieDB/Darsey-MovieDB/Language/en.lproj/Localizable.strings b/Darsey-MovieDB/Darsey-MovieDB/Language/en.lproj/Localizable.strings new file mode 100644 index 00000000..51353e5c --- /dev/null +++ b/Darsey-MovieDB/Darsey-MovieDB/Language/en.lproj/Localizable.strings @@ -0,0 +1,31 @@ +/* + Localizable.strings + Darsey-MovieDB + + Created by Emil Vaklinov on 01/04/2021. + +*/ +// MoviesVC +"Movies" = "Movies"; +"now.playing" = "Now playing"; +"in.theaters" = "Now in cinema"; +"popular" = "Popular"; +"most.popular" = "Most popular"; +"top.rated" = "Top Rated"; +"top.rated.films" = "Top Rated Films"; +"now.playing.in.theathers" = "Now playing in theaters"; +"change.language" = "Change language?"; +"choose.language" = "Choose your language"; +"english.language" = "English"; +"spanish.language" = "Spanish"; +"swedish.language" = "Swedish"; +"cancel" = "Cancel"; +"refresh.catalog" = "Refresh Catalog"; + +"error" = "Error"; +"error.unknown" = "An unknown error has occurred"; +"error.invalidURL" = "Please verify the URL"; +"error.response" = "Invalid response from the server"; +"error.data" = "Invalid data received from the server"; +"error.decode" = "Cannot decode data"; +"ok" = "Ok"; diff --git a/Darsey-MovieDB/Darsey-MovieDB/Language/es.lproj/Localizable.strings b/Darsey-MovieDB/Darsey-MovieDB/Language/es.lproj/Localizable.strings new file mode 100644 index 00000000..8a066092 --- /dev/null +++ b/Darsey-MovieDB/Darsey-MovieDB/Language/es.lproj/Localizable.strings @@ -0,0 +1,31 @@ +/* + Localizable.strings + Darsey-MovieDB + + Created by Emil Vaklinov on 01/04/2021. + +*/ +// MoviesVC +"Movies" = "PelĂ­culas"; +"now.playing" = "Reproduciendo ahora"; +"in.theaters" = "Ahora en el cine"; +"popular" = "Popular"; +"most.popular" = "MĂ¡s popular"; +"top.rated" = "Mejor calificados"; +"top.rated.films" = "PelĂ­culas mejor calificadas"; +"now.playing.in.theathers" = "Ahora se proyecta en cines"; +"change.language" = "Cambiar idioma?"; +"choose.language" = "Elija su idioma"; +"english.language" = "InglĂ©s"; +"spanish.language" = "Español"; +"swedish.language" = "Sueco"; +"cancel" = "Cancelar"; +"refresh.catalog" = "Actualizar catĂ¡logo"; + +"error" = "Error"; +"error.unknown" = "Se ha producido un error desconocido"; +"error.invalidURL" = "Verifique la URL"; +"error.response" = "Respuesta no vĂ¡lida del servidor"; +"error.data" = "Datos no vĂ¡lidos recibidos del servidor"; +"error.decode" = "No se pueden decodificar los datos"; +"ok" = "Ok"; diff --git a/Darsey-MovieDB/Darsey-MovieDB/Language/sv.lproj/Localizable.strings b/Darsey-MovieDB/Darsey-MovieDB/Language/sv.lproj/Localizable.strings new file mode 100644 index 00000000..ca9dd612 --- /dev/null +++ b/Darsey-MovieDB/Darsey-MovieDB/Language/sv.lproj/Localizable.strings @@ -0,0 +1,32 @@ +/* + Localizable.strings + Darsey-MovieDB + + Created by Emil Vaklinov on 01/04/2021. + +*/ +// MoviesVC + +"Movies" = "Filmer"; +"now.playing" = "Spelas nu"; +"in.theaters" = "Nu pĂ¥ bio"; +"popular" = "Popular"; +"most.popular" = "Mest populära"; +"top.rated" = "Högst rankad"; +"top.rated.films" = "Topprankade filmer"; +"now.playing.in.theathers" = "Spelas nu i teatrar"; +"change.language" = "Ă„ndra sprĂ¥k?"; +"choose.language" = "Välj ditt sprĂ¥k"; +"english.language" = "Engelska"; +"spanish.language" = "Spanska"; +"swedish.language" = "Svenska"; +"cancel" = "Avbryt"; +"refresh.catalog" = "Uppdatera katalog"; + +"error" = "Fel"; +"error.unknown" = "Ett okänt fel har inträffat"; +"error.invalidURL" = "Verifiera webbadressen"; +"error.response" = "Ogiltigt svar frĂ¥n servern"; +"error.data" = "Ogiltiga data mottagna frĂ¥n servern"; +"error.decode" = "Det gĂ¥r inte att avkoda data"; +"ok" = "Ok"; diff --git a/Darsey-MovieDB/Darsey-MovieDB/Localizable.strings b/Darsey-MovieDB/Darsey-MovieDB/Localizable.strings new file mode 100644 index 00000000..7b5422f7 --- /dev/null +++ b/Darsey-MovieDB/Darsey-MovieDB/Localizable.strings @@ -0,0 +1,64 @@ +/* + Localizables(English).strings + TMDBChallenge + + Created by Miguel Aquino on 14/01/21. + +*/ + +// MoviesVC +"Movies" = "Movies"; +"now.playing" = "Now playing"; +"in.theaters" = "Now in cinema"; +"popular" = "Popular"; +"most.popular" = "Most popular"; +"top.rated" = "Top Rated"; +"top.rated.films" = "Top Rated Films"; +"now.playing.in.theathers" = "Now playing in theaters"; +"change.language" = "Change language?"; +"choose.language" = "Choose your language"; +"english.language" = "English"; +"spanish.language" = "Spanish"; +"swidish.language" = "Swidish"; +"cancel" = "Cancel"; +"refresh.catalog" = "Refresh Catalog"; + +"Movies" = "PelĂ­culas"; +"now.playing" = "Reproduciendo ahora"; +"in.theaters" = "Ahora en el cine"; +"popular" = "Popular"; +"most.popular" = "MĂ¡s popular"; +"top.rated" = "Mejor calificados"; +"top.rated.films" = "PelĂ­culas mejor calificadas"; +"now.playing.in.theathers" = "Ahora se proyecta en cines"; +"change.language" = "Cambiar idioma?"; +"choose.language" = "Elija su idioma"; +"english.language" = "InglĂ©s"; +"spanish.language" = "Español"; +"swidish.language" = "Swidish"; +"cancel" = "Cancelar"; +"refresh.catalog" = "Actualizar catĂ¡logo"; + +"Movies" = "Filmer"; +"now.playing" = "Spelas nu"; +"in.theaters" = "Nu pĂ¥ bio"; +"popular" = "Popular"; +"most.popular" = "Mest populära"; +"top.rated" = "Högst rankad"; +"top.rated.films" = "Topprankade filmer"; +"now.playing.in.theathers" = "Spelas nu i teatrar"; +"change.language" = "Ă„ndra sprĂ¥k?"; +"select.language" = "Välj ditt sprĂ¥k"; +"english.language" = "Engelska"; +"spanish.language" = "Spanska"; +"swidish.language" = "Svenska"; +"cancel" = "Avbryt"; +"refresh.catalog" = "Uppdatera katalog"; + +"error" = "Error"; +"error.unknown" = "An unknown error has occurred"; +"error.invalidURL" = "Please verify the URL"; +"error.response" = "Invalid response from the server"; +"error.data" = "Invalid data received from the server"; +"error.decode" = "Cannot decode data"; +"ok" = "Ok"; diff --git a/Darsey-MovieDB/Darsey-MovieDB/Main.storyboard b/Darsey-MovieDB/Darsey-MovieDB/Main.storyboard new file mode 100644 index 00000000..f9a048ed --- /dev/null +++ b/Darsey-MovieDB/Darsey-MovieDB/Main.storyboard @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Darsey-MovieDB/Darsey-MovieDB/Models/Movies.swift b/Darsey-MovieDB/Darsey-MovieDB/Models/Movies.swift new file mode 100644 index 00000000..b8a17412 --- /dev/null +++ b/Darsey-MovieDB/Darsey-MovieDB/Models/Movies.swift @@ -0,0 +1,24 @@ +// +// Movies.swift +// Darsey-MovieDB +// +// Created by Emil Vaklinov on 08/03/2021. +// + +import Foundation + +// Entity used in `MoviesVC` collectionView. +struct Movies: Codable { + var results: [Movie] +} + + +// Entity entity `MovieList` +struct Movie: Codable { + var title: String + var release_date: String + var vote_average: Double + var poster_path: String? + var backdrop_path: String? + var id: Int +} diff --git a/Darsey-MovieDB/Darsey-MovieDB/Models/SelectedMovie.swift b/Darsey-MovieDB/Darsey-MovieDB/Models/SelectedMovie.swift new file mode 100644 index 00000000..575107e7 --- /dev/null +++ b/Darsey-MovieDB/Darsey-MovieDB/Models/SelectedMovie.swift @@ -0,0 +1,22 @@ +// +// SelectedMovie.swift +// Darsey-MovieDB +// +// Created by Emil Vaklinov on 08/03/2021. +// + +import Foundation + +struct SelectedMovie: Codable { + var title: String + var runtime: Int + var release_date: String + var vote_average: Double + var genres: [Genre] + var overview: String +} + +struct Genre: Codable, Equatable { + var id: Int + var name: String +} diff --git a/Darsey-MovieDB/Darsey-MovieDB/Models/Trailers.swift b/Darsey-MovieDB/Darsey-MovieDB/Models/Trailers.swift new file mode 100644 index 00000000..0cdf99a7 --- /dev/null +++ b/Darsey-MovieDB/Darsey-MovieDB/Models/Trailers.swift @@ -0,0 +1,17 @@ +// +// Trailers.swift +// Darsey-MovieDB +// +// Created by Emil Vaklinov on 08/03/2021. +// + +import Foundation + +struct Trailers: Codable { + var results: [Trailer] +} + +struct Trailer: Codable { + var id: String + var key: String +} diff --git a/Darsey-MovieDB/Darsey-MovieDB/Protocols/Protocols.swift b/Darsey-MovieDB/Darsey-MovieDB/Protocols/Protocols.swift new file mode 100644 index 00000000..9defaeeb --- /dev/null +++ b/Darsey-MovieDB/Darsey-MovieDB/Protocols/Protocols.swift @@ -0,0 +1,16 @@ +// +// Protocols.swift +// Darsey-MovieDB +// +// Created by Emil Vaklinov on 09/03/2021. +// + +import Foundation + +protocol LanguageDelegate{ + func chooseLanguage() +} + +protocol MovieSegmentDelegate { + func loadMovie(movieType: MovieType) +} diff --git a/Darsey-MovieDB/Darsey-MovieDB/Services/MovieService.swift b/Darsey-MovieDB/Darsey-MovieDB/Services/MovieService.swift new file mode 100644 index 00000000..b25556e3 --- /dev/null +++ b/Darsey-MovieDB/Darsey-MovieDB/Services/MovieService.swift @@ -0,0 +1,180 @@ +// +// MovieServices.swift +// Darsey-MovieDB +// +// Created by Emil Vaklinov on 09/03/2021. +// + +import UIKit + +class MovieService { + + //MARK: - Properties + static let shared = MovieService() + let cache = NSCache() + let apiKey = PlistService.getPlistKey(key: .apiKey) + + /** + Function used to get all the movies that are currently "now playing" in theaters + - parameter movieType: The type of the movies (`MovieType`) to be retrieved from the API. + */ + func getMovies(movieType: MovieType, language: String, completion: @escaping (Result<[Movie], APIError> ) -> Void) { + + guard let url = URL(string: ApiURL.baseURL + movieType.value) else { + completion(.failure(.invalidURL)) + + return + } + guard var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false) else { return } + + var queryItem = [URLQueryItem(name: Keys.API_KEY, value: apiKey)] + queryItem.append(URLQueryItem(name: Keys.LANGUAGE, value: language)) + urlComponents.queryItems = queryItem + + guard let finalURL = urlComponents.url else { return } + + URLSession.shared.dataTask(with: finalURL, completionHandler: {( data, response ,error) in + if let _ = error { + completion(.failure(.unknownError)) + return + } + + guard let data = data else { + completion(.failure(.invalidData)) + return + } + + guard let response = response as? HTTPURLResponse, response.statusCode == 200 else { completion(.failure(.invalidResponse)) + return + } + + do{ + let movieResponse = try JSONDecoder().decode(Movies.self, from: data) + completion(.success(movieResponse.results)) + } catch { + completion(.failure(.decodeError)) + } + }).resume() + } + + /** + Function used to get all the movies that are currently "now playing" in theaters + - parameter movieId: The identifier of the selected movie + - parameter language: The current language identifier set on the device + */ + func getSelectedMovie(movieId: Int, language: String, completion: @escaping (Result< SelectedMovie, APIError> ) -> Void) { + + guard let url = URL(string: ApiURL.selectedMovie + "\(movieId)") else { + completion(.failure(.invalidURL)) + return + } + guard var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false) else { return } + + var queryItem = [URLQueryItem(name: Keys.API_KEY, value: apiKey)] + queryItem.append(URLQueryItem(name: Keys.LANGUAGE, value: language)) + urlComponents.queryItems = queryItem + + guard let finalURL = urlComponents.url else { return } + + URLSession.shared.dataTask(with: finalURL, completionHandler: {( data, response ,error) in + if let _ = error { + completion(.failure(.unknownError)) + return + } + + guard let data = data else { + completion(.failure(.invalidData)) + return + } + + guard let response = response as? HTTPURLResponse, response.statusCode == 200 else { completion(.failure(.invalidResponse)) + return + } + + do{ + let movieResponse = try JSONDecoder().decode(SelectedMovie.self, from: data) + completion(.success(movieResponse)) + } catch { + completion(.failure(.decodeError)) + } + }).resume() + } + + /** + Function used to get all the trailers from a specific movie + - parameter movieId: The identifier of the selected movie + - parameter language: The current language identifier set on the device + */ + func getTrailers(movieId: Int, language: String, completion: @escaping (Result< [Trailer], APIError> ) -> Void) { + + guard let url = URL(string: ApiURL.selectedMovie + "\(movieId)/videos") else { + completion(.failure(.invalidURL)) + return + } + guard var urlComponents = URLComponents(url: url, resolvingAgainstBaseURL: false) else { return } + + var queryItem = [URLQueryItem(name: Keys.API_KEY, value: apiKey)] + queryItem.append(URLQueryItem(name: Keys.LANGUAGE, value: language)) + urlComponents.queryItems = queryItem + + guard let finalURL = urlComponents.url else { return } + + URLSession.shared.dataTask(with: finalURL, completionHandler: {( data, response ,error) in + if let _ = error { + completion(.failure(.unknownError)) + return + } + + guard let data = data else { + completion(.failure(.invalidData)) + return + } + + guard let response = response as? HTTPURLResponse, response.statusCode == 200 else { completion(.failure(.invalidResponse)) + return + } + + do{ + let trailerResponse = try JSONDecoder().decode(Trailers.self, from: data) + completion(.success(trailerResponse.results)) + } catch { + completion(.failure(.decodeError)) + } + }).resume() + } + + /** + Function used to download the image from the API + - parameter urlString: The path of the the image as a `String` + */ + func getImage(from urlString: String, completed: @escaping (UIImage?) -> Void) { + let cacheKey = NSString(string: urlString) + + if let image = cache.object(forKey: cacheKey) { + completed(image) + return + } + + guard let url = URL(string: urlString) else { + completed(nil) + return + } + + let task = URLSession.shared.dataTask(with: url) { [weak self] data, response, error in + + guard let self = self, + error == nil, + let response = response as? HTTPURLResponse, response.statusCode == 200, + let data = data, + let image = UIImage(data: data) else { + completed(nil) + return + } + + self.cache.setObject(image, forKey: cacheKey) + completed(image) + } + + task.resume() + } +} diff --git a/Darsey-MovieDB/Darsey-MovieDB/Services/PlistService.swift b/Darsey-MovieDB/Darsey-MovieDB/Services/PlistService.swift new file mode 100644 index 00000000..f58e62cf --- /dev/null +++ b/Darsey-MovieDB/Darsey-MovieDB/Services/PlistService.swift @@ -0,0 +1,28 @@ +// +// PlistService.swift +// Darsey-MovieDB +// +// Created by Emil Vaklinov on 09/03/2021. +// + +import Foundation + +class PlistService { + + // Function used to read the keys from the `key-info.plist` + static func getPlistKey(key: PlistKey) -> String { + guard let path = Bundle.main.path(forResource: "key-info", ofType: "plist"), + let xml = FileManager.default.contents(atPath: path) + else { return "" } + + switch key { + case .apiKey: + guard let apiKey = try? PropertyListDecoder().decode(APIKey.self, from: xml) else { return "" } + return apiKey.api_key + } + } +} + +enum PlistKey { + case apiKey +} diff --git a/Darsey-MovieDB/Darsey-MovieDB/Services/key-info.plist b/Darsey-MovieDB/Darsey-MovieDB/Services/key-info.plist new file mode 100644 index 00000000..2598207f --- /dev/null +++ b/Darsey-MovieDB/Darsey-MovieDB/Services/key-info.plist @@ -0,0 +1,8 @@ + + + + + api_key + 7e8a76c1bd818cc68473abb1e5fc2a20 + + diff --git a/Darsey-MovieDB/Darsey-MovieDB/Utilities/APIError.swift b/Darsey-MovieDB/Darsey-MovieDB/Utilities/APIError.swift new file mode 100644 index 00000000..8263ab62 --- /dev/null +++ b/Darsey-MovieDB/Darsey-MovieDB/Utilities/APIError.swift @@ -0,0 +1,31 @@ +// +// APIError.swift +// Darsey-MovieDB +// +// Created by Emil Vaklinov on 09/03/2021. +// + +import Foundation + +enum APIError: Error { + case unknownError + case invalidURL + case invalidResponse + case invalidData + case decodeError + + var localizedDescription : String{ + switch self { + case .unknownError: + return "error.unknown".localized + case .invalidURL: + return "error.invalidURL".localized + case .invalidResponse: + return "error.response".localized + case .invalidData: + return "error.data".localized + case .decodeError: + return "error.decode".localized + } + } +} diff --git a/Darsey-MovieDB/Darsey-MovieDB/Utilities/APIKey.swift b/Darsey-MovieDB/Darsey-MovieDB/Utilities/APIKey.swift new file mode 100644 index 00000000..2b7e0197 --- /dev/null +++ b/Darsey-MovieDB/Darsey-MovieDB/Utilities/APIKey.swift @@ -0,0 +1,12 @@ +// +// APIKey.swift +// Darsey-MovieDB +// +// Created by Emil Vaklinov on 09/03/2021. +// + +import Foundation + +struct APIKey: Codable { + var api_key: String +} diff --git a/Darsey-MovieDB/Darsey-MovieDB/Utilities/ActivityIndicator/ActivityIndicator.swift b/Darsey-MovieDB/Darsey-MovieDB/Utilities/ActivityIndicator/ActivityIndicator.swift new file mode 100644 index 00000000..022f51b1 --- /dev/null +++ b/Darsey-MovieDB/Darsey-MovieDB/Utilities/ActivityIndicator/ActivityIndicator.swift @@ -0,0 +1,82 @@ +// +// ActivityIndicator.swift +// Darsey-MovieDB +// +// Created by Emil Vaklinov on 09/03/2021. +// + +import UIKit + +fileprivate var containerView : UIView! +fileprivate var alertView : UIView! +fileprivate var indicatorView : UIView! + +extension UIViewController { + func showActivityIndicator() { + containerView = UIView(frame: view.bounds) + alertView = UIView(frame: .zero) + indicatorView = UIView(frame: .zero) + + containerView.backgroundColor = .black + alertView.backgroundColor = .white + + containerView.alpha = 0 + alertView.alpha = 0 + indicatorView.alpha = 0 + + UIView.animate(withDuration: 0.5) { + containerView.alpha = 0.5 + alertView.alpha = 1 + indicatorView.alpha = 1 + } + view.addSubview(containerView) + NSLayoutConstraint.activate([ + containerView.centerYAnchor.constraint(equalTo: view.centerYAnchor), + containerView.centerXAnchor.constraint(equalTo: view.centerXAnchor) + ]) + + ViewProperties.configureViewWithRoundBorderAndShadow(backgroundView: alertView) + view.addSubview(alertView) + alertView.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + alertView.centerYAnchor.constraint(equalTo: view.centerYAnchor), + alertView.centerXAnchor.constraint(equalTo: view.centerXAnchor), + alertView.widthAnchor.constraint(equalToConstant: 80), + alertView.heightAnchor.constraint(equalToConstant: 80) + ]) + + let activityIndicator = UIImage(named: "activity_indicator") + let imageView = UIImageView(image: activityIndicator) + view.addSubview(indicatorView) + indicatorView.addSubview(imageView) + imageView.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + imageView.topAnchor.constraint(equalTo: indicatorView.topAnchor, constant: 0), + imageView.leadingAnchor.constraint(equalTo: indicatorView.leadingAnchor, constant: 0), + imageView.trailingAnchor.constraint(equalTo: indicatorView.trailingAnchor, constant: 0), + imageView.bottomAnchor.constraint(equalTo: indicatorView.bottomAnchor, constant: 0) + ]) + + + indicatorView.translatesAutoresizingMaskIntoConstraints = false + ViewProperties.animateViewInfinitely(backgroundView: indicatorView) + NSLayoutConstraint.activate([ + indicatorView.centerYAnchor.constraint(equalTo: view.centerYAnchor), + indicatorView.centerXAnchor.constraint(equalTo: view.centerXAnchor), + indicatorView.widthAnchor.constraint(equalToConstant: 50), + indicatorView.heightAnchor.constraint(equalToConstant: 50) + ]) + } + + func hideActivityIndicator() { + DispatchQueue.main.async { + containerView?.removeFromSuperview() + alertView?.removeFromSuperview() + indicatorView?.removeFromSuperview() + + containerView = nil + alertView = nil + indicatorView = nil + } + } +} diff --git a/Darsey-MovieDB/Darsey-MovieDB/Utilities/ActivityIndicator/ViewProperties.swift b/Darsey-MovieDB/Darsey-MovieDB/Utilities/ActivityIndicator/ViewProperties.swift new file mode 100644 index 00000000..f2a1e967 --- /dev/null +++ b/Darsey-MovieDB/Darsey-MovieDB/Utilities/ActivityIndicator/ViewProperties.swift @@ -0,0 +1,54 @@ +// +// ViewProperties.swift +// Darsey-MovieDB +// +// Created by Emil Vaklinov on 09/03/2021. +// + +import UIKit + +class ViewProperties { + static func configureCircularViewWithShadow(backgroundView: UIView ){ + backgroundView.layer.shadowColor = UIColor.black.cgColor + backgroundView.layer.shadowOpacity = 0.7 + backgroundView.layer.shadowOffset = CGSize(width: 0, height: 2) + backgroundView.layer.shadowRadius = 5 + backgroundView.layer.shouldRasterize = false + backgroundView.layer.cornerRadius = backgroundView.frame.height / 2 + } + + static func configureViewWithRoundBorderAndShadow(backgroundView: UIView ){ + backgroundView.layer.shadowColor = UIColor.black.cgColor + backgroundView.layer.shadowOpacity = 0.7 + backgroundView.layer.shadowOffset = CGSize(width: 0, height: 3) + backgroundView.layer.shadowRadius = 5 + backgroundView.layer.shouldRasterize = false + backgroundView.layer.cornerRadius = 8 + } + + static func animateViewInfinitely(backgroundView: UIView ){ + Timer.scheduledTimer(withTimeInterval: 0.5, repeats: true) { (timer) in + if backgroundView.window != nil { + UIView.animate(withDuration: 0.25, delay: 0, options: [.curveEaseIn], animations: { + backgroundView.transform = CGAffineTransform(rotationAngle: .pi) + }, completion: { (_) in + UIView.animate(withDuration: 0.25, delay: 0, options: [.curveEaseOut], animations: { + backgroundView.transform = CGAffineTransform(rotationAngle: .pi * 2) + }, completion: nil)}) + } + else { + timer.invalidate() + } + } + } + + static func animateBounceEffect(backgroundView: UIView ){ + backgroundView.transform = CGAffineTransform(scaleX: 0.7, y: 0.7) + + UIView.animate(withDuration: 2.0, delay: 0.0, usingSpringWithDamping: 0.2, initialSpringVelocity: 20.0, options: .allowUserInteraction, animations: { [] in + backgroundView.isHidden = false + backgroundView.transform = CGAffineTransform(scaleX: 1.0, y: 1.0) + }, + completion: nil) + } +} diff --git a/Darsey-MovieDB/Darsey-MovieDB/Utilities/Constants.swift b/Darsey-MovieDB/Darsey-MovieDB/Utilities/Constants.swift new file mode 100644 index 00000000..e0a2a9b3 --- /dev/null +++ b/Darsey-MovieDB/Darsey-MovieDB/Utilities/Constants.swift @@ -0,0 +1,44 @@ +// +// Constants.swift +// Darsey-MovieDB +// +// Created by Emil Vaklinov on 09/03/2021. +// + +import UIKit + +struct Keys { + static let API_KEY = "api_key" + static let LANGUAGE = "language" + static let ORIENTATION = "orientation" + static let APP_LANGUAGE = "app_lang" +} + +struct ApiURL { + static let baseURL = "https://api.themoviedb.org/3/movie/" + static let selectedMovie = baseURL + static let image = "https://image.tmdb.org/t/p/" + static let youtubeURL = "https://www.youtube.com/embed/" +} + +struct Images { + static let emptyImage = UIImage(named: "empty_image") + static let back = UIImageView(image: UIImage(named: "back_white")) +} + +enum MovieType { + case nowPlaying + case topRated + case popular + + var value : String { + switch self { + case .nowPlaying: + return "now_playing" + case .topRated: + return "top_rated" + case .popular: + return "popular" + } + } +} diff --git a/Darsey-MovieDB/Darsey-MovieDB/Views/Movies/MovieCell/MovieCell.swift b/Darsey-MovieDB/Darsey-MovieDB/Views/Movies/MovieCell/MovieCell.swift new file mode 100644 index 00000000..e5078991 --- /dev/null +++ b/Darsey-MovieDB/Darsey-MovieDB/Views/Movies/MovieCell/MovieCell.swift @@ -0,0 +1,52 @@ +// +// MovieCell.swift +// Darsey-MovieDB +// +// Created by Emil Vaklinov on 09/03/2021. +// + +import UIKit + +class MovieCell: UICollectionViewCell { + + //MARK: - Outlets + @IBOutlet weak var cellBackgroundView: UIView! + @IBOutlet weak var titleLabel: UILabel! + @IBOutlet weak var releaseDateLabel: UILabel! + @IBOutlet weak var scoreLabel: UILabel! + @IBOutlet weak var backgroundImageView: UIImageView! + + //MARK: - Properties + static let resuseId = "MovieCell" + + var movieViewModel: MoviesViewModel? { + didSet{ + titleLabel.text = movieViewModel?.title + releaseDateLabel.text = movieViewModel?.release_date + scoreLabel.text = "\(movieViewModel?.vote_average ?? 0.0)" + guard let url = movieViewModel?.posterURL else { return } + loadImage(fromURL: url) + } + } + + override func awakeFromNib() { + super.awakeFromNib() + // Initialization code + cellBackgroundView.layer.masksToBounds = true + cellBackgroundView.layer.cornerRadius = 10 + layer.shadowRadius = 4 + layer.shadowColor = UIColor.black.cgColor + layer.shadowOffset = CGSize(width: 0, height: 0.5) + layer.shadowOpacity = 0.5 + } + override func prepareForReuse() { + self.backgroundImageView.image = Images.emptyImage + } + private func loadImage(fromURL: String) { + MovieService.shared.getImage(from: fromURL, completed: { image in + DispatchQueue.main.async { + self.backgroundImageView.image = image != nil ? image : Images.emptyImage + } + }) + } +} diff --git a/Darsey-MovieDB/Darsey-MovieDB/Views/Movies/MovieCell/MovieCell.xib b/Darsey-MovieDB/Darsey-MovieDB/Views/Movies/MovieCell/MovieCell.xib new file mode 100644 index 00000000..4849d2ad --- /dev/null +++ b/Darsey-MovieDB/Darsey-MovieDB/Views/Movies/MovieCell/MovieCell.xib @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Darsey-MovieDB/Darsey-MovieDB/Views/Movies/MoviesHeader/MoviesHeader.swift b/Darsey-MovieDB/Darsey-MovieDB/Views/Movies/MoviesHeader/MoviesHeader.swift new file mode 100644 index 00000000..4afe87c5 --- /dev/null +++ b/Darsey-MovieDB/Darsey-MovieDB/Views/Movies/MoviesHeader/MoviesHeader.swift @@ -0,0 +1,81 @@ +// +// MoviesHeader.swift +// Darsey-MovieDB +// +// Created by Emil Vaklinov on 09/03/2021. +// + +import UIKit + +class MoviesHeader: UICollectionReusableView { + + //MARK:- Outlets + + @IBOutlet weak var titleLabel: UILabel! + @IBOutlet weak var movieSegmentedControl: UISegmentedControl! + @IBOutlet weak var view: UIView! + + //MARK: Properties + + static var languageDelegate: LanguageDelegate? + static var movieSegmentDelegate: MovieSegmentDelegate? + static let reuseId = "MoviesHeader" + + //MARK:- Actions + + @IBAction func languageButtonTapped(_ sender: Any) { + MoviesHeader.languageDelegate?.chooseLanguage() + + } + + @IBAction func segmentedControlTapped(_ sender: Any) { + switch movieSegmentedControl.selectedSegmentIndex { + case 0: + MoviesHeader.movieSegmentDelegate?.loadMovie(movieType: .nowPlaying) + setTitleLabel() + case 1: + MoviesHeader.movieSegmentDelegate?.loadMovie(movieType: .popular) + setTitleLabel() + case 2: + MoviesHeader.movieSegmentDelegate?.loadMovie(movieType: .topRated) + setTitleLabel() + default: + break + } + } + + override func awakeFromNib() { + super.awakeFromNib() + + movieSegmentedControl.removeAllSegments() + movieSegmentedControl.insertSegment(withTitle: "now.playing".localized, at: 0, animated: true) + movieSegmentedControl.insertSegment(withTitle: "popular".localized, at: 1, animated: true) + movieSegmentedControl.insertSegment(withTitle: "top.rated".localized, at: 2, animated: true) + view.layer.cornerRadius = 10 + movieSegmentedControl.selectedSegmentIndex = 0 + setTitleLabel() + + MoviesHeader.movieSegmentDelegate?.loadMovie(movieType: .nowPlaying) + } + + override func prepareForReuse() { + movieSegmentedControl.setTitle("now.playing".localized, forSegmentAt: 0) + movieSegmentedControl.setTitle("popular".localized, forSegmentAt: 1) + movieSegmentedControl.setTitle("top.rated".localized, forSegmentAt: 2) + + setTitleLabel() + } + + private func setTitleLabel() { + switch movieSegmentedControl.selectedSegmentIndex { + case 0: + titleLabel.text = "in.theaters".localized + case 1: + titleLabel.text = "most.popular".localized + case 2: + titleLabel.text = "top.rated.films".localized + default: + break + } + } +} diff --git a/Darsey-MovieDB/Darsey-MovieDB/Views/Movies/MoviesHeader/MoviesHeader.xib b/Darsey-MovieDB/Darsey-MovieDB/Views/Movies/MoviesHeader/MoviesHeader.xib new file mode 100644 index 00000000..2a160bfb --- /dev/null +++ b/Darsey-MovieDB/Darsey-MovieDB/Views/Movies/MoviesHeader/MoviesHeader.xib @@ -0,0 +1,87 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Darsey-MovieDB/Darsey-MovieDB/Views/Movies/MoviesVC.swift b/Darsey-MovieDB/Darsey-MovieDB/Views/Movies/MoviesVC.swift new file mode 100644 index 00000000..daef3f59 --- /dev/null +++ b/Darsey-MovieDB/Darsey-MovieDB/Views/Movies/MoviesVC.swift @@ -0,0 +1,253 @@ +// +// MoviesVC.swift +// Darsey-MovieDB +// +// Created by Emil Vaklinov on 09/03/2021. +// + +import UIKit + +class MoviesVC: UIViewController { + + //MARK:- Outlets + @IBOutlet weak var collectionView: UICollectionView! + + //MARK: - Properties + + var refreshController: UIRefreshControl = UIRefreshControl() + var movieViewModelList = [MoviesViewModel]() + var movieType = MovieType.nowPlaying + + //MARK:- LifeCycle + + override func viewDidLoad() { + super.viewDidLoad() + } + + override func viewWillAppear(_ animated: Bool) { + setupVC() + setupPullToRefresh() + } +} + +//MARK: VC Extension, Methods and Actions + +extension MoviesVC { + private func setupVC(){ + + collectionView.register(UINib(nibName: MoviesHeader.reuseId, bundle: nil), + forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, + withReuseIdentifier: MoviesHeader.reuseId) + + collectionView.register(UINib(nibName: MovieCell.resuseId , bundle: nil), + forCellWithReuseIdentifier: MovieCell.resuseId) + + collectionView.delegate = self + collectionView.dataSource = self + collectionView.contentInsetAdjustmentBehavior = .never + + MoviesHeader.languageDelegate = self + MoviesHeader.movieSegmentDelegate = self + + self.navigationController?.navigationBar.isHidden = true + + if UIDevice.current.userInterfaceIdiom == .pad { + let orientation = UIInterfaceOrientation.landscapeLeft.rawValue + UIDevice.current.setValue(orientation, forKey: Keys.ORIENTATION ) + } else { + let orientation = UIInterfaceOrientation.portrait.rawValue + UIDevice.current.setValue(orientation, forKey: Keys.ORIENTATION ) + } + } + + private func setupPullToRefresh(){ + refreshController.bounds = CGRect(x: 0, y: 50, + width: refreshController.bounds.size.width, + height: refreshController.bounds.size.height) + refreshController.tintColor = UIColor(red: 1, green: 165/255, blue: 0, alpha: 1) + refreshController.addTarget(self, action: #selector(refreshMovies), + for: UIControl.Event.valueChanged) + refreshController.attributedTitle = NSAttributedString(string: "refresh.catalog".localized, + attributes: [NSAttributedString.Key.foregroundColor: UIColor.label, NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: 15)]) +// refreshController.tintColor = .label + collectionView.refreshControl = refreshController + } + + @objc func refreshMovies(sender:AnyObject) { + loadMovies(movieType: movieType) + refreshController.endRefreshing() + } + + private func loadMovies(movieType: MovieType){ + guard let currentLanguage = UserDefaults.standard.string(forKey: Keys.APP_LANGUAGE) else { return } + + showActivityIndicator() + MovieService.init().getMovies(movieType: movieType, language: currentLanguage) { [weak self] response in + guard let self = self else { return } + switch response { + case .success (let movies): + self.movieViewModelList = movies.map( { return MoviesViewModel($0)}) + DispatchQueue.main.async { + self.hideActivityIndicator() + self.collectionView.reloadData() + } + case .failure (let error): + DispatchQueue.main.async { + self.hideActivityIndicator() + self.showError(error: error.localizedDescription) + } + } + } + } + + func showError(error: String){ + let alert = UIAlertController(title: "error".localized, + message: error, + preferredStyle: .alert) + + alert.addAction(UIAlertAction(title: "ok".localized, + style: .default, + handler: nil )) + + self.present(alert, animated: true, completion: nil) + } +} + + +//MARK: - CollectionView Delegate & DataSource +extension MoviesVC: UICollectionViewDataSource, UICollectionViewDelegate { + + func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView { + let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: MoviesHeader.reuseId, for: indexPath) as! MoviesHeader + return header + } + + func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { + if !movieViewModelList.isEmpty{ + return movieViewModelList.count + } + return 0 + } + + func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { + let cell = collectionView.dequeueReusableCell(withReuseIdentifier: MovieCell.resuseId, for: indexPath) as! MovieCell + + cell.movieViewModel = movieViewModelList[indexPath.row] + + return cell + } + + func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { + collectionView.deselectItem(at: indexPath, animated: false) + + if !movieViewModelList.isEmpty{ + let selectedMovieVC = SelectedMovieVC(nibName: SelectedMovieVC.reuseId, bundle: nil) + selectedMovieVC.movieId = movieViewModelList[indexPath.row].id + selectedMovieVC.backdropUrl = movieViewModelList[indexPath.row].backdropURL + self.present(selectedMovieVC, animated: true, completion: nil) + } + } +} + +//MARK: - CollectionView FlowLayout +extension MoviesVC : UICollectionViewDelegateFlowLayout { + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, insetForSectionAt section: Int) -> UIEdgeInsets { + return UIEdgeInsets(top: 0 , left: 4 , bottom: 0, right: 4) + } + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize { + var cellWidth = collectionView.bounds.width + var cellHeight: CGFloat = 0 + + if UIDevice.current.userInterfaceIdiom == .pad { + cellHeight = 320 + switch UIDevice.current.orientation{ + case .portrait: + cellWidth = cellWidth / 3 - 10 + case .landscapeLeft, .landscapeRight : + cellWidth = cellWidth / 4 - 10 + default: + cellWidth = cellWidth / 3 - 10 + } + } else { + cellHeight = 220 + switch UIDevice.current.orientation{ + case .portrait: + cellWidth = cellWidth / 2 - 10 + case .landscapeLeft, .landscapeRight : + cellHeight = 270 + cellWidth = cellWidth / 3 - 10 + default: + cellWidth = cellWidth / 2 - 10 + } + } + + return CGSize(width: cellWidth , height: cellHeight) + } + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumLineSpacingForSectionAt section: Int) -> CGFloat { + return 1 + } + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, minimumInteritemSpacingForSectionAt section: Int) -> CGFloat { + return 1 + } + + func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, referenceSizeForHeaderInSection section: Int) -> CGSize { + return CGSize(width: view.frame.width, height: 90) + } +} + + +//MARK:- Language Delegate + +extension MoviesVC: LanguageDelegate { + + func chooseLanguage() { + let alert = UIAlertController(title: "change.language".localized, + message: "choose.language".localized, + preferredStyle: .alert) + + alert.addAction(UIAlertAction(title: "english.language".localized, + style: .default, + handler: { (_) in + UserDefaults.standard.set("en", forKey: Keys.APP_LANGUAGE) + self.resetCollectionView() + })) + + alert.addAction(UIAlertAction(title: "swedish.language".localized, + style: .default, + handler: { (_) in + UserDefaults.standard.set("sv", forKey: Keys.APP_LANGUAGE) + self.resetCollectionView() + })) + + alert.addAction(UIAlertAction(title: "spanish.language".localized, + style: .default, + handler: { (_) in + UserDefaults.standard.set("es", forKey: Keys.APP_LANGUAGE) + self.resetCollectionView() + })) + + alert.addAction(UIAlertAction(title: "cancel".localized, + style: .destructive, + handler: nil)) + + self.present(alert, animated: true, completion: nil) + } + + func resetCollectionView(){ + loadMovies(movieType: movieType) + self.viewWillAppear(true) + self.collectionView.setContentOffset(CGPoint(x: 0, y: 100), animated: false) + self.collectionView.setContentOffset(CGPoint(x: 0, y: 0), animated: true) + } + +} + +extension MoviesVC: MovieSegmentDelegate { + func loadMovie(movieType: MovieType) { + self.movieType = movieType + loadMovies(movieType: movieType) + } +} diff --git a/Darsey-MovieDB/Darsey-MovieDB/Views/Movies/MoviesVC.xib b/Darsey-MovieDB/Darsey-MovieDB/Views/Movies/MoviesVC.xib new file mode 100644 index 00000000..de112171 --- /dev/null +++ b/Darsey-MovieDB/Darsey-MovieDB/Views/Movies/MoviesVC.xib @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Darsey-MovieDB/Darsey-MovieDB/Views/Movies/MoviesVM.swift b/Darsey-MovieDB/Darsey-MovieDB/Views/Movies/MoviesVM.swift new file mode 100644 index 00000000..c4eb4ecb --- /dev/null +++ b/Darsey-MovieDB/Darsey-MovieDB/Views/Movies/MoviesVM.swift @@ -0,0 +1,51 @@ +// +// MoviesViewModel.swift +// Darsey-MovieDB +// +// Created by Emil Vaklinov on 09/03/2021. +// + +import Foundation + +struct MoviesViewModel { + + private let movie: Movie + + init(_ movie: Movie) { + self.movie = movie + } + + var title: String { + return self.movie.title + } + + var release_date: String { + return self.movie.release_date + } + + var vote_average: Double { + return self.movie.vote_average + } + + var poster_path: String? { + return self.movie.poster_path + } + + var backdrop_path: String? { + return self.movie.backdrop_path + } + + var id: Int { + return self.movie.id + } + + var posterURL: String { + guard let poster_path = movie.poster_path else { return "" } + return ApiURL.image + "w342/\(poster_path)" + } + + var backdropURL: String { + guard let backdrop_path = movie.backdrop_path else { return "" } + return ApiURL.image + "original/\(backdrop_path)" + } +} diff --git a/Darsey-MovieDB/Darsey-MovieDB/Views/SelectedMovie/SelectedMovieVC.swift b/Darsey-MovieDB/Darsey-MovieDB/Views/SelectedMovie/SelectedMovieVC.swift new file mode 100644 index 00000000..a0c3584e --- /dev/null +++ b/Darsey-MovieDB/Darsey-MovieDB/Views/SelectedMovie/SelectedMovieVC.swift @@ -0,0 +1,118 @@ +// +// SelectedMovieVC.swift +// Darsey-MovieDB +// +// Created by Emil Vaklinov on 09/03/2021. +// + +import UIKit + +class SelectedMovieVC: UIViewController { + + //MARK:- Outlets + @IBOutlet weak var titleLabel: UILabel! + @IBOutlet weak var posterImageView: UIImageView! + @IBOutlet weak var yearLabel: UILabel! + @IBOutlet weak var runtimeLabel: UILabel! + @IBOutlet weak var scoreLabel: UILabel! + @IBOutlet weak var genresLabel: UILabel! + @IBOutlet weak var summaryView: UIView! + @IBOutlet weak var summaryTextView: UITextView! + @IBOutlet weak var closeButtonView: UIView! + @IBOutlet weak var playTrailerView: UIView! + @IBOutlet weak var playTrailerLabel: UILabel! + @IBOutlet weak var posterLeadingConstraint: NSLayoutConstraint! + @IBOutlet weak var posterTrailingConstraint: NSLayoutConstraint! + @IBOutlet weak var trailerViewConstraint: NSLayoutConstraint! + + //MARK: - Properties + static let reuseId = "SelectedMovieVC" + var movieId: Int? + var viewModel: SelectedMovieViewModel? + var backdropUrl: String? + var trailerId: String? + + //MARK:- LifeCycle + + override func viewDidLoad() { + super.viewDidLoad() + posterImageView.layer.cornerRadius = 7 + setupVC() + } + + override func viewDidAppear(_ animated: Bool) { + loadMovieDetails() + } +} + +//MARK: VC Extension, Methods and Actions +extension SelectedMovieVC { + + private func setupVC() { + ViewProperties.configureCircularViewWithShadow(backgroundView: closeButtonView) + ViewProperties.configureViewWithRoundBorderAndShadow(backgroundView: playTrailerView) + if UIDevice.current.userInterfaceIdiom == .pad { + summaryTextView.font = .systemFont(ofSize: 20) + } + posterImageView.contentMode = .scaleAspectFill + } + + private func loadMovieDetails(){ + guard let movieId = movieId else { return } + guard let currentLanguage = UserDefaults.standard.string(forKey: Keys.APP_LANGUAGE) else { return } + showActivityIndicator() + + MovieService.shared.getSelectedMovie(movieId: movieId, language: currentLanguage) { [weak self] response in + guard let self = self else { return } + switch response { + case .success(let movieDetails): + DispatchQueue.main.async { + self.viewModel = SelectedMovieViewModel(movieDetails) + + self.titleLabel.text = self.viewModel?.title + self.runtimeLabel.text = self.viewModel?.runtimeInMinutes + self.yearLabel.text = self.viewModel?.yearOfRelease + self.scoreLabel.text = self.viewModel?.score + self.summaryTextView.text = self.viewModel?.overview + self.genresLabel.text = self.viewModel?.allGenres + } + case .failure: + self.hideActivityIndicator() + } + } + + guard let posterURL = backdropUrl else { return } + MovieService.shared.getImage(from: posterURL, completed: { image in + DispatchQueue.main.async { + self.hideActivityIndicator() + self.posterImageView.image = image != nil ? image : Images.emptyImage + } + }) + + MovieService.shared.getTrailers(movieId: movieId, language: currentLanguage) { [weak self] response in + guard let self = self else { return } + switch response { + case .success(let movieTrailers): + DispatchQueue.main.async { + guard ((movieTrailers.first?.key) != nil) else { return } + self.trailerId = movieTrailers.first?.key + self.playTrailerView.isHidden = false + ViewProperties.animateBounceEffect(backgroundView: self.playTrailerView) + } + case .failure: + self.hideActivityIndicator() + } + } + } + + @IBAction func closeButtonTapped(_ sender: Any) { + self.dismiss(animated: true, completion: nil) + } + + @IBAction func trailerButtonTapped(_ sender: Any) { + guard let trailerId = trailerId else { return } + let trailerVC = TrailerVC(trailerId: trailerId) + + self.present(trailerVC, animated: true, completion: nil) + } +} diff --git a/Darsey-MovieDB/Darsey-MovieDB/Views/SelectedMovie/SelectedMovieVC.xib b/Darsey-MovieDB/Darsey-MovieDB/Views/SelectedMovie/SelectedMovieVC.xib new file mode 100644 index 00000000..c79d1628 --- /dev/null +++ b/Darsey-MovieDB/Darsey-MovieDB/Views/SelectedMovie/SelectedMovieVC.xib @@ -0,0 +1,320 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Darsey-MovieDB/Darsey-MovieDB/Views/SelectedMovie/SelectedMovieVM.swift b/Darsey-MovieDB/Darsey-MovieDB/Views/SelectedMovie/SelectedMovieVM.swift new file mode 100644 index 00000000..ecb3ca2f --- /dev/null +++ b/Darsey-MovieDB/Darsey-MovieDB/Views/SelectedMovie/SelectedMovieVM.swift @@ -0,0 +1,75 @@ +// +// SelectedMovieViewModel.swift +// Darsey-MovieDB +// +// Created by Emil Vaklinov on 09/03/2021. +// + +import Foundation + +struct SelectedMovieViewModel { + private let selectedMovie: SelectedMovie + + init(_ selectedMovie: SelectedMovie) { + self.selectedMovie = selectedMovie + } + + var title: String { + return self.selectedMovie.title + } + + var runtime: Int { + return self.selectedMovie.runtime + } + + var release_date: String { + return self.selectedMovie.release_date + } + + var vote_average: Double { + return self.selectedMovie.vote_average + } + + var genres: [Genre] { + return self.selectedMovie.genres + } + + var overview: String { + return self.selectedMovie.overview + } + + var runtimeInMinutes: String { + return "\(self.selectedMovie.runtime) min" + } + + var yearOfRelease: String { + let originalDateFormat = DateFormatter() + originalDateFormat.dateFormat = "yyyy-MM-dd" + + guard let date = originalDateFormat.date(from: self.selectedMovie.release_date) else { return "" } + + let newDateFormat = DateFormatter() + newDateFormat.dateFormat = "yyyy" + + return newDateFormat.string(from: date) + } + + var score: String { + return "\(self.selectedMovie.vote_average) / 10" + } + + var allGenres: String { + let genres = selectedMovie.genres + + var allGenres = "" + for (index,genre) in genres.enumerated(){ + if index == 0 { + allGenres = genre.name + } else { + allGenres += ", \(genre.name)" + } + } + + return allGenres + } +} diff --git a/Darsey-MovieDB/Darsey-MovieDB/Views/Trailer/TrailerVC.swift b/Darsey-MovieDB/Darsey-MovieDB/Views/Trailer/TrailerVC.swift new file mode 100644 index 00000000..f905ca11 --- /dev/null +++ b/Darsey-MovieDB/Darsey-MovieDB/Views/Trailer/TrailerVC.swift @@ -0,0 +1,57 @@ +// +// TrailerVC.swift +// Darsey-MovieDB +// +// Created by Emil Vaklinov on 09/03/2021. +// + +import UIKit +import WebKit + +class TrailerVC: UIViewController { + + //MARK:- Outlets + @IBOutlet weak var webView: WKWebView! + @IBOutlet weak var closeView: UIView! + + //MARK:- Properties + static let reuseId = "TrailerVC" + var trailerId: String + + init(trailerId: String) { + self.trailerId = trailerId + super.init(nibName: nil, bundle: nil) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + override func viewDidLoad() { + super.viewDidLoad() + } + + override func viewDidAppear(_ animated: Bool) { + setupVC() + loadTrailer() + } +} + +extension TrailerVC { + + private func setupVC(){ + closeView.isHidden = false + ViewProperties.configureCircularViewWithShadow(backgroundView: closeView) + ViewProperties.animateBounceEffect(backgroundView: closeView) + } + + func loadTrailer() { + guard let trailerURL = URL(string: ApiURL.youtubeURL + "\(trailerId)") else { return } + let request = URLRequest(url: trailerURL) + webView.load(request) + } + + @IBAction func closeButtonTapped(_ sender: Any) { + self.dismiss(animated: true, completion: nil) + } +} diff --git a/Darsey-MovieDB/Darsey-MovieDB/Views/Trailer/TrailerVC.xib b/Darsey-MovieDB/Darsey-MovieDB/Views/Trailer/TrailerVC.xib new file mode 100644 index 00000000..663b1391 --- /dev/null +++ b/Darsey-MovieDB/Darsey-MovieDB/Views/Trailer/TrailerVC.xib @@ -0,0 +1,68 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Darsey-MovieDB/Darsey-MovieDB/es.lproj/LaunchScreen.storyboard b/Darsey-MovieDB/Darsey-MovieDB/es.lproj/LaunchScreen.storyboard new file mode 100644 index 00000000..6642620f --- /dev/null +++ b/Darsey-MovieDB/Darsey-MovieDB/es.lproj/LaunchScreen.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Darsey-MovieDB/Darsey-MovieDB/es.lproj/LaunchScreen.strings b/Darsey-MovieDB/Darsey-MovieDB/es.lproj/LaunchScreen.strings new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/Darsey-MovieDB/Darsey-MovieDB/es.lproj/LaunchScreen.strings @@ -0,0 +1 @@ + diff --git a/Darsey-MovieDB/Darsey-MovieDB/sv.lproj/LaunchScreen.storyboard b/Darsey-MovieDB/Darsey-MovieDB/sv.lproj/LaunchScreen.storyboard new file mode 100644 index 00000000..6642620f --- /dev/null +++ b/Darsey-MovieDB/Darsey-MovieDB/sv.lproj/LaunchScreen.storyboard @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Darsey-MovieDB/Darsey-MovieDB/sv.lproj/LaunchScreen.strings b/Darsey-MovieDB/Darsey-MovieDB/sv.lproj/LaunchScreen.strings new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/Darsey-MovieDB/Darsey-MovieDB/sv.lproj/LaunchScreen.strings @@ -0,0 +1 @@ + diff --git a/Darsey-MovieDB/Darsey-MovieDBTests/Darsey_MovieDBTests.swift b/Darsey-MovieDB/Darsey-MovieDBTests/Darsey_MovieDBTests.swift new file mode 100644 index 00000000..fb09cdf4 --- /dev/null +++ b/Darsey-MovieDB/Darsey-MovieDBTests/Darsey_MovieDBTests.swift @@ -0,0 +1,52 @@ +// +// Darsey_MovieDBTests.swift +// Darsey-MovieDBTests +// +// Created by Emil Vaklinov on 08/03/2021. +// + +import XCTest +@testable import Darsey_MovieDB + +class Darsey_MovieDBTests: XCTestCase { + + //MARK: - Properties + + private var movie: Movie! + + // MARK: Test lifecycle + override func setUp() { + movie = Movie(title: "Inception", + release_date: "2010", + vote_average: 9.0, + poster_path: "poster.url.com", + backdrop_path: "backdrop.url.com", + id: 123456) + } + + func testMovieViewModel() { + let viewModel = MoviesViewModel(movie) + + XCTAssertEqual(movie.id, viewModel.id) + XCTAssertEqual(movie.release_date, viewModel.release_date) + XCTAssertEqual(movie.vote_average, viewModel.vote_average) + XCTAssertEqual(movie.poster_path, viewModel.poster_path) + XCTAssertEqual(movie.backdrop_path, viewModel.backdrop_path) + XCTAssertEqual(movie.title, viewModel.title) + } + + func testMoviePosterURL(){ + let movieVM = MoviesViewModel(movie) + let mockPosterURL = ApiURL.image + "w342/\(movie.poster_path!)" + + XCTAssertEqual(mockPosterURL, movieVM.posterURL) + } + + func testMovieBackdropURL(){ + let movieVM = MoviesViewModel(movie) + let mockBackdropURL = ApiURL.image + "original/\(movie.backdrop_path!)" + + XCTAssertEqual(mockBackdropURL, movieVM.backdropURL) + } + +} diff --git a/Darsey-MovieDB/Darsey-MovieDBTests/Info.plist b/Darsey-MovieDB/Darsey-MovieDBTests/Info.plist new file mode 100644 index 00000000..64d65ca4 --- /dev/null +++ b/Darsey-MovieDB/Darsey-MovieDBTests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/Darsey-MovieDB/Darsey-MovieDBTests/SelectedMovieVMTests.swift b/Darsey-MovieDB/Darsey-MovieDBTests/SelectedMovieVMTests.swift new file mode 100644 index 00000000..262970fe --- /dev/null +++ b/Darsey-MovieDB/Darsey-MovieDBTests/SelectedMovieVMTests.swift @@ -0,0 +1,64 @@ +// +// SelectedMovieVMTests.swift +// Darsey-MovieDBTests +// +// Created by Emil Vaklinov on 09/03/2021. +// + +import XCTest +@testable import Darsey_MovieDB + +class SelectedMovieVMTests: XCTestCase { + + //MARK: - Properties + + private var selectedMovie: SelectedMovie! + + // MARK: Test lifecycle + + override func setUp() { + let genres = [Genre(id: 1, name: "Suspense"), + Genre(id: 2, name: "Action"), + Genre(id: 3, name: "Thriller")] + + selectedMovie = SelectedMovie(title: "Inception", + runtime: 120, + release_date: "2010-10-16", + vote_average: 9.0, + genres: genres, + overview: "Movie Overview") + } + + func testSelectedMovieViewModel() { + let viewModel = SelectedMovieViewModel(selectedMovie) + + XCTAssertEqual(selectedMovie.title, viewModel.title) + XCTAssertEqual(selectedMovie.release_date, viewModel.release_date) + XCTAssertEqual(selectedMovie.vote_average, viewModel.vote_average) + XCTAssertEqual(selectedMovie.runtime, viewModel.runtime) + XCTAssertEqual(selectedMovie.genres, viewModel.genres) + XCTAssertEqual(selectedMovie.title, viewModel.title) + } + + func testYearOfRelease() { + let viewModel = SelectedMovieViewModel(selectedMovie) + let mockYearOfRelease = "2010" + + XCTAssertEqual(mockYearOfRelease, viewModel.yearOfRelease) + } + + func testScore() { + let viewModel = SelectedMovieViewModel(selectedMovie) + let mockScore = "9.0 / 10" + + XCTAssertEqual(mockScore, viewModel.score) + } + + func testAllGenres() { + let viewModel = SelectedMovieViewModel(selectedMovie) + let mockAllGenres = "Suspense, Action, Thriller" + + XCTAssertEqual(mockAllGenres, viewModel.allGenres) + } + +} diff --git a/Darsey-MovieDB/Darsey-MovieDBUITests/Darsey_MovieDBUITests.swift b/Darsey-MovieDB/Darsey-MovieDBUITests/Darsey_MovieDBUITests.swift new file mode 100644 index 00000000..0f344f6e --- /dev/null +++ b/Darsey-MovieDB/Darsey-MovieDBUITests/Darsey_MovieDBUITests.swift @@ -0,0 +1,42 @@ +// +// Darsey_MovieDBUITests.swift +// Darsey-MovieDBUITests +// +// Created by Emil Vaklinov on 08/03/2021. +// + +import XCTest + +class Darsey_MovieDBUITests: XCTestCase { + + override func setUpWithError() throws { + // Put setup code here. This method is called before the invocation of each test method in the class. + + // In UI tests it is usually best to stop immediately when a failure occurs. + continueAfterFailure = false + + // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. + } + + override func tearDownWithError() throws { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testExample() throws { + // UI tests must launch the application that they test. + let app = XCUIApplication() + app.launch() + + // Use recording to get started writing UI tests. + // Use XCTAssert and related functions to verify your tests produce the correct results. + } + + func testLaunchPerformance() throws { + if #available(macOS 10.15, iOS 13.0, tvOS 13.0, *) { + // This measures how long it takes to launch your application. + measure(metrics: [XCTApplicationLaunchMetric()]) { + XCUIApplication().launch() + } + } + } +} diff --git a/Darsey-MovieDB/Darsey-MovieDBUITests/Info.plist b/Darsey-MovieDB/Darsey-MovieDBUITests/Info.plist new file mode 100644 index 00000000..64d65ca4 --- /dev/null +++ b/Darsey-MovieDB/Darsey-MovieDBUITests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + $(PRODUCT_BUNDLE_PACKAGE_TYPE) + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + +