Compare commits

...

12 Commits

Author SHA1 Message Date
eyupyasuntimur f3cfa9a622 HomeContracts updated 2023-01-06 21:22:22 +03:00
eyupyasuntimur f53d097d67 +++ 2023-01-06 19:52:22 +03:00
eyupyasuntimur 8054c4c707 tableView cell selection updated 2023-01-06 19:52:00 +03:00
eyupyasuntimur dced6087d7 VIPER part completed 2023-01-06 19:46:10 +03:00
eyupyasuntimur 479d89a47c HomeInteractor added 2023-01-06 10:01:44 +03:00
eyupyasuntimur c4b072dcf0 HomeView added 2023-01-06 09:54:23 +03:00
eyupyasuntimur 93fcb4f3a2 all targets main.storyboard deleted, VIPER empty files added respect to architecture 2023-01-05 21:16:45 +03:00
eyupyasuntimur 54aee8775c VIPER target created 2023-01-05 18:06:38 +03:00
eyupyasuntimur 439a7e2bde MVC infoPlist updated 2023-01-05 15:50:01 +03:00
eyupyasuntimur 6dc64129f2 MVVM part completed 2023-01-03 09:18:16 +03:00
eyupyasuntimur a8ec22590f BaseProtocols added for MVVM target 2023-01-02 17:07:33 +03:00
eyupyasuntimur 635b637ef9 MVC unitTests improving 2023-01-02 16:42:49 +03:00
77 changed files with 2820 additions and 279 deletions

View File

@ -0,0 +1,20 @@
//
// Array+Extension.swift
// iMoviesMVC
//
// Created by Eyüp Yasuntimur on 1.01.2023.
//
import Foundation
public extension Array {
struct IndexOutOfBoundsError: Error { }
func element(at index: Int) throws -> Element {
guard index >= 0 && index < self.count else {
throw IndexOutOfBoundsError()
}
return self[index]
}
}

View File

@ -0,0 +1,22 @@
//
// Optional+Extension.swift
// iMoviesMVC
//
// Created by Eyüp Yasuntimur on 1.01.2023.
//
import Foundation
public extension Optional {
struct FoundNilWhileUnwrappingError: Error { }
func unwrap() throws -> Wrapped {
switch self {
case .some(let wrapped):
return wrapped
case .none:
throw FoundNilWhileUnwrappingError()
}
}
}

View File

@ -0,0 +1,18 @@
//
// UICollectionView+Extension.swift
// iMovies
//
// Created by Eyüp Yasuntimur on 18.12.2022.
//
import UIKit
extension UICollectionView {
func registerCell<T: UICollectionViewCell>(type: T.Type) where T: Reuseable {
self.register(UINib(nibName: T.reuseIdentifier, bundle: nil), forCellWithReuseIdentifier: T.reuseIdentifier)
}
func dequeueReusableCell<T: UICollectionViewCell> (forIndexPath indexPath: IndexPath) -> T where T: Reuseable {
return dequeueReusableCell(withReuseIdentifier: T.reuseIdentifier, for: indexPath as IndexPath) as! T
}
}

View File

@ -0,0 +1,23 @@
//
// UIImageView+Extension.swift
// iMovies
//
// Created by Eyüp Yasuntimur on 2.01.2023.
//
import UIKit
import Kingfisher
// MARK: - Kingfiher
extension UIImageView {
func setKfImage(for urlString: String?) {
guard let urlString = urlString else {
return
}
let url = URL(string: urlString)
self.kf.indicatorType = .activity
self.kf.setImage(with: url, options: KingfisherManager.shared.defaultOptions)
}
}

View File

@ -0,0 +1,18 @@
//
// UITableView+Extension.swift
// iMovies
//
// Created by Eyüp Yasuntimur on 18.12.2022.
//
import UIKit
extension UITableView {
func registerCell<T: UITableViewCell>(type: T.Type) where T: Reuseable {
self.register(T.self, forCellReuseIdentifier: T.reuseIdentifier)
}
func dequeueReusableCell<T: UITableViewCell> (forIndexPath indexPath: IndexPath) -> T where T: Reuseable {
return dequeueReusableCell(withIdentifier: T.reuseIdentifier, for: indexPath as IndexPath) as! T
}
}

View File

@ -0,0 +1,22 @@
//
// UIViewController+Extension.swift
// iMoviesMVC
//
// Created by Eyüp Yasuntimur on 18.12.2022.
//
import Foundation
import UIKit
extension UIViewController {
func embedInNavigationController(
_ modalPresentationStyle: UIModalPresentationStyle = .fullScreen
) -> UINavigationController {
let navigationController = UINavigationController(rootViewController: self)
navigationController.modalPresentationStyle = modalPresentationStyle
return navigationController
}
}

View File

@ -0,0 +1,44 @@
//
// MoviePresentation.swift
// MovieBoxMVC
//
// Created by Ilter Cengiz on 18/11/18.
// Copyright © 2018 Late Night Muhabbetleri. All rights reserved.
//
import UIKit
import iMoviesAPI
typealias MoviePresentations = [MoviePresentation]
final class MoviePresentation: NSObject {
let title: String?
let summary: String?
let imageUrl: String?
init(
title: String?,
summary: String?,
imageUrl: String?
) {
self.title = title
self.summary = summary
self.imageUrl = imageUrl
super.init()
}
override func isEqual(_ object: Any?) -> Bool {
guard let other = object as? MoviePresentation else { return false }
return self.title == other.title && self.summary == other.summary
}
}
extension MoviePresentation {
convenience init(movie: Movie) {
self.init(title: movie.displayTitle,
summary: movie.summaryShort,
imageUrl: movie.multimedia?.src)
}
}

View File

@ -0,0 +1,19 @@
//
// Reuseable.swift
// iMovies
//
// Created by Eyüp Yasuntimur on 18.12.2022.
//
import Foundation
import UIKit
protocol Reuseable: AnyObject {
static var reuseIdentifier: String { get }
}
extension Reuseable {
static var reuseIdentifier: String {
return String(describing: self)
}
}

View File

@ -0,0 +1,72 @@
//
// ThemeManager.swift
// iMovies
//
// Created by Eyüp Yasuntimur on 18.12.2022.
//
#if canImport(UIKit)
import UIKit
public enum Theme: String {
case light, dark // , graphical
var barStyle: UIBarStyle {
.default
}
}
public struct ThemeManager {
public enum Font: String {
/// Avenir-Book
case Book = "Avenir-Book"
/// Avenir-Roman
case Roman = "Avenir-Roman"
/// Avenir-Light
case Light = "Avenir-Light"
/// Avenir-Medium
case Medium = "Avenir-Medium"
/// Avenir-Heavy
case Heavy = "Avenir-Heavy"
/// Avenir-Black
case Black = "Avenir-Black"
public enum Size: CGFloat {
/// 10pt
case xxsmall = 10
/// 12.4pt
case xsmall = 12.4
/// 13pt
case small = 13
/// 14pt
case smallmedium = 14
/// 15pt
case medium = 15
/// 18pt
case mediumlarge = 18
///20pt
case large = 20
/// 22pt
case larger = 22
/// 24pt
case xlarge = 24
/// 28pt
case xxlarge = 28
}
/// size as predifined size
public func font(size: Size) -> UIFont? {
let fontSize = size.rawValue
let fontName = self.rawValue
return UIFont(name: fontName, size: fontSize)
}
/// size as CGFloat
public func font(size: CGFloat) -> UIFont? {
let fontSize = size
let fontName = self.rawValue
return UIFont(name: fontName, size: fontSize)
}
}
}
#endif

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="20037" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="20037" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<device id="retina6_1" orientation="portrait" appearance="light"/>
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="20020"/>
@ -7,33 +7,33 @@
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Splash Controller-->
<scene sceneID="tne-QT-ifu">
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<objects>
<viewController storyboardIdentifier="SplashController" id="BYZ-38-t0r" customClass="SplashController" customModule="iMoviesMVC" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<rect key="frame" x="0.0" y="0.0" width="414" height="896"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="applelogo" catalog="system" translatesAutoresizingMaskIntoConstraints="NO" id="ge7-OE-eno">
<rect key="frame" x="40" y="403.5" width="334" height="88.5"/>
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleAspectFit" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="applelogo" catalog="system" translatesAutoresizingMaskIntoConstraints="NO" id="cWH-AK-2bM">
<rect key="frame" x="157" y="398.5" width="100" height="98.5"/>
<constraints>
<constraint firstAttribute="height" constant="90" id="XE6-sM-vQZ"/>
<constraint firstAttribute="height" constant="100" id="c4m-pu-aDN"/>
<constraint firstAttribute="width" constant="100" id="mkQ-bz-G7M"/>
</constraints>
</imageView>
</subviews>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
<color key="backgroundColor" white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<viewLayoutGuide key="safeArea" id="Bcu-3y-fUS"/>
<color key="backgroundColor" white="0.95615433673469385" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
<constraints>
<constraint firstItem="ge7-OE-eno" firstAttribute="leading" secondItem="6Tk-OE-BBY" secondAttribute="leading" constant="40" id="8in-U5-78v"/>
<constraint firstItem="6Tk-OE-BBY" firstAttribute="trailing" secondItem="ge7-OE-eno" secondAttribute="trailing" constant="40" id="aaX-mF-hX0"/>
<constraint firstItem="ge7-OE-eno" firstAttribute="centerY" secondItem="8bC-Xf-vdC" secondAttribute="centerY" id="lZp-th-Wgi"/>
<constraint firstItem="cWH-AK-2bM" firstAttribute="centerY" secondItem="Ze5-6b-2t3" secondAttribute="centerY" id="Map-pc-UHU"/>
<constraint firstItem="cWH-AK-2bM" firstAttribute="centerX" secondItem="Ze5-6b-2t3" secondAttribute="centerX" id="puy-o2-8Yu"/>
</constraints>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="3" y="54"/>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
<resources>

View File

