├── Movie
├── Resources
│ ├── Colors.xcassets
│ │ ├── Contents.json
│ │ ├── bg.colorset
│ │ │ └── Contents.json
│ │ ├── icon2.colorset
│ │ │ └── Contents.json
│ │ ├── icons.colorset
│ │ │ └── Contents.json
│ │ ├── star.colorset
│ │ │ └── Contents.json
│ │ ├── tabbar.colorset
│ │ │ └── Contents.json
│ │ ├── title.colorset
│ │ │ └── Contents.json
│ │ ├── commetsView.colorset
│ │ │ └── Contents.json
│ │ ├── logoRed.colorset
│ │ │ └── Contents.json
│ │ └── statusBar.colorset
│ │ │ └── Contents.json
│ ├── Icons.xcassets
│ │ ├── Contents.json
│ │ ├── Tabbar
│ │ │ ├── Contents.json
│ │ │ ├── home.imageset
│ │ │ │ ├── Contents.json
│ │ │ │ └── home.svg
│ │ │ ├── user.imageset
│ │ │ │ ├── Contents.json
│ │ │ │ └── user.svg
│ │ │ ├── bookmark.imageset
│ │ │ │ ├── Contents.json
│ │ │ │ └── bookmark.svg
│ │ │ └── explore.imageset
│ │ │ │ ├── Contents.json
│ │ │ │ └── explore.svg
│ │ ├── Navigation
│ │ │ ├── Contents.json
│ │ │ ├── filter.imageset
│ │ │ │ ├── filter.svg
│ │ │ │ └── Contents.json
│ │ │ ├── menu.imageset
│ │ │ │ ├── Contents.json
│ │ │ │ └── menu.svg
│ │ │ └── search.imageset
│ │ │ │ ├── Contents.json
│ │ │ │ └── search.svg
│ │ ├── logo.imageset
│ │ │ ├── MOVIE LOGO4 1.png
│ │ │ └── Contents.json
│ │ ├── launchPhoto.imageset
│ │ │ ├── launchPhoto.png
│ │ │ └── Contents.json
│ │ └── gradient.imageset
│ │ │ ├── Contents.json
│ │ │ └── gradient.svg
│ ├── Assets.xcassets
│ │ └── AppIcon.appiconset
│ │ │ ├── 100.png
│ │ │ ├── 1024.png
│ │ │ ├── 114.png
│ │ │ ├── 120.png
│ │ │ ├── 144.png
│ │ │ ├── 152.png
│ │ │ ├── 167.png
│ │ │ ├── 180.png
│ │ │ ├── 20.png
│ │ │ ├── 29.png
│ │ │ ├── 40.png
│ │ │ ├── 50.png
│ │ │ ├── 57.png
│ │ │ ├── 58.png
│ │ │ ├── 60.png
│ │ │ ├── 72.png
│ │ │ ├── 76.png
│ │ │ ├── 80.png
│ │ │ ├── 87.png
│ │ │ └── Contents.json
│ ├── GeneratedColors.swift
│ ├── GeneratedIcons.swift
│ ├── Base.lproj
│ │ └── LaunchScreen.storyboard
│ └── movies.json
├── Models
│ ├── Comments.swift
│ └── MovieDTO.swift
├── NetworkService
│ ├── Network Components
│ │ ├── HTTPMethods.swift
│ │ ├── NetworkErrors.swift
│ │ └── ProResult.swift
│ ├── Base Router
│ │ ├── MainScreenRouter.swift
│ │ └── BaseRouter.swift
│ └── NetworkService.swift
├── AppDelegate.swift
├── Extension
│ └── UIKit
│ │ ├── UIImage + Extension.swift
│ │ ├── UINavigationController + Extension.swift
│ │ ├── UICollectionViewCell+Register+extension.swift
│ │ └── ViewController + Extension.swift
├── MovieDetailsScreen
│ ├── View
│ │ ├── SectionDecorationView.swift
│ │ ├── CommentsViewFooter.swift
│ │ ├── LableImage.swift
│ │ ├── NavigationBarBack.swift
│ │ └── TopView.swift
│ ├── Cells
│ │ └── CommentsCell.swift
│ └── MovieDetailedScreenViewController.swift
├── SceneDelegate.swift
├── Info.plist
├── MainScreen
│ ├── Cells
│ │ └── PhotoCell.swift
│ ├── View
│ │ ├── HeaderView.swift
│ │ └── NavigationBar.swift
│ ├── MainScreenViewModel.swift
│ ├── MainScreenViewController.swift
│ └── MainContentView.swift
└── Tabbar
│ └── UITabBarController.swift
├── Movie.xcodeproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ ├── xcuserdata
│ │ └── siuzanna.xcuserdatad
│ │ │ └── UserInterfaceState.xcuserstate
│ └── xcshareddata
│ │ ├── IDEWorkspaceChecks.plist
│ │ └── swiftpm
│ │ └── Package.resolved
├── xcuserdata
│ └── siuzanna.xcuserdatad
│ │ ├── xcdebugger
│ │ └── Breakpoints_v2.xcbkptlist
│ │ └── xcschemes
│ │ └── xcschememanagement.plist
└── project.pbxproj
├── .gitignore
├── .swiftlint.yml
├── swiftgen.yml
└── README.md
/Movie/Resources/Colors.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Movie/Resources/Icons.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Movie/Resources/Icons.xcassets/Tabbar/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Movie/Resources/Icons.xcassets/Navigation/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/Movie/Resources/Assets.xcassets/AppIcon.appiconset/100.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/siuzanna/Movie-MVVM/HEAD/Movie/Resources/Assets.xcassets/AppIcon.appiconset/100.png
--------------------------------------------------------------------------------
/Movie/Resources/Assets.xcassets/AppIcon.appiconset/1024.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/siuzanna/Movie-MVVM/HEAD/Movie/Resources/Assets.xcassets/AppIcon.appiconset/1024.png
--------------------------------------------------------------------------------
/Movie/Resources/Assets.xcassets/AppIcon.appiconset/114.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/siuzanna/Movie-MVVM/HEAD/Movie/Resources/Assets.xcassets/AppIcon.appiconset/114.png
--------------------------------------------------------------------------------
/Movie/Resources/Assets.xcassets/AppIcon.appiconset/120.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/siuzanna/Movie-MVVM/HEAD/Movie/Resources/Assets.xcassets/AppIcon.appiconset/120.png
--------------------------------------------------------------------------------
/Movie/Resources/Assets.xcassets/AppIcon.appiconset/144.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/siuzanna/Movie-MVVM/HEAD/Movie/Resources/Assets.xcassets/AppIcon.appiconset/144.png
--------------------------------------------------------------------------------
/Movie/Resources/Assets.xcassets/AppIcon.appiconset/152.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/siuzanna/Movie-MVVM/HEAD/Movie/Resources/Assets.xcassets/AppIcon.appiconset/152.png
--------------------------------------------------------------------------------
/Movie/Resources/Assets.xcassets/AppIcon.appiconset/167.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/siuzanna/Movie-MVVM/HEAD/Movie/Resources/Assets.xcassets/AppIcon.appiconset/167.png
--------------------------------------------------------------------------------
/Movie/Resources/Assets.xcassets/AppIcon.appiconset/180.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/siuzanna/Movie-MVVM/HEAD/Movie/Resources/Assets.xcassets/AppIcon.appiconset/180.png
--------------------------------------------------------------------------------
/Movie/Resources/Assets.xcassets/AppIcon.appiconset/20.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/siuzanna/Movie-MVVM/HEAD/Movie/Resources/Assets.xcassets/AppIcon.appiconset/20.png
--------------------------------------------------------------------------------
/Movie/Resources/Assets.xcassets/AppIcon.appiconset/29.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/siuzanna/Movie-MVVM/HEAD/Movie/Resources/Assets.xcassets/AppIcon.appiconset/29.png
--------------------------------------------------------------------------------
/Movie/Resources/Assets.xcassets/AppIcon.appiconset/40.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/siuzanna/Movie-MVVM/HEAD/Movie/Resources/Assets.xcassets/AppIcon.appiconset/40.png
--------------------------------------------------------------------------------
/Movie/Resources/Assets.xcassets/AppIcon.appiconset/50.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/siuzanna/Movie-MVVM/HEAD/Movie/Resources/Assets.xcassets/AppIcon.appiconset/50.png
--------------------------------------------------------------------------------
/Movie/Resources/Assets.xcassets/AppIcon.appiconset/57.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/siuzanna/Movie-MVVM/HEAD/Movie/Resources/Assets.xcassets/AppIcon.appiconset/57.png
--------------------------------------------------------------------------------
/Movie/Resources/Assets.xcassets/AppIcon.appiconset/58.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/siuzanna/Movie-MVVM/HEAD/Movie/Resources/Assets.xcassets/AppIcon.appiconset/58.png
--------------------------------------------------------------------------------
/Movie/Resources/Assets.xcassets/AppIcon.appiconset/60.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/siuzanna/Movie-MVVM/HEAD/Movie/Resources/Assets.xcassets/AppIcon.appiconset/60.png
--------------------------------------------------------------------------------
/Movie/Resources/Assets.xcassets/AppIcon.appiconset/72.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/siuzanna/Movie-MVVM/HEAD/Movie/Resources/Assets.xcassets/AppIcon.appiconset/72.png
--------------------------------------------------------------------------------
/Movie/Resources/Assets.xcassets/AppIcon.appiconset/76.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/siuzanna/Movie-MVVM/HEAD/Movie/Resources/Assets.xcassets/AppIcon.appiconset/76.png
--------------------------------------------------------------------------------
/Movie/Resources/Assets.xcassets/AppIcon.appiconset/80.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/siuzanna/Movie-MVVM/HEAD/Movie/Resources/Assets.xcassets/AppIcon.appiconset/80.png
--------------------------------------------------------------------------------
/Movie/Resources/Assets.xcassets/AppIcon.appiconset/87.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/siuzanna/Movie-MVVM/HEAD/Movie/Resources/Assets.xcassets/AppIcon.appiconset/87.png
--------------------------------------------------------------------------------
/Movie/Resources/Icons.xcassets/logo.imageset/MOVIE LOGO4 1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/siuzanna/Movie-MVVM/HEAD/Movie/Resources/Icons.xcassets/logo.imageset/MOVIE LOGO4 1.png
--------------------------------------------------------------------------------
/Movie/Resources/Icons.xcassets/launchPhoto.imageset/launchPhoto.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/siuzanna/Movie-MVVM/HEAD/Movie/Resources/Icons.xcassets/launchPhoto.imageset/launchPhoto.png
--------------------------------------------------------------------------------
/Movie.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/Movie.xcodeproj/xcuserdata/siuzanna.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
--------------------------------------------------------------------------------
/Movie.xcodeproj/project.xcworkspace/xcuserdata/siuzanna.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/siuzanna/Movie-MVVM/HEAD/Movie.xcodeproj/project.xcworkspace/xcuserdata/siuzanna.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # Ignore macOS system files
3 | .DS_Store
4 |
5 | # Ignore build products
6 | /build/
7 | *.xcarchive
8 |
9 | # Ignore Pods folder (for iOS projects using CocoaPods)
10 | /Pods/
11 |
12 | # Ignore personal Xcode user data
13 | *.xcuserdatad/
--------------------------------------------------------------------------------
/.swiftlint.yml:
--------------------------------------------------------------------------------
1 | excluded:
2 | - Carthage
3 | - Pods
4 |
5 | disabled_rules:
6 | - trailing_whitespace
7 | - force_cast
8 | - switch_case_alignment
9 | - redundant_string_enum_value
10 | - closure_parameter_position
11 |
12 | line_length: 140
13 |
14 | #$ swiftlint autocorrect
15 |
--------------------------------------------------------------------------------
/Movie/Resources/Icons.xcassets/Navigation/filter.imageset/filter.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/Movie/Models/Comments.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Comments.swift
3 | // Movie
4 | //
5 | // Created by siuzanna on 19/12/21.
6 | //
7 |
8 | import Foundation
9 |
10 | struct Comments: Codable, Hashable {
11 | let id: Int?
12 | let name: String?
13 | let comment: String?
14 | let picture: String?
15 | }
16 |
17 |
--------------------------------------------------------------------------------
/Movie/NetworkService/Network Components/HTTPMethods.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HTTPMethods.swift
3 | // Movie
4 | //
5 | // Created by siuzanna on 19/12/21.
6 | //
7 |
8 | import Foundation
9 |
10 | enum HttpMethod: String {
11 | case GET
12 | case POST
13 | case PATCH
14 | case DELETE
15 | case PUT
16 | }
17 |
--------------------------------------------------------------------------------
/Movie.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/Movie/Resources/Icons.xcassets/Navigation/menu.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "menu.svg",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | },
12 | "properties" : {
13 | "preserves-vector-representation" : true
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Movie/Resources/Icons.xcassets/Tabbar/home.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "home.svg",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | },
12 | "properties" : {
13 | "preserves-vector-representation" : true
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Movie/Resources/Icons.xcassets/Tabbar/user.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "user.svg",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | },
12 | "properties" : {
13 | "preserves-vector-representation" : true
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Movie/Resources/Icons.xcassets/Navigation/filter.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "filter.svg",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | },
12 | "properties" : {
13 | "preserves-vector-representation" : true
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Movie/Resources/Icons.xcassets/Navigation/search.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "search.svg",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | },
12 | "properties" : {
13 | "preserves-vector-representation" : true
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Movie/Resources/Icons.xcassets/Tabbar/bookmark.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "bookmark.svg",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | },
12 | "properties" : {
13 | "preserves-vector-representation" : true
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Movie/Resources/Icons.xcassets/Tabbar/explore.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "explore.svg",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | },
12 | "properties" : {
13 | "preserves-vector-representation" : true
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Movie/Resources/Icons.xcassets/launchPhoto.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "launchPhoto.png",
5 | "idiom" : "universal"
6 | }
7 | ],
8 | "info" : {
9 | "author" : "xcode",
10 | "version" : 1
11 | },
12 | "properties" : {
13 | "preserves-vector-representation" : true
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/Movie/NetworkService/Network Components/NetworkErrors.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NetworkErrors.swift
3 | // Movie
4 | //
5 | // Created by siuzanna on 19/12/21.
6 | //
7 |
8 | import Foundation
9 |
10 | enum NetworkErrors: Error {
11 | case badRequest
12 | case unauthorized
13 | }
14 |
15 | enum StatusCode: Int {
16 | case okey = 200
17 | case badRequest = 400
18 | }
19 |
--------------------------------------------------------------------------------
/Movie/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // Movie
4 | //
5 | // Created by siuzanna on 17/12/21.
6 | //
7 |
8 | import UIKit
9 |
10 | @main
11 | class AppDelegate: UIResponder, UIApplicationDelegate {
12 |
13 | func application(
14 | _ application: UIApplication,
15 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
16 | return true
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/Movie/Resources/Icons.xcassets/gradient.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "gradient.svg",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Movie/Resources/Icons.xcassets/logo.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "MOVIE LOGO4 1.png",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Movie/Resources/Colors.xcassets/bg.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0x29",
9 | "green" : "0x1A",
10 | "red" : "0x1C"
11 | }
12 | },
13 | "idiom" : "universal"
14 | }
15 | ],
16 | "info" : {
17 | "author" : "xcode",
18 | "version" : 1
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Movie/Resources/Colors.xcassets/icon2.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0xDF",
9 | "green" : "0xDD",
10 | "red" : "0xDE"
11 | }
12 | },
13 | "idiom" : "universal"
14 | }
15 | ],
16 | "info" : {
17 | "author" : "xcode",
18 | "version" : 1
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Movie/Resources/Colors.xcassets/icons.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0xA9",
9 | "green" : "0xA3",
10 | "red" : "0xA4"
11 | }
12 | },
13 | "idiom" : "universal"
14 | }
15 | ],
16 | "info" : {
17 | "author" : "xcode",
18 | "version" : 1
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Movie/Resources/Colors.xcassets/star.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0x01",
9 | "green" : "0x96",
10 | "red" : "0xF9"
11 | }
12 | },
13 | "idiom" : "universal"
14 | }
15 | ],
16 | "info" : {
17 | "author" : "xcode",
18 | "version" : 1
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Movie/Resources/Colors.xcassets/tabbar.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0x4B",
9 | "green" : "0x35",
10 | "red" : "0x38"
11 | }
12 | },
13 | "idiom" : "universal"
14 | }
15 | ],
16 | "info" : {
17 | "author" : "xcode",
18 | "version" : 1
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Movie/Resources/Colors.xcassets/title.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0xDF",
9 | "green" : "0xDD",
10 | "red" : "0xDE"
11 | }
12 | },
13 | "idiom" : "universal"
14 | }
15 | ],
16 | "info" : {
17 | "author" : "xcode",
18 | "version" : 1
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Movie/Resources/Colors.xcassets/commetsView.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0x35",
9 | "green" : "0x21",
10 | "red" : "0x24"
11 | }
12 | },
13 | "idiom" : "universal"
14 | }
15 | ],
16 | "info" : {
17 | "author" : "xcode",
18 | "version" : 1
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Movie/Resources/Colors.xcassets/logoRed.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0x26",
9 | "green" : "0x26",
10 | "red" : "0xE8"
11 | }
12 | },
13 | "idiom" : "universal"
14 | }
15 | ],
16 | "info" : {
17 | "author" : "xcode",
18 | "version" : 1
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Movie/Resources/Colors.xcassets/statusBar.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0x24",
9 | "green" : "0x14",
10 | "red" : "0x17"
11 | }
12 | },
13 | "idiom" : "universal"
14 | }
15 | ],
16 | "info" : {
17 | "author" : "xcode",
18 | "version" : 1
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/Movie/Resources/Icons.xcassets/gradient.imageset/gradient.svg:
--------------------------------------------------------------------------------
1 |
10 |
--------------------------------------------------------------------------------
/Movie/Resources/Icons.xcassets/Navigation/menu.imageset/menu.svg:
--------------------------------------------------------------------------------
1 |
6 |
--------------------------------------------------------------------------------
/Movie/Models/MovieDTO.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Movies.swift
3 | // Movie
4 | //
5 | // Created by siuzanna on 19/12/21.
6 | //
7 |
8 | import Foundation
9 |
10 | struct MovieDTO: Codable, Hashable {
11 | let id: Int
12 | let type: Int
13 | let series: Bool
14 | let name: String
15 | let time: Int
16 | let genre: [String]
17 | let rating: String
18 | let votes: String
19 | let photo: String
20 | let miniPhoto: String
21 | let description: String
22 | let trailer: String
23 | let comments: [Comments]
24 | }
25 |
--------------------------------------------------------------------------------
/Movie/Extension/UIKit/UIImage + Extension.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UIImage + Extension.swift
3 | // Movie
4 | //
5 | // Created by siuzanna on 17/12/21.
6 | //
7 |
8 | import UIKit
9 |
10 | extension UIImage {
11 | static func from(color: UIColor) -> UIImage {
12 | let rect = CGRect(x: 0, y: 0, width: 1, height: 1)
13 | UIGraphicsBeginImageContext(rect.size)
14 | let context = UIGraphicsGetCurrentContext()
15 | context!.setFillColor(color.cgColor)
16 | context!.fill(rect)
17 | let img = UIGraphicsGetImageFromCurrentImageContext()
18 | UIGraphicsEndImageContext()
19 | return img!
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/Movie/MovieDetailsScreen/View/SectionDecorationView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SectionDecorationView.swift
3 | // Movie
4 | //
5 | // Created by siuzanna on 20/12/21.
6 | //
7 |
8 | import UIKit
9 |
10 | class SectionBackgroundDecorationView: UICollectionReusableView {
11 |
12 | override init(frame: CGRect) {
13 | super.init(frame: frame)
14 | configure()
15 | }
16 |
17 | required init?(coder: NSCoder) {
18 | fatalError("init(coder:) has not been implemented")
19 | }
20 |
21 | private func configure() {
22 | backgroundColor = Colors.commentsView.color
23 | layer.cornerRadius = 12
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/swiftgen.yml:
--------------------------------------------------------------------------------
1 |
2 | # MARK: - Colors
3 |
4 | # xcassets:
5 | # inputs: Movie/Resources/Colors.xcassets
6 | # outputs:
7 | # templateName: swift5
8 | # params:
9 | # forceProvidesNamespaces: true
10 | # enumName: Colors
11 | # output: Movie/Resources/GeneratedColors.swift
12 |
13 |
14 |
15 | # MARK: - Icons
16 |
17 | # xcassets:
18 | # inputs: Movie/Resources/Icons.xcassets
19 | # outputs:
20 | # templateName: swift5
21 | # params:
22 | # forceProvidesNamespaces: true
23 | # enumName: Icons
24 | # output: Movie/Resources/GeneratedIcons.swift
25 |
26 |
27 | # Pods/SwiftGen/bin/swiftgen -- runs the pod
28 |
--------------------------------------------------------------------------------
/Movie/SceneDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SceneDelegate.swift
3 | // Movie
4 | //
5 | // Created by siuzanna on 17/12/21.
6 | //
7 |
8 | import UIKit
9 |
10 | class SceneDelegate: UIResponder, UIWindowSceneDelegate {
11 |
12 | var window: UIWindow?
13 |
14 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession,
15 | options connectionOptions: UIScene.ConnectionOptions) {
16 | guard let windowScene = (scene as? UIWindowScene) else { return }
17 | let window = UIWindow(windowScene: windowScene)
18 | window.backgroundColor = UIColor.white
19 | window.rootViewController = TabBarController()
20 | window.makeKeyAndVisible()
21 | self.window = window
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Movie/Resources/Icons.xcassets/Navigation/search.imageset/search.svg:
--------------------------------------------------------------------------------
1 |
12 |
--------------------------------------------------------------------------------
/Movie.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved:
--------------------------------------------------------------------------------
1 | {
2 | "originHash" : "6b6ce0f12bbd7c3a70ad4db9892c7afaef3ba38abf1df2e0ab744794af7d3b86",
3 | "pins" : [
4 | {
5 | "identity" : "kingfisher",
6 | "kind" : "remoteSourceControl",
7 | "location" : "https://github.com/onevcat/Kingfisher.git",
8 | "state" : {
9 | "revision" : "2ef543ee21d63734e1c004ad6c870255e8716c50",
10 | "version" : "7.12.0"
11 | }
12 | },
13 | {
14 | "identity" : "snapkit",
15 | "kind" : "remoteSourceControl",
16 | "location" : "https://github.com/SnapKit/SnapKit.git",
17 | "state" : {
18 | "revision" : "2842e6e84e82eb9a8dac0100ca90d9444b0307f4",
19 | "version" : "5.7.1"
20 | }
21 | }
22 | ],
23 | "version" : 3
24 | }
25 |
--------------------------------------------------------------------------------
/Movie/NetworkService/Network Components/ProResult.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ProResult.swift
3 | // Movie
4 | //
5 | // Created by siuzanna on 19/12/21.
6 | //
7 |
8 | import Foundation
9 |
10 | enum ProResult {
11 | case success(Success)
12 | case badRequest(FailureModel)
13 | case failure(String)
14 | }
15 |
16 | struct FailureModel: Codable {
17 | let statusCode: Int?
18 | let errors: [String]?
19 |
20 | enum CodingKeys: String, CodingKey {
21 | case statusCode
22 | case errors
23 | }
24 |
25 | init(from decoder: Decoder) throws {
26 | let values = try decoder.container(keyedBy: CodingKeys.self)
27 | statusCode = try? values.decodeIfPresent(Int.self, forKey: .statusCode)
28 | errors = try? values.decodeIfPresent([String].self, forKey: .errors)
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/Movie/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | NSAppTransportSecurity
6 |
7 | NSAllowsArbitraryLoads
8 |
9 |
10 | UIApplicationSceneManifest
11 |
12 | UIApplicationSupportsMultipleScenes
13 |
14 | UISceneConfigurations
15 |
16 | UIWindowSceneSessionRoleApplication
17 |
18 |
19 | UISceneConfigurationName
20 | Default Configuration
21 | UISceneDelegateClassName
22 | $(PRODUCT_MODULE_NAME).SceneDelegate
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/Movie/Resources/Icons.xcassets/Tabbar/bookmark.imageset/bookmark.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/Movie/Resources/Icons.xcassets/Tabbar/user.imageset/user.svg:
--------------------------------------------------------------------------------
1 |
5 |
--------------------------------------------------------------------------------
/Movie/NetworkService/Base Router/MainScreenRouter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MainScreenRouter.swift
3 | // Movie
4 | //
5 | // Created by siuzanna on 19/12/21.
6 | //
7 |
8 | import Foundation
9 |
10 | enum MainScreenRouter: BaseRouter {
11 | case getAllPosts
12 |
13 | var path: String {
14 | switch self {
15 | case .getAllPosts:
16 | return "/v3/cbd74aa0-318f-4a38-a4d6-9b1b99a275cf"
17 | }
18 | }
19 |
20 | var queryParameter: [URLQueryItem]? {
21 | switch self {
22 | case .getAllPosts:
23 | return nil
24 | }
25 | }
26 |
27 | var method: HttpMethod {
28 | switch self {
29 | case .getAllPosts:
30 | return .GET
31 | }
32 | }
33 |
34 | var httpBody: Data? {
35 | switch self {
36 | case .getAllPosts:
37 | return nil
38 | }
39 | }
40 |
41 | var httpHeader: [HttpHeader]? {
42 | switch self {
43 | case .getAllPosts:
44 | return nil
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/Movie/MovieDetailsScreen/View/CommentsViewFooter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CollectionReusableView.swift
3 | // Movie
4 | //
5 | // Created by siuzanna on 20/12/21.
6 | //
7 |
8 | import UIKit
9 |
10 | class CommentsViewFooter: UICollectionReusableView {
11 |
12 | private let buttonLabel: UILabel = {
13 | let text = UILabel()
14 | text.text = "See All >"
15 | text.textColor = UIColor(red: 0.467, green: 0.467, blue: 0.467, alpha: 1)
16 | text.font = UIFont.systemFont(ofSize: 15)
17 | return text
18 | }()
19 |
20 | override init(frame: CGRect) {
21 | super.init(frame: frame)
22 | configure()
23 | }
24 |
25 | required init?(coder: NSCoder) {
26 | fatalError("init(coder:) has not been implemented")
27 | }
28 |
29 | private func configure() {
30 | addSubview(buttonLabel)
31 | buttonLabel.snp.makeConstraints { make in
32 | make.center.equalToSuperview()
33 | }
34 | }
35 |
36 | func setTitle(_ text: String) {
37 | buttonLabel.text = text
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/Movie/Extension/UIKit/UINavigationController + Extension.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UINavigationController + Extension.swift
3 | // Movie
4 | //
5 | // Created by siuzanna on 17/12/21.
6 | //
7 |
8 | import UIKit
9 |
10 | extension UINavigationController {
11 |
12 | func makeTransparent() -> UINavigationController {
13 | navigationBar.setBackgroundImage(UIImage(), for: .default)
14 | navigationBar.shadowImage = UIImage()
15 | view.backgroundColor = .clear
16 | let barButtonItemAppearance = UIBarButtonItem.appearance()
17 | barButtonItemAppearance.setTitleTextAttributes(
18 | [NSAttributedString.Key.foregroundColor: UIColor.clear], for: .normal)
19 | navigationItem.backButtonTitle = " "
20 | navigationBar.backItem?.title = " "
21 | navigationItem.backBarButtonItem?.title = " "
22 | navigationBar.tintColor = UIColor.black
23 | return self
24 | }
25 |
26 | func removeTitle() -> UINavigationController {
27 | navigationBar.topItem?.title = " "
28 | return self
29 | }
30 |
31 | func changeTintColor(to color: UIColor) -> UINavigationController {
32 | navigationBar.tintColor = color
33 | return self
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/Movie/Extension/UIKit/UICollectionViewCell+Register+extension.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UICollectionViewCell+Register+extension.swift
3 | // Neo-Cafe
4 | //
5 | // Created by siuzanna on 11/11/21.
6 | //
7 |
8 | import UIKit
9 |
10 | extension UICollectionView {
11 | func register(cell: T.Type) {
12 | register(T.self, forCellWithReuseIdentifier: T.reuseIdentifier)
13 | }
14 |
15 | func register(header: T.Type) {
16 | register(T.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: T.reuseIdentifier)
17 | }
18 |
19 | func register(footer: T.Type) {
20 | register(T.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionFooter, withReuseIdentifier: T.reuseIdentifier)
21 | }
22 |
23 | func dequeue(for indexPath: IndexPath) -> T {
24 | return dequeueReusableCell(withReuseIdentifier: T.reuseIdentifier, for: indexPath) as! T
25 | }
26 |
27 | func dequeue(for indexPath: IndexPath, kind: String) -> T {
28 | return dequeueReusableSupplementaryView( ofKind: kind, withReuseIdentifier: T.reuseIdentifier, for: indexPath) as! T
29 | }
30 | }
31 |
32 | extension UIView {
33 | static var reuseIdentifier: String {
34 | return String(describing: Self.self)
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/Movie/MainScreen/Cells/PhotoCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // PhotoCell.swift
3 | // Movie
4 | //
5 | // Created by siuzanna on 18/12/21.
6 | //
7 |
8 | import UIKit
9 | import SnapKit
10 | import Kingfisher
11 |
12 | final class PhotoCell: UICollectionViewCell {
13 |
14 | private lazy var imageView: UIImageView = {
15 | let image = UIImageView()
16 | image.layer.masksToBounds = true
17 | image.clipsToBounds = true
18 | image.layer.cornerRadius = 15
19 | image.contentMode = .scaleAspectFill
20 | image.backgroundColor = Colors.commentsView.color
21 | return image
22 | }()
23 |
24 | override init(frame: CGRect) {
25 | super.init(frame: frame)
26 | configure()
27 | }
28 |
29 | public func configureCell(model: MovieDTO?, invertImage: Bool = false) {
30 | let urlString = invertImage ? model?.photo : model?.miniPhoto
31 | if let url = urlString, let finalURL = URL(string: url) {
32 | imageView.kf.setImage(with: finalURL)
33 | } else {
34 | imageView.image = Icons.launchPhoto.image
35 | }
36 | }
37 |
38 | private func configure() {
39 | contentView.addSubview(imageView)
40 | imageView.snp.makeConstraints { make in
41 | make.edges.equalToSuperview()
42 | }
43 | }
44 |
45 | required init?(coder: NSCoder) {
46 | fatalError("init(coder:) has not been implemented")
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/Movie/Extension/UIKit/ViewController + Extension.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ViewController + Extension.swift
3 | // Movie
4 | //
5 | // Created by siuzanna on 17/12/21.
6 | //
7 |
8 | import UIKit
9 |
10 | extension UIViewController {
11 |
12 | func embeddedInNavigationController() -> UINavigationController {
13 | return BaseNavigationController(rootViewController: self)
14 | }
15 |
16 | func setNavigationBarAppearance(style: UIBarStyle) {
17 | navigationController?.navigationBar.barStyle = style
18 | navigationController?.navigationBar.setBackgroundImage(UIImage(), for: .default)
19 | navigationController?.navigationBar.shadowImage = UIImage()
20 | navigationController?.view.backgroundColor = .clear
21 | }
22 |
23 | func removeBackButtonTitle() {
24 | navigationItem.backButtonTitle = ""
25 | }
26 |
27 | func showAlert(withTitle title: String, message: String, buttonName: String = "OK") {
28 | let alertController = UIAlertController(title: title, message: message, preferredStyle: .alert)
29 | alertController.addAction(UIAlertAction(title: buttonName, style: .default))
30 | present(alertController, animated: true)
31 | }
32 | }
33 |
34 | class BaseNavigationController: UINavigationController {
35 |
36 | override var preferredStatusBarStyle: UIStatusBarStyle {
37 | return .darkContent
38 | }
39 |
40 | override func viewDidLoad() {
41 | super.viewDidLoad()
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/Movie/MainScreen/View/HeaderView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HeaderView.swift
3 | // Movie
4 | //
5 | // Created by siuzanna on 18/12/21.
6 | //
7 |
8 | import UIKit
9 | import SnapKit
10 |
11 | final class HeaderView: UICollectionReusableView {
12 |
13 | public let label: UILabel = {
14 | let text = UILabel()
15 | text.textColor = .white
16 | text.font = UIFont.boldSystemFont(ofSize: 21)
17 | text.numberOfLines = 0
18 | return text
19 | }()
20 |
21 | public let buttonLabel: UILabel = {
22 | let text = UILabel()
23 | text.text = "See All >"
24 | text.textColor = UIColor(red: 0.467, green: 0.467, blue: 0.467, alpha: 1)
25 | text.font = UIFont.systemFont(ofSize: 15)
26 | return text
27 | }()
28 |
29 | override init(frame: CGRect) {
30 | super.init(frame: frame)
31 | configure()
32 | }
33 |
34 | required init?(coder: NSCoder) {
35 | fatalError("init(coder:) has not been implemented")
36 | }
37 |
38 | private func configure() {
39 | addSubview(label)
40 | addSubview(buttonLabel)
41 | label.snp.makeConstraints { make in
42 | make.bottom.top.equalToSuperview().inset(8)
43 | make.leading.trailing.equalToSuperview().inset(8)
44 | }
45 | buttonLabel.snp.makeConstraints { make in
46 | make.centerY.equalTo(label.snp.centerY)
47 | make.trailing.equalToSuperview().inset(16)
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/Movie.xcodeproj/xcuserdata/siuzanna.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | Movie.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 | SnapKitPlayground (Playground) 1.xcscheme
13 |
14 | isShown
15 |
16 | orderHint
17 | 2
18 |
19 | SnapKitPlayground (Playground) 2.xcscheme
20 |
21 | isShown
22 |
23 | orderHint
24 | 3
25 |
26 | SnapKitPlayground (Playground) 3.xcscheme
27 |
28 | isShown
29 |
30 | orderHint
31 | 4
32 |
33 | SnapKitPlayground (Playground) 4.xcscheme
34 |
35 | isShown
36 |
37 | orderHint
38 | 5
39 |
40 | SnapKitPlayground (Playground) 5.xcscheme
41 |
42 | isShown
43 |
44 | orderHint
45 | 6
46 |
47 | SnapKitPlayground (Playground).xcscheme
48 |
49 | isShown
50 |
51 | orderHint
52 | 1
53 |
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/Movie/NetworkService/Base Router/BaseRouter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // BaseRouter.swift
3 | // Movie
4 | //
5 | // Created by siuzanna on 19/12/21.
6 | //
7 |
8 | import Foundation
9 |
10 | struct HttpHeader {
11 | var field: String
12 | var value: String
13 | }
14 |
15 | protocol BaseRouter {
16 | var path: String { get }
17 | var queryParameter: [URLQueryItem]? { get }
18 | var method: HttpMethod { get }
19 | var httpBody: Data? { get }
20 | var httpHeader: [HttpHeader]? { get }
21 | }
22 |
23 | extension BaseRouter {
24 |
25 | var scheme: String {
26 | return "https"
27 | }
28 |
29 | var host: String {
30 | return "run.mocky.io"
31 | }
32 |
33 | func createURLRequest() -> URLRequest {
34 | var urlComponents = URLComponents()
35 | urlComponents.scheme = scheme
36 | urlComponents.host = host
37 | urlComponents.path = path
38 | urlComponents.queryItems = queryParameter
39 | guard let url = urlComponents.url else {
40 | fatalError(URLError(.unsupportedURL).localizedDescription)
41 | }
42 | var urlRequest = URLRequest(url: url)
43 | urlRequest.httpMethod = method.rawValue
44 | urlRequest.httpBody = httpBody
45 | httpHeader?.forEach { (header) in
46 | urlRequest.setValue(header.value, forHTTPHeaderField: header.field)
47 | }
48 | urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
49 | urlRequest.setValue("application/json", forHTTPHeaderField: "Accept")
50 |
51 | return urlRequest
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/Movie/MovieDetailsScreen/View/LableImage.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LableImage.swift
3 | // Movie
4 | //
5 | // Created by siuzanna on 19/12/21.
6 | //
7 |
8 | import UIKit
9 |
10 | class LableImage: UIView {
11 |
12 | private lazy var imageView: UIImageView = {
13 | let image = UIImageView()
14 | image.contentMode = .scaleAspectFill
15 | return image
16 | }()
17 |
18 | private lazy var nameLabel: UILabel = {
19 | let text = UILabel()
20 | text.textColor = Colors.title.color
21 | text.font = UIFont.boldSystemFont(ofSize: 13)
22 | text.numberOfLines = 0
23 | return text
24 | }()
25 |
26 | override init(frame: CGRect) {
27 | super.init(frame: frame)
28 | configure()
29 | }
30 |
31 | required init?(coder: NSCoder) {
32 | fatalError("init(coder:) has not been implemented")
33 | }
34 |
35 | private func configure() {
36 | addSubview(imageView)
37 | addSubview(nameLabel)
38 | imageView.snp.makeConstraints { make in
39 | make.leading.equalToSuperview()
40 | make.centerY.equalToSuperview()
41 | make.size.equalTo(16)
42 | }
43 | nameLabel.snp.makeConstraints { make in
44 | make.leading.equalTo(imageView.snp.trailing).inset(-5)
45 | make.centerY.equalToSuperview()
46 | }
47 | }
48 |
49 | func setUp(text: String, image: UIImage? = UIImage(), imageColor: UIColor) {
50 | nameLabel.text = text
51 | imageView.image = image
52 | imageView.tintColor = imageColor
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/Movie/Resources/Icons.xcassets/Tabbar/home.imageset/home.svg:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/Movie/Resources/Icons.xcassets/Tabbar/explore.imageset/explore.svg:
--------------------------------------------------------------------------------
1 |
4 |
--------------------------------------------------------------------------------
/Movie/MovieDetailsScreen/View/NavigationBarBack.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NavigationBarBack.swift
3 | // Movie
4 | //
5 | // Created by siuzanna on 19/12/21.
6 | //
7 |
8 | import UIKit
9 |
10 | class NavigationBarBack: UIView {
11 |
12 | private lazy var backButton: UIButton = {
13 | let button = UIButton()
14 | let config = UIImage.SymbolConfiguration(textStyle: .largeTitle, scale: .medium)
15 | button.setBackgroundImage(UIImage(systemName: "chevron.backward", withConfiguration: config)?
16 | .withTintColor(Colors.title.color, renderingMode: .alwaysOriginal), for: .normal)
17 | button.setTitleColor(.white, for: .normal)
18 | return button
19 | }()
20 |
21 | private lazy var saveButton: UIButton = {
22 | let button = UIButton()
23 | let config = UIImage.SymbolConfiguration(textStyle: .largeTitle, scale: .medium)
24 | button.setBackgroundImage(UIImage( systemName: "bookmark", withConfiguration: config)?
25 | .withTintColor(Colors.title.color, renderingMode: .alwaysOriginal), for: .normal)
26 | return button
27 | }()
28 |
29 | override func layoutSubviews() {
30 | super.layoutSubviews()
31 | setup()
32 | }
33 |
34 | private func setup() {
35 | setupSubviews()
36 | setupContstraints()
37 | }
38 |
39 | private func setupSubviews() {
40 | addSubview(backButton)
41 | addSubview(saveButton)
42 | }
43 |
44 | private func setupContstraints() {
45 | backButton.snp.makeConstraints { make in
46 | make.leading.equalToSuperview()
47 | make.centerY.equalToSuperview()
48 | }
49 | saveButton.snp.makeConstraints { make in
50 | make.trailing.equalToSuperview()
51 | make.centerY.equalToSuperview()
52 | }
53 | }
54 |
55 | func addBackButtonTarget(_ target: Any?, action: Selector, forEvent: UIControl.Event) {
56 | backButton.addTarget(target, action: action, for: forEvent)
57 | }
58 |
59 | func addSaveButtonTarget(_ target: Any?, action: Selector, forEvent: UIControl.Event) {
60 | backButton.addTarget(target, action: action, for: forEvent)
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/Movie/Tabbar/UITabBarController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UITabBarController.swift
3 | // Movie
4 | //
5 | // Created by siuzanna on 17/12/21.
6 | //
7 |
8 | import UIKit
9 |
10 | class TabBarController: UITabBarController, UITabBarControllerDelegate {
11 |
12 | override func viewDidLoad() {
13 | viewControllers = TabBar.allCases.map {$0.viewController}
14 | tabBar.barTintColor = .white
15 | addCustomTabBarView()
16 | hideTabBarBorder()
17 | self.selectedIndex = 0
18 | self.delegate = self
19 | self.tabBar.tintColor = Colors.logoRed.color
20 | self.tabBar.unselectedItemTintColor = Colors.icons.color
21 | }
22 |
23 | let customTabBarView: UIView = {
24 | let view = UIView(frame: .zero)
25 | view.backgroundColor = Colors.tabbar.color
26 | view.layer.masksToBounds = true
27 | view.clipsToBounds = true
28 | return view
29 | }()
30 |
31 | override func viewDidLayoutSubviews() {
32 | super.viewDidLayoutSubviews()
33 | customTabBarView.frame = tabBar.frame
34 | }
35 |
36 | func addCustomTabBarView() {
37 | customTabBarView.frame = tabBar.frame
38 | view.addSubview(customTabBarView)
39 | view.bringSubviewToFront(self.tabBar)
40 | }
41 |
42 | func hideTabBarBorder() {
43 | let tabBar = self.tabBar
44 | tabBar.backgroundImage = UIImage.from(color: .clear)
45 | tabBar.shadowImage = UIImage()
46 | tabBar.clipsToBounds = true
47 | tabBar.layer.masksToBounds = false
48 | }
49 | }
50 |
51 | enum TabBar: String, CaseIterable {
52 |
53 | case home
54 |
55 | var viewController: UINavigationController {
56 | var viewController = UINavigationController()
57 | switch self {
58 | case .home:
59 | let viewModel = MainScreenViewModel()
60 | viewController = UINavigationController(rootViewController: MainScreenViewController(viewModel: viewModel))
61 | }
62 | viewController.setNavigationBarHidden(true, animated: true)
63 | viewController.tabBarItem = tabBarItem
64 | viewController.tabBarItem.imageInsets.top = 5
65 | return viewController
66 | }
67 |
68 | var tabBarItem: UITabBarItem {
69 | switch self {
70 | case .home:
71 | return .init(title: nil,
72 | image: UIImage(named: TabBar.home.rawValue),
73 | selectedImage: UIImage(named: TabBar.home.rawValue))
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/Movie/NetworkService/NetworkService.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NetworkService.swift
3 | // Movie
4 | //
5 | // Created by siuzanna on 19/12/21.
6 | //
7 |
8 | import Foundation
9 |
10 | class NetworkService {
11 |
12 | private let urlSession: URLSession
13 | required init(session: URLSession = URLSession.shared) {
14 | urlSession = session
15 | }
16 |
17 | func sendRequest(
18 | urlRequest: URLRequest,
19 | successModel: SuccessModel.Type,
20 | completion: @escaping (ProResult) -> Void
21 | ) {
22 | urlSession.dataTask(with: urlRequest) { [weak self] data, response, error in
23 | guard let self = self else {
24 | debugPrint("Your class is dead")
25 | return
26 | }
27 | if let error = self.validateErrors(data: data, response: response, error: error) {
28 | if case NetworkErrors.unauthorized = error {
29 | // here you can create an unauthorized error
30 | } else if case NetworkErrors.badRequest = error {
31 | if let model = self.transformFromJSON(data: data, objectType: FailureModel.self) {
32 | completion(.badRequest(model))
33 | } else {
34 | completion(.failure("Check you JSON MODEL"))
35 | }
36 | } else {
37 | completion(.failure("Check you JSON MODEL"))
38 | }
39 | } else if let model = self.transformFromJSON(data: data, objectType: successModel) {
40 | completion(.success(model))
41 | } else {
42 | completion(.failure("Some supernatural things happened, check api!!!"))
43 | }
44 | }.resume()
45 | }
46 |
47 | private func validateErrors(data: Data?, response: URLResponse?, error: Error?) -> Error? {
48 | if let error = error { return error }
49 |
50 | guard let statusCode = (response as? HTTPURLResponse)?.statusCode else {
51 | return URLError(.badServerResponse)
52 | }
53 | switch statusCode {
54 | case StatusCode.okey.rawValue:
55 | return nil
56 | default:
57 | return NetworkErrors.badRequest
58 | }
59 | }
60 |
61 | private func transformFromJSON(
62 | data: Data?,
63 | objectType: Model.Type
64 | ) -> Model? {
65 |
66 | guard let data = data else {
67 | print("no Data, no Data, no Data, no Data")
68 | return nil
69 | }
70 | do {
71 | return try JSONDecoder().decode(Model.self, from: data)
72 | } catch let error {
73 | print("Couldn't decode!!! Check you JSON MODEL", error)
74 | }
75 | return nil
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/Movie/MainScreen/View/NavigationBar.swift:
--------------------------------------------------------------------------------
1 | //
2 | // NavigationBar.swift
3 | // Movie
4 | //
5 | // Created by siuzanna on 19/12/21.
6 | //
7 |
8 | import UIKit
9 |
10 | final class NavigationBar: UIView {
11 |
12 | private lazy var logoImageView: UIImageView = {
13 | let image = UIImageView()
14 | image.image = Icons.logo.image
15 | return image
16 | }()
17 |
18 | private lazy var searchButton: UIButton = {
19 | let button = UIButton()
20 | button.setBackgroundImage(Icons.Navigation.search.image
21 | .withTintColor(Colors.logoRed.color), for: .normal)
22 | button.setTitleColor(.white, for: .normal)
23 | return button
24 | }()
25 |
26 | private lazy var filterButton: UIButton = {
27 | let button = UIButton()
28 | button.setBackgroundImage(Icons.Navigation.filter.image
29 | .withTintColor(Colors.title.color), for: .normal)
30 | return button
31 | }()
32 |
33 | private lazy var menuButton: UIButton = {
34 | let button = UIButton()
35 | button.setBackgroundImage(Icons.Navigation.menu.image
36 | .withTintColor(Colors.title.color), for: .normal)
37 | return button
38 | }()
39 |
40 | override func layoutSubviews() {
41 | super.layoutSubviews()
42 | setup()
43 | }
44 |
45 | private func setup() {
46 | setupSubviews()
47 | setupContstraints()
48 | }
49 |
50 | private func setupSubviews() {
51 | addSubview(logoImageView)
52 | addSubview(searchButton)
53 | addSubview(filterButton)
54 | addSubview(menuButton)
55 | }
56 |
57 | private func setupContstraints() {
58 | logoImageView.snp.makeConstraints { make in
59 | make.leading.equalToSuperview()
60 | make.centerY.equalToSuperview()
61 | make.width.equalTo(73)
62 | make.height.equalTo(55)
63 | }
64 | menuButton.snp.makeConstraints { make in
65 | make.trailing.equalToSuperview()
66 | make.centerY.equalToSuperview()
67 | make.width.height.equalTo(27)
68 | }
69 | filterButton.snp.makeConstraints { make in
70 | make.trailing.equalTo(menuButton.snp.leading).inset(-20)
71 | make.centerY.equalToSuperview()
72 | make.width.height.equalTo(27)
73 | }
74 | searchButton.snp.makeConstraints { make in
75 | make.trailing.equalTo(filterButton.snp.leading).inset(-20)
76 | make.centerY.equalToSuperview()
77 | make.width.height.equalTo(27)
78 | }
79 |
80 | [menuButton, filterButton, searchButton].forEach {
81 | $0.addTarget(self, action: #selector(tapped(_:)), for: .touchUpInside)
82 | }
83 | }
84 |
85 | @objc private func tapped(_ sender: UIButton) {
86 | if sender == filterButton {
87 | } else if sender == searchButton {
88 | } else if sender == menuButton {
89 | }
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/Movie/Resources/GeneratedColors.swift:
--------------------------------------------------------------------------------
1 | // swiftlint:disable all
2 | // Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen
3 |
4 | #if os(macOS)
5 | import AppKit
6 | #elseif os(iOS)
7 | import UIKit
8 | #elseif os(tvOS) || os(watchOS)
9 | import UIKit
10 | #endif
11 |
12 | // Deprecated typealiases
13 | @available(*, deprecated, renamed: "ColorAsset.Color", message: "This typealias will be removed in SwiftGen 7.0")
14 | internal typealias AssetColorTypeAlias = ColorAsset.Color
15 |
16 | // swiftlint:disable superfluous_disable_command file_length implicit_return
17 |
18 | // MARK: - Asset Catalogs
19 |
20 | // swiftlint:disable identifier_name line_length nesting type_body_length type_name
21 | internal enum Colors {
22 | internal static let bg = ColorAsset(name: "bg")
23 | internal static let commentsView = ColorAsset(name: "commetsView")
24 | internal static let icon2 = ColorAsset(name: "icon2")
25 | internal static let icons = ColorAsset(name: "icons")
26 | internal static let logoRed = ColorAsset(name: "logoRed")
27 | internal static let star = ColorAsset(name: "star")
28 | internal static let statusBar = ColorAsset(name: "statusBar")
29 | internal static let tabbar = ColorAsset(name: "tabbar")
30 | internal static let title = ColorAsset(name: "title")
31 | }
32 | // swiftlint:enable identifier_name line_length nesting type_body_length type_name
33 |
34 | // MARK: - Implementation Details
35 |
36 | internal final class ColorAsset {
37 | internal fileprivate(set) var name: String
38 |
39 | #if os(macOS)
40 | internal typealias Color = NSColor
41 | #elseif os(iOS) || os(tvOS) || os(watchOS)
42 | internal typealias Color = UIColor
43 | #endif
44 |
45 | @available(iOS 11.0, tvOS 11.0, watchOS 4.0, macOS 10.13, *)
46 | internal private(set) lazy var color: Color = {
47 | guard let color = Color(asset: self) else {
48 | fatalError("Unable to load color asset named \(name).")
49 | }
50 | return color
51 | }()
52 |
53 | #if os(iOS) || os(tvOS)
54 | @available(iOS 11.0, tvOS 11.0, *)
55 | internal func color(compatibleWith traitCollection: UITraitCollection) -> Color {
56 | let bundle = BundleToken.bundle
57 | guard let color = Color(named: name, in: bundle, compatibleWith: traitCollection) else {
58 | fatalError("Unable to load color asset named \(name).")
59 | }
60 | return color
61 | }
62 | #endif
63 |
64 | fileprivate init(name: String) {
65 | self.name = name
66 | }
67 | }
68 |
69 | internal extension ColorAsset.Color {
70 | @available(iOS 11.0, tvOS 11.0, watchOS 4.0, macOS 10.13, *)
71 | convenience init?(asset: ColorAsset) {
72 | let bundle = BundleToken.bundle
73 | #if os(iOS) || os(tvOS)
74 | self.init(named: asset.name, in: bundle, compatibleWith: nil)
75 | #elseif os(macOS)
76 | self.init(named: NSColor.Name(asset.name), bundle: bundle)
77 | #elseif os(watchOS)
78 | self.init(named: asset.name)
79 | #endif
80 | }
81 | }
82 |
83 | // swiftlint:disable convenience_type
84 | private final class BundleToken {
85 | static let bundle: Bundle = {
86 | #if SWIFT_PACKAGE
87 | return Bundle.module
88 | #else
89 | return Bundle(for: BundleToken.self)
90 | #endif
91 | }()
92 | }
93 | // swiftlint:enable convenience_type
94 |
--------------------------------------------------------------------------------
/Movie/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {"images":[{"size":"60x60","expected-size":"180","filename":"180.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"40x40","expected-size":"80","filename":"80.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"40x40","expected-size":"120","filename":"120.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"60x60","expected-size":"120","filename":"120.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"57x57","expected-size":"57","filename":"57.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"1x"},{"size":"29x29","expected-size":"58","filename":"58.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"29x29","expected-size":"29","filename":"29.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"1x"},{"size":"29x29","expected-size":"87","filename":"87.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"57x57","expected-size":"114","filename":"114.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"20x20","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"2x"},{"size":"20x20","expected-size":"60","filename":"60.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"iphone","scale":"3x"},{"size":"1024x1024","filename":"1024.png","expected-size":"1024","idiom":"ios-marketing","folder":"Assets.xcassets/AppIcon.appiconset/","scale":"1x"},{"size":"40x40","expected-size":"80","filename":"80.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"72x72","expected-size":"72","filename":"72.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"76x76","expected-size":"152","filename":"152.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"50x50","expected-size":"100","filename":"100.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"29x29","expected-size":"58","filename":"58.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"76x76","expected-size":"76","filename":"76.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"29x29","expected-size":"29","filename":"29.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"50x50","expected-size":"50","filename":"50.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"72x72","expected-size":"144","filename":"144.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"40x40","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"83.5x83.5","expected-size":"167","filename":"167.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"},{"size":"20x20","expected-size":"20","filename":"20.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"1x"},{"size":"20x20","expected-size":"40","filename":"40.png","folder":"Assets.xcassets/AppIcon.appiconset/","idiom":"ipad","scale":"2x"}]}
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Movie [](https://wakatime.com/badge/github/siuzanna/Movie)
2 |
3 |
4 |
5 | App shows you collections of TV streaming and other movies. Movie app written in Swift & UIKit using the [Custom API](https://www.mocky.io/v3/3feccf06-6bc1-480d-97af-bbc05d785f86) created on the Mocky website. App built using the MVVM architecture and 100% programmatic UI (No Storyboard). + NSDiffableDataSourceSnapshot, UICollectionViewDiffableDataSource
6 |
7 | ### Screen Shots
8 | 
9 | ---
10 |
11 | ### Table of Contents
12 |
13 | - [Description](#description)
14 | - [Dependencies](#dependencies)
15 | - [Resources](#resources)
16 | - [How To Use](#how-to-use)
17 | - [Author Info](#author-info)
18 |
19 | ---
20 |
21 | ## Description
22 |
23 | - [X] App shows you collections of TV streaming and other movies.
24 | - [X] Layout created using a UICollectionviewCompositionalLayout.
25 | - [X] Project was completed using 100% programmatic UI (No Storyboard).
26 | - [X] App built using the MVVM architecture.
27 | - [X] This app includes descriptions for each movie as well as trailers and the movie’s rating.
28 | - [X] Movie also contains movies that are from paid apps such as Netflix.
29 | - [X] User can view movie details by tapping on a cell.
30 | - [x] All images are cached uising SDWebImage cocoapod.
31 | - [X] Movie details page contain backdrop and poster image, overview, duration and other relevant information.
32 | - [X] User can view trailer of a particular movie in the youtube app or a web browser.
33 | - [X] It also features the best movies that refresh weekly so you can choose and watch the latest movies that have the best ratings.
34 |
35 | ### Todo
36 |
37 | - [ ] Refresh API data - trailer ulr, description, comments, rating for each movie.
38 |
39 | ## Dependencies
40 | |#|Library|Description|
41 | |-|-|-|
42 | |1|[SwiftLint](https://github.com/realm/SwiftLint)|A tool to enforce Swift style and conventions. SwiftLint enforces the style guide rules that are generally accepted by the Swift community.|
43 | |2|[SwiftGen](https://github.com/SwiftGen/SwiftGen)|SwiftGen is a tool to automatically generate Swift code for resources of your projects (like images, localised strings, etc), to make them type-safe to use.|
44 | |3|[SDWebImage](https://github.com/SDWebImage/SDWebImage)|This library provides an async image downloader with cache support.|
45 | |4|[SnapKit](https://github.com/SnapKit/SnapKit)|SnapKit is a DSL allows building constraints with minimal amounts of code while ensuring they are easy to read and understand.|
46 |
47 | #### Frameworks
48 |
49 | - UIKit
50 | - WebKit
51 |
52 | ## Resources
53 |
54 | - Design - [Figma design](https://www.figma.com/community/file/988093088740037911/VOD-Platform-App) created by [Mehri Fekri](https://www.figma.com/community/file/988093088740037911/VOD-Platform-App)
55 | - API - [Link to Mocky](https://www.mocky.io/v3/3feccf06-6bc1-480d-97af-bbc05d785f86)
56 |
57 | ## How To Use
58 |
59 | - Clone the project and run it on Xcode 12 or above.
60 | - Open a terminal window, and $ cd into your project directory.
61 | - Run $ pod install.
62 | - Open the Movie.xcworkspace.
63 |
64 | [Back To The Top](#Movie)
65 |
--------------------------------------------------------------------------------
/Movie/MainScreen/MainScreenViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MainScreenViewModel.swift
3 | // Movie
4 | //
5 | // Created by siuzanna on 18/12/21.
6 | //
7 |
8 | import Foundation
9 | import UIKit.NSDiffableDataSourceSectionSnapshot
10 |
11 | protocol MainScreenViewModelDelegate: AnyObject {
12 | func updateCollectionView()
13 | func showError(text: String)
14 | }
15 |
16 | protocol MainScreenServiceProtocol {
17 | var delegate: MainScreenViewModelDelegate? { get set }
18 | var dataSource: UICollectionViewDiffableDataSource? { get set }
19 |
20 | func requestMovieList()
21 | func getMoviesBySection(_ indexPath: Int) -> [MovieDTO]
22 | func getMoviesBySection(_ section: Section) -> [MovieDTO]
23 | }
24 |
25 | final class MainScreenViewModel: MainScreenServiceProtocol {
26 |
27 | private let networkService: NetworkService
28 | private var movieList: [MovieDTO]
29 |
30 | public var dataSource: UICollectionViewDiffableDataSource? = nil
31 | weak var delegate: MainScreenViewModelDelegate?
32 |
33 | init(networkService: NetworkService = NetworkService()) {
34 | self.networkService = networkService
35 | self.movieList = []
36 | }
37 | }
38 |
39 | extension MainScreenViewModel {
40 |
41 | func getByIndexPath(_ indexPath: IndexPath) -> MovieDTO {
42 | return movieList[indexPath.row]
43 | }
44 |
45 | func requestMovieList() {
46 | networkService.sendRequest(
47 | urlRequest: MainScreenRouter.getAllPosts.createURLRequest(),
48 | successModel: [MovieDTO].self
49 | ) { [weak self] (result) in
50 | guard let self else { return }
51 | switch result {
52 | case .success(let model):
53 | self.movieList = model
54 | self.delegate?.updateCollectionView()
55 | case .badRequest(let error):
56 | self.delegate?.showError(text: error.errors?.first ?? "Error occurred")
57 | debugPrint(#function, error)
58 | self.fetchLocalJsonMovies()
59 | case .failure(let error):
60 | self.delegate?.showError(text: error)
61 | debugPrint(#function, error)
62 | self.fetchLocalJsonMovies()
63 | }
64 | }
65 | }
66 |
67 | private func fetchLocalJsonMovies() {
68 | guard let path = Bundle.main.path(forResource: "movies", ofType: "json") else {
69 | debugPrint("Local JSON file 'move.json' not found.")
70 | return
71 | }
72 |
73 | do {
74 | let data = try Data(contentsOf: URL(fileURLWithPath: path))
75 | let localMovies = try JSONDecoder().decode([MovieDTO].self, from: data)
76 | self.movieList = localMovies
77 | self.delegate?.updateCollectionView()
78 | } catch {
79 | debugPrint("Error decoding local JSON file: \(error)")
80 | self.delegate?.showError(text: "Failed to load data from the local file")
81 | }
82 | }
83 |
84 | func getMoviesBySection(_ indexPath: Int) -> [MovieDTO] {
85 | return movieList.filter { $0.type == indexPath }
86 | }
87 |
88 | func getMoviesBySection(_ section: Section) -> [MovieDTO] {
89 | switch section {
90 | case .topSlide:
91 | getMoviesBySection(0)
92 | case .mostPopular:
93 | getMoviesBySection(1)
94 | case .comingSoon:
95 | getMoviesBySection(2)
96 | case .lastUpdate:
97 | getMoviesBySection(3)
98 | case .bestSeries:
99 | getMoviesBySection(4)
100 | }
101 | }
102 | }
103 |
--------------------------------------------------------------------------------
/Movie/Resources/GeneratedIcons.swift:
--------------------------------------------------------------------------------
1 | // swiftlint:disable all
2 | // Generated using SwiftGen — https://github.com/SwiftGen/SwiftGen
3 |
4 | #if os(macOS)
5 | import AppKit
6 | #elseif os(iOS)
7 | import UIKit
8 | #elseif os(tvOS) || os(watchOS)
9 | import UIKit
10 | #endif
11 |
12 | // Deprecated typealiases
13 | @available(*, deprecated, renamed: "ImageAsset.Image", message: "This typealias will be removed in SwiftGen 7.0")
14 | internal typealias AssetImageTypeAlias = ImageAsset.Image
15 |
16 | // swiftlint:disable superfluous_disable_command file_length implicit_return
17 |
18 | // MARK: - Asset Catalogs
19 |
20 | // swiftlint:disable identifier_name line_length nesting type_body_length type_name
21 | internal enum Icons {
22 | internal enum Navigation {
23 | internal static let filter = ImageAsset(name: "filter")
24 | internal static let menu = ImageAsset(name: "menu")
25 | internal static let search = ImageAsset(name: "search")
26 | }
27 | internal enum Tabbar {
28 | internal static let bookmark = ImageAsset(name: "bookmark")
29 | internal static let explore = ImageAsset(name: "explore")
30 | internal static let home = ImageAsset(name: "home")
31 | internal static let user = ImageAsset(name: "user")
32 | }
33 | internal static let gradient = ImageAsset(name: "gradient")
34 | internal static let launchPhoto = ImageAsset(name: "launchPhoto")
35 | internal static let logo = ImageAsset(name: "logo")
36 | }
37 | // swiftlint:enable identifier_name line_length nesting type_body_length type_name
38 |
39 | // MARK: - Implementation Details
40 |
41 | internal struct ImageAsset {
42 | internal fileprivate(set) var name: String
43 |
44 | #if os(macOS)
45 | internal typealias Image = NSImage
46 | #elseif os(iOS) || os(tvOS) || os(watchOS)
47 | internal typealias Image = UIImage
48 | #endif
49 |
50 | @available(iOS 8.0, tvOS 9.0, watchOS 2.0, macOS 10.7, *)
51 | internal var image: Image {
52 | let bundle = BundleToken.bundle
53 | #if os(iOS) || os(tvOS)
54 | let image = Image(named: name, in: bundle, compatibleWith: nil)
55 | #elseif os(macOS)
56 | let name = NSImage.Name(self.name)
57 | let image = (bundle == .main) ? NSImage(named: name) : bundle.image(forResource: name)
58 | #elseif os(watchOS)
59 | let image = Image(named: name)
60 | #endif
61 | guard let result = image else {
62 | fatalError("Unable to load image asset named \(name).")
63 | }
64 | return result
65 | }
66 |
67 | #if os(iOS) || os(tvOS)
68 | @available(iOS 8.0, tvOS 9.0, *)
69 | internal func image(compatibleWith traitCollection: UITraitCollection) -> Image {
70 | let bundle = BundleToken.bundle
71 | guard let result = Image(named: name, in: bundle, compatibleWith: traitCollection) else {
72 | fatalError("Unable to load image asset named \(name).")
73 | }
74 | return result
75 | }
76 | #endif
77 | }
78 |
79 | internal extension ImageAsset.Image {
80 | @available(iOS 8.0, tvOS 9.0, watchOS 2.0, *)
81 | @available(macOS, deprecated,
82 | message: "This initializer is unsafe on macOS, please use the ImageAsset.image property")
83 | convenience init?(asset: ImageAsset) {
84 | #if os(iOS) || os(tvOS)
85 | let bundle = BundleToken.bundle
86 | self.init(named: asset.name, in: bundle, compatibleWith: nil)
87 | #elseif os(macOS)
88 | self.init(named: NSImage.Name(asset.name))
89 | #elseif os(watchOS)
90 | self.init(named: asset.name)
91 | #endif
92 | }
93 | }
94 |
95 | // swiftlint:disable convenience_type
96 | private final class BundleToken {
97 | static let bundle: Bundle = {
98 | #if SWIFT_PACKAGE
99 | return Bundle.module
100 | #else
101 | return Bundle(for: BundleToken.self)
102 | #endif
103 | }()
104 | }
105 | // swiftlint:enable convenience_type
106 |
--------------------------------------------------------------------------------
/Movie/MovieDetailsScreen/Cells/CommentsCell.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CommentsCell.swift
3 | // Movie
4 | //
5 | // Created by siuzanna on 20/12/21.
6 | //
7 |
8 | import UIKit
9 |
10 | class CommentsCell: UICollectionViewCell {
11 |
12 | public lazy var pictureView: UIImageView = {
13 | let image = UIImageView()
14 | image.layer.masksToBounds = true
15 | image.clipsToBounds = true
16 | image.image = UIImage(systemName: "person.circle.fill")
17 | image.layer.cornerRadius = image.frame.height/2
18 | image.contentMode = .scaleAspectFill
19 | image.tintColor = Colors.title.color
20 | return image
21 | }()
22 |
23 | private lazy var nameLabel: UILabel = {
24 | let text = UILabel()
25 | text.textColor = Colors.title.color
26 | text.font = UIFont.systemFont(ofSize: 13, weight: .bold)
27 | text.numberOfLines = 1
28 | return text
29 | }()
30 |
31 | private lazy var commentLabel: UILabel = {
32 | let text = UILabel()
33 | text.textColor = Colors.title.color
34 | text.font = UIFont.systemFont(ofSize: 11, weight: .light)
35 | text.numberOfLines = 0
36 | return text
37 | }()
38 |
39 | private lazy var thumbsupButton: UIButton = {
40 | let button = UIButton()
41 | button.titleLabel?.font = UIFont.systemFont(ofSize: 14)
42 | button.tintColor = Colors.title.color
43 | button.setImage(UIImage(systemName: "hand.thumbsup"), for: .normal)
44 | return button
45 | }()
46 |
47 | private lazy var thumbsdownButton: UIButton = {
48 | let button = UIButton()
49 | button.titleLabel?.font = UIFont.systemFont(ofSize: 14)
50 | button.tintColor = Colors.title.color
51 | button.setImage(UIImage(systemName: "hand.thumbsdown"), for: .normal)
52 | return button
53 | }()
54 |
55 | let seperatorView = UIView()
56 | let containerView = UIView()
57 |
58 | var cellViewModel: Comments? {
59 | didSet {
60 | nameLabel.text = cellViewModel?.name
61 | commentLabel.text = cellViewModel?.comment
62 | }
63 | }
64 |
65 | override init(frame: CGRect) {
66 | super.init(frame: frame)
67 | configure()
68 | }
69 |
70 | required init?(coder: NSCoder) {
71 | fatalError("init(coder:) has not been implemented")
72 | }
73 |
74 | func configure() {
75 | containerView.addSubview(nameLabel)
76 | containerView.addSubview(commentLabel)
77 | containerView.addSubview(pictureView)
78 | containerView.addSubview(seperatorView)
79 | containerView.addSubview(thumbsupButton)
80 | containerView.addSubview(thumbsdownButton)
81 | contentView.addSubview(containerView)
82 | seperatorView.backgroundColor = .lightGray
83 |
84 | containerView.snp.makeConstraints { make in
85 | make.height.equalTo(70)
86 | make.top.equalToSuperview().offset(15)
87 | make.leading.trailing.equalToSuperview().inset(15)
88 | }
89 | pictureView.snp.makeConstraints { make in
90 | make.top.equalToSuperview()
91 | make.size.equalTo(25)
92 | make.leading.equalToSuperview().inset(15)
93 | }
94 | nameLabel.snp.makeConstraints { make in
95 | make.leading.equalTo(pictureView.snp.trailing).offset(10)
96 | make.centerY.equalTo(pictureView)
97 | }
98 | thumbsupButton.snp.makeConstraints { make in
99 | make.trailing.equalToSuperview().inset(10)
100 | make.centerY.equalTo(pictureView)
101 | }
102 | thumbsdownButton.snp.makeConstraints { make in
103 | make.trailing.equalTo(thumbsupButton.snp.leading).inset(-10)
104 | make.centerY.equalTo(pictureView)
105 | }
106 | commentLabel.snp.makeConstraints { make in
107 | make.top.equalTo(pictureView.snp.bottom).offset(5)
108 | make.leading.trailing.equalToSuperview().inset(15)
109 | }
110 | seperatorView.snp.makeConstraints { make in
111 | make.height.equalTo(0.5)
112 | make.bottom.equalToSuperview()
113 | make.leading.trailing.equalToSuperview().inset(15)
114 | }
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/Movie/Resources/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
--------------------------------------------------------------------------------
/Movie/MainScreen/MainScreenViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MainScreenViewController.swift
3 | // Movie
4 | //
5 | // Created by siuzanna on 17/12/21.
6 | //
7 |
8 | import UIKit
9 | import SnapKit
10 |
11 | enum Section: String, CaseIterable {
12 | case topSlide = ""
13 | case mostPopular = "Most Popular"
14 | case comingSoon = "Coming Soon"
15 | case lastUpdate = "Last Updated"
16 | case bestSeries = "Best Series"
17 | }
18 |
19 | final class MainScreenViewController: UIViewController {
20 | typealias Snapshot = NSDiffableDataSourceSnapshot
21 |
22 | private var viewModel: MainScreenServiceProtocol
23 | private lazy var contentView = Mainview()
24 |
25 | init(viewModel: MainScreenServiceProtocol) {
26 | self.viewModel = viewModel
27 | super.init(nibName: nil, bundle: nil)
28 | }
29 |
30 | override func loadView() {
31 | view = contentView
32 | }
33 |
34 | override func viewDidLoad() {
35 | super.viewDidLoad()
36 | contentView.collectionView.delegate = self
37 | viewModel.delegate = self
38 |
39 | contentView.activityControl.startAnimating()
40 | configureDataSource()
41 | DispatchQueue.global().async {
42 | self.viewModel.requestMovieList()
43 | }
44 | }
45 |
46 | required init?(coder: NSCoder) {
47 | fatalError("init(coder:) has not been implemented")
48 | }
49 | }
50 |
51 | extension MainScreenViewController: MainScreenViewModelDelegate {
52 |
53 | func updateCollectionView() {
54 | DispatchQueue.main.async {
55 | let snapshot = self.snapshot()
56 | self.viewModel.dataSource?.apply(snapshot, animatingDifferences: true)
57 | self.contentView.activityControl.stopAnimating()
58 | }
59 | }
60 |
61 | func showError(text: String) {
62 | DispatchQueue.main.async {
63 | self.showAlert(withTitle: "Error occurred", message: text)
64 | self.contentView.activityControl.stopAnimating()
65 | }
66 | }
67 | }
68 |
69 | extension MainScreenViewController {
70 |
71 | func configureDataSource() {
72 | viewModel.dataSource = UICollectionViewDiffableDataSource(collectionView: contentView.collectionView) {
73 | (collectionView: UICollectionView, indexPath: IndexPath, item: MovieDTO) -> UICollectionViewCell? in
74 | let sectionType = Section.allCases[indexPath.section]
75 | let cell: PhotoCell = collectionView.dequeue(for: indexPath)
76 | switch sectionType {
77 | case .topSlide, .comingSoon:
78 | cell.configureCell(model: item, invertImage: true)
79 | case .mostPopular, .lastUpdate, .bestSeries:
80 | cell.configureCell(model: item)
81 | }
82 | return cell
83 | }
84 |
85 | viewModel.dataSource?.supplementaryViewProvider = {
86 | ( collectionView: UICollectionView, kind: String, indexPath: IndexPath) -> UICollectionReusableView? in
87 |
88 | let sectionType = Section.allCases[indexPath.section]
89 | switch sectionType {
90 | case .topSlide:
91 | let header: UICollectionReusableView = collectionView.dequeue(for: indexPath, kind: kind)
92 | return header
93 | case .comingSoon:
94 | let header: HeaderView = collectionView.dequeue(for: indexPath, kind: kind)
95 | header.label.text = Section.allCases[indexPath.section].rawValue
96 | header.buttonLabel.text = ""
97 | return header
98 | case .mostPopular, .lastUpdate, .bestSeries:
99 | let header: HeaderView = collectionView.dequeue(for: indexPath, kind: kind)
100 | header.label.text = Section.allCases[indexPath.section].rawValue
101 | return header
102 | }
103 | }
104 |
105 | let snapshot = snapshot()
106 | viewModel.dataSource?.apply(snapshot, animatingDifferences: true)
107 | }
108 |
109 | func snapshot() -> Snapshot {
110 | var snapshot = Snapshot()
111 |
112 | let sections: [Section] = [.topSlide, .mostPopular, .comingSoon, .lastUpdate, .bestSeries]
113 |
114 | for section in sections {
115 | let items = viewModel.getMoviesBySection(section)
116 | if !items.isEmpty {
117 | snapshot.appendSections([section])
118 | if section == .comingSoon {
119 | snapshot.appendItems(items.suffix(1), toSection: section)
120 | } else {
121 | snapshot.appendItems(items, toSection: section)
122 | }
123 | }
124 | }
125 |
126 | return snapshot
127 | }
128 | }
129 |
130 | extension MainScreenViewController: UICollectionViewDelegate {
131 | func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
132 | let model: MovieDTO = viewModel.getMoviesBySection(indexPath.section)[indexPath.row]
133 | let viewController = MovieDetailedScreenViewController(viewModel: model)
134 | viewController.modalPresentationStyle = .overCurrentContext
135 | present(viewController, animated: true, completion: nil)
136 | }
137 | }
138 |
--------------------------------------------------------------------------------
/Movie/MainScreen/MainContentView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MainContentView.swift
3 | // Movie
4 | //
5 | // Created by Siuzanna Karagulova on 4/3/24.
6 | //
7 |
8 | import UIKit
9 | import SnapKit
10 |
11 | final class Mainview: UIView {
12 |
13 | lazy var navigationBar = NavigationBar()
14 | lazy var collectionView: UICollectionView = configureCollectionView()
15 | lazy var activityControl = UIActivityIndicatorView(style: .large)
16 |
17 | override init(frame: CGRect) {
18 | super.init(frame: frame)
19 | setup()
20 | activityControl.color = .white
21 | backgroundColor = Colors.bg.color
22 | }
23 |
24 | required init?(coder: NSCoder) {
25 | fatalError("init(coder:) has not been implemented")
26 | }
27 | }
28 |
29 | extension Mainview {
30 | private func setup() {
31 | setUpSubviews()
32 | setUpConstraints()
33 | }
34 |
35 | private func setUpSubviews() {
36 | addSubview(navigationBar)
37 | addSubview(activityControl)
38 | addSubview(collectionView)
39 | }
40 |
41 | private func setUpConstraints() {
42 | navigationBar.snp.makeConstraints { make in
43 | make.top.equalTo(safeAreaLayoutGuide.snp.top)
44 | make.leading.trailing.equalToSuperview().inset(16)
45 | make.height.equalTo(92)
46 | }
47 |
48 | activityControl.snp.makeConstraints { make in
49 | make.center.equalToSuperview()
50 | }
51 |
52 | collectionView.snp.makeConstraints { make in
53 | make.top.equalTo(safeAreaLayoutGuide.snp.top).offset(85)
54 | make.leading.trailing.bottom.equalToSuperview()
55 | }
56 | }
57 | }
58 |
59 | extension Mainview {
60 | func configureCollectionView() -> UICollectionView {
61 | let collectionView = UICollectionView(frame: .zero, collectionViewLayout: generateLayout())
62 | collectionView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
63 | collectionView.backgroundColor = .clear
64 | collectionView.showsVerticalScrollIndicator = false
65 | collectionView.showsHorizontalScrollIndicator = false
66 | collectionView.contentInset.bottom = 20
67 | collectionView.contentInset.top = 20
68 | collectionView.register(cell: PhotoCell.self)
69 | collectionView.register(header: HeaderView.self)
70 | return collectionView
71 | }
72 |
73 | func generateLayout() -> UICollectionViewLayout {
74 | let layout = UICollectionViewCompositionalLayout {
75 | (sectionIndex: Int, _: NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection? in
76 |
77 | let sectionLayoutKind = Section.allCases[sectionIndex]
78 | switch sectionLayoutKind {
79 | case .topSlide: return self.generateTopSlideLayout()
80 | case .mostPopular: return self.generateCustomLayout()
81 | case .comingSoon: return self.generateComingSoonLayout()
82 | case .lastUpdate: return self.generateCustomLayout()
83 | case .bestSeries: return self.generateCustomLayout()
84 | }
85 | }
86 | return layout
87 | }
88 |
89 | func generateTopSlideLayout() -> NSCollectionLayoutSection {
90 | let itemSize = NSCollectionLayoutSize(
91 | widthDimension: .fractionalWidth(1),
92 | heightDimension: .fractionalHeight(1))
93 | let item = NSCollectionLayoutItem(layoutSize: itemSize)
94 | item.contentInsets = NSDirectionalEdgeInsets(
95 | top: 0, leading: 4, bottom: 0, trailing: 4)
96 |
97 | let groupSize = NSCollectionLayoutSize(
98 | widthDimension: .fractionalWidth(0.78),
99 | heightDimension: .fractionalWidth(0.576))
100 | let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
101 | group.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 4, bottom: 0, trailing: 4)
102 |
103 | let section = NSCollectionLayoutSection(group: group)
104 | section.orthogonalScrollingBehavior = .continuous
105 | section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 12, bottom: 0, trailing: 12)
106 | section.orthogonalScrollingBehavior = .continuous
107 |
108 | return section
109 | }
110 |
111 | func generateCustomLayout() -> NSCollectionLayoutSection {
112 | let itemSize = NSCollectionLayoutSize(
113 | widthDimension: .fractionalWidth(1),
114 | heightDimension: .fractionalHeight(1))
115 | let item = NSCollectionLayoutItem(layoutSize: itemSize)
116 | item.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 4, bottom: 0, trailing: 4)
117 |
118 | let groupSize = NSCollectionLayoutSize(
119 | widthDimension: .fractionalWidth(0.43),
120 | heightDimension: .fractionalWidth(0.533))
121 | let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
122 | group.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 2, bottom: 0, trailing: 2)
123 |
124 | let headerSize = NSCollectionLayoutSize(
125 | widthDimension: .fractionalWidth(1.0),
126 | heightDimension: .absolute(59))
127 | let sectionHeader = NSCollectionLayoutBoundarySupplementaryItem(
128 | layoutSize: headerSize,
129 | elementKind: UICollectionView.elementKindSectionHeader,
130 | alignment: .top)
131 | sectionHeader.zIndex = 2
132 |
133 | let section = NSCollectionLayoutSection(group: group)
134 | section.orthogonalScrollingBehavior = .continuous
135 | section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 12, bottom: 0, trailing: 12)
136 | section.boundarySupplementaryItems = [sectionHeader]
137 | section.orthogonalScrollingBehavior = .continuous
138 |
139 | return section
140 | }
141 |
142 | func generateComingSoonLayout() -> NSCollectionLayoutSection {
143 | let itemSize = NSCollectionLayoutSize(
144 | widthDimension: .fractionalWidth(1),
145 | heightDimension: .fractionalHeight(1))
146 | let item = NSCollectionLayoutItem(layoutSize: itemSize)
147 |
148 | let groupSize = NSCollectionLayoutSize(
149 | widthDimension: .fractionalWidth(1),
150 | heightDimension: .fractionalWidth(0.426))
151 | let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitem: item, count: 1)
152 |
153 | let headerSize = NSCollectionLayoutSize(
154 | widthDimension: .fractionalWidth(1.0),
155 | heightDimension: .absolute(59))
156 | let sectionHeader = NSCollectionLayoutBoundarySupplementaryItem(
157 | layoutSize: headerSize,
158 | elementKind: UICollectionView.elementKindSectionHeader,
159 | alignment: .top)
160 | sectionHeader.zIndex = 2
161 |
162 | let section = NSCollectionLayoutSection(group: group)
163 | section.orthogonalScrollingBehavior = .continuous
164 | section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 20, bottom: 0, trailing: 20)
165 | section.boundarySupplementaryItems = [sectionHeader]
166 | section.orthogonalScrollingBehavior = .none
167 |
168 | return section
169 | }
170 | }
171 |
172 |
--------------------------------------------------------------------------------
/Movie/MovieDetailsScreen/View/TopView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TopView.swift
3 | // Movie
4 | //
5 | // Created by siuzanna on 19/12/21.
6 | //
7 |
8 | import UIKit
9 | import WebKit
10 | import Kingfisher
11 | import SnapKit
12 |
13 | final class TopView: UICollectionReusableView {
14 |
15 | private lazy var imageView: UIImageView = {
16 | let image = UIImageView()
17 | image.contentMode = .scaleAspectFill
18 | return image
19 | }()
20 |
21 | private lazy var gradientView: UIImageView = {
22 | let image = UIImageView()
23 | image.contentMode = .scaleAspectFill
24 | image.image = Icons.gradient.image
25 | return image
26 | }()
27 |
28 | /// stats View
29 | private lazy var posterView: UIImageView = {
30 | let image = UIImageView()
31 | image.layer.masksToBounds = true
32 | image.clipsToBounds = true
33 | image.layer.cornerRadius = 15
34 | image.contentMode = .scaleAspectFill
35 | return image
36 | }()
37 |
38 | private lazy var statsView: UIStackView = {
39 | let stackView = UIStackView(arrangedSubviews: [nameLabel, timeLabel, ganreLabel, starsLabel, ratingLabel])
40 | stackView.axis = NSLayoutConstraint.Axis.vertical
41 | stackView.distribution = UIStackView.Distribution.fillEqually
42 | stackView.alignment = UIStackView.Alignment.leading
43 | stackView.spacing = 8
44 | return stackView
45 | }()
46 |
47 | private lazy var nameLabel: UILabel = {
48 | let text = UILabel()
49 | text.textColor = .white
50 | text.font = UIFont.boldSystemFont(ofSize: 21)
51 | text.numberOfLines = 0
52 | return text
53 | }()
54 |
55 | private lazy var timeLabel: UILabel = {
56 | let text = UILabel()
57 | text.textColor = .white
58 | text.font = UIFont.boldSystemFont(ofSize: 13)
59 | text.numberOfLines = 0
60 | return text
61 | }()
62 |
63 | private lazy var ganreLabel: UILabel = {
64 | let text = UILabel()
65 | text.textColor = .white
66 | text.font = UIFont.boldSystemFont(ofSize: 13)
67 | text.numberOfLines = 0
68 | return text
69 | }()
70 |
71 | private lazy var starsLabel: LableImage = {
72 | let text = LableImage()
73 | return text
74 | }()
75 |
76 | private lazy var ratingLabel: LableImage = {
77 | let text = LableImage()
78 | return text
79 | }()
80 |
81 | private lazy var button: UIButton = {
82 | let button = UIButton()
83 | button.layer.cornerRadius = 8
84 | button.titleLabel?.font = UIFont.systemFont(ofSize: 14)
85 | button.setTitleColor(.white, for: .normal)
86 | button.setTitle("Watch Now", for: .normal)
87 | button.backgroundColor = .red
88 | return button
89 | }()
90 |
91 | /// stats View
92 | private lazy var descriptionLabel: UILabel = {
93 | let text = UILabel()
94 | text.textColor = .white
95 | text.font = UIFont.systemFont(ofSize: 13)
96 | text.numberOfLines = 0
97 | return text
98 | }()
99 |
100 | private lazy var trailerLabel: UILabel = {
101 | let text = UILabel()
102 | text.textColor = .white
103 | text.text = "Trailer"
104 | text.font = UIFont.boldSystemFont(ofSize: 21)
105 | text.numberOfLines = 0
106 | return text
107 | }()
108 |
109 | private lazy var trailerView: UIView = {
110 | var view = UIView()
111 | view.backgroundColor = .black
112 | view.layer.cornerRadius = 15
113 | view.clipsToBounds = true
114 | return view
115 | }()
116 |
117 | private lazy var commentsLabel: UILabel = {
118 | let text = UILabel()
119 | text.textColor = .white
120 | text.text = "Comments"
121 | text.font = UIFont.boldSystemFont(ofSize: 21)
122 | text.numberOfLines = 0
123 | return text
124 | }()
125 |
126 | private var webView = WKWebView()
127 |
128 | override init(frame: CGRect) {
129 | super.init(frame: frame)
130 | configure()
131 | configureTrailerView()
132 | }
133 |
134 | required init?(coder: NSCoder) {
135 | fatalError("init(coder:) has not been implemented")
136 | }
137 |
138 | var cellViewModel: MovieDTO? {
139 | didSet {
140 | if let url = cellViewModel?.photo {
141 | self.imageView.kf.setImage(with: URL(string: url))
142 | }
143 | if let url = cellViewModel?.miniPhoto {
144 | self.posterView.kf.setImage(with: URL(string: url))
145 | }
146 | if let url = cellViewModel?.trailer {
147 | let webConfiguration = WKWebViewConfiguration()
148 | webConfiguration.allowsInlineMediaPlayback = true
149 |
150 | DispatchQueue.main.asyncAfter(deadline: .now() + 2) {
151 | self.webView = WKWebView(frame: self.trailerView.bounds, configuration: webConfiguration)
152 | self.trailerView.addSubview(self.webView)
153 | guard let videoURL = URL(string: url) else { return }
154 | let request = URLRequest(url: videoURL)
155 | self.webView.load(request)
156 | }
157 | }
158 | if let name = cellViewModel?.name,
159 | let time = cellViewModel?.time,
160 | let description = cellViewModel?.description,
161 | let genres = cellViewModel?.genre,
162 | let stars = cellViewModel?.rating,
163 | let votes = cellViewModel?.votes {
164 |
165 | self.nameLabel.text = name
166 | self.timeLabel.text = "\(time / 60) hour \(time / 360) minute(s)"
167 | self.descriptionLabel.text = description
168 | var string = ""
169 | for genre in genres {
170 | string += "\(genre), "
171 | }
172 | string.removeLast()
173 | string.removeLast()
174 | self.ganreLabel.text = string
175 | self.starsLabel.setUp(
176 | text: stars,
177 | image: UIImage(systemName: "star.fill"),
178 | imageColor: Colors.star.color)
179 | self.ratingLabel.setUp(
180 | text: "\(votes)% from users",
181 | image: UIImage(systemName: "hand.thumbsup"),
182 | imageColor: Colors.title.color)
183 | }
184 | }
185 | }
186 |
187 | func configure() {
188 | addSubview(imageView)
189 | addSubview(gradientView)
190 | addSubview(statsView)
191 | addSubview(button)
192 | addSubview(posterView)
193 | imageView.snp.makeConstraints { make in
194 | make.top.leading.trailing.equalToSuperview()
195 | make.height.equalTo(imageView.snp.width).multipliedBy(0.96)
196 | }
197 | gradientView.snp.makeConstraints { make in
198 | make.top.leading.trailing.equalToSuperview()
199 | make.height.equalTo(imageView.snp.width).multipliedBy(0.96)
200 | }
201 | button.snp.makeConstraints { make in
202 | make.bottom.equalTo(posterView.snp.bottom)
203 | make.height.equalTo(30)
204 | make.width.equalTo(160)
205 | make.leading.equalToSuperview()
206 | }
207 | statsView.snp.makeConstraints { make in
208 | make.leading.equalToSuperview()
209 | make.bottom.equalTo((button.snp.top)).inset(-20)
210 | make.height.equalTo(120)
211 | make.width.equalToSuperview().multipliedBy(0.4)
212 | }
213 | posterView.snp.makeConstraints { make in
214 | make.bottom.equalTo(imageView.snp.bottom).offset(10)
215 | make.height.equalTo(posterView.snp.width).multipliedBy(1.2)
216 | make.width.equalToSuperview().multipliedBy(0.45)
217 | make.trailing.equalToSuperview()
218 | }
219 | }
220 |
221 | func configureTrailerView() {
222 | addSubview(trailerLabel)
223 | addSubview(trailerView)
224 | addSubview(descriptionLabel)
225 | addSubview(commentsLabel)
226 | webView.backgroundColor = .black
227 | descriptionLabel.snp.makeConstraints { make in
228 | make.top.equalTo(imageView.snp.bottom).offset(30)
229 | make.leading.trailing.equalToSuperview()
230 | }
231 | trailerLabel.snp.makeConstraints { make in
232 | make.top.equalTo(descriptionLabel.snp.bottom).offset(20)
233 | make.leading.trailing.equalToSuperview()
234 | }
235 | trailerView.snp.makeConstraints { make in
236 | make.top.equalTo(trailerLabel.snp.bottom).offset(10)
237 | make.leading.trailing.equalToSuperview()
238 | make.height.equalTo(trailerView.snp.width).multipliedBy(0.5)
239 | }
240 | commentsLabel.snp.makeConstraints { make in
241 | make.top.equalTo(trailerView.snp.bottom).offset(20)
242 | make.leading.trailing.equalToSuperview()
243 | make.bottom.equalToSuperview().inset(10)
244 | }
245 | }
246 | }
247 |
--------------------------------------------------------------------------------
/Movie/MovieDetailsScreen/MovieDetailedScreenViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MovieDetailedScreenViewController.swift
3 | // Movie
4 | //
5 | // Created by siuzanna on 19/12/21.
6 | //
7 |
8 | import UIKit
9 |
10 | class MovieDetailedScreenViewController: UIViewController {
11 |
12 | enum Section: String, CaseIterable {
13 | case comments = "Comments"
14 | case recommend = ""
15 | }
16 |
17 | typealias Snapshot = NSDiffableDataSourceSnapshot
18 | private var dataSource: UICollectionViewDiffableDataSource! = nil
19 | private var viewModel: MovieDTO
20 |
21 | private lazy var navigationBar = NavigationBarBack()
22 | private lazy var collectionView = configureCollectionView()
23 |
24 | init(viewModel: MovieDTO) {
25 | self.viewModel = viewModel
26 | super.init(nibName: nil, bundle: nil)
27 | }
28 |
29 | override func viewDidLoad() {
30 | super.viewDidLoad()
31 | view.backgroundColor = Colors.bg.color
32 | navigationBar.addBackButtonTarget(self, action: #selector(dismissView),
33 | forEvent: .touchUpInside)
34 | configureDataSource()
35 | setupNavigationBar()
36 | }
37 |
38 | @objc func dismissView() {
39 | self.dismiss(animated: true, completion: nil)
40 | }
41 |
42 | required init?(coder: NSCoder) {
43 | fatalError("init(coder:) has not been implemented")
44 | }
45 | }
46 |
47 | // MARK: Configure Constraints
48 | extension MovieDetailedScreenViewController {
49 |
50 | private func setupNavigationBar() {
51 | view.addSubview(collectionView)
52 | view.addSubview(navigationBar)
53 | navigationBar.snp.makeConstraints { make in
54 | make.top.equalTo(view.safeAreaLayoutGuide.snp.top)
55 | make.leading.trailing.equalToSuperview().inset(16)
56 | make.height.equalTo(92)
57 | }
58 | collectionView.snp.makeConstraints { make in
59 | make.top.equalTo(view.snp.topMargin)
60 | make.leading.trailing.bottom.equalToSuperview()
61 | }
62 | }
63 | }
64 |
65 | // MARK: Configure UICollectionView
66 | extension MovieDetailedScreenViewController {
67 |
68 | func configureCollectionView() -> UICollectionView {
69 | let collectionView = UICollectionView(frame: .zero, collectionViewLayout: generateLayout())
70 | collectionView.autoresizingMask = [.flexibleHeight, .flexibleWidth]
71 | collectionView.backgroundColor = .clear
72 | collectionView.showsVerticalScrollIndicator = false
73 | collectionView.showsHorizontalScrollIndicator = false
74 | collectionView.contentInset.bottom = 50
75 | collectionView.bounces = false
76 | collectionView.register(header: TopView.self)
77 | collectionView.register(header: HeaderView.self)
78 | collectionView.register(cell: CommentsCell.self)
79 | collectionView.register(footer: CommentsViewFooter.self)
80 | return collectionView
81 | }
82 | }
83 |
84 | // MARK: UICollectionViewDiffableDataSource
85 | extension MovieDetailedScreenViewController {
86 |
87 | func configureDataSource() {
88 | dataSource = UICollectionViewDiffableDataSource
89 | (collectionView: collectionView) {
90 | (collectionView: UICollectionView, indexPath: IndexPath, item: Comments) -> UICollectionViewCell? in
91 |
92 | let sectionType = Section.allCases[indexPath.section]
93 | switch sectionType {
94 | case .comments:
95 | return UICollectionViewCell()
96 | case .recommend:
97 | let cell: CommentsCell = collectionView.dequeue(for: indexPath)
98 | cell.cellViewModel = item
99 | return cell
100 | }
101 | }
102 |
103 | dataSource.supplementaryViewProvider = { [ weak self ]
104 | ( collectionView: UICollectionView, kind: String, indexPath: IndexPath) -> UICollectionReusableView? in
105 | let sectionType = Section.allCases[indexPath.section]
106 | switch sectionType {
107 | case .comments:
108 | let supplementaryView: TopView = collectionView.dequeue(for: indexPath, kind: kind)
109 | supplementaryView.cellViewModel = self?.viewModel
110 | return supplementaryView
111 | case .recommend:
112 | let supplementaryView: CommentsViewFooter = collectionView.dequeue(for: indexPath, kind: kind)
113 | return supplementaryView
114 | }
115 | }
116 |
117 | let snapshot = snapshot()
118 | dataSource.apply(snapshot, animatingDifferences: true)
119 | }
120 |
121 | func snapshot() -> Snapshot {
122 | var snapshot = Snapshot()
123 | snapshot.appendSections([.comments, .recommend])
124 | let comments = viewModel.comments
125 | snapshot.appendItems(comments, toSection: .recommend)
126 | snapshot.appendItems(comments.suffix(0), toSection: .comments)
127 |
128 | return snapshot
129 | }
130 | }
131 |
132 | // MARK: UICollectionViewLayout
133 | extension MovieDetailedScreenViewController {
134 |
135 | func generateLayout() -> UICollectionViewLayout {
136 | let layout = UICollectionViewCompositionalLayout { [weak self ]
137 | (sectionIndex: Int, _: NSCollectionLayoutEnvironment) -> NSCollectionLayoutSection? in
138 |
139 | let sectionLayoutKind = Section.allCases[sectionIndex]
140 | switch sectionLayoutKind {
141 | case .comments: return self?.generateEmtyCellLayout()
142 | case .recommend: return self?.generateCommentsLayout()
143 | }
144 | }
145 | layout.register(
146 | SectionBackgroundDecorationView.self,
147 | forDecorationViewOfKind: "NSCollectionLayoutDecorationItem")
148 | return layout
149 | }
150 |
151 | func generateEmtyCellLayout() -> NSCollectionLayoutSection {
152 | let itemSize = NSCollectionLayoutSize(
153 | widthDimension: .fractionalWidth(1),
154 | heightDimension: .fractionalHeight(1))
155 | let item = NSCollectionLayoutItem(layoutSize: itemSize)
156 |
157 | let groupSize = NSCollectionLayoutSize(
158 | widthDimension: .fractionalWidth(0.43),
159 | heightDimension: .absolute(10))
160 | let group = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitems: [item])
161 |
162 | let headerSize = NSCollectionLayoutSize(
163 | widthDimension: .fractionalWidth(1.0),
164 | heightDimension: .estimated(685))
165 | let sectionHeader = NSCollectionLayoutBoundarySupplementaryItem(
166 | layoutSize: headerSize,
167 | elementKind: UICollectionView.elementKindSectionHeader,
168 | alignment: .top)
169 | sectionHeader.zIndex = 2
170 |
171 | let section = NSCollectionLayoutSection(group: group)
172 | section.orthogonalScrollingBehavior = UICollectionLayoutSectionOrthogonalScrollingBehavior.continuous
173 | section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 20, bottom: 0, trailing: 20)
174 | section.boundarySupplementaryItems = [sectionHeader]
175 | section.orthogonalScrollingBehavior = .continuous
176 |
177 | return section
178 | }
179 |
180 | func generateCommentsLayout() -> NSCollectionLayoutSection {
181 | let itemSize = NSCollectionLayoutSize(
182 | widthDimension: .fractionalWidth(1.0),
183 | heightDimension: .fractionalHeight(1.0))
184 | let item = NSCollectionLayoutItem(layoutSize: itemSize)
185 |
186 | let groupSize = NSCollectionLayoutSize(
187 | widthDimension: .fractionalWidth(1.0),
188 | heightDimension: .absolute(82))
189 | let group = NSCollectionLayoutGroup.horizontal(
190 | layoutSize: groupSize, subitems: [item])
191 |
192 | let footerSize = NSCollectionLayoutSize(
193 | widthDimension: .fractionalWidth(1.0),
194 | heightDimension: .absolute(52))
195 | let sectionFooter = NSCollectionLayoutBoundarySupplementaryItem(
196 | layoutSize: footerSize,
197 | elementKind: UICollectionView.elementKindSectionFooter,
198 | alignment: .bottom)
199 | sectionFooter.zIndex = 2
200 |
201 | let section = NSCollectionLayoutSection(group: group)
202 | section.interGroupSpacing = 5
203 |
204 | let sectionBackgroundDecoration = NSCollectionLayoutDecorationItem.background(
205 | elementKind: "NSCollectionLayoutDecorationItem" )
206 | sectionBackgroundDecoration.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 20, bottom: 0, trailing: 20)
207 | section.decorationItems = [sectionBackgroundDecoration]
208 |
209 | section.orthogonalScrollingBehavior = UICollectionLayoutSectionOrthogonalScrollingBehavior.continuous
210 | section.contentInsets = NSDirectionalEdgeInsets(top: 0, leading: 20, bottom: 0, trailing: 20)
211 | section.boundarySupplementaryItems = [sectionFooter]
212 | section.orthogonalScrollingBehavior = .none
213 |
214 | return section
215 | }
216 | }
217 |
--------------------------------------------------------------------------------
/Movie/Resources/movies.json:
--------------------------------------------------------------------------------
1 | [
2 | {
3 | "id": 101,
4 | "type": 0,
5 | "series": false,
6 | "name": "Money Heist",
7 | "time": 120,
8 | "genre": ["Crime", "Drama"],
9 | "rating": "8.2",
10 | "votes": "529K",
11 | "photo": "https://i.pinimg.com/originals/cc/3d/c2/cc3dc28c61ecc4e3943ef52a5be72415.jpg",
12 | "miniPhoto": "https://i.pinimg.com/originals/9b/3f/ed/9b3fedaf0922a2538fc8083bbfcb3d2f.jpg",
13 | "description": "An unusual group of robbers attempt to carry out the most perfect robbery in Spanish history - stealing 2.4 billion euros from the Royal Mint of Spain.",
14 | "trailer": "https://www.youtube.com/watch?v=_InqQJRqGW4",
15 | "comments": [
16 | {
17 | "id": 1,
18 | "name": "John Doe",
19 | "comment": "Great series!",
20 | "picture": "url_to_picture"
21 | },
22 | {
23 | "id": 2,
24 | "name": "Jane Smith",
25 | "comment": "Loved the storyline.",
26 | "picture": "url_to_picture"
27 | }
28 | ]
29 | },
30 | {
31 | "id": 102,
32 | "type": 0,
33 | "series": false,
34 | "name": "The Avengers",
35 | "time": 143,
36 | "genre": ["Action", "Adventure"],
37 | "rating": "8.0",
38 | "votes": "1.5M",
39 | "photo": "https://avatars.mds.yandex.net/i?id=9dd4bdd1197e4c470546f18681d944c6c7da6fdf-10555250-images-thumbs&n=13",
40 | "miniPhoto": "http://images6.fanpop.com/image/photos/37300000/Avengers-2-Age-of-Ultron-the-avengers-37328228-900-1185.jpg",
41 | "description": "Earth's mightiest heroes must come together and learn to fight as a team if they are going to stop the mischievous Loki and his alien army.",
42 | "trailer": "https://www.youtube.com/watch?v=6ZfuNTqbHE8",
43 | "comments": [
44 | {
45 | "id": 1,
46 | "name": "John Doe",
47 | "comment": "Great movie!",
48 | "picture": "url_to_picture"
49 | },
50 | {
51 | "id": 2,
52 | "name": "Jane Smith",
53 | "comment": "Loved the action scenes.",
54 | "picture": "url_to_picture"
55 | }
56 | ]
57 | },
58 | {
59 | "id": 201,
60 | "type": 0,
61 | "series": false,
62 | "name": "Fatman",
63 | "time": 100,
64 | "genre": ["Action", "Comedy", "Fantasy"],
65 | "rating": "5.9",
66 | "votes": "30K",
67 | "photo": "https://avatars.dzeninfra.ru/get-zen_doc/3385233/pub_602e2d835f462a3bfd27a7f2_602e2e30ffa2d86ae404fe1c/scale_1200",
68 | "miniPhoto": "https://i.pinimg.com/originals/13/97/ae/1397ae69601ebc7e76e73f643f8a9114.jpg",
69 | "description": "A rowdy, unorthodox Santa Claus is fighting to save his declining business. Meanwhile, Billy, a neglected and precocious 12-year-old, hires a hitman to kill Santa after receiving a lump of coal in his stocking.",
70 | "trailer": "url_to_trailer",
71 | "comments": [
72 | {
73 | "id": 1,
74 | "name": "John Doe",
75 | "comment": "An unconventional holiday film.",
76 | "picture": "url_to_picture"
77 | },
78 | {
79 | "id": 2,
80 | "name": "Jane Smith",
81 | "comment": "Unique take on Santa Claus.",
82 | "picture": "url_to_picture"
83 | }
84 | ]
85 | },
86 | {
87 | "id": 202,
88 | "type": 1,
89 | "series": true,
90 | "name": "30 Coins",
91 | "time": 60,
92 | "genre": ["Drama", "Fantasy", "Horror"],
93 | "rating": "7.1",
94 | "votes": "11K",
95 | "photo": "https://pogd.es/assets/bg/30-Coins.jpg",
96 | "miniPhoto": "https://i.pinimg.com/originals/3e/78/65/3e7865c4646ece782f1d6d1c3971225f.jpg",
97 | "description": "An exiled priest tries to escape his demons while living in a remote village in Spain.",
98 | "trailer": "https://www.youtube.com/watch?v=jOAxchyLZ00",
99 | "comments": [
100 | {
101 | "id": 1,
102 | "name": "John Doe",
103 | "comment": "Intriguing and suspenseful.",
104 | "picture": "url_to_picture"
105 | },
106 | {
107 | "id": 2,
108 | "name": "Jane Smith",
109 | "comment": "Captivating storyline.",
110 | "picture": "url_to_picture"
111 | }
112 | ]
113 | },
114 | {
115 | "id": 301,
116 | "type": 1,
117 | "series": false,
118 | "name": "Inception",
119 | "time": 148,
120 | "genre": ["Action", "Sci-Fi", "Thriller"],
121 | "rating": "8.8",
122 | "votes": "2.3M",
123 | "photo": "https://m.media-amazon.com/images/I/51zUbui+gbL._AC_.jpg",
124 | "miniPhoto": "https://m.media-amazon.com/images/I/51zUbui+gbL._AC_SY679_.jpg",
125 | "description": "A thief who steals corporate secrets through the use of dream-sharing technology is given the inverse task of planting an idea into the mind of a CEO.",
126 | "trailer": "https://www.youtube.com/watch?v=YoHD9XEInc0",
127 | "comments": [
128 | {
129 | "id": 1,
130 | "name": "Alice Johnson",
131 | "comment": "Mind-bending masterpiece!",
132 | "picture": "url_to_picture"
133 | },
134 | {
135 | "id": 2,
136 | "name": "Bob Lee",
137 | "comment": "A complex and thrilling experience.",
138 | "picture": "url_to_picture"
139 | }
140 | ]
141 | },
142 | {
143 | "id": 302,
144 | "type": 1,
145 | "series": false,
146 | "name": "The Grand Budapest Hotel",
147 | "time": 99,
148 | "genre": ["Adventure", "Comedy", "Drama"],
149 | "rating": "8.1",
150 | "votes": "800K",
151 | "photo": "https://static01.nyt.com/images/2014/03/05/multimedia/budapest-anatomy/budapest-anatomy-superJumbo.jpg",
152 | "miniPhoto": "https://m.media-amazon.com/images/M/MV5BOTVjOGNhMmUtMDMzOS00YTljLWEwYWUtNWZjMTgwZmZkNTI0XkEyXkFqcGdeQXVyNTIzOTk5ODM@._V1_.jpg",
153 | "description": "A writer encounters the owner of an aging high-class hotel, who tells him of his early years serving as a lobby boy in the hotel's glorious years under an exceptional concierge.",
154 | "trailer": "https://www.youtube.com/watch?v=1Fg5iWmQjwk",
155 | "comments": [
156 | {
157 | "id": 1,
158 | "name": "Clara Smith",
159 | "comment": "A whimsical and visually stunning film.",
160 | "picture": "url_to_picture"
161 | },
162 | {
163 | "id": 2,
164 | "name": "David Brown",
165 | "comment": "Wes Anderson at his best.",
166 | "picture": "url_to_picture"
167 | }
168 | ]
169 | },
170 | {
171 | "id": 303,
172 | "type": 2,
173 | "series": false,
174 | "name": "Parasite",
175 | "time": 132,
176 | "genre": ["Comedy", "Drama", "Thriller"],
177 | "rating": "8.6",
178 | "votes": "1.1M",
179 | "photo": "https://static.kinoafisha.info/upload/news/391683356050.jpg",
180 | "miniPhoto": "https://m.media-amazon.com/images/M/MV5BYjU3MTEzNGItMDhkOS00YTgyLWIzZGQtMjQ0ZjdiYjE1YmM5XkEyXkFqcGdeQXVyMTA4NjE0NjEy._V1_.jpg",
181 | "description": "Greed and class discrimination threaten the newly formed symbiotic relationship between the wealthy Park family and the destitute Kim clan.",
182 | "trailer": "https://www.youtube.com/watch?v=5xH0HfJHsaY",
183 | "comments": [
184 | {
185 | "id": 1,
186 | "name": "Eva Green",
187 | "comment": "A brilliant social commentary.",
188 | "picture": "url_to_picture"
189 | },
190 | {
191 | "id": 2,
192 | "name": "Frank White",
193 | "comment": "Unpredictable and captivating.",
194 | "picture": "url_to_picture"
195 | }
196 | ]
197 | },
198 | {
199 | "id": 304,
200 | "type": 3,
201 | "series": false,
202 | "name": "The Dark Knight",
203 | "time": 152,
204 | "genre": ["Action", "Crime", "Drama"],
205 | "rating": "9.0",
206 | "votes": "2.5M",
207 | "photo": "https://a.d-cd.net/d4AAAgNbKuA-1920.jpg",
208 | "miniPhoto": "https://images-na.ssl-images-amazon.com/images/I/91ebheNmoUL._RI_.jpg",
209 | "description": "When the menace known as the Joker emerges from his mysterious past, he wreaks havoc and chaos on the people of Gotham.",
210 | "trailer": "https://www.youtube.com/watch?v=EXeTwQWrcwY",
211 | "comments": [
212 | {
213 | "id": 1,
214 | "name": "George Miller",
215 | "comment": "Heath Ledger's performance is legendary.",
216 | "picture": "url_to_picture"
217 | },
218 | {
219 | "id": 2,
220 | "name": "Hannah Davis",
221 | "comment": "A masterpiece in the superhero genre.",
222 | "picture": "url_to_picture"
223 | }
224 | ]
225 | },
226 | {
227 | "id": 305,
228 | "type": 4,
229 | "series": false,
230 | "name": "Pulp Fiction",
231 | "time": 154,
232 | "genre": ["Crime", "Drama"],
233 | "rating": "8.9",
234 | "votes": "1.9M",
235 | "photo": "https://m.media-amazon.com/images/I/71c05lTE03L._AC_SY679_.jpg",
236 | "miniPhoto": "https://m.media-amazon.com/images/I/71c05lTE03L._AC_SY679_.jpg",
237 | "description": "The lives of two mob hitmen, a boxer, a gangster's wife, and a pair of diner bandits intertwine in four tales of violence and redemption.",
238 | "trailer": "https://www.youtube.com/watch?v=s7EdQ4FqbhY",
239 | "comments": [
240 | {
241 | "id": 1,
242 | "name": "Ian Thompson",
243 | "comment": "Quentin Tarantino's magnum opus.",
244 | "picture": "url_to_picture"
245 | },
246 | {
247 | "id": 2,
248 | "name": "Julia Roberts",
249 | "comment": "A cult classic with unforgettable dialogue.",
250 | "picture": "url_to_picture"
251 | }
252 | ]
253 | },
254 | {
255 | "id": 403,
256 | "type": 3,
257 | "series": false,
258 | "name": "The Conjuring",
259 | "time": 112,
260 | "genre": ["Horror", "Mystery", "Thriller"],
261 | "rating": "7.5",
262 | "votes": "528K",
263 | "photo": "https://newcdn.igromania.ru/mnt/news/e/9/e/a/2/2/99139/html/more/fded9944f363f333733fcc3b_1920xH.webp",
264 | "miniPhoto": "https://m.media-amazon.com/images/M/MV5BZTlhZWY0YzAtMDA2Zi00NGVlLThhNzQtYjFjMWI0YjU0Yzg0XkEyXkFqcGdeQXVyNjIzNzM4NzA@._V1_.jpg",
265 | "description": "Paranormal investigators Ed and Lorraine Warren work to help a family terrorized by a dark presence in their farmhouse.",
266 | "trailer": "https://www.youtube.com/watch?v=k10ETZ41q5o",
267 | "comments": [
268 | {
269 | "id": 1,
270 | "name": "Nathan Brooks",
271 | "comment": "Terrifying and unforgettable.",
272 | "picture": "url_to_picture"
273 | },
274 | {
275 | "id": 2,
276 | "name": "Olivia Parker",
277 | "comment": "A hauntingly good time.",
278 | "picture": "url_to_picture"
279 | }
280 | ]
281 | },
282 | {
283 | "id": 404,
284 | "type": 4,
285 | "series": true,
286 | "name": "Game of Thrones",
287 | "time": 57,
288 | "genre": ["Fantasy", "Adventure", "Drama"],
289 | "rating": "9.2",
290 | "votes": "2.0M",
291 | "photo": "https://avatars.mds.yandex.net/get-marketpic/5654221/pic9f3d3ef1bac6f563bcd9f9b17ece84ef/orig",
292 | "miniPhoto": "https://avatars.mds.yandex.net/get-marketpic/5888040/picebb756d60998254792428e8d566183fe/orig",
293 | "description": "Nine noble families fight for control over the lands of Westeros, while an ancient enemy returns after being dormant for millennia.",
294 | "trailer": "https://www.youtube.com/watch?v=BpJYNVhGf1s",
295 | "comments": [
296 | {
297 | "id": 1,
298 | "name": "Henry Evans",
299 | "comment": "Epic storytelling with rich world-building.",
300 | "picture": "url_to_picture"
301 | },
302 | {
303 | "id": 2,
304 | "name": "Isabella Garcia",
305 | "comment": "A must-watch for fantasy fans.",
306 | "picture": "url_to_picture"
307 | }
308 | ]
309 | }
310 | ]
--------------------------------------------------------------------------------
/Movie.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 55;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 9605D4272B9504F80084D92F /* MainContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9605D4262B9504F80084D92F /* MainContentView.swift */; };
11 | 96290EDE276D0F2E00AFD992 /* PhotoCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96290EDD276D0F2E00AFD992 /* PhotoCell.swift */; };
12 | 96290EE0276D117700AFD992 /* UICollectionViewCell+Register+extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96290EDF276D117700AFD992 /* UICollectionViewCell+Register+extension.swift */; };
13 | 96290EE2276D130A00AFD992 /* HeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96290EE1276D130A00AFD992 /* HeaderView.swift */; };
14 | 96290EE4276D19BA00AFD992 /* MainScreenViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96290EE3276D19BA00AFD992 /* MainScreenViewModel.swift */; };
15 | 96290EF4276E06D500AFD992 /* MainScreenRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96290EEF276E06D500AFD992 /* MainScreenRouter.swift */; };
16 | 96290EF5276E06D500AFD992 /* ProResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96290EF2276E06D500AFD992 /* ProResult.swift */; };
17 | 96290EF6276E06D500AFD992 /* NetworkService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96290EF0276E06D500AFD992 /* NetworkService.swift */; };
18 | 96290EF7276E06D500AFD992 /* BaseRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96290EED276E06D500AFD992 /* BaseRouter.swift */; };
19 | 96290EF8276E06D500AFD992 /* NetworkErrors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96290EF1276E06D500AFD992 /* NetworkErrors.swift */; };
20 | 96290EFF276E130100AFD992 /* MovieDTO.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96290EFC276E130100AFD992 /* MovieDTO.swift */; };
21 | 96290F00276E130100AFD992 /* Comments.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96290EFD276E130100AFD992 /* Comments.swift */; };
22 | 96290F05276E5A7C00AFD992 /* NavigationBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96290F04276E5A7C00AFD992 /* NavigationBar.swift */; };
23 | 96290F09276E656F00AFD992 /* HTTPMethods.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96290F08276E656F00AFD992 /* HTTPMethods.swift */; };
24 | 96290F0E276E8C7F00AFD992 /* .swiftlint.yml in Resources */ = {isa = PBXBuildFile; fileRef = 96290F0D276E8C7F00AFD992 /* .swiftlint.yml */; };
25 | 96290F11276F384800AFD992 /* MovieDetailedScreenViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96290F10276F384800AFD992 /* MovieDetailedScreenViewController.swift */; };
26 | 96290F15276F397B00AFD992 /* NavigationBarBack.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96290F14276F397B00AFD992 /* NavigationBarBack.swift */; };
27 | 96290F17276F426C00AFD992 /* TopView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96290F16276F426C00AFD992 /* TopView.swift */; };
28 | 96290F1A276F69BC00AFD992 /* LableImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96290F19276F69BC00AFD992 /* LableImage.swift */; };
29 | 96290F1C276F754F00AFD992 /* WebKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 96290F1B276F754F00AFD992 /* WebKit.framework */; };
30 | 9635412A276FCC370036133D /* CommentsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 96354129276FCC370036133D /* CommentsCell.swift */; };
31 | 9635412D276FDFEA0036133D /* SectionDecorationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9635412C276FDFEA0036133D /* SectionDecorationView.swift */; };
32 | 9635412F276FE0AE0036133D /* CommentsViewFooter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9635412E276FE0AE0036133D /* CommentsViewFooter.swift */; };
33 | 963541372770A7FC0036133D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 963541362770A7FC0036133D /* Assets.xcassets */; };
34 | 963EA3C82D32748300EDF8C7 /* movies.json in Resources */ = {isa = PBXBuildFile; fileRef = 963EA3C72D32748300EDF8C7 /* movies.json */; };
35 | 964938AD276CA661009C6708 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 964938AC276CA661009C6708 /* AppDelegate.swift */; };
36 | 964938AF276CA661009C6708 /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 964938AE276CA661009C6708 /* SceneDelegate.swift */; };
37 | 964938B1276CA661009C6708 /* MainScreenViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 964938B0276CA661009C6708 /* MainScreenViewController.swift */; };
38 | 964938B6276CA666009C6708 /* Icons.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 964938B5276CA666009C6708 /* Icons.xcassets */; };
39 | 964938B9276CA666009C6708 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 964938B7276CA666009C6708 /* LaunchScreen.storyboard */; };
40 | 964938C3276CA73D009C6708 /* ViewController + Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 964938C2276CA73D009C6708 /* ViewController + Extension.swift */; };
41 | 964938C5276CA780009C6708 /* UINavigationController + Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 964938C4276CA780009C6708 /* UINavigationController + Extension.swift */; };
42 | 964938C8276CAD60009C6708 /* Colors.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 964938C7276CAD60009C6708 /* Colors.xcassets */; };
43 | 964938CA276CAE61009C6708 /* swiftgen.yml in Resources */ = {isa = PBXBuildFile; fileRef = 964938C9276CAE61009C6708 /* swiftgen.yml */; };
44 | 964938D3276CB5F6009C6708 /* UITabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 964938D2276CB5F6009C6708 /* UITabBarController.swift */; };
45 | 964938D5276CB7D1009C6708 /* UIImage + Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 964938D4276CB7D1009C6708 /* UIImage + Extension.swift */; };
46 | 964938D8276CBEA4009C6708 /* GeneratedColors.swift in Sources */ = {isa = PBXBuildFile; fileRef = 964938D6276CBEA4009C6708 /* GeneratedColors.swift */; };
47 | 964938D9276CBEA4009C6708 /* GeneratedIcons.swift in Sources */ = {isa = PBXBuildFile; fileRef = 964938D7276CBEA4009C6708 /* GeneratedIcons.swift */; };
48 | 967792682CAEDCCC0031C2F3 /* SnapKit-Dynamic in Embed Frameworks */ = {isa = PBXBuildFile; productRef = 96AADD912B47481100D823D6 /* SnapKit-Dynamic */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
49 | 96AADD922B47481100D823D6 /* SnapKit-Dynamic in Frameworks */ = {isa = PBXBuildFile; productRef = 96AADD912B47481100D823D6 /* SnapKit-Dynamic */; };
50 | 96F59F612B41D1BA004C298A /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = 96F59F602B41D1BA004C298A /* Kingfisher */; };
51 | /* End PBXBuildFile section */
52 |
53 | /* Begin PBXCopyFilesBuildPhase section */
54 | 967792692CAEDCCC0031C2F3 /* Embed Frameworks */ = {
55 | isa = PBXCopyFilesBuildPhase;
56 | buildActionMask = 2147483647;
57 | dstPath = "";
58 | dstSubfolderSpec = 10;
59 | files = (
60 | 967792682CAEDCCC0031C2F3 /* SnapKit-Dynamic in Embed Frameworks */,
61 | );
62 | name = "Embed Frameworks";
63 | runOnlyForDeploymentPostprocessing = 0;
64 | };
65 | /* End PBXCopyFilesBuildPhase section */
66 |
67 | /* Begin PBXFileReference section */
68 | 94D69698E83CFDFC9C1B27BC /* Pods_Movie.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Movie.framework; sourceTree = BUILT_PRODUCTS_DIR; };
69 | 9605D4262B9504F80084D92F /* MainContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainContentView.swift; sourceTree = ""; };
70 | 96290EDD276D0F2E00AFD992 /* PhotoCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PhotoCell.swift; sourceTree = ""; };
71 | 96290EDF276D117700AFD992 /* UICollectionViewCell+Register+extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UICollectionViewCell+Register+extension.swift"; sourceTree = ""; };
72 | 96290EE1276D130A00AFD992 /* HeaderView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HeaderView.swift; sourceTree = ""; };
73 | 96290EE3276D19BA00AFD992 /* MainScreenViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainScreenViewModel.swift; sourceTree = ""; };
74 | 96290EED276E06D500AFD992 /* BaseRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseRouter.swift; sourceTree = ""; };
75 | 96290EEF276E06D500AFD992 /* MainScreenRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainScreenRouter.swift; sourceTree = ""; };
76 | 96290EF0276E06D500AFD992 /* NetworkService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkService.swift; sourceTree = ""; };
77 | 96290EF1276E06D500AFD992 /* NetworkErrors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkErrors.swift; sourceTree = ""; };
78 | 96290EF2276E06D500AFD992 /* ProResult.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProResult.swift; sourceTree = ""; };
79 | 96290EFC276E130100AFD992 /* MovieDTO.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MovieDTO.swift; sourceTree = ""; };
80 | 96290EFD276E130100AFD992 /* Comments.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Comments.swift; sourceTree = ""; };
81 | 96290F04276E5A7C00AFD992 /* NavigationBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationBar.swift; sourceTree = ""; };
82 | 96290F08276E656F00AFD992 /* HTTPMethods.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTTPMethods.swift; sourceTree = ""; };
83 | 96290F0D276E8C7F00AFD992 /* .swiftlint.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = .swiftlint.yml; sourceTree = ""; };
84 | 96290F10276F384800AFD992 /* MovieDetailedScreenViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MovieDetailedScreenViewController.swift; sourceTree = ""; };
85 | 96290F14276F397B00AFD992 /* NavigationBarBack.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationBarBack.swift; sourceTree = ""; };
86 | 96290F16276F426C00AFD992 /* TopView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TopView.swift; sourceTree = ""; };
87 | 96290F19276F69BC00AFD992 /* LableImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LableImage.swift; sourceTree = ""; };
88 | 96290F1B276F754F00AFD992 /* WebKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WebKit.framework; path = System/Library/Frameworks/WebKit.framework; sourceTree = SDKROOT; };
89 | 96354129276FCC370036133D /* CommentsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentsCell.swift; sourceTree = ""; };
90 | 9635412C276FDFEA0036133D /* SectionDecorationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SectionDecorationView.swift; sourceTree = ""; };
91 | 9635412E276FE0AE0036133D /* CommentsViewFooter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommentsViewFooter.swift; sourceTree = ""; };
92 | 963541362770A7FC0036133D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
93 | 963EA3C72D32748300EDF8C7 /* movies.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = movies.json; sourceTree = ""; };
94 | 964938A9276CA661009C6708 /* Movie.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Movie.app; sourceTree = BUILT_PRODUCTS_DIR; };
95 | 964938AC276CA661009C6708 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
96 | 964938AE276CA661009C6708 /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; };
97 | 964938B0276CA661009C6708 /* MainScreenViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainScreenViewController.swift; sourceTree = ""; };
98 | 964938B5276CA666009C6708 /* Icons.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Icons.xcassets; sourceTree = ""; };
99 | 964938B8276CA666009C6708 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
100 | 964938BA276CA666009C6708 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
101 | 964938C2276CA73D009C6708 /* ViewController + Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ViewController + Extension.swift"; sourceTree = ""; };
102 | 964938C4276CA780009C6708 /* UINavigationController + Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UINavigationController + Extension.swift"; sourceTree = ""; };
103 | 964938C7276CAD60009C6708 /* Colors.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Colors.xcassets; sourceTree = ""; };
104 | 964938C9276CAE61009C6708 /* swiftgen.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = swiftgen.yml; sourceTree = ""; };
105 | 964938D2276CB5F6009C6708 /* UITabBarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UITabBarController.swift; sourceTree = ""; };
106 | 964938D4276CB7D1009C6708 /* UIImage + Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage + Extension.swift"; sourceTree = ""; };
107 | 964938D6276CBEA4009C6708 /* GeneratedColors.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedColors.swift; sourceTree = ""; };
108 | 964938D7276CBEA4009C6708 /* GeneratedIcons.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedIcons.swift; sourceTree = ""; };
109 | /* End PBXFileReference section */
110 |
111 | /* Begin PBXFrameworksBuildPhase section */
112 | 964938A6276CA661009C6708 /* Frameworks */ = {
113 | isa = PBXFrameworksBuildPhase;
114 | buildActionMask = 2147483647;
115 | files = (
116 | 96AADD922B47481100D823D6 /* SnapKit-Dynamic in Frameworks */,
117 | 96F59F612B41D1BA004C298A /* Kingfisher in Frameworks */,
118 | 96290F1C276F754F00AFD992 /* WebKit.framework in Frameworks */,
119 | );
120 | runOnlyForDeploymentPostprocessing = 0;
121 | };
122 | /* End PBXFrameworksBuildPhase section */
123 |
124 | /* Begin PBXGroup section */
125 | 90660853EFED07B4999132CC /* Frameworks */ = {
126 | isa = PBXGroup;
127 | children = (
128 | 96290F1B276F754F00AFD992 /* WebKit.framework */,
129 | 94D69698E83CFDFC9C1B27BC /* Pods_Movie.framework */,
130 | );
131 | name = Frameworks;
132 | sourceTree = "";
133 | };
134 | 96290ED8276CC02900AFD992 /* MainScreen */ = {
135 | isa = PBXGroup;
136 | children = (
137 | 964938B0276CA661009C6708 /* MainScreenViewController.swift */,
138 | 96290EE3276D19BA00AFD992 /* MainScreenViewModel.swift */,
139 | 9605D4262B9504F80084D92F /* MainContentView.swift */,
140 | 96290EDC276D0F0300AFD992 /* View */,
141 | 96290F06276E64D700AFD992 /* Cells */,
142 | );
143 | path = MainScreen;
144 | sourceTree = "";
145 | };
146 | 96290EDC276D0F0300AFD992 /* View */ = {
147 | isa = PBXGroup;
148 | children = (
149 | 96290EE1276D130A00AFD992 /* HeaderView.swift */,
150 | 96290F04276E5A7C00AFD992 /* NavigationBar.swift */,
151 | );
152 | path = View;
153 | sourceTree = "";
154 | };
155 | 96290EE5276D19FF00AFD992 /* Models */ = {
156 | isa = PBXGroup;
157 | children = (
158 | 96290EFD276E130100AFD992 /* Comments.swift */,
159 | 96290EFC276E130100AFD992 /* MovieDTO.swift */,
160 | );
161 | path = Models;
162 | sourceTree = "";
163 | };
164 | 96290EE8276D288900AFD992 /* NetworkService */ = {
165 | isa = PBXGroup;
166 | children = (
167 | 96290EF0276E06D500AFD992 /* NetworkService.swift */,
168 | 96290F0A276E657C00AFD992 /* Network Components */,
169 | 96290F07276E653800AFD992 /* Base Router */,
170 | );
171 | path = NetworkService;
172 | sourceTree = "";
173 | };
174 | 96290F06276E64D700AFD992 /* Cells */ = {
175 | isa = PBXGroup;
176 | children = (
177 | 96290EDD276D0F2E00AFD992 /* PhotoCell.swift */,
178 | );
179 | path = Cells;
180 | sourceTree = "";
181 | };
182 | 96290F07276E653800AFD992 /* Base Router */ = {
183 | isa = PBXGroup;
184 | children = (
185 | 96290EED276E06D500AFD992 /* BaseRouter.swift */,
186 | 96290EEF276E06D500AFD992 /* MainScreenRouter.swift */,
187 | );
188 | path = "Base Router";
189 | sourceTree = "";
190 | };
191 | 96290F0A276E657C00AFD992 /* Network Components */ = {
192 | isa = PBXGroup;
193 | children = (
194 | 96290EF1276E06D500AFD992 /* NetworkErrors.swift */,
195 | 96290EF2276E06D500AFD992 /* ProResult.swift */,
196 | 96290F08276E656F00AFD992 /* HTTPMethods.swift */,
197 | );
198 | path = "Network Components";
199 | sourceTree = "";
200 | };
201 | 96290F0F276F37E100AFD992 /* MovieDetailsScreen */ = {
202 | isa = PBXGroup;
203 | children = (
204 | 96290F10276F384800AFD992 /* MovieDetailedScreenViewController.swift */,
205 | 9635412B276FDFD00036133D /* Cells */,
206 | 96290F12276F385400AFD992 /* View */,
207 | );
208 | path = MovieDetailsScreen;
209 | sourceTree = "";
210 | };
211 | 96290F12276F385400AFD992 /* View */ = {
212 | isa = PBXGroup;
213 | children = (
214 | 96290F14276F397B00AFD992 /* NavigationBarBack.swift */,
215 | 96290F16276F426C00AFD992 /* TopView.swift */,
216 | 9635412E276FE0AE0036133D /* CommentsViewFooter.swift */,
217 | 9635412C276FDFEA0036133D /* SectionDecorationView.swift */,
218 | 96290F19276F69BC00AFD992 /* LableImage.swift */,
219 | );
220 | path = View;
221 | sourceTree = "";
222 | };
223 | 9635412B276FDFD00036133D /* Cells */ = {
224 | isa = PBXGroup;
225 | children = (
226 | 96354129276FCC370036133D /* CommentsCell.swift */,
227 | );
228 | path = Cells;
229 | sourceTree = "";
230 | };
231 | 964938A0276CA661009C6708 = {
232 | isa = PBXGroup;
233 | children = (
234 | 96290F0D276E8C7F00AFD992 /* .swiftlint.yml */,
235 | 964938C9276CAE61009C6708 /* swiftgen.yml */,
236 | 964938AB276CA661009C6708 /* Movie */,
237 | 964938AA276CA661009C6708 /* Products */,
238 | 90660853EFED07B4999132CC /* Frameworks */,
239 | );
240 | sourceTree = "";
241 | };
242 | 964938AA276CA661009C6708 /* Products */ = {
243 | isa = PBXGroup;
244 | children = (
245 | 964938A9276CA661009C6708 /* Movie.app */,
246 | );
247 | name = Products;
248 | sourceTree = "";
249 | };
250 | 964938AB276CA661009C6708 /* Movie */ = {
251 | isa = PBXGroup;
252 | children = (
253 | 964938AC276CA661009C6708 /* AppDelegate.swift */,
254 | 964938AE276CA661009C6708 /* SceneDelegate.swift */,
255 | 96290EE5276D19FF00AFD992 /* Models */,
256 | 96290ED8276CC02900AFD992 /* MainScreen */,
257 | 96290F0F276F37E100AFD992 /* MovieDetailsScreen */,
258 | 964938D1276CB5A2009C6708 /* Tabbar */,
259 | 96290EE8276D288900AFD992 /* NetworkService */,
260 | 964938C0276CA719009C6708 /* Extension */,
261 | 964938C6276CABEF009C6708 /* Resources */,
262 | 964938BA276CA666009C6708 /* Info.plist */,
263 | );
264 | path = Movie;
265 | sourceTree = "";
266 | };
267 | 964938C0276CA719009C6708 /* Extension */ = {
268 | isa = PBXGroup;
269 | children = (
270 | 964938C1276CA724009C6708 /* UIKit */,
271 | );
272 | path = Extension;
273 | sourceTree = "";
274 | };
275 | 964938C1276CA724009C6708 /* UIKit */ = {
276 | isa = PBXGroup;
277 | children = (
278 | 96290EDF276D117700AFD992 /* UICollectionViewCell+Register+extension.swift */,
279 | 964938C2276CA73D009C6708 /* ViewController + Extension.swift */,
280 | 964938C4276CA780009C6708 /* UINavigationController + Extension.swift */,
281 | 964938D4276CB7D1009C6708 /* UIImage + Extension.swift */,
282 | );
283 | path = UIKit;
284 | sourceTree = "";
285 | };
286 | 964938C6276CABEF009C6708 /* Resources */ = {
287 | isa = PBXGroup;
288 | children = (
289 | 963EA3C72D32748300EDF8C7 /* movies.json */,
290 | 964938B7276CA666009C6708 /* LaunchScreen.storyboard */,
291 | 964938D6276CBEA4009C6708 /* GeneratedColors.swift */,
292 | 964938D7276CBEA4009C6708 /* GeneratedIcons.swift */,
293 | 964938B5276CA666009C6708 /* Icons.xcassets */,
294 | 964938C7276CAD60009C6708 /* Colors.xcassets */,
295 | 963541362770A7FC0036133D /* Assets.xcassets */,
296 | );
297 | path = Resources;
298 | sourceTree = "";
299 | };
300 | 964938D1276CB5A2009C6708 /* Tabbar */ = {
301 | isa = PBXGroup;
302 | children = (
303 | 964938D2276CB5F6009C6708 /* UITabBarController.swift */,
304 | );
305 | path = Tabbar;
306 | sourceTree = "";
307 | };
308 | /* End PBXGroup section */
309 |
310 | /* Begin PBXNativeTarget section */
311 | 964938A8276CA661009C6708 /* Movie */ = {
312 | isa = PBXNativeTarget;
313 | buildConfigurationList = 964938BD276CA666009C6708 /* Build configuration list for PBXNativeTarget "Movie" */;
314 | buildPhases = (
315 | 964938A5276CA661009C6708 /* Sources */,
316 | 964938A6276CA661009C6708 /* Frameworks */,
317 | 964938A7276CA661009C6708 /* Resources */,
318 | 967792692CAEDCCC0031C2F3 /* Embed Frameworks */,
319 | );
320 | buildRules = (
321 | );
322 | dependencies = (
323 | );
324 | name = Movie;
325 | packageProductDependencies = (
326 | 96F59F602B41D1BA004C298A /* Kingfisher */,
327 | 96AADD912B47481100D823D6 /* SnapKit-Dynamic */,
328 | );
329 | productName = Movie;
330 | productReference = 964938A9276CA661009C6708 /* Movie.app */;
331 | productType = "com.apple.product-type.application";
332 | };
333 | /* End PBXNativeTarget section */
334 |
335 | /* Begin PBXProject section */
336 | 964938A1276CA661009C6708 /* Project object */ = {
337 | isa = PBXProject;
338 | attributes = {
339 | BuildIndependentTargetsInParallel = 1;
340 | LastSwiftUpdateCheck = 1320;
341 | LastUpgradeCheck = 1320;
342 | TargetAttributes = {
343 | 964938A8276CA661009C6708 = {
344 | CreatedOnToolsVersion = 13.2;
345 | };
346 | };
347 | };
348 | buildConfigurationList = 964938A4276CA661009C6708 /* Build configuration list for PBXProject "Movie" */;
349 | compatibilityVersion = "Xcode 13.0";
350 | developmentRegion = en;
351 | hasScannedForEncodings = 0;
352 | knownRegions = (
353 | en,
354 | Base,
355 | );
356 | mainGroup = 964938A0276CA661009C6708;
357 | packageReferences = (
358 | 96F59F5F2B41D1BA004C298A /* XCRemoteSwiftPackageReference "Kingfisher" */,
359 | 96AADD8E2B47481100D823D6 /* XCRemoteSwiftPackageReference "SnapKit" */,
360 | );
361 | productRefGroup = 964938AA276CA661009C6708 /* Products */;
362 | projectDirPath = "";
363 | projectRoot = "";
364 | targets = (
365 | 964938A8276CA661009C6708 /* Movie */,
366 | );
367 | };
368 | /* End PBXProject section */
369 |
370 | /* Begin PBXResourcesBuildPhase section */
371 | 964938A7276CA661009C6708 /* Resources */ = {
372 | isa = PBXResourcesBuildPhase;
373 | buildActionMask = 2147483647;
374 | files = (
375 | 96290F0E276E8C7F00AFD992 /* .swiftlint.yml in Resources */,
376 | 964938C8276CAD60009C6708 /* Colors.xcassets in Resources */,
377 | 964938B9276CA666009C6708 /* LaunchScreen.storyboard in Resources */,
378 | 963EA3C82D32748300EDF8C7 /* movies.json in Resources */,
379 | 964938B6276CA666009C6708 /* Icons.xcassets in Resources */,
380 | 963541372770A7FC0036133D /* Assets.xcassets in Resources */,
381 | 964938CA276CAE61009C6708 /* swiftgen.yml in Resources */,
382 | );
383 | runOnlyForDeploymentPostprocessing = 0;
384 | };
385 | /* End PBXResourcesBuildPhase section */
386 |
387 | /* Begin PBXSourcesBuildPhase section */
388 | 964938A5276CA661009C6708 /* Sources */ = {
389 | isa = PBXSourcesBuildPhase;
390 | buildActionMask = 2147483647;
391 | files = (
392 | 964938C3276CA73D009C6708 /* ViewController + Extension.swift in Sources */,
393 | 9635412A276FCC370036133D /* CommentsCell.swift in Sources */,
394 | 964938D8276CBEA4009C6708 /* GeneratedColors.swift in Sources */,
395 | 964938D3276CB5F6009C6708 /* UITabBarController.swift in Sources */,
396 | 96290EF4276E06D500AFD992 /* MainScreenRouter.swift in Sources */,
397 | 964938C5276CA780009C6708 /* UINavigationController + Extension.swift in Sources */,
398 | 964938B1276CA661009C6708 /* MainScreenViewController.swift in Sources */,
399 | 964938AD276CA661009C6708 /* AppDelegate.swift in Sources */,
400 | 96290F1A276F69BC00AFD992 /* LableImage.swift in Sources */,
401 | 9605D4272B9504F80084D92F /* MainContentView.swift in Sources */,
402 | 96290F15276F397B00AFD992 /* NavigationBarBack.swift in Sources */,
403 | 964938D5276CB7D1009C6708 /* UIImage + Extension.swift in Sources */,
404 | 96290EF5276E06D500AFD992 /* ProResult.swift in Sources */,
405 | 9635412D276FDFEA0036133D /* SectionDecorationView.swift in Sources */,
406 | 96290EFF276E130100AFD992 /* MovieDTO.swift in Sources */,
407 | 96290F05276E5A7C00AFD992 /* NavigationBar.swift in Sources */,
408 | 96290EF6276E06D500AFD992 /* NetworkService.swift in Sources */,
409 | 9635412F276FE0AE0036133D /* CommentsViewFooter.swift in Sources */,
410 | 96290F17276F426C00AFD992 /* TopView.swift in Sources */,
411 | 964938D9276CBEA4009C6708 /* GeneratedIcons.swift in Sources */,
412 | 964938AF276CA661009C6708 /* SceneDelegate.swift in Sources */,
413 | 96290EE4276D19BA00AFD992 /* MainScreenViewModel.swift in Sources */,
414 | 96290EE0276D117700AFD992 /* UICollectionViewCell+Register+extension.swift in Sources */,
415 | 96290EF7276E06D500AFD992 /* BaseRouter.swift in Sources */,
416 | 96290EDE276D0F2E00AFD992 /* PhotoCell.swift in Sources */,
417 | 96290F09276E656F00AFD992 /* HTTPMethods.swift in Sources */,
418 | 96290F11276F384800AFD992 /* MovieDetailedScreenViewController.swift in Sources */,
419 | 96290EF8276E06D500AFD992 /* NetworkErrors.swift in Sources */,
420 | 96290EE2276D130A00AFD992 /* HeaderView.swift in Sources */,
421 | 96290F00276E130100AFD992 /* Comments.swift in Sources */,
422 | );
423 | runOnlyForDeploymentPostprocessing = 0;
424 | };
425 | /* End PBXSourcesBuildPhase section */
426 |
427 | /* Begin PBXVariantGroup section */
428 | 964938B7276CA666009C6708 /* LaunchScreen.storyboard */ = {
429 | isa = PBXVariantGroup;
430 | children = (
431 | 964938B8276CA666009C6708 /* Base */,
432 | );
433 | name = LaunchScreen.storyboard;
434 | sourceTree = "";
435 | };
436 | /* End PBXVariantGroup section */
437 |
438 | /* Begin XCBuildConfiguration section */
439 | 964938BB276CA666009C6708 /* Debug */ = {
440 | isa = XCBuildConfiguration;
441 | buildSettings = {
442 | ALWAYS_SEARCH_USER_PATHS = NO;
443 | CLANG_ANALYZER_NONNULL = YES;
444 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
445 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
446 | CLANG_CXX_LIBRARY = "libc++";
447 | CLANG_ENABLE_MODULES = YES;
448 | CLANG_ENABLE_OBJC_ARC = YES;
449 | CLANG_ENABLE_OBJC_WEAK = YES;
450 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
451 | CLANG_WARN_BOOL_CONVERSION = YES;
452 | CLANG_WARN_COMMA = YES;
453 | CLANG_WARN_CONSTANT_CONVERSION = YES;
454 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
455 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
456 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
457 | CLANG_WARN_EMPTY_BODY = YES;
458 | CLANG_WARN_ENUM_CONVERSION = YES;
459 | CLANG_WARN_INFINITE_RECURSION = YES;
460 | CLANG_WARN_INT_CONVERSION = YES;
461 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
462 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
463 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
464 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
465 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
466 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
467 | CLANG_WARN_STRICT_PROTOTYPES = YES;
468 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
469 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
470 | CLANG_WARN_UNREACHABLE_CODE = YES;
471 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
472 | COPY_PHASE_STRIP = NO;
473 | DEBUG_INFORMATION_FORMAT = dwarf;
474 | ENABLE_STRICT_OBJC_MSGSEND = YES;
475 | ENABLE_TESTABILITY = YES;
476 | GCC_C_LANGUAGE_STANDARD = gnu11;
477 | GCC_DYNAMIC_NO_PIC = NO;
478 | GCC_NO_COMMON_BLOCKS = YES;
479 | GCC_OPTIMIZATION_LEVEL = 0;
480 | GCC_PREPROCESSOR_DEFINITIONS = (
481 | "DEBUG=1",
482 | "$(inherited)",
483 | );
484 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
485 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
486 | GCC_WARN_UNDECLARED_SELECTOR = YES;
487 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
488 | GCC_WARN_UNUSED_FUNCTION = YES;
489 | GCC_WARN_UNUSED_VARIABLE = YES;
490 | IPHONEOS_DEPLOYMENT_TARGET = 13.2;
491 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
492 | MTL_FAST_MATH = YES;
493 | ONLY_ACTIVE_ARCH = YES;
494 | SDKROOT = iphoneos;
495 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
496 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
497 | };
498 | name = Debug;
499 | };
500 | 964938BC276CA666009C6708 /* Release */ = {
501 | isa = XCBuildConfiguration;
502 | buildSettings = {
503 | ALWAYS_SEARCH_USER_PATHS = NO;
504 | CLANG_ANALYZER_NONNULL = YES;
505 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
506 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++17";
507 | CLANG_CXX_LIBRARY = "libc++";
508 | CLANG_ENABLE_MODULES = YES;
509 | CLANG_ENABLE_OBJC_ARC = YES;
510 | CLANG_ENABLE_OBJC_WEAK = YES;
511 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
512 | CLANG_WARN_BOOL_CONVERSION = YES;
513 | CLANG_WARN_COMMA = YES;
514 | CLANG_WARN_CONSTANT_CONVERSION = YES;
515 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
516 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
517 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
518 | CLANG_WARN_EMPTY_BODY = YES;
519 | CLANG_WARN_ENUM_CONVERSION = YES;
520 | CLANG_WARN_INFINITE_RECURSION = YES;
521 | CLANG_WARN_INT_CONVERSION = YES;
522 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
523 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
524 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
525 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
526 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
527 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
528 | CLANG_WARN_STRICT_PROTOTYPES = YES;
529 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
530 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
531 | CLANG_WARN_UNREACHABLE_CODE = YES;
532 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
533 | COPY_PHASE_STRIP = NO;
534 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
535 | ENABLE_NS_ASSERTIONS = NO;
536 | ENABLE_STRICT_OBJC_MSGSEND = YES;
537 | GCC_C_LANGUAGE_STANDARD = gnu11;
538 | GCC_NO_COMMON_BLOCKS = YES;
539 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
540 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
541 | GCC_WARN_UNDECLARED_SELECTOR = YES;
542 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
543 | GCC_WARN_UNUSED_FUNCTION = YES;
544 | GCC_WARN_UNUSED_VARIABLE = YES;
545 | IPHONEOS_DEPLOYMENT_TARGET = 13.2;
546 | MTL_ENABLE_DEBUG_INFO = NO;
547 | MTL_FAST_MATH = YES;
548 | SDKROOT = iphoneos;
549 | SWIFT_COMPILATION_MODE = wholemodule;
550 | SWIFT_OPTIMIZATION_LEVEL = "-O";
551 | VALIDATE_PRODUCT = YES;
552 | };
553 | name = Release;
554 | };
555 | 964938BE276CA666009C6708 /* Debug */ = {
556 | isa = XCBuildConfiguration;
557 | buildSettings = {
558 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
559 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
560 | CODE_SIGN_STYLE = Automatic;
561 | CURRENT_PROJECT_VERSION = 1;
562 | DEVELOPMENT_TEAM = W652FWVRGR;
563 | GENERATE_INFOPLIST_FILE = YES;
564 | INFOPLIST_FILE = Movie/Info.plist;
565 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
566 | INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
567 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
568 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
569 | IPHONEOS_DEPLOYMENT_TARGET = 13.2;
570 | LD_RUNPATH_SEARCH_PATHS = (
571 | "$(inherited)",
572 | "@executable_path/Frameworks",
573 | );
574 | MARKETING_VERSION = 1.0;
575 | PRODUCT_BUNDLE_IDENTIFIER = com.SiuzannaKaragulova.Movie;
576 | PRODUCT_NAME = "$(TARGET_NAME)";
577 | SWIFT_EMIT_LOC_STRINGS = YES;
578 | SWIFT_VERSION = 5.0;
579 | TARGETED_DEVICE_FAMILY = "1,2";
580 | };
581 | name = Debug;
582 | };
583 | 964938BF276CA666009C6708 /* Release */ = {
584 | isa = XCBuildConfiguration;
585 | buildSettings = {
586 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
587 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
588 | CODE_SIGN_STYLE = Automatic;
589 | CURRENT_PROJECT_VERSION = 1;
590 | DEVELOPMENT_TEAM = W652FWVRGR;
591 | GENERATE_INFOPLIST_FILE = YES;
592 | INFOPLIST_FILE = Movie/Info.plist;
593 | INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
594 | INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
595 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
596 | INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
597 | IPHONEOS_DEPLOYMENT_TARGET = 13.2;
598 | LD_RUNPATH_SEARCH_PATHS = (
599 | "$(inherited)",
600 | "@executable_path/Frameworks",
601 | );
602 | MARKETING_VERSION = 1.0;
603 | PRODUCT_BUNDLE_IDENTIFIER = com.SiuzannaKaragulova.Movie;
604 | PRODUCT_NAME = "$(TARGET_NAME)";
605 | SWIFT_EMIT_LOC_STRINGS = YES;
606 | SWIFT_VERSION = 5.0;
607 | TARGETED_DEVICE_FAMILY = "1,2";
608 | };
609 | name = Release;
610 | };
611 | /* End XCBuildConfiguration section */
612 |
613 | /* Begin XCConfigurationList section */
614 | 964938A4276CA661009C6708 /* Build configuration list for PBXProject "Movie" */ = {
615 | isa = XCConfigurationList;
616 | buildConfigurations = (
617 | 964938BB276CA666009C6708 /* Debug */,
618 | 964938BC276CA666009C6708 /* Release */,
619 | );
620 | defaultConfigurationIsVisible = 0;
621 | defaultConfigurationName = Release;
622 | };
623 | 964938BD276CA666009C6708 /* Build configuration list for PBXNativeTarget "Movie" */ = {
624 | isa = XCConfigurationList;
625 | buildConfigurations = (
626 | 964938BE276CA666009C6708 /* Debug */,
627 | 964938BF276CA666009C6708 /* Release */,
628 | );
629 | defaultConfigurationIsVisible = 0;
630 | defaultConfigurationName = Release;
631 | };
632 | /* End XCConfigurationList section */
633 |
634 | /* Begin XCRemoteSwiftPackageReference section */
635 | 96AADD8E2B47481100D823D6 /* XCRemoteSwiftPackageReference "SnapKit" */ = {
636 | isa = XCRemoteSwiftPackageReference;
637 | repositoryURL = "https://github.com/SnapKit/SnapKit.git";
638 | requirement = {
639 | kind = upToNextMajorVersion;
640 | minimumVersion = 5.7.0;
641 | };
642 | };
643 | 96F59F5F2B41D1BA004C298A /* XCRemoteSwiftPackageReference "Kingfisher" */ = {
644 | isa = XCRemoteSwiftPackageReference;
645 | repositoryURL = "https://github.com/onevcat/Kingfisher.git";
646 | requirement = {
647 | kind = upToNextMajorVersion;
648 | minimumVersion = 7.10.1;
649 | };
650 | };
651 | /* End XCRemoteSwiftPackageReference section */
652 |
653 | /* Begin XCSwiftPackageProductDependency section */
654 | 96AADD912B47481100D823D6 /* SnapKit-Dynamic */ = {
655 | isa = XCSwiftPackageProductDependency;
656 | package = 96AADD8E2B47481100D823D6 /* XCRemoteSwiftPackageReference "SnapKit" */;
657 | productName = "SnapKit-Dynamic";
658 | };
659 | 96F59F602B41D1BA004C298A /* Kingfisher */ = {
660 | isa = XCSwiftPackageProductDependency;
661 | package = 96F59F5F2B41D1BA004C298A /* XCRemoteSwiftPackageReference "Kingfisher" */;
662 | productName = Kingfisher;
663 | };
664 | /* End XCSwiftPackageProductDependency section */
665 | };
666 | rootObject = 964938A1276CA661009C6708 /* Project object */;
667 | }
668 |
--------------------------------------------------------------------------------