@ -7,12 +7,10 @@
objects = {
/* Begin PBXBuildFile section */
5F002485294DC32900E2D56B /* SplashBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F002484294DC32900E2D56B /* SplashBuilder.swift */; };
5F002488294EFF2200E2D56B /* UIViewController+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F002487294EFF2200E2D56B /* UIViewController+Extension.swift */; };
5F0024A8294F332200E2D56B /* MoviePresentation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F002498294F332200E2D56B /* MoviePresentation.swift */; };
5F0024A9294F332200E2D56B /* MoviePresentation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F002498294F332200E2D56B /* MoviePresentation.swift */; };
5F0024AD294F386000E2D56B /* HomeCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F0024AC294F386000E2D56B /* HomeCell.swift */; };
5F0024AE294F386000E2D56B /* HomeCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F0024AC294F386000E2D56B /* HomeCell.swift */; };
5F0024B1294F38CE00E2D56B /* Reuseable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F0024B0294F38CE00E2D56B /* Reuseable.swift */; };
5F0024B2294F38CE00E2D56B /* Reuseable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F0024B0294F38CE00E2D56B /* Reuseable.swift */; };
5F0024B4294F395F00E2D56B /* UITableView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F0024B3294F395F00E2D56B /* UITableView+Extension.swift */; };
@ -22,30 +20,82 @@
5F0024BA294F3A3100E2D56B /* ThemeManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F0024B9294F3A3100E2D56B /* ThemeManager.swift */; };
5F0024BB294F3A3100E2D56B /* ThemeManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F0024B9294F3A3100E2D56B /* ThemeManager.swift */; };
5F071769293FA18600A54133 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F071768293FA18600A54133 /* AppDelegate.swift */; };
5F07176D293FA18600A54133 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F07176C293FA18600A54133 /* ViewController.swift */; };
5F071770293FA18600A54133 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5F07176E293FA18600A54133 /* Main.storyboard */; };
5F071772293FA18700A54133 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5F071771293FA18700A54133 /* Assets.xcassets */; };
5F071775293FA18700A54133 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5F071773293FA18700A54133 /* LaunchScreen.storyboard */; };
5F071780293FA18700A54133 /* iMoviesMVVMTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F07177F293FA18700A54133 /* iMoviesMVVMTests.swift */; };
5F168BC3296328FC0073BE71 /* UIViewController+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F002487294EFF2200E2D56B /* UIViewController+Extension.swift */; };
5F168BC4296328FF0073BE71 /* Array+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F23B3FD29619886008090DD /* Array+Extension.swift */; };
5F168BC5296329230073BE71 /* ViewProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F168BBB29631A9F0073BE71 /* ViewProtocol.swift */; };
5F168BC6296329270073BE71 /* ControllerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F168BBD29631B570073BE71 /* ControllerProtocol.swift */; };
5F168BC8296329900073BE71 /* HomeCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F168BC7296329900073BE71 /* HomeCell.swift */; };
5F168BCA29632A4F0073BE71 /* UIImageView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F168BC929632A4F0073BE71 /* UIImageView+Extension.swift */; };
5F168BCB29632A4F0073BE71 /* UIImageView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F168BC929632A4F0073BE71 /* UIImageView+Extension.swift */; };
5F168BCE29632BBC0073BE71 /* iMoviesAPI in Frameworks */ = {isa = PBXBuildFile; productRef = 5F168BCD29632BBC0073BE71 /* iMoviesAPI */; };
5F168BD129632BE70073BE71 /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = 5F168BD029632BE70073BE71 /* Kingfisher */; };
5F168BD429632EAF0073BE71 /* DetailBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F168BD329632EAF0073BE71 /* DetailBuilder.swift */; };
5F168BD529632EAF0073BE71 /* DetailBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F168BD329632EAF0073BE71 /* DetailBuilder.swift */; };
5F168BD729632EB80073BE71 /* DetailController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F168BD629632EB80073BE71 /* DetailController.swift */; };
5F168BD829632EB80073BE71 /* DetailController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F168BD629632EB80073BE71 /* DetailController.swift */; };
5F168BDA29632ED00073BE71 /* DetailViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F168BD929632ED00073BE71 /* DetailViewModel.swift */; };
5F168BDB29632ED00073BE71 /* DetailViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F168BD929632ED00073BE71 /* DetailViewModel.swift */; };
5F168BDD29632EDA0073BE71 /* DetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F168BDC29632EDA0073BE71 /* DetailView.swift */; };
5F168BDE29632EDA0073BE71 /* DetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F168BDC29632EDA0073BE71 /* DetailView.swift */; };
5F23B3E5295F1767008090DD /* iMoviesAPI in Frameworks */ = {isa = PBXBuildFile; productRef = 5F23B3E4295F1767008090DD /* iMoviesAPI */; };
5F23B3EA295F68A9008090DD /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = 5F23B3E9295F68A9008090DD /* Kingfisher */; };
5F23B3EE295F7689008090DD /* DetailView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F23B3ED295F7689008090DD /* DetailView.swift */; };
5F23B3F0295F7B38008090DD /* DetailController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F23B3EF295F7B38008090DD /* DetailController.swift */; };
5F23B3F429600471008090DD /* DetailBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F23B3F329600471008090DD /* DetailBuilder.swift */; };
5F23B3FA296197CD008090DD /* ResourceLoader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F23B3F9296197CD008090DD /* ResourceLoader.swift */; };
5F23B3FC29619821008090DD /* Optional+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F23B3FB29619821008090DD /* Optional+Extension.swift */; };
5F23B3FE29619886008090DD /* Array+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F23B3FD29619886008090DD /* Array+Extension.swift */; };
5F23B40029619CE7008090DD /* movie1.json in Sources */ = {isa = PBXBuildFile; fileRef = 5F23B3FF29619CE7008090DD /* movie1.json */; };
5F23B40229619F06008090DD /* movie2.json in Sources */ = {isa = PBXBuildFile; fileRef = 5F23B40129619F06008090DD /* movie2.json */; };
5F23B40429619F0C008090DD /* movie3.json in Sources */ = {isa = PBXBuildFile; fileRef = 5F23B40329619F0C008090DD /* movie3.json */; };
5F23B41029631510008090DD /* HomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F23B40F29631510008090DD /* HomeView.swift */; };
5F23B41229631519008090DD /* HomeController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F23B41129631519008090DD /* HomeController.swift */; };
5F23B41429631523008090DD /* HomeViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F23B41329631523008090DD /* HomeViewModel.swift */; };
5F23B4162963152C008090DD /* HomeBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F23B4152963152C008090DD /* HomeBuilder.swift */; };
5F4D7F7A296881C4007180AA /* DetailContracts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F4D7F79296881C4007180AA /* DetailContracts.swift */; };
5F9292972967001400B2CBC8 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F9292962967001400B2CBC8 /* AppDelegate.swift */; };
5F9292A02967001500B2CBC8 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5F92929F2967001500B2CBC8 /* Assets.xcassets */; };
5F9292AE2967001500B2CBC8 /* iMoviesVIPERTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F9292AD2967001500B2CBC8 /* iMoviesVIPERTests.swift */; };
5F9292C42967004A00B2CBC8 /* UIViewController+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F002487294EFF2200E2D56B /* UIViewController+Extension.swift */; };
5F9292C52967004D00B2CBC8 /* UIImageView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F168BC929632A4F0073BE71 /* UIImageView+Extension.swift */; };
5F9292C62967004F00B2CBC8 /* Array+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F23B3FD29619886008090DD /* Array+Extension.swift */; };
5F9292C72967005200B2CBC8 /* UITableView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F0024B3294F395F00E2D56B /* UITableView+Extension.swift */; };
5F9292C82967005400B2CBC8 /* UICollectionView+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F0024B6294F397E00E2D56B /* UICollectionView+Extension.swift */; };
5F9292C92967005800B2CBC8 /* Optional+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F23B3FB29619821008090DD /* Optional+Extension.swift */; };
5F9292CA2967005800B2CBC8 /* Optional+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F23B3FB29619821008090DD /* Optional+Extension.swift */; };
5F9292CB2967006C00B2CBC8 /* MoviePresentation.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F002498294F332200E2D56B /* MoviePresentation.swift */; };
5F9292CE296700BD00B2CBC8 /* AppContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F9292CD296700BD00B2CBC8 /* AppContainer.swift */; };
5F9292D0296700D000B2CBC8 /* AppRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F9292CF296700D000B2CBC8 /* AppRouter.swift */; };
5F9292D12967014A00B2CBC8 /* Reuseable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F0024B0294F38CE00E2D56B /* Reuseable.swift */; };
5F9292D22967015000B2CBC8 /* ThemeManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F0024B9294F3A3100E2D56B /* ThemeManager.swift */; };
5F9292D5296743F400B2CBC8 /* iMoviesAPI in Frameworks */ = {isa = PBXBuildFile; productRef = 5F9292D4296743F400B2CBC8 /* iMoviesAPI */; };
5F9292D82967440E00B2CBC8 /* Kingfisher in Frameworks */ = {isa = PBXBuildFile; productRef = 5F9292D72967440E00B2CBC8 /* Kingfisher */; };
5F9292DA2967474100B2CBC8 /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5F9292D92967474100B2CBC8 /* Launch Screen.storyboard */; };
5F9292DB2967477200B2CBC8 /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5F9292D92967474100B2CBC8 /* Launch Screen.storyboard */; };
5F9292DC2967477400B2CBC8 /* Launch Screen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5F9292D92967474100B2CBC8 /* Launch Screen.storyboard */; };
5F9292E2296749BA00B2CBC8 /* HomeBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F9292E1296749BA00B2CBC8 /* HomeBuilder.swift */; };
5F9292E4296749C200B2CBC8 /* HomeViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F9292E3296749C200B2CBC8 /* HomeViewController.swift */; };
5F9292E8296749E100B2CBC8 /* HomePresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F9292E7296749E100B2CBC8 /* HomePresenter.swift */; };
5F9292EA296749EA00B2CBC8 /* HomeInteractor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F9292E9296749EA00B2CBC8 /* HomeInteractor.swift */; };
5F9292EC296749FE00B2CBC8 /* HomeRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F9292EB296749FE00B2CBC8 /* HomeRouter.swift */; };
5F9292EE29674A0C00B2CBC8 /* DetailBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F9292ED29674A0C00B2CBC8 /* DetailBuilder.swift */; };
5F9292F029674A1600B2CBC8 /* DetailPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F9292EF29674A1600B2CBC8 /* DetailPresenter.swift */; };
5F9292F229674A2500B2CBC8 /* DetailViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F9292F129674A2500B2CBC8 /* DetailViewController.swift */; };
5F9292F52968038A00B2CBC8 /* HomeContracts.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F9292F42968038A00B2CBC8 /* HomeContracts.swift */; };
5F9292F72968048800B2CBC8 /* HomeCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F9292F62968048800B2CBC8 /* HomeCell.swift */; };
5FA4D079293FB304005D5501 /* AppRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F071760293F9F6E00A54133 /* AppRouter.swift */; };
5FA4D07A293FB304005D5501 /* AppContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5F07175E293F9F5300A54133 /* AppContainer.swift */; };
5FA4D082293FB349005D5501 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FA4D081293FB349005D5501 /* AppDelegate.swift */; };
5FA4D086293FB349005D5501 /* HomeController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FA4D085293FB349005D5501 /* HomeController.swift */; };
5FA4D089293FB349005D5501 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5FA4D087293FB349005D5501 /* Main.storyboard */; };
5FA4D08B293FB34A005D5501 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5FA4D08A293FB34A005D5501 /* Assets.xcassets */; };
5FA4D08E293FB34A005D5501 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 5FA4D08C293FB34A005D5501 /* LaunchScreen.storyboard */; };
5FA4D0B0293FB384005D5501 /* AppContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FA4D0AF293FB384005D5501 /* AppContainer.swift */; };
5FA4D0B2293FB398005D5501 /* AppRouter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FA4D0B1293FB398005D5501 /* AppRouter.swift */; };
5FA4D0BC293FB499005D5501 /* iMoviesMVCTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FA4D0BB293FB499005D5501 /* iMoviesMVCTests.swift */; };
5FE686ED294DB17300318A15 /* HomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FE686EC294DB17300318A15 /* HomeView.swift */; };
5FE686F0294DB1DC00318A15 /* ViewProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FE686EF294DB1DC00318A15 /* ViewProtocol.swift */; };
5FE686F4294DBD1F00318A15 /* ControllerProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FE686F3294DBD1F00318A15 /* ControllerProtocol.swift */; };
5FE686F6294DBE7A00318A15 /* SplashController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FE686F5294DBE7A00318A15 /* SplashController.swift */; };
5FE686F0294DB1DC00318A15 /* BaseAppView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FE686EF294DB1DC00318A15 /* BaseAppView.swift */; };
5FE686F4294DBD1F00318A15 /* BaseAppController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FE686F3294DBD1F00318A15 /* BaseAppController.swift */; };
5FE686F8294DC07900318A15 /* HomeBuilder.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5FE686F7294DC07900318A15 /* HomeBuilder.swift */; };
/* End PBXBuildFile section */
@ -57,6 +107,13 @@
remoteGlobalIDString = 5F071765293FA18600A54133;
remoteInfo = iMoviesMVVM;
};
5F9292AA2967001500B2CBC8 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 5F69D2C6293CE36100768933 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 5F9292932967001400B2CBC8;
remoteInfo = iMoviesVIPER;
};
5FA4D095293FB34A005D5501 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 5F69D2C6293CE36100768933 /* Project object */;
@ -90,7 +147,6 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
5F002484294DC32900E2D56B /* SplashBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplashBuilder.swift; sourceTree = "<group>"; };
5F002487294EFF2200E2D56B /* UIViewController+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController+Extension.swift"; sourceTree = "<group>"; };
5F002498294F332200E2D56B /* MoviePresentation.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MoviePresentation.swift; sourceTree = "<group>"; };
5F0024AC294F386000E2D56B /* HomeCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeCell.swift; sourceTree = "<group>"; };
@ -102,31 +158,63 @@
5F071760293F9F6E00A54133 /* AppRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppRouter.swift; sourceTree = "<group>"; };
5F071766293FA18600A54133 /* iMoviesMVVM.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = iMoviesMVVM.app; sourceTree = BUILT_PRODUCTS_DIR; };
5F071768293FA18600A54133 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
5F07176C293FA18600A54133 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = "<group>"; };
5F07176F293FA18600A54133 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
5F071771293FA18700A54133 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
5F071774293FA18700A54133 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
5F071776293FA18700A54133 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
5F07177B293FA18700A54133 /* iMoviesMVVMTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = iMoviesMVVMTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
5F07177F293FA18700A54133 /* iMoviesMVVMTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iMoviesMVVMTests.swift; sourceTree = "<group>"; };
5F168BBB29631A9F0073BE71 /* ViewProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewProtocol.swift; sourceTree = "<group>"; };
5F168BBD29631B570073BE71 /* ControllerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ControllerProtocol.swift; sourceTree = "<group>"; };
5F168BC7296329900073BE71 /* HomeCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeCell.swift; sourceTree = "<group>"; };
5F168BC929632A4F0073BE71 /* UIImageView+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImageView+Extension.swift"; sourceTree = "<group>"; };
5F168BD329632EAF0073BE71 /* DetailBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailBuilder.swift; sourceTree = "<group>"; };
5F168BD629632EB80073BE71 /* DetailController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailController.swift; sourceTree = "<group>"; };
5F168BD929632ED00073BE71 /* DetailViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailViewModel.swift; sourceTree = "<group>"; };
5F168BDC29632EDA0073BE71 /* DetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailView.swift; sourceTree = "<group>"; };
5F23B3ED295F7689008090DD /* DetailView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailView.swift; sourceTree = "<group>"; };
5F23B3EF295F7B38008090DD /* DetailController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailController.swift; sourceTree = "<group>"; };
5F23B3F329600471008090DD /* DetailBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailBuilder.swift; sourceTree = "<group>"; };
5F23B3F9296197CD008090DD /* ResourceLoader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ResourceLoader.swift; sourceTree = "<group>"; };
5F23B3FB29619821008090DD /* Optional+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Optional+Extension.swift"; sourceTree = "<group>"; };
5F23B3FD29619886008090DD /* Array+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+Extension.swift"; sourceTree = "<group>"; };
5F23B3FF29619CE7008090DD /* movie1.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = movie1.json; sourceTree = "<group>"; };
5F23B40129619F06008090DD /* movie2.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = movie2.json; sourceTree = "<group>"; };
5F23B40329619F0C008090DD /* movie3.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = movie3.json; sourceTree = "<group>"; };
5F23B40F29631510008090DD /* HomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeView.swift; sourceTree = "<group>"; };
5F23B41129631519008090DD /* HomeController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeController.swift; sourceTree = "<group>"; };
5F23B41329631523008090DD /* HomeViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeViewModel.swift; sourceTree = "<group>"; };
5F23B4152963152C008090DD /* HomeBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeBuilder.swift; sourceTree = "<group>"; };
5F4D7F79296881C4007180AA /* DetailContracts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailContracts.swift; sourceTree = "<group>"; };
5F9292942967001400B2CBC8 /* iMoviesVIPER.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = iMoviesVIPER.app; sourceTree = BUILT_PRODUCTS_DIR; };
5F9292962967001400B2CBC8 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
5F92929F2967001500B2CBC8 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
5F9292A42967001500B2CBC8 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
5F9292A92967001500B2CBC8 /* iMoviesVIPERTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = iMoviesVIPERTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
5F9292AD2967001500B2CBC8 /* iMoviesVIPERTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iMoviesVIPERTests.swift; sourceTree = "<group>"; };
5F9292CD296700BD00B2CBC8 /* AppContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppContainer.swift; sourceTree = "<group>"; };
5F9292CF296700D000B2CBC8 /* AppRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppRouter.swift; sourceTree = "<group>"; };
5F9292D92967474100B2CBC8 /* Launch Screen.storyboard */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; path = "Launch Screen.storyboard"; sourceTree = "<group>"; };
5F9292E1296749BA00B2CBC8 /* HomeBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeBuilder.swift; sourceTree = "<group>"; };
5F9292E3296749C200B2CBC8 /* HomeViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeViewController.swift; sourceTree = "<group>"; };
5F9292E7296749E100B2CBC8 /* HomePresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomePresenter.swift; sourceTree = "<group>"; };
5F9292E9296749EA00B2CBC8 /* HomeInteractor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeInteractor.swift; sourceTree = "<group>"; };
5F9292EB296749FE00B2CBC8 /* HomeRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeRouter.swift; sourceTree = "<group>"; };
5F9292ED29674A0C00B2CBC8 /* DetailBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailBuilder.swift; sourceTree = "<group>"; };
5F9292EF29674A1600B2CBC8 /* DetailPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailPresenter.swift; sourceTree = "<group>"; };
5F9292F129674A2500B2CBC8 /* DetailViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetailViewController.swift; sourceTree = "<group>"; };
5F9292F42968038A00B2CBC8 /* HomeContracts.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeContracts.swift; sourceTree = "<group>"; };
5F9292F62968048800B2CBC8 /* HomeCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeCell.swift; sourceTree = "<group>"; };
5FA4D07F293FB349005D5501 /* iMoviesMVC.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = iMoviesMVC.app; sourceTree = BUILT_PRODUCTS_DIR; };
5FA4D081293FB349005D5501 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; };
5FA4D085293FB349005D5501 /* HomeController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeController.swift; sourceTree = "<group>"; };
5FA4D088293FB349005D5501 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = "<group>"; };
5FA4D08A293FB34A005D5501 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
5FA4D08D293FB34A005D5501 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
5FA4D08F293FB34A005D5501 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
5FA4D094293FB34A005D5501 /* iMoviesMVCTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = iMoviesMVCTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
5FA4D0AF293FB384005D5501 /* AppContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppContainer.swift; sourceTree = "<group>"; };
5FA4D0B1293FB398005D5501 /* AppRouter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppRouter.swift; sourceTree = "<group>"; };
5FA4D0BB293FB499005D5501 /* iMoviesMVCTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = iMoviesMVCTests.swift; sourceTree = "<group>"; };
5FE686EC294DB17300318A15 /* HomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeView.swift; sourceTree = "<group>"; };
5FE686EF294DB1DC00318A15 /* ViewProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewProtocol.swift; sourceTree = "<group>"; };
5FE686F3294DBD1F00318A15 /* ControllerProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ControllerProtocol.swift; sourceTree = "<group>"; };
5FE686F5294DBE7A00318A15 /* SplashController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SplashController.swift; sourceTree = "<group>"; };
5FE686EF294DB1DC00318A15 /* BaseAppView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseAppView.swift; sourceTree = "<group>"; };
5FE686F3294DBD1F00318A15 /* BaseAppController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseAppController.swift; sourceTree = "<group>"; };
5FE686F7294DC07900318A15 /* HomeBuilder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeBuilder.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
@ -135,6 +223,8 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
5F168BD129632BE70073BE71 /* Kingfisher in Frameworks */,
5F168BCE29632BBC0073BE71 /* iMoviesAPI in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -145,6 +235,22 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
5F9292912967001400B2CBC8 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
5F9292D82967440E00B2CBC8 /* Kingfisher in Frameworks */,
5F9292D5296743F400B2CBC8 /* iMoviesAPI in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
5F9292A62967001500B2CBC8 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
5FA4D07C293FB349005D5501 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
@ -168,8 +274,11 @@
isa = PBXGroup;
children = (
5F002487294EFF2200E2D56B /* UIViewController+Extension.swift */,
5F168BC929632A4F0073BE71 /* UIImageView+Extension.swift */,
5F23B3FD29619886008090DD /* Array+Extension.swift */,
5F0024B3294F395F00E2D56B /* UITableView+Extension.swift */,
5F0024B6294F397E00E2D56B /* UICollectionView+Extension.swift */,
5F23B3FB29619821008090DD /* Optional+Extension.swift */,
);
path = Extensions;
sourceTree = "<group>";
@ -189,8 +298,6 @@
5FA4D0C0293FB57D005D5501 /* App */,
5FA4D0C1293FB58D005D5501 /* Scenes */,
5FA4D0C2293FB599005D5501 /* Resources */,
5F07176E293FA18600A54133 /* Main.storyboard */,
5F071773293FA18700A54133 /* LaunchScreen.storyboard */,
5F071776293FA18700A54133 /* Info.plist */,
);
path = iMoviesMVVM;
@ -204,12 +311,24 @@
path = iMoviesMVVMTests;
sourceTree = "<group>";
};
5F23B3EB295F6F0E008090DD /* Presentations */ = {
5F168BBA29631A600073BE71 /* BaseProtocols */ = {
isa = PBXGroup;
children = (
5F002498294F332200E2D56B /* MoviePresentation.swift */,
5F168BBB29631A9F0073BE71 /* ViewProtocol.swift */,
5F168BBD29631B570073BE71 /* ControllerProtocol.swift */,
);
path = Presentations;
path = BaseProtocols;
sourceTree = "<group>";
};
5F168BD229632E9F0073BE71 /* Detail */ = {
isa = PBXGroup;
children = (
5F168BD329632EAF0073BE71 /* DetailBuilder.swift */,
5F168BD629632EB80073BE71 /* DetailController.swift */,
5F168BD929632ED00073BE71 /* DetailViewModel.swift */,
5F168BDC29632EDA0073BE71 /* DetailView.swift */,
);
path = Detail;
sourceTree = "<group>";
};
5F23B3EC295F766E008090DD /* Detail */ = {
@ -222,13 +341,58 @@
path = Detail;
sourceTree = "<group>";
};
5F23B3F8296197C1008090DD /* Resources */ = {
isa = PBXGroup;
children = (
5F23B3F9296197CD008090DD /* ResourceLoader.swift */,
5F23B3FF29619CE7008090DD /* movie1.json */,
5F23B40129619F06008090DD /* movie2.json */,
5F23B40329619F0C008090DD /* movie3.json */,
);
path = Resources;
sourceTree = "<group>";
};
5F23B405296313E7008090DD /* Presentations */ = {
isa = PBXGroup;
children = (
5F002498294F332200E2D56B /* MoviePresentation.swift */,
);
path = Presentations;
sourceTree = "<group>";
};
5F23B4082963147E008090DD /* Commons */ = {
isa = PBXGroup;
children = (
5F002486294EFF0D00E2D56B /* Extensions */,
5F0024AF294F38BD00E2D56B /* Utils */,
5F23B405296313E7008090DD /* Presentations */,
);
path = Commons;
sourceTree = "<group>";
};
5F23B40E29631501008090DD /* Home */ = {
isa = PBXGroup;
children = (
5F23B4152963152C008090DD /* HomeBuilder.swift */,
5F23B40F29631510008090DD /* HomeView.swift */,
5F23B41129631519008090DD /* HomeController.swift */,
5F23B41329631523008090DD /* HomeViewModel.swift */,
5F168BC7296329900073BE71 /* HomeCell.swift */,
);
path = Home;
sourceTree = "<group>";
};
5F69D2C5293CE36100768933 = {
isa = PBXGroup;
children = (
5F9292D92967474100B2CBC8 /* Launch Screen.storyboard */,
5F23B4082963147E008090DD /* Commons */,
5FA4D080293FB349005D5501 /* iMoviesMVC */,
5FA4D097293FB34A005D5501 /* iMoviesMVCTests */,
5F071767293FA18600A54133 /* iMoviesMVVM */,
5F07177E293FA18700A54133 /* iMoviesMVVMTests */,
5F9292952967001400B2CBC8 /* iMoviesVIPER */,
5F9292AC2967001500B2CBC8 /* iMoviesVIPERTests */,
5F69D2CF293CE36100768933 /* Products */,
5F69D304293DAE9000768933 /* Frameworks */,
);
@ -241,6 +405,8 @@
5F07177B293FA18700A54133 /* iMoviesMVVMTests.xctest */,
5FA4D07F293FB349005D5501 /* iMoviesMVC.app */,
5FA4D094293FB34A005D5501 /* iMoviesMVCTests.xctest */,
5F9292942967001400B2CBC8 /* iMoviesVIPER.app */,
5F9292A92967001500B2CBC8 /* iMoviesVIPERTests.xctest */,
);
name = Products;
sourceTree = SOURCE_ROOT;
@ -252,17 +418,83 @@
name = Frameworks;
sourceTree = "<group>";
};
5F9292952967001400B2CBC8 /* iMoviesVIPER */ = {
isa = PBXGroup;
children = (
5F9292CC2967008A00B2CBC8 /* App */,
5F9292DD2967498D00B2CBC8 /* Scenes */,
5F9292F329674A4800B2CBC8 /* Resources */,
5F9292A42967001500B2CBC8 /* Info.plist */,
);
path = iMoviesVIPER;
sourceTree = "<group>";
};
5F9292AC2967001500B2CBC8 /* iMoviesVIPERTests */ = {
isa = PBXGroup;
children = (
5F9292AD2967001500B2CBC8 /* iMoviesVIPERTests.swift */,
);
path = iMoviesVIPERTests;
sourceTree = "<group>";
};
5F9292CC2967008A00B2CBC8 /* App */ = {
isa = PBXGroup;
children = (
5F9292962967001400B2CBC8 /* AppDelegate.swift */,
5F9292CD296700BD00B2CBC8 /* AppContainer.swift */,
5F9292CF296700D000B2CBC8 /* AppRouter.swift */,
);
path = App;
sourceTree = "<group>";
};
5F9292DD2967498D00B2CBC8 /* Scenes */ = {
isa = PBXGroup;
children = (
5F9292DE2967499A00B2CBC8 /* Home */,
5F9292E0296749A900B2CBC8 /* Detail */,
);
path = Scenes;
sourceTree = "<group>";
};
5F9292DE2967499A00B2CBC8 /* Home */ = {
isa = PBXGroup;
children = (
5F9292E1296749BA00B2CBC8 /* HomeBuilder.swift */,
5F9292EB296749FE00B2CBC8 /* HomeRouter.swift */,
5F9292E3296749C200B2CBC8 /* HomeViewController.swift */,
5F9292E7296749E100B2CBC8 /* HomePresenter.swift */,
5F9292E9296749EA00B2CBC8 /* HomeInteractor.swift */,
5F9292F42968038A00B2CBC8 /* HomeContracts.swift */,
5F9292F62968048800B2CBC8 /* HomeCell.swift */,
);
path = Home;
sourceTree = "<group>";
};
5F9292E0296749A900B2CBC8 /* Detail */ = {
isa = PBXGroup;
children = (
5F9292ED29674A0C00B2CBC8 /* DetailBuilder.swift */,
5F9292F129674A2500B2CBC8 /* DetailViewController.swift */,
5F9292EF29674A1600B2CBC8 /* DetailPresenter.swift */,
5F4D7F79296881C4007180AA /* DetailContracts.swift */,
);
path = Detail;
sourceTree = "<group>";
};
5F9292F329674A4800B2CBC8 /* Resources */ = {
isa = PBXGroup;
children = (
5F92929F2967001500B2CBC8 /* Assets.xcassets */,
);
path = Resources;
sourceTree = "<group>";
};
5FA4D080293FB349005D5501 /* iMoviesMVC */ = {
isa = PBXGroup;
children = (
5F23B3EB295F6F0E008090DD /* Presentations */,
5F0024AF294F38BD00E2D56B /* Utils */,
5F002486294EFF0D00E2D56B /* Extensions */,
5FA4D0BD293FB4BD005D5501 /* App */,
5FA4D0BE293FB526005D5501 /* Scenes */,
5FA4D0BF293FB541005D5501 /* Resources */,
5FA4D087293FB349005D5501 /* Main.storyboard */,
5FA4D08C293FB34A005D5501 /* LaunchScreen.storyboard */,
5FA4D08F293FB34A005D5501 /* Info.plist */,
);
path = iMoviesMVC;
@ -271,6 +503,7 @@
5FA4D097293FB34A005D5501 /* iMoviesMVCTests */ = {
isa = PBXGroup;
children = (
5F23B3F8296197C1008090DD /* Resources */,
5FA4D0BB293FB499005D5501 /* iMoviesMVCTests.swift */,
);
path = iMoviesMVCTests;
@ -289,8 +522,7 @@
5FA4D0BE293FB526005D5501 /* Scenes */ = {
isa = PBXGroup;
children = (
5FE686EE294DB1C900318A15 /* Base */,
5FE686F1294DBCB200318A15 /* Splash */,
5FE686EE294DB1C900318A15 /* BaseProtocols */,
5FE686EB294DB16200318A15 /* Home */,
5F23B3EC295F766E008090DD /* Detail */,
);
@ -318,7 +550,9 @@
5FA4D0C1293FB58D005D5501 /* Scenes */ = {
isa = PBXGroup;
children = (
5F07176C293FA18600A54133 /* ViewController.swift */,
5F168BBA29631A600073BE71 /* BaseProtocols */,
5F23B40E29631501008090DD /* Home */,
5F168BD229632E9F0073BE71 /* Detail */,
);
path = Scenes;
sourceTree = "<group>";
@ -342,22 +576,13 @@
path = Home;
sourceTree = "<group>";
};
5FE686EE294DB1C900318A15 /* Base */ = {
5FE686EE294DB1C900318A15 /* BaseProtocols */ = {
isa = PBXGroup;
children = (
5FE686EF294DB1DC00318A15 /* ViewProtocol.swift */,
5FE686F3294DBD1F00318A15 /* ControllerProtocol.swift */,
5FE686EF294DB1DC00318A15 /* BaseAppView.swift */,
5FE686F3294DBD1F00318A15 /* BaseAppController.swift */,
);
path = Base;
sourceTree = "<group>";
};
5FE686F1294DBCB200318A15 /* Splash */ = {
isa = PBXGroup;
children = (
5F002484294DC32900E2D56B /* SplashBuilder.swift */,
5FE686F5294DBE7A00318A15 /* SplashController.swift */,
);
path = Splash;
path = BaseProtocols;
sourceTree = "<group>";
};
/* End PBXGroup section */
@ -377,6 +602,10 @@
dependencies = (
);
name = iMoviesMVVM;
packageProductDependencies = (
5F168BCD29632BBC0073BE71 /* iMoviesAPI */,
5F168BD029632BE70073BE71 /* Kingfisher */,
);
productName = iMoviesMVVM;
productReference = 5F071766293FA18600A54133 /* iMoviesMVVM.app */;
productType = "com.apple.product-type.application";
@ -399,6 +628,45 @@
productReference = 5F07177B293FA18700A54133 /* iMoviesMVVMTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
5F9292932967001400B2CBC8 /* iMoviesVIPER */ = {
isa = PBXNativeTarget;
buildConfigurationList = 5F9292BB2967001500B2CBC8 /* Build configuration list for PBXNativeTarget "iMoviesVIPER" */;
buildPhases = (
5F9292902967001400B2CBC8 /* Sources */,
5F9292912967001400B2CBC8 /* Frameworks */,
5F9292922967001400B2CBC8 /* Resources */,
);
buildRules = (
);
dependencies = (
);
name = iMoviesVIPER;
packageProductDependencies = (
5F9292D4296743F400B2CBC8 /* iMoviesAPI */,
5F9292D72967440E00B2CBC8 /* Kingfisher */,
);
productName = iMoviesVIPER;
productReference = 5F9292942967001400B2CBC8 /* iMoviesVIPER.app */;
productType = "com.apple.product-type.application";
};
5F9292A82967001500B2CBC8 /* iMoviesVIPERTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 5F9292BE2967001500B2CBC8 /* Build configuration list for PBXNativeTarget "iMoviesVIPERTests" */;
buildPhases = (
5F9292A52967001500B2CBC8 /* Sources */,
5F9292A62967001500B2CBC8 /* Frameworks */,
5F9292A72967001500B2CBC8 /* Resources */,
);
buildRules = (
);
dependencies = (
5F9292AB2967001500B2CBC8 /* PBXTargetDependency */,
);
name = iMoviesVIPERTests;
productName = iMoviesVIPERTests;
productReference = 5F9292A92967001500B2CBC8 /* iMoviesVIPERTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
5FA4D07E293FB349005D5501 /* iMoviesMVC */ = {
isa = PBXNativeTarget;
buildConfigurationList = 5FA4D0A6293FB34B005D5501 /* Build configuration list for PBXNativeTarget "iMoviesMVC" */;
@ -456,6 +724,13 @@
CreatedOnToolsVersion = 13.4.1;
TestTargetID = 5F071765293FA18600A54133;
};
5F9292932967001400B2CBC8 = {
CreatedOnToolsVersion = 13.4.1;
};
5F9292A82967001500B2CBC8 = {
CreatedOnToolsVersion = 13.4.1;
TestTargetID = 5F9292932967001400B2CBC8;
};
5FA4D07E293FB349005D5501 = {
CreatedOnToolsVersion = 13.4.1;
};
@ -478,6 +753,10 @@
packageReferences = (
5F23B3E3295F1767008090DD /* XCRemoteSwiftPackageReference "iMoviesAPI" */,
5F23B3E8295F68A9008090DD /* XCRemoteSwiftPackageReference "Kingfisher" */,
5F168BCC29632BBC0073BE71 /* XCRemoteSwiftPackageReference "iMoviesAPI" */,
5F168BCF29632BE70073BE71 /* XCRemoteSwiftPackageReference "Kingfisher" */,
5F9292D3296743F400B2CBC8 /* XCRemoteSwiftPackageReference "iMoviesAPI" */,
5F9292D62967440E00B2CBC8 /* XCRemoteSwiftPackageReference "Kingfisher" */,
);
productRefGroup = 5F69D2CF293CE36100768933 /* Products */;
projectDirPath = "";
@ -487,6 +766,8 @@
5FA4D093293FB34A005D5501 /* iMoviesMVCTests */,
5F071765293FA18600A54133 /* iMoviesMVVM */,
5F07177A293FA18700A54133 /* iMoviesMVVMTests */,
5F9292932967001400B2CBC8 /* iMoviesVIPER */,
5F9292A82967001500B2CBC8 /* iMoviesVIPERTests */,
);
};
/* End PBXProject section */
@ -496,9 +777,8 @@
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
5F071775293FA18700A54133 /* LaunchScreen.storyboard in Resources */,
5F9292DA2967474100B2CBC8 /* Launch Screen.storyboard in Resources */,
5F071772293FA18700A54133 /* Assets.xcassets in Resources */,
5F071770293FA18600A54133 /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -509,13 +789,28 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
5F9292922967001400B2CBC8 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
5F9292DC2967477400B2CBC8 /* Launch Screen.storyboard in Resources */,
5F9292A02967001500B2CBC8 /* Assets.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
5F9292A72967001500B2CBC8 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
5FA4D07D293FB349005D5501 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
5FA4D08E293FB34A005D5501 /* LaunchScreen.storyboard in Resources */,
5F9292DB2967477200B2CBC8 /* Launch Screen.storyboard in Resources */,
5FA4D08B293FB34A005D5501 /* Assets.xcassets in Resources */,
5FA4D089293FB349005D5501 /* Main.storyboard in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@ -534,13 +829,26 @@
buildActionMask = 2147483647;
files = (
5F0024A9294F332200E2D56B /* MoviePresentation.swift in Sources */,
5F168BDE29632EDA0073BE71 /* DetailView.swift in Sources */,
5FA4D079293FB304005D5501 /* AppRouter.swift in Sources */,
5F07176D293FA18600A54133 /* ViewController.swift in Sources */,
5F0024AE294F386000E2D56B /* HomeCell.swift in Sources */,
5F168BD829632EB80073BE71 /* DetailController.swift in Sources */,
5F168BC8296329900073BE71 /* HomeCell.swift in Sources */,
5F0024B2294F38CE00E2D56B /* Reuseable.swift in Sources */,
5F168BC3296328FC0073BE71 /* UIViewController+Extension.swift in Sources */,
5F0024B8294F397E00E2D56B /* UICollectionView+Extension.swift in Sources */,
5F23B41229631519008090DD /* HomeController.swift in Sources */,
5F168BC5296329230073BE71 /* ViewProtocol.swift in Sources */,
5F168BC4296328FF0073BE71 /* Array+Extension.swift in Sources */,
5F168BDB29632ED00073BE71 /* DetailViewModel.swift in Sources */,
5F168BCB29632A4F0073BE71 /* UIImageView+Extension.swift in Sources */,
5FA4D07A293FB304005D5501 /* AppContainer.swift in Sources */,
5F23B41429631523008090DD /* HomeViewModel.swift in Sources */,
5F168BD529632EAF0073BE71 /* DetailBuilder.swift in Sources */,
5F0024BB294F3A3100E2D56B /* ThemeManager.swift in Sources */,
5F168BC6296329270073BE71 /* ControllerProtocol.swift in Sources */,
5F23B41029631510008090DD /* HomeView.swift in Sources */,
5F23B4162963152C008090DD /* HomeBuilder.swift in Sources */,
5F9292C92967005800B2CBC8 /* Optional+Extension.swift in Sources */,
5F071769293FA18600A54133 /* AppDelegate.swift in Sources */,
5F0024B5294F395F00E2D56B /* UITableView+Extension.swift in Sources */,
);
@ -554,28 +862,71 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
5F9292902967001400B2CBC8 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
5F9292CB2967006C00B2CBC8 /* MoviePresentation.swift in Sources */,
5F9292E4296749C200B2CBC8 /* HomeViewController.swift in Sources */,
5F9292E2296749BA00B2CBC8 /* HomeBuilder.swift in Sources */,
5F9292EA296749EA00B2CBC8 /* HomeInteractor.swift in Sources */,
5F9292E8296749E100B2CBC8 /* HomePresenter.swift in Sources */,
5F9292C82967005400B2CBC8 /* UICollectionView+Extension.swift in Sources */,
5F9292C52967004D00B2CBC8 /* UIImageView+Extension.swift in Sources */,
5F9292C42967004A00B2CBC8 /* UIViewController+Extension.swift in Sources */,
5F9292EC296749FE00B2CBC8 /* HomeRouter.swift in Sources */,
5F9292F52968038A00B2CBC8 /* HomeContracts.swift in Sources */,
5F9292C72967005200B2CBC8 /* UITableView+Extension.swift in Sources */,
5F9292F029674A1600B2CBC8 /* DetailPresenter.swift in Sources */,
5F9292CA2967005800B2CBC8 /* Optional+Extension.swift in Sources */,
5F9292D22967015000B2CBC8 /* ThemeManager.swift in Sources */,
5F9292F72968048800B2CBC8 /* HomeCell.swift in Sources */,
5F9292F229674A2500B2CBC8 /* DetailViewController.swift in Sources */,
5F9292D12967014A00B2CBC8 /* Reuseable.swift in Sources */,
5F9292D0296700D000B2CBC8 /* AppRouter.swift in Sources */,
5F9292EE29674A0C00B2CBC8 /* DetailBuilder.swift in Sources */,
5F4D7F7A296881C4007180AA /* DetailContracts.swift in Sources */,
5F9292CE296700BD00B2CBC8 /* AppContainer.swift in Sources */,
5F9292C62967004F00B2CBC8 /* Array+Extension.swift in Sources */,
5F9292972967001400B2CBC8 /* AppDelegate.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
5F9292A52967001500B2CBC8 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
5F9292AE2967001500B2CBC8 /* iMoviesVIPERTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
5FA4D07B293FB349005D5501 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
5FE686F6294DBE7A00318A15 /* SplashController.swift in Sources */,
5F23B3EE295F7689008090DD /* DetailView.swift in Sources */,
5FA4D086293FB349005D5501 /* HomeController.swift in Sources */,
5F23B3FE29619886008090DD /* Array+Extension.swift in Sources */,
5F168BCA29632A4F0073BE71 /* UIImageView+Extension.swift in Sources */,
5F002488294EFF2200E2D56B /* UIViewController+Extension.swift in Sources */,
5FE686F4294DBD1F00318A15 /* ControllerProtocol.swift in Sources */,
5F23B3FC29619821008090DD /* Optional+Extension.swift in Sources */,
5FE686F4294DBD1F00318A15 /* BaseAppController.swift in Sources */,
5F168BD429632EAF0073BE71 /* DetailBuilder.swift in Sources */,
5F168BDA29632ED00073BE71 /* DetailViewModel.swift in Sources */,
5F0024AD294F386000E2D56B /* HomeCell.swift in Sources */,
5F0024B4294F395F00E2D56B /* UITableView+Extension.swift in Sources */,
5F23B3F0295F7B38008090DD /* DetailController.swift in Sources */,
5FA4D0B0293FB384005D5501 /* AppContainer.swift in Sources */,
5FE686F0294DB1DC00318A15 /* ViewProtocol.swift in Sources */,
5FE686F0294DB1DC00318A15 /* BaseAppView.swift in Sources */,
5F0024B1294F38CE00E2D56B /* Reuseable.swift in Sources */,
5FA4D082293FB349005D5501 /* AppDelegate.swift in Sources */,
5F0024BA294F3A3100E2D56B /* ThemeManager.swift in Sources */,
5F0024B7294F397E00E2D56B /* UICollectionView+Extension.swift in Sources */,
5F168BDD29632EDA0073BE71 /* DetailView.swift in Sources */,
5F168BD729632EB80073BE71 /* DetailController.swift in Sources */,
5FE686F8294DC07900318A15 /* HomeBuilder.swift in Sources */,
5FE686ED294DB17300318A15 /* HomeView.swift in Sources */,
5F23B3F429600471008090DD /* DetailBuilder.swift in Sources */,
5F002485294DC32900E2D56B /* SplashBuilder.swift in Sources */,
5F0024A8294F332200E2D56B /* MoviePresentation.swift in Sources */,
5FA4D0B2293FB398005D5501 /* AppRouter.swift in Sources */,
);
@ -585,6 +936,10 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
5F23B40029619CE7008090DD /* movie1.json in Sources */,
5F23B40429619F0C008090DD /* movie3.json in Sources */,
5F23B40229619F06008090DD /* movie2.json in Sources */,
5F23B3FA296197CD008090DD /* ResourceLoader.swift in Sources */,
5FA4D0BC293FB499005D5501 /* iMoviesMVCTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@ -597,6 +952,11 @@
target = 5F071765293FA18600A54133 /* iMoviesMVVM */;
targetProxy = 5F07177C293FA18700A54133 /* PBXContainerItemProxy */;
};
5F9292AB2967001500B2CBC8 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 5F9292932967001400B2CBC8 /* iMoviesVIPER */;
targetProxy = 5F9292AA2967001500B2CBC8 /* PBXContainerItemProxy */;
};
5FA4D096293FB34A005D5501 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 5FA4D07E293FB349005D5501 /* iMoviesMVC */;
@ -604,41 +964,6 @@
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
5F07176E293FA18600A54133 /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
5F07176F293FA18600A54133 /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
5F071773293FA18700A54133 /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
5F071774293FA18700A54133 /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
5FA4D087293FB349005D5501 /* Main.storyboard */ = {
isa = PBXVariantGroup;
children = (
5FA4D088293FB349005D5501 /* Base */,
);
name = Main.storyboard;
sourceTree = "<group>";
};
5FA4D08C293FB34A005D5501 /* LaunchScreen.storyboard */ = {
isa = PBXVariantGroup;
children = (
5FA4D08D293FB34A005D5501 /* Base */,
);
name = LaunchScreen.storyboard;
sourceTree = "<group>";
};
/* End PBXVariantGroup section */
/* Begin XCBuildConfiguration section */
5F07178E293FA18700A54133 /* Debug */ = {
isa = XCBuildConfiguration;
@ -651,8 +976,7 @@
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = iMoviesMVVM/Info.plist;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
INFOPLIST_KEY_UIMainStoryboardFile = Main;
INFOPLIST_KEY_UILaunchStoryboardName = "Launch Screen";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
LD_RUNPATH_SEARCH_PATHS = (
@ -679,8 +1003,7 @@
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = iMoviesMVVM/Info.plist;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
INFOPLIST_KEY_UIMainStoryboardFile = Main;
INFOPLIST_KEY_UILaunchStoryboardName = "Launch Screen";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
LD_RUNPATH_SEARCH_PATHS = (
@ -850,6 +1173,100 @@
};
name = Release;
};
5F9292BC2967001500B2CBC8 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = JGMV95L4F7;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = iMoviesVIPER/Info.plist;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchStoryboardName = "Launch Screen";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.eyupyasuntimur.iMoviesVIPER;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
5F9292BD2967001500B2CBC8 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = JGMV95L4F7;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = iMoviesVIPER/Info.plist;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchStoryboardName = "Launch Screen";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
);
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.eyupyasuntimur.iMoviesVIPER;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Release;
};
5F9292BF2967001500B2CBC8 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = JGMV95L4F7;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.5;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.eyupyasuntimur.iMoviesVIPERTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = NO;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/iMoviesVIPER.app/iMoviesVIPER";
};
name = Debug;
};
5F9292C02967001500B2CBC8 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
BUNDLE_LOADER = "$(TEST_HOST)";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = JGMV95L4F7;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.5;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.eyupyasuntimur.iMoviesVIPERTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = NO;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/iMoviesVIPER.app/iMoviesVIPER";
};
name = Release;
};
5FA4D0A7293FB34B005D5501 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@ -861,8 +1278,7 @@
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = iMoviesMVC/Info.plist;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
INFOPLIST_KEY_UIMainStoryboardFile = Main;
INFOPLIST_KEY_UILaunchStoryboardName = "Launch Screen";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
LD_RUNPATH_SEARCH_PATHS = (
@ -889,8 +1305,7 @@
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = iMoviesMVC/Info.plist;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchStoryboardName = LaunchScreen;
INFOPLIST_KEY_UIMainStoryboardFile = Main;
INFOPLIST_KEY_UILaunchStoryboardName = "Launch Screen";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight";
LD_RUNPATH_SEARCH_PATHS = (
@ -989,6 +1404,24 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
5F9292BB2967001500B2CBC8 /* Build configuration list for PBXNativeTarget "iMoviesVIPER" */ = {
isa = XCConfigurationList;
buildConfigurations = (
5F9292BC2967001500B2CBC8 /* Debug */,
5F9292BD2967001500B2CBC8 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
5F9292BE2967001500B2CBC8 /* Build configuration list for PBXNativeTarget "iMoviesVIPERTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
5F9292BF2967001500B2CBC8 /* Debug */,
5F9292C02967001500B2CBC8 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
5FA4D0A6293FB34B005D5501 /* Build configuration list for PBXNativeTarget "iMoviesMVC" */ = {
isa = XCConfigurationList;
buildConfigurations = (
@ -1010,6 +1443,22 @@
/* End XCConfigurationList section */
/* Begin XCRemoteSwiftPackageReference section */
5F168BCC29632BBC0073BE71 /* XCRemoteSwiftPackageReference "iMoviesAPI" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/yasuntimure/iMoviesAPI.git";
requirement = {
branch = development;
kind = branch;
};
};
5F168BCF29632BE70073BE71 /* XCRemoteSwiftPackageReference "Kingfisher" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/onevcat/Kingfisher.git";
requirement = {
branch = master;
kind = branch;
};
};
5F23B3E3295F1767008090DD /* XCRemoteSwiftPackageReference "iMoviesAPI" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/yasuntimure/iMoviesAPI.git";
@ -1026,9 +1475,35 @@
minimumVersion = 7.0.0;
};
};
5F9292D3296743F400B2CBC8 /* XCRemoteSwiftPackageReference "iMoviesAPI" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/yasuntimure/iMoviesAPI.git";
requirement = {
branch = development;
kind = branch;
};
};
5F9292D62967440E00B2CBC8 /* XCRemoteSwiftPackageReference "Kingfisher" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/onevcat/Kingfisher.git";
requirement = {
branch = master;
kind = branch;
};
};
/* End XCRemoteSwiftPackageReference section */
/* Begin XCSwiftPackageProductDependency section */
5F168BCD29632BBC0073BE71 /* iMoviesAPI */ = {
isa = XCSwiftPackageProductDependency;
package = 5F23B3E3295F1767008090DD /* XCRemoteSwiftPackageReference "iMoviesAPI" */;
productName = iMoviesAPI;
};
5F168BD029632BE70073BE71 /* Kingfisher */ = {
isa = XCSwiftPackageProductDependency;
package = 5F168BCF29632BE70073BE71 /* XCRemoteSwiftPackageReference "Kingfisher" */;
productName = Kingfisher;
};
5F23B3E4295F1767008090DD /* iMoviesAPI */ = {
isa = XCSwiftPackageProductDependency;
package = 5F23B3E3295F1767008090DD /* XCRemoteSwiftPackageReference "iMoviesAPI" */;
@ -1039,6 +1514,16 @@
package = 5F23B3E8295F68A9008090DD /* XCRemoteSwiftPackageReference "Kingfisher" */;
productName = Kingfisher;
};
5F9292D4296743F400B2CBC8 /* iMoviesAPI */ = {
isa = XCSwiftPackageProductDependency;
package = 5F23B3E3295F1767008090DD /* XCRemoteSwiftPackageReference "iMoviesAPI" */;
productName = iMoviesAPI;
};
5F9292D72967440E00B2CBC8 /* Kingfisher */ = {
isa = XCSwiftPackageProductDependency;
package = 5F168BCF29632BE70073BE71 /* XCRemoteSwiftPackageReference "Kingfisher" */;
productName = Kingfisher;
};
/* End XCSwiftPackageProductDependency section */
};
rootObject = 5F69D2C6293CE36100768933 /* Project object */;

View File

@ -7,7 +7,6 @@
import UIKit
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {

View File

@ -16,7 +16,7 @@ final class AppRouter {
}
func start() {
window.rootViewController = SplashBuilder.make()
window.rootViewController = HomeBuilder.make()
window.makeKeyAndVisible()
}
}

View File

@ -3,17 +3,3 @@
<plist version="1.0">
<dict/>
</plist>
<key>NSAppTransportSecurity</key>
<dict>
<key>NSExceptionDomains</key>
<dict>
<key>https://api.nytimes.com</key>
<dict>
<key>NSExceptionRequiresForwardSecrecy</key>
<false/>
<key>NSIncludesSubdomains</key>
<true/>
</dict>
</dict>
</dict>

View File

@ -0,0 +1,28 @@
//
// ControllerProtocol.swift
// iMoviesMVC
//
// Created by Eyüp Yasuntimur on 17.12.2022.
//
import UIKit
public protocol BaseAppController {
associatedtype ViewType: BaseAppView
var viewImpl: ViewType? { get set }
func registerObservers()
func configureController()
}
extension BaseAppController where Self: UIViewController {
public var viewImpl: ViewType? {
get { return self.view as? ViewType }
set {}
}
public func configureController() {
self.registerObservers()
self.navigationController?.isNavigationBarHidden = false
self.setNeedsStatusBarAppearanceUpdate()
}
}

View File

@ -8,17 +8,17 @@
import Combine
import UIKit
// MARK: Base View Protocol
// MARK: Base App View Protocol
public protocol ViewProtocol: AnyObject {
public protocol BaseAppView: AnyObject {
func setup()
func setConstraints()
func registerObservers()
}
extension ViewProtocol where Self: UIView {
extension BaseAppView where Self: UIView {
/// Start initializing the base `AppView` protocol methods
/// Start initializing the base `BaseAppView` protocol methods
public func configureContents() {
self.setup()
self.registerObservers()

View File

@ -9,7 +9,7 @@ import UIKit
import Combine
import iMoviesAPI
class DetailController: UIViewController, ControllerProtocol {
class DetailController: UIViewController, BaseAppController {
private var subscribers = Set<AnyCancellable>()

View File

@ -9,7 +9,7 @@ import UIKit
import Combine
import iMoviesAPI
final class DetailView: UIView, ViewProtocol, UIScrollViewDelegate {
final class DetailView: UIView, BaseAppView, UIScrollViewDelegate {
private var subscribers = Set<AnyCancellable>()
@ -69,7 +69,7 @@ final class DetailView: UIView, ViewProtocol, UIScrollViewDelegate {
}
final class HeaderView: UIView, ViewProtocol {
final class HeaderView: UIView, BaseAppView {
private var subscribers = Set<AnyCancellable>()
@ -132,7 +132,7 @@ final class HeaderView: UIView, ViewProtocol {
}
final class BodyView: UIView, ViewProtocol {
final class BodyView: UIView, BaseAppView {
private var subscribers = Set<AnyCancellable>()

View File

@ -11,7 +11,8 @@ final class HomeBuilder {
static func make() -> HomeController {
let view = HomeView()
let controller = HomeController(view: view, service: app.service)
let controller = HomeController(view: view,
service: app.service)
return controller
}
}

View File

@ -11,7 +11,7 @@ import Combine
import Kingfisher
import iMoviesAPI
class HomeCell: UITableViewCell, ViewProtocol, Reuseable {
class HomeCell: UITableViewCell, BaseAppView, Reuseable {
private var subscribers = Set<AnyCancellable>()
@ -87,18 +87,3 @@ class HomeCell: UITableViewCell, ViewProtocol, Reuseable {
])
}
}
// MARK: - Kingfiher
extension UIImageView {
func setKfImage(for urlString: String?) {
guard let urlString = urlString else {
return
}
let url = URL(string: urlString)
self.kf.indicatorType = .activity
self.kf.setImage(with: url, options: KingfisherManager.shared.defaultOptions)
}
}

View File

@ -9,7 +9,7 @@ import UIKit
import Combine
import iMoviesAPI
class HomeController: UIViewController, ControllerProtocol {
class HomeController: UIViewController, BaseAppController {
private var subscribers = Set<AnyCancellable>()

View File

@ -7,15 +7,14 @@
import UIKit
import Combine
import iMoviesAPI
final class HomeView: UIView, ViewProtocol {
final class HomeView: UIView, BaseAppView {
var onCellSelect: ((_ movie: MoviePresentation) -> Void)?
private var subscribers = Set<AnyCancellable>()
@Published public var movies: [MoviePresentation] = []
@Published public var movies: MoviePresentations = []
@Published public var isLoading: Bool = false
lazy var tableView: UITableView = {
@ -45,6 +44,7 @@ final class HomeView: UIView, ViewProtocol {
func registerObservers() {
$movies
.dropFirst()
.receive(on: DispatchQueue.main)
.sink { _ in
self.tableView.reloadData()
@ -80,6 +80,7 @@ extension HomeView: UITableViewDelegate {
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: false)
self.onCellSelect?(movies[indexPath.row])
}

View File

@ -1,17 +0,0 @@
//
// SplashBuilder.swift
// iMoviesMVC
//
// Created by Eyüp Yasuntimur on 17.12.2022.
//
import UIKit
final class SplashBuilder {
static func make() -> SplashController {
let storyboard = UIStoryboard(name: "Main", bundle: nil)
let controller = storyboard.instantiateViewController(withIdentifier: "SplashController") as! SplashController
return controller
}
}

View File

@ -1,21 +0,0 @@
//
// SplashController.swift
// iMoviesMVC
//
// Created by Eyüp Yasuntimur on 17.12.2022.
//
import Foundation
import UIKit
final class SplashController: UIViewController {
override func viewDidLoad() {
DispatchQueue.main.asyncAfter(deadline: .now() + .seconds(1)) {
let homeController = HomeBuilder.make()
self.present(homeController.embedInNavigationController(), animated: false)
}
}
}

View File

@ -0,0 +1,34 @@
//
// ResourceLoader.swift
// iMoviesMVCTests
//
// Created by Eyüp Yasuntimur on 1.01.2023.
//
import Foundation
import iMoviesAPI
class ResourceLoader {
enum MovieResource: String {
case movie1
case movie2
case movie3
}
static func loadMovie(resource: MovieResource) throws -> Movie {
let bundle = Bundle.test
let url = try bundle.url(forResource: resource.rawValue, withExtension: ".json").unwrap()
let data = try Data(contentsOf: url)
let decoder = Decoders.plainDateDecoder
let movie = try decoder.decode(Movie.self, from: data)
return movie
}
}
private extension Bundle {
class Dummy { }
static let test = Bundle(for: Dummy.self)
}

View File

@ -0,0 +1,22 @@
{
"display_title": "The 22nd Annual Animation Show of Shows",
"mpaa_rating": "",
"critics_pick": 0,
"byline": "Jeannette Catsoulis",
"headline": "Annual Animation Show of Shows Review: A Mix of Whimsy and Dread",
"summary_short": "This festivals 22nd edition covers themes of crisis, both personal and planetary, with short works from the likes of Gil Alkabetz and Frédéric Back.",
"publication_date": "2022-12-29",
"opening_date": null,
"date_updated": "2022-12-29 19:42:13",
"link": {
"type": "article",
"url": "https://www.nytimes.com/2022/12/29/movies/annual-animation-show-of-shows-review.html",
"suggested_link_text": "Read the New York Times Review of The 22nd Annual Animation Show of Shows"
},
"multimedia": {
"type": "mediumThreeByTwo210",
"src": "https://static01.nyt.com/images/2022/12/29/multimedia/29animation-show-review-1-856a/29animation-show-review-1-856a-mediumThreeByTwo440.jpg",
"height": 140,
"width": 210
}
}

View File

@ -0,0 +1,25 @@
{
"display_title": "Farha",
"mpaa_rating": "",
"critics_pick": 0,
"byline": "Beatrice Loayza",
"headline": "Farha Review: A Most Brutal Coming-of-Age Story",
"summary_short": "Set in the early days of the Israeli-Palestinian conflict, this drama depicts the upheaval of Palestinian society from a 14-year-old girls perspective.",
"publication_date": "2022-12-01",
"opening_date": null,
"date_updated": "2022-12-01 16:32:03",
"link": {
"type": "article",
"url": "https://www.nytimes.com/2022/12/01/movies/farha-review.html",
"suggested_link_text": "Read the New York Times Review of Farha"
},
"multimedia": {
"type": "mediumThreeByTwo210",
"src": "https://static01.nyt.com/images/2022/12/01/multimedia/01farha-review-1-26c4/01farha-review-1-26c4-mediumThreeByTwo440.jpg",
"height": 140,
"width": 210
}
}

View File

@ -0,0 +1,25 @@
{
"display_title": "A Man Called Otto",
"mpaa_rating": "PG-13",
"critics_pick": 0,
"byline": "Glenn Kenny",
"headline": "A Man Called Otto Review: Tom Hanks Learns Life Lessons",
"summary_short": "Going against nice-guy type (at first), the star plays a misanthrope whos pulled into caring for a neighboring family in need.",
"publication_date": "2022-12-29",
"opening_date": "2023-01-04",
"date_updated": "2022-12-29 12:02:02",
"link": {
"type": "article",
"url": "https://www.nytimes.com/2022/12/29/movies/a-man-called-otto-review.html",
"suggested_link_text": "Read the New York Times Review of A Man Called Otto"
},
"multimedia": {
"type": "mediumThreeByTwo210",
"src": "https://static01.nyt.com/images/2022/12/30/multimedia/29man-called-otto-1-438a/29man-called-otto-1-438a-mediumThreeByTwo440.jpg",
"height": 140,
"width": 210
}
}

View File

@ -6,30 +6,49 @@
//
import XCTest
@testable import iMoviesAPI
@testable import iMoviesMVC
class iMoviesMVCTests: XCTestCase {
fileprivate var service: MockService!
var view: HomeView!
var controller: HomeController!
override func setUpWithError() throws {
// Put setup code here. This method is called before the invocation of each test method in the class.
service = MockService()
view = HomeView()
controller = HomeController()
}
override func tearDownWithError() throws {
// Put teardown code here. This method is called after the invocation of each test method in the class.
service = nil
view = nil
controller = nil
}
func testExample() throws {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct results.
// Any test you write for XCTest can be annotated as throws and async.
// Mark your test throws to produce an unexpected failure when your test encounters an uncaught error.
// Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards.
}
func testMovieList() throws {
func testPerformanceExample() throws {
// This is an example of a performance test case.
self.measure {
// Put the code you want to measure the time of here.
}
}
// Given
let movie1 = try ResourceLoader.loadMovie(resource: .movie1)
service.movies = [movie1]
// When
controller.loadViewIfNeeded()
// Then
XCTAssertEqual(view.movies.count, 1)
XCTAssertEqual(try view.movies.element(at: 0).title, movie1.displayTitle)
}
}
private final class MockService: WebServiceProtocol {
var movies: [Movie] = []
func search(movie: String, completion: @escaping ([Movie]?) -> Void) {
completion(movies)
}
}

View File

@ -6,7 +6,7 @@
//
import Foundation
//import iMoviesAPI
import iMoviesAPI
let app = AppContainer()

View File

@ -10,7 +10,8 @@ import UIKit
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
app.router.start()
return true
}

View File

@ -16,8 +16,8 @@ final class AppRouter {
}
func start() {
let viewController = ViewController()
let navigationController = UINavigationController(rootViewController: viewController)
let homeController = HomeBuilder.make()
let navigationController = homeController.embedInNavigationController()
window.rootViewController = navigationController
window.makeKeyAndVisible()
}

View File

@ -1,30 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="20037" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="20020"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
<capability name="System colors in document resources" minToolsVersion="11.0"/>
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
</dependencies>
<scenes>
<!--Home Controller-->
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="BYZ-38-t0r" customClass="HomeController" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="600" height="600"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
</scene>
</scenes>
<resources>
<systemColor name="systemBackgroundColor">
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
</systemColor>
</resources>
</document>

View File

@ -1,21 +0,0 @@
{
"images" : [
{
"filename" : "logo.png",
"idiom" : "universal",
"scale" : "1x"
},
{
"idiom" : "universal",
"scale" : "2x"
},
{
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 10 KiB

View File

@ -1,10 +1,11 @@
//
// ControllerProtocol.swift
// iMoviesMVC
// iMoviesMVVM
//
// Created by Eyüp Yasuntimur on 17.12.2022.
// Created by Eyüp Yasuntimur on 2.01.2023.
//
import Foundation
import UIKit
public protocol ControllerProtocol {

View File

@ -0,0 +1,32 @@
//
// BaseViewProtocol.swift
// iMoviesMVVM
//
// Created by Eyüp Yasuntimur on 2.01.2023.
//
#if canImport(UIKit)
import UIKit
// MARK: View Protocol
public protocol ViewProtocol: AnyObject {
associatedtype ViewModel
var viewModel: ViewModel? { get set }
func setup()
func registerObservers()
func setConstraints()
func configureView(_ viewModel: ViewModel?)
}
extension ViewProtocol where Self: UIView {
/// Start initializing the base `ViewProtocol` methods
public func configureContents() {
self.setup()
self.registerObservers()
self.setConstraints()
}
}
#endif

View File

@ -0,0 +1,19 @@
//
// DetailBuilder.swift
// iMovies
//
// Created by Eyüp Yasuntimur on 2.01.2023.
//
import Foundation
final class DetailBuilder {
static func make(movie: MoviePresentation) -> DetailController {
let view = DetailView()
let viewModel = DetailViewModel(movie: movie)
let controller = DetailController(view: view,
viewModel: viewModel)
return controller
}
}

View File

@ -0,0 +1,45 @@
//
// DetailController.swift
// iMovies
//
// Created by Eyüp Yasuntimur on 2.01.2023.
//
import UIKit
import Combine
import iMoviesAPI
class DetailController: UIViewController, ControllerProtocol {
private var subscribers = Set<AnyCancellable>()
@Published internal var viewImpl: DetailView?
@Published internal var viewModel: DetailViewModel?
convenience init(
view: DetailView?,
viewModel: DetailViewModel?
) {
self.init(nibName: nil, bundle: nil)
self.viewImpl = view
self.viewModel = viewModel
self.configureController()
}
func registerObservers() {
$viewImpl.sink { [weak self] _view in
guard let view = _view else { return }
view.frame = UIScreen.main.bounds
// TODO: view implementation
self?.view.addSubview(view)
}.store(in: &subscribers)
viewModel?.$movie.sink { [weak self] (movie) in
self?.viewImpl?.viewModel = movie
}.store(in: &subscribers)
}
}

View File

@ -0,0 +1,196 @@
//
// DetailView.swift
// iMovies
//
// Created by Eyüp Yasuntimur on 2.01.2023.
//
import UIKit
import Combine
final class DetailView: UIView, ViewProtocol {
private var subscribers = Set<AnyCancellable>()
@Published public var viewModel: MoviePresentation?
@Published public var isLoading: Bool = false
lazy var headerView: HeaderView = {
let view = HeaderView()
return view
}()
lazy var bodyView: BodyView = {
let view = BodyView()
return view
}()
override init(frame: CGRect) {
super.init(frame: frame)
configureContents()
registerObservers()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setup() {
self.backgroundColor = .systemBackground
addSubview(headerView)
addSubview(bodyView)
}
func registerObservers() {
$viewModel
.dropFirst()
.sink { model in
self.configureView(model)
}.store(in: &subscribers)
}
func configureView(_ viewModel: MoviePresentation?) {
self.headerView.viewModel = viewModel
self.bodyView.viewModel = viewModel?.summary
}
func setConstraints() {
headerView.translatesAutoresizingMaskIntoConstraints = false
bodyView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
headerView.topAnchor.constraint(equalTo: topAnchor),
headerView.leadingAnchor.constraint(equalTo: leadingAnchor),
headerView.trailingAnchor.constraint(equalTo: trailingAnchor),
headerView.heightAnchor.constraint(equalToConstant: UIScreen.main.bounds.height / 3),
bodyView.topAnchor.constraint(equalTo: headerView.bottomAnchor),
bodyView.leadingAnchor.constraint(equalTo: leadingAnchor),
bodyView.trailingAnchor.constraint(equalTo: trailingAnchor),
bodyView.bottomAnchor.constraint(equalTo: bottomAnchor),
])
}
}
final class HeaderView: UIView, ViewProtocol {
private var subscribers = Set<AnyCancellable>()
@Published public var viewModel: MoviePresentation?
lazy var thumbImage: UIImageView = {
let view = UIImageView()
view.contentMode = .scaleAspectFill
view.alpha = 0.7
return view
}()
lazy var titleLabel: UILabel = {
let label = UILabel()
label.font = ThemeManager.Font.Black.font(size: .xlarge)
label.textColor = .white
label.numberOfLines = 0
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
configureContents()
registerObservers()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setup() {
self.backgroundColor = .black
[thumbImage, titleLabel].forEach({ addSubview($0) })
}
func registerObservers() {
$viewModel
.dropFirst()
.sink { model in
self.configureView(model)
}.store(in: &subscribers)
}
func configureView(_ viewModel: MoviePresentation?) {
self.titleLabel.text = viewModel?.title
self.thumbImage.setKfImage(for: viewModel?.imageUrl)
}
func setConstraints() {
[thumbImage, titleLabel].forEach({
$0.translatesAutoresizingMaskIntoConstraints = false
})
NSLayoutConstraint.activate([
thumbImage.topAnchor.constraint(equalTo: topAnchor),
thumbImage.leadingAnchor.constraint(equalTo: leadingAnchor),
thumbImage.trailingAnchor.constraint(equalTo: trailingAnchor),
thumbImage.bottomAnchor.constraint(equalTo: bottomAnchor),
titleLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 15),
titleLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -15),
titleLabel.bottomAnchor.constraint(equalTo: thumbImage.bottomAnchor, constant: -5)
])
}
}
final class BodyView: UIView, ViewProtocol {
private var subscribers = Set<AnyCancellable>()
@Published public var viewModel: String?
lazy var summaryLabel: UILabel = {
let label = UILabel()
label.font = ThemeManager.Font.Book.font(size: .medium)
label.numberOfLines = 0
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
configureContents()
registerObservers()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setup() {
self.backgroundColor = .systemBackground
addSubview(summaryLabel)
}
func registerObservers() {
$viewModel
.dropFirst()
.sink { model in
self.configureView(model)
}.store(in: &subscribers)
}
func configureView(_ viewModel: String?) {
self.summaryLabel.text = viewModel
}
func setConstraints() {
summaryLabel.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
summaryLabel.topAnchor.constraint(equalTo: topAnchor, constant: 25),
summaryLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 20),
summaryLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -20),
])
}
}

View File

@ -0,0 +1,22 @@
//
// DetailViewModel.swift
// iMovies
//
// Created by Eyüp Yasuntimur on 2.01.2023.
//
import Combine
import iMoviesAPI
final class DetailViewModel {
private var subscribers = Set<AnyCancellable>()
@Published var movie: MoviePresentation?
init(movie: MoviePresentation?) {
self.movie = movie
}
}

View File

@ -0,0 +1,19 @@
//
// HomeBuilder.swift
// iMoviesMVVM
//
// Created by Eyüp Yasuntimur on 2.01.2023.
//
import UIKit
final class HomeBuilder {
static func make() -> HomeController {
let view = HomeView()
let viewModel = HomeViewModel(service: app.service)
let controller = HomeController(view: view,
viewModel: viewModel)
return controller
}
}

View File

@ -0,0 +1,95 @@
//
// HomeCell.swift
// iMoviesMVVM
//
// Created by Eyüp Yasuntimur on 2.01.2023.
//
import Foundation
import UIKit
import Combine
import Kingfisher
import iMoviesAPI
class HomeCell: UITableViewCell, ViewProtocol, Reuseable {
private var subscribers = Set<AnyCancellable>()
@Published internal var viewModel: MoviePresentation?
lazy var thumbImage: UIImageView = {
let view = UIImageView()
view.contentMode = .scaleAspectFill
view.layer.cornerRadius = 12
view.clipsToBounds = true
return view
}()
lazy var titleLabel: UILabel = {
let label = UILabel()
label.font = ThemeManager.Font.Medium.font(size: .mediumlarge)
label.numberOfLines = 1
return label
}()
lazy var detailLabel: UILabel = {
let label = UILabel()
label.font = ThemeManager.Font.Roman.font(size: .smallmedium)
label.numberOfLines = 3
return label
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
self.configureContents()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setup() {
[thumbImage, titleLabel, detailLabel].forEach { contentView.addSubview($0) }
}
func registerObservers() {
$viewModel.sink { model in
self.configureView(model)
}.store(in: &subscribers)
}
func configureView(_ viewModel: MoviePresentation?) {
self.titleLabel.text = viewModel?.title
self.detailLabel.text = viewModel?.summary
self.thumbImage.setKfImage(for: viewModel?.imageUrl)
}
func setConstraints() {
[contentView, thumbImage, titleLabel, detailLabel]
.forEach { $0.translatesAutoresizingMaskIntoConstraints = false }
NSLayoutConstraint.activate([
contentView.topAnchor.constraint(equalTo: topAnchor),
contentView.leadingAnchor.constraint(equalTo: leadingAnchor),
contentView.trailingAnchor.constraint(equalTo: trailingAnchor),
contentView.bottomAnchor.constraint(equalTo: bottomAnchor),
thumbImage.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 20),
thumbImage.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 18),
thumbImage.heightAnchor.constraint(equalToConstant: 85),
thumbImage.widthAnchor.constraint(equalToConstant: 100),
titleLabel.topAnchor.constraint(equalTo: thumbImage.topAnchor),
titleLabel.leadingAnchor.constraint(equalTo: thumbImage.trailingAnchor, constant: 15),
titleLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -15),
detailLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 5),
detailLabel.leadingAnchor.constraint(equalTo: titleLabel.leadingAnchor),
detailLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -18),
detailLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -15)
])
}
}

View File

@ -0,0 +1,55 @@
//
// HomeController.swift
// iMoviesMVVM
//
// Created by Eyüp Yasuntimur on 2.01.2023.
//
import UIKit
import Combine
class HomeController: UIViewController, ControllerProtocol {
private var subscribers = Set<AnyCancellable>()
@Published internal var viewImpl: HomeView?
@Published internal var viewModel: HomeViewModel?
convenience init(
view: HomeView?,
viewModel: HomeViewModel?
) {
self.init(nibName: nil, bundle: nil)
self.viewImpl = view
self.viewModel = viewModel
self.configureController()
}
override func viewDidLoad() {
super.viewDidLoad()
viewModel?.searchReviews(for: "all")
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
title = "iMovies"
}
func registerObservers() {
$viewImpl.sink { [weak self] _view in
guard let view = _view else { return }
view.frame = UIScreen.main.bounds
view.onCellSelect = { movie in
let controller = DetailBuilder.make(movie: movie)
self?.navigationController?.pushViewController(controller, animated: true)
}
self?.view.addSubview(view)
}.store(in: &subscribers)
viewModel?.$movies.sink{ [weak self] (movies) in
self?.viewImpl?.movies = movies
}.store(in: &subscribers)
}
}

View File

@ -0,0 +1,101 @@
//
// HomeView.swift
// iMoviesMVVM
//
// Created by Eyüp Yasuntimur on 2.01.2023.
//
import UIKit
import Combine
final class HomeView: UIView, ViewProtocol {
var onCellSelect: ((_ movie: MoviePresentation) -> Void)?
private var subscribers = Set<AnyCancellable>()
var viewModel: String?
@Published public var movies: MoviePresentations = []
@Published public var isLoading: Bool = false
lazy var tableView: UITableView = {
let tableView = UITableView()
tableView.rowHeight = UITableView.automaticDimension
return tableView
}()
override init(frame: CGRect) {
super.init(frame: frame)
configureContents()
registerObservers()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setup() {
self.backgroundColor = .systemBackground
tableView.delegate = self
tableView.dataSource = self
tableView.registerCell(type: HomeCell.self)
addSubview(tableView)
}
func registerObservers() {
$movies
.dropFirst()
.receive(on: DispatchQueue.main)
.sink { _ in
self.tableView.reloadData()
}.store(in: &subscribers)
$isLoading.sink { isLoading in
//
}.store(in: &subscribers)
}
func configureView(_ viewModel: String?) { }
func setConstraints() {
tableView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
tableView.topAnchor.constraint(equalTo: safeAreaLayoutGuide.topAnchor),
tableView.leadingAnchor.constraint(equalTo: leadingAnchor),
tableView.trailingAnchor.constraint(equalTo: trailingAnchor),
tableView.bottomAnchor.constraint(equalTo: bottomAnchor),
])
}
}
// MARK: - UITableViewDelegate
extension HomeView: UITableViewDelegate {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(forIndexPath: indexPath) as HomeCell
cell.viewModel = movies[indexPath.row]
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: false)
self.onCellSelect?(movies[indexPath.row])
}
}
// MARK: - UITableViewDataSource
extension HomeView: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return movies.count
}
}

View File

@ -0,0 +1,31 @@
//
// HomeViewModel.swift
// iMoviesMVVM
//
// Created by Eyüp Yasuntimur on 2.01.2023.
//
import Combine
import iMoviesAPI
final class HomeViewModel {
private var subscribers = Set<AnyCancellable>()
@Published var movies: MoviePresentations = []
internal var service: WebServiceProtocol?
init(service: WebServiceProtocol?) {
self.service = service
}
func searchReviews(for movie: String) {
service?.search(movie: movie, completion: { [weak self] (response) in
guard let response = response else {
return
}
self?.movies = response.map(MoviePresentation.init)
})
}
}

View File

@ -0,0 +1,18 @@
//
// AppContainer.swift
// iMoviesVIPER
//
// Created by Eyüp Yasuntimur on 5.01.2023.
//
import Foundation
import iMoviesAPI
let app = AppContainer()
final class AppContainer {
let router = AppRouter()
let service = WebService()
}

View File

@ -0,0 +1,19 @@
//
// AppDelegate.swift
// iMoviesVIPER
//
// Created by Eyüp Yasuntimur on 5.01.2023.
//
import UIKit
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
app.router.start()
return true
}
}

View File

@ -0,0 +1,25 @@
//
// AppRouter.swift
// iMoviesVIPER
//
// Created by Eyüp Yasuntimur on 5.01.2023.
//
import UIKit
final class AppRouter {
let window: UIWindow
init() {
window = UIWindow(frame: UIScreen.main.bounds)
}
func start() {
let homeController = HomeBuilder.make()
let navigationController = homeController.embedInNavigationController()
window.rootViewController = navigationController
window.makeKeyAndVisible()
}
}

View File

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict/>
</plist>

View File

@ -0,0 +1,11 @@
{
"colors" : [
{
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,98 @@
{
"images" : [
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "20x20"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "20x20"
},
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "29x29"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "29x29"
},
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "40x40"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "40x40"
},
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "60x60"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "60x60"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "20x20"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "20x20"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "29x29"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "29x29"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "40x40"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "40x40"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "76x76"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "76x76"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "83.5x83.5"
},
{
"idiom" : "ios-marketing",
"scale" : "1x",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,18 @@
//
// DetailBuilder.swift
// iMoviesVIPER
//
// Created by Eyüp Yasuntimur on 5.01.2023.
//
import Foundation
final class DetailBuilder {
static func make(moviePresentation: MoviePresentation) -> DetailViewController {
let viewController = DetailViewController()
let presenter = DetailPresenter(view: viewController, moviePresentation: moviePresentation)
viewController.presenter = presenter
return viewController
}
}

View File

@ -0,0 +1,16 @@
//
// DetailContracts.swift
// iMoviesVIPER
//
// Created by Eyüp Yasuntimur on 6.01.2023.
//
import UIKit
protocol DetailPresenterProtocol {
func load()
}
protocol DetailViewProtocol: AnyObject {
func update(_ presentation: MoviePresentation)
}

View File

@ -0,0 +1,27 @@
//
// DetailPresenter.swift
// iMoviesVIPER
//
// Created by Eyüp Yasuntimur on 5.01.2023.
//
import Foundation
final class DetailPresenter: DetailPresenterProtocol {
unowned var view: DetailViewProtocol
private let moviePresentation: MoviePresentation
init(
view: DetailViewProtocol,
moviePresentation: MoviePresentation
) {
self.view = view
self.moviePresentation = moviePresentation
}
func load() {
view.update(moviePresentation)
}
}

View File

@ -0,0 +1,165 @@
//
// DetailView.swift
// iMoviesVIPER
//
// Created by Eyüp Yasuntimur on 5.01.2023.
//
import UIKit
import Combine
final class DetailViewController: UIViewController, DetailViewProtocol {
internal var presenter: DetailPresenterProtocol?
lazy var headerView: HeaderView = {
let view = HeaderView()
return view
}()
lazy var bodyView: BodyView = {
let view = BodyView()
return view
}()
override func viewDidLoad() {
super.viewDidLoad()
setup()
presenter?.load()
}
func setup() {
view.backgroundColor = .systemBackground
view.addSubview(headerView)
view.addSubview(bodyView)
setConstraints()
}
func setConstraints() {
headerView.translatesAutoresizingMaskIntoConstraints = false
bodyView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
headerView.topAnchor.constraint(equalTo: view.topAnchor),
headerView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
headerView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
headerView.heightAnchor.constraint(equalToConstant: UIScreen.main.bounds.height / 3),
bodyView.topAnchor.constraint(equalTo: headerView.bottomAnchor),
bodyView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
bodyView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
bodyView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
])
}
func update(_ presentation: MoviePresentation) {
self.headerView.movie = presentation
self.bodyView.summary = presentation.summary
}
}
// MARK: - Header View
final class HeaderView: UIView {
internal var movie: MoviePresentation? {
didSet {
self.titleLabel.text = movie?.title
self.thumbImage.setKfImage(for: movie?.imageUrl)
}
}
lazy var thumbImage: UIImageView = {
let view = UIImageView()
view.contentMode = .scaleAspectFill
view.alpha = 0.7
return view
}()
lazy var titleLabel: UILabel = {
let label = UILabel()
label.font = ThemeManager.Font.Black.font(size: .xlarge)
label.textColor = .white
label.numberOfLines = 0
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setup() {
self.backgroundColor = .black
[thumbImage, titleLabel].forEach({ addSubview($0) })
setConstraints()
}
func setConstraints() {
[thumbImage, titleLabel].forEach({
$0.translatesAutoresizingMaskIntoConstraints = false
})
NSLayoutConstraint.activate([
thumbImage.topAnchor.constraint(equalTo: topAnchor),
thumbImage.leadingAnchor.constraint(equalTo: leadingAnchor),
thumbImage.trailingAnchor.constraint(equalTo: trailingAnchor),
thumbImage.bottomAnchor.constraint(equalTo: bottomAnchor),
titleLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 15),
titleLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -15),
titleLabel.bottomAnchor.constraint(equalTo: thumbImage.bottomAnchor, constant: -5)
])
}
}
// MARK: - Body View
final class BodyView: UIView {
internal var summary: String? {
didSet {
self.summaryLabel.text = summary
}
}
lazy var summaryLabel: UILabel = {
let label = UILabel()
label.font = ThemeManager.Font.Book.font(size: .medium)
label.numberOfLines = 0
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
setup()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setup() {
self.backgroundColor = .systemBackground
addSubview(summaryLabel)
setConstraints()
}
func setConstraints() {
summaryLabel.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
summaryLabel.topAnchor.constraint(equalTo: topAnchor, constant: 25),
summaryLabel.leadingAnchor.constraint(equalTo: leadingAnchor, constant: 20),
summaryLabel.trailingAnchor.constraint(equalTo: trailingAnchor, constant: -20),
])
}
}

View File

@ -0,0 +1,22 @@
//
// HomeBuilder.swift
// iMoviesVIPER
//
// Created by Eyüp Yasuntimur on 5.01.2023.
//
import Foundation
final class HomeBuilder {
static func make() -> HomeViewController {
let view = HomeViewController()
let router = HomeRouter(view: view)
let interactor = HomeInteractor(service: app.service)
let presenter = HomePresenter(view: view,
interactor: interactor,
router: router)
view.presenter = presenter
return view
}
}

View File

@ -0,0 +1,82 @@
//
// HomeCell.swift
// iMoviesVIPER
//
// Created by Eyüp Yasuntimur on 6.01.2023.
//
import Foundation
import UIKit
import Kingfisher
class HomeCell: UITableViewCell, Reuseable {
lazy var thumbImage: UIImageView = {
let view = UIImageView()
view.contentMode = .scaleAspectFill
view.layer.cornerRadius = 12
view.clipsToBounds = true
return view
}()
lazy var titleLabel: UILabel = {
let label = UILabel()
label.font = ThemeManager.Font.Medium.font(size: .mediumlarge)
label.numberOfLines = 1
return label
}()
lazy var detailLabel: UILabel = {
let label = UILabel()
label.font = ThemeManager.Font.Roman.font(size: .smallmedium)
label.numberOfLines = 3
return label
}()
override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
super.init(style: style, reuseIdentifier: reuseIdentifier)
setup()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func setup() {
[thumbImage, titleLabel, detailLabel].forEach { contentView.addSubview($0) }
setConstraints()
}
func update(_ presentation: MoviePresentation?) {
self.titleLabel.text = presentation?.title
self.detailLabel.text = presentation?.summary
self.thumbImage.setKfImage(for: presentation?.imageUrl)
}
func setConstraints() {
[contentView, thumbImage, titleLabel, detailLabel]
.forEach { $0.translatesAutoresizingMaskIntoConstraints = false }
NSLayoutConstraint.activate([
contentView.topAnchor.constraint(equalTo: topAnchor),
contentView.leadingAnchor.constraint(equalTo: leadingAnchor),
contentView.trailingAnchor.constraint(equalTo: trailingAnchor),
contentView.bottomAnchor.constraint(equalTo: bottomAnchor),
thumbImage.topAnchor.constraint(equalTo: contentView.topAnchor, constant: 20),
thumbImage.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 18),
thumbImage.heightAnchor.constraint(equalToConstant: 85),
thumbImage.widthAnchor.constraint(equalToConstant: 100),
titleLabel.topAnchor.constraint(equalTo: thumbImage.topAnchor),
titleLabel.leadingAnchor.constraint(equalTo: thumbImage.trailingAnchor, constant: 15),
titleLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -15),
detailLabel.topAnchor.constraint(equalTo: titleLabel.bottomAnchor, constant: 5),
detailLabel.leadingAnchor.constraint(equalTo: titleLabel.leadingAnchor),
detailLabel.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -18),
detailLabel.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -15)
])
}
}

View File

@ -0,0 +1,64 @@
//
// HomeContracts.swift
// iMoviesVIPER
//
// Created by Eyüp Yasuntimur on 6.01.2023.
//
import Foundation
import iMoviesAPI
// MARK: - Interactor
protocol HomeInteractorProtocol: AnyObject {
var delegate: HomeInteractorDelegate? { get set }
var service: WebServiceProtocol { get set }
var movies: [Movie] { get set }
func searchReviews()
func selectMovie(at index: Int)
}
protocol HomeInteractorDelegate: AnyObject {
func handleOutput(_ output: HomeInteractorOutput)
}
enum HomeInteractorOutput {
case showMovies([Movie])
case showMovieDetail(MoviePresentation)
}
// MARK: - Presenter
protocol HomePresenterProtocol: AnyObject {
var view: HomeViewProtocol { get set }
var router: HomeRouterProtocol { get set }
var interactor: HomeInteractorProtocol { get set }
func load()
func selectMovie(at index: Int)
}
enum HomePresenterOutput {
case updateTitle(String)
case showMovies([MoviePresentation])
}
// MARK: - View
protocol HomeViewProtocol: AnyObject {
var movies: MoviePresentations { get set }
var presenter: HomePresenterProtocol? { get set }
func handleOutput(_ output: HomePresenterOutput)
}
// MARK: - Router
protocol HomeRouterProtocol: AnyObject {
func navigate(to route: HomeRoute)
}
enum HomeRoute {
case detail(MoviePresentation)
}

View File

@ -0,0 +1,37 @@
//
// HomeInteractor.swift
// iMoviesVIPER
//
// Created by Eyüp Yasuntimur on 5.01.2023.
//
import iMoviesAPI
final class HomeInteractor: HomeInteractorProtocol {
var delegate: HomeInteractorDelegate?
internal var movies: [Movie] = []
internal var service: WebServiceProtocol
init(service: WebServiceProtocol) {
self.service = service
}
func searchReviews() {
service.search(movie: "all", completion: { [weak self] (response) in
guard let response = response else {
return
}
self?.movies = response
self?.delegate?.handleOutput(.showMovies(response))
})
}
func selectMovie(at index: Int) {
let movie = movies[index]
let moviePresentation = MoviePresentation(movie: movie)
self.delegate?.handleOutput(.showMovieDetail(moviePresentation))
}
}

View File

@ -0,0 +1,52 @@
//
// HomePresenter.swift
// iMoviesVIPER
//
// Created by Eyüp Yasuntimur on 5.01.2023.
//
import Foundation
final class HomePresenter: HomePresenterProtocol {
internal var view: HomeViewProtocol
internal var router: HomeRouterProtocol
internal var interactor: HomeInteractorProtocol
init(view: HomeViewProtocol,
interactor: HomeInteractorProtocol,
router: HomeRouter
) {
self.view = view
self.interactor = interactor
self.router = router
interactor.delegate = self
view.handleOutput(.updateTitle("Movies"))
}
func load() {
interactor.searchReviews()
}
func selectMovie(at index: Int) {
interactor.selectMovie(at: index)
}
}
// MARK: - Home Interactor Delegate
extension HomePresenter: HomeInteractorDelegate {
func handleOutput(_ output: HomeInteractorOutput) {
switch output {
case .showMovies(let movies):
let moviePresentations = movies.map(MoviePresentation.init)
view.handleOutput(.showMovies(moviePresentations))
case .showMovieDetail(let movie):
router.navigate(to: .detail(movie))
}
}
}

View File

@ -0,0 +1,26 @@
//
// HomeRouter.swift
// iMoviesVIPER
//
// Created by Eyüp Yasuntimur on 5.01.2023.
//
import UIKit
final class HomeRouter: HomeRouterProtocol {
unowned let view: UIViewController
init(view: UIViewController) {
self.view = view
}
func navigate(to route: HomeRoute) {
switch route {
case .detail(let moviePresentation):
let detailView = DetailBuilder.make(moviePresentation: moviePresentation)
view.show(detailView, sender: nil)
}
}
}

View File

@ -0,0 +1,88 @@
//
// HomeView.swift
// iMoviesVIPER
//
// Created by Eyüp Yasuntimur on 5.01.2023.
//
import UIKit
final class HomeViewController: UIViewController, HomeViewProtocol {
internal var presenter: HomePresenterProtocol?
internal var movies: MoviePresentations = []
lazy var tableView: UITableView = {
let tableView = UITableView()
tableView.delegate = self
tableView.dataSource = self
tableView.rowHeight = UITableView.automaticDimension
tableView.registerCell(type: HomeCell.self)
return tableView
}()
override func viewDidLoad() {
super.viewDidLoad()
setup()
presenter?.load()
}
func setup() {
view.backgroundColor = .systemBackground
view.addSubview(tableView)
setConstraints()
}
func setConstraints() {
tableView.translatesAutoresizingMaskIntoConstraints = false
NSLayoutConstraint.activate([
tableView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor),
])
}
func handleOutput(_ output: HomePresenterOutput) {
switch output {
case .updateTitle(let title):
self.title = title
case .showMovies(let movies):
self.movies = movies
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
}
}
// MARK: - UITableViewDelegate
extension HomeViewController: UITableViewDelegate {
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(forIndexPath: indexPath) as HomeCell
cell.update(movies[indexPath.row])
return cell
}
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
tableView.deselectRow(at: indexPath, animated: false)
presenter?.selectMovie(at: indexPath.row)
}
}
// MARK: - UITableViewDataSource
extension HomeViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return movies.count
}
}

View File

@ -0,0 +1,36 @@
//
// AppDelegate.swift
// iMoviesVIPER
//
// Created by Eyüp Yasuntimur on 5.01.2023.
//
import UIKit
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
return true
}
// MARK: UISceneSession Lifecycle
func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration {
// Called when a new scene session is being created.
// Use this method to select a configuration to create the new scene with.
return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
}
func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
// Called when the user discards a scene session.
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
}
}

View File

@ -0,0 +1,11 @@
{
"colors" : [
{
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,93 @@
{
"images" : [
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "20x20"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "20x20"
},
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "29x29"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "29x29"
},
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "40x40"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "40x40"
},
{
"idiom" : "iphone",
"scale" : "2x",
"size" : "60x60"
},
{
"idiom" : "iphone",
"scale" : "3x",
"size" : "60x60"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "20x20"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "20x20"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "29x29"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "29x29"
},
{
"idiom" : "ipad",
"scale" : "1x",
"size" : "40x40"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "40x40"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "76x76"
},
{
"idiom" : "ipad",
"scale" : "2x",
"size" : "83.5x83.5"
},
{
"idiom" : "ios-marketing",
"scale" : "1x",
"size" : "1024x1024"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -1,5 +1,5 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
<?xml version="1.0" encoding="UTF-8"?>
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="BYZ-38-t0r">
<dependencies>
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
@ -7,19 +7,18 @@
</dependencies>
<scenes>
<!--View Controller-->
<scene sceneID="EHf-IW-A2E">
<scene sceneID="tne-QT-ifu">
<objects>
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
<viewController id="BYZ-38-t0r" customClass="ViewController" customModuleProvider="target" sceneMemberID="viewController">
<view key="view" contentMode="scaleToFill" id="8bC-Xf-vdC">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
</view>
</viewController>
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
<placeholder placeholderIdentifier="IBFirstResponder" id="dkx-z0-nzr" sceneMemberID="firstResponder"/>
</objects>
<point key="canvasLocation" x="53" y="375"/>
</scene>
</scenes>
</document>

View File

@ -0,0 +1,25 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>UIApplicationSceneManifest</key>
<dict>
<key>UIApplicationSupportsMultipleScenes</key>
<false/>
<key>UISceneConfigurations</key>
<dict>
<key>UIWindowSceneSessionRoleApplication</key>
<array>
<dict>
<key>UISceneConfigurationName</key>
<string>Default Configuration</string>
<key>UISceneDelegateClassName</key>
<string>$(PRODUCT_MODULE_NAME).SceneDelegate</string>
<key>UISceneStoryboardFile</key>
<string>Main</string>
</dict>
</array>
</dict>
</dict>
</dict>
</plist>

View File

@ -0,0 +1,52 @@
//
// SceneDelegate.swift
// iMoviesVIPER
//
// Created by Eyüp Yasuntimur on 5.01.2023.
//
import UIKit
class SceneDelegate: UIResponder, UIWindowSceneDelegate {
var window: UIWindow?
func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) {
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
guard let _ = (scene as? UIWindowScene) else { return }
}
func sceneDidDisconnect(_ scene: UIScene) {
// Called as the scene is being released by the system.
// This occurs shortly after the scene enters the background, or when its session is discarded.
// Release any resources associated with this scene that can be re-created the next time the scene connects.
// The scene may re-connect later, as its session was not necessarily discarded (see `application:didDiscardSceneSessions` instead).
}
func sceneDidBecomeActive(_ scene: UIScene) {
// Called when the scene has moved from an inactive state to an active state.
// Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
}
func sceneWillResignActive(_ scene: UIScene) {
// Called when the scene will move from an active state to an inactive state.
// This may occur due to temporary interruptions (ex. an incoming phone call).
}
func sceneWillEnterForeground(_ scene: UIScene) {
// Called as the scene transitions from the background to the foreground.
// Use this method to undo the changes made on entering the background.
}
func sceneDidEnterBackground(_ scene: UIScene) {
// Called as the scene transitions from the foreground to the background.
// Use this method to save data, release shared resources, and store enough scene-specific state information
// to restore the scene back to its current state.
}
}

View File

@ -1,8 +1,8 @@
//
// ViewController.swift
// iMoviesMVVM
// iMoviesVIPER
//
// Created by Eyüp Yasuntimur on 6.12.2022.
// Created by Eyüp Yasuntimur on 5.01.2023.
//
import UIKit
@ -11,8 +11,7 @@ class ViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
print("Hello MVVM")
view.backgroundColor = .green
// Do any additional setup after loading the view.
}

View File

@ -0,0 +1,36 @@
//
// iMoviesVIPERTests.swift
// iMoviesVIPERTests
//
// Created by Eyüp Yasuntimur on 5.01.2023.
//
import XCTest
@testable import iMoviesVIPER
class iMoviesVIPERTests: XCTestCase {
override func setUpWithError() throws {
// Put setup code here. This method is called before the invocation of each test method in the class.
}
override func tearDownWithError() throws {
// Put teardown code here. This method is called after the invocation of each test method in the class.
}
func testExample() throws {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct results.
// Any test you write for XCTest can be annotated as throws and async.
// Mark your test throws to produce an unexpected failure when your test encounters an uncaught error.
// Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards.
}
func testPerformanceExample() throws {
// This is an example of a performance test case.
self.measure {
// Put the code you want to measure the time of here.
}
}
}

View File

@ -0,0 +1,41 @@
//
// iMoviesVIPERUITests.swift
// iMoviesVIPERUITests
//
// Created by Eyüp Yasuntimur on 5.01.2023.
//
import XCTest
class iMoviesVIPERUITests: XCTestCase {
override func setUpWithError() throws {
// Put setup code here. This method is called before the invocation of each test method in the class.
// In UI tests it is usually best to stop immediately when a failure occurs.
continueAfterFailure = false
// In UI tests its important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
}
override func tearDownWithError() throws {
// Put teardown code here. This method is called after the invocation of each test method in the class.
}
func testExample() throws {
// UI tests must launch the application that they test.
let app = XCUIApplication()
app.launch()
// Use XCTAssert and related functions to verify your tests produce the correct results.
}
func testLaunchPerformance() throws {
if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) {
// This measures how long it takes to launch your application.
measure(metrics: [XCTApplicationLaunchMetric()]) {
XCUIApplication().launch()
}
}
}
}

View File

@ -0,0 +1,32 @@
//
// iMoviesVIPERUITestsLaunchTests.swift
// iMoviesVIPERUITests
//
// Created by Eyüp Yasuntimur on 5.01.2023.
//
import XCTest
class iMoviesVIPERUITestsLaunchTests: XCTestCase {
override class var runsForEachTargetApplicationUIConfiguration: Bool {
true
}
override func setUpWithError() throws {
continueAfterFailure = false
}
func testLaunch() throws {
let app = XCUIApplication()
app.launch()
// Insert steps here to perform after app launch but before taking a screenshot,
// such as logging into a test account or navigating somewhere in the app
let attachment = XCTAttachment(screenshot: app.screenshot())
attachment.name = "Launch Screen"
attachment.lifetime = .keepAlways
add(attachment)
}
}

View File

@ -0,0 +1,36 @@
//
// iMoviesVIPERTests.swift
// iMoviesVIPERTests
//
// Created by Eyüp Yasuntimur on 5.01.2023.
//
import XCTest
@testable import iMoviesVIPER
class iMoviesVIPERTests: XCTestCase {
override func setUpWithError() throws {
// Put setup code here. This method is called before the invocation of each test method in the class.
}
override func tearDownWithError() throws {
// Put teardown code here. This method is called after the invocation of each test method in the class.
}
func testExample() throws {
// This is an example of a functional test case.
// Use XCTAssert and related functions to verify your tests produce the correct results.
// Any test you write for XCTest can be annotated as throws and async.
// Mark your test throws to produce an unexpected failure when your test encounters an uncaught error.
// Mark your test async to allow awaiting for asynchronous code to complete. Check the results with assertions afterwards.
}
func testPerformanceExample() throws {
// This is an example of a performance test case.
self.measure {
// Put the code you want to measure the time of here.
}
}
}