Skip to content

Commit fe52148

Browse files
authored
Merge pull request #8 from engingulek/feature/connect-network-manager
connected network
2 parents 050bee5 + ecc089e commit fe52148

18 files changed

Lines changed: 533 additions & 43 deletions

ICTMDBDetailModule/DetailInteractor.swift

Lines changed: 39 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,21 +4,52 @@
44
//
55
// Created by Engin Gülek on 12.11.2025.
66
//
7+
import Foundation
8+
import ICTMDBNetworkManagerKit
79

8-
9-
final class TvShowDetailInteractor : PresenterToInteractorTvShowDetailProtocol {
10+
final class TvShowDetailInteractor : @preconcurrency PresenterToInteractorTvShowDetailProtocol {
11+
1012

1113
var presenter: (any InteractorToPresenterTvShowDetailProtocol)?
1214

13-
14-
15+
private let network : NetworkManagerProtocol
1516

16-
func loadTvShowDetail(id: Int?) {
17-
17+
init(network: NetworkManagerProtocol) {
18+
19+
self.network = network
1820
}
19-
20-
func loadTvShowCasts(id: Int?) {
21+
let deviceLanguageCode = Locale.current.language.languageCode ?? .english
22+
@MainActor func loadTvShowDetail(id: Int?) {
23+
guard let id = id else {return}
24+
let request = TvShowDetailRequest(
25+
language: deviceLanguageCode == .turkish ? .tr : .en,
26+
id: id)
2127

28+
network.execute(request) {[weak self] result in
29+
guard let self else {return}
30+
switch result {
31+
case .success(let data):
32+
presenter?.onHandle(handle: .sendData(data))
33+
34+
case .failure:
35+
presenter?.onHandle(handle: .sendError(.detailError))
36+
37+
}
38+
}
39+
}
40+
41+
@MainActor func loadTvShowCasts(id: Int?) {
42+
guard let id = id else {return}
43+
let request = CastRequest(id: id)
44+
network.execute(request) { [weak self] result in
45+
guard let self else {return}
46+
switch result {
47+
case .success(let casts):
48+
presenter?.onHandle(handle: .sendCast(casts.cast))
49+
case .failure:
50+
presenter?.onHandle(handle: .sendError(.castError))
51+
}
52+
}
2253
}
2354
}
2455

ICTMDBDetailModule/DetailPresenter.swift

Lines changed: 82 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ final class TvShowDetailPresenter {
1212
weak var view: PresenterToViewTvShowDetailProtocol?
1313

1414
private var interactor: PresenterToInteractorTvShowDetailProtocol
15-
15+
private let title = TvShowDetailTitlePresentation()
16+
private var seasonList: [SeasonPresentation] = []
17+
private var castList : [CastPresentation] = []
1618

1719
init(view: PresenterToViewTvShowDetailProtocol?,
1820
interactor: PresenterToInteractorTvShowDetailProtocol) {
@@ -22,52 +24,122 @@ final class TvShowDetailPresenter {
2224

2325
func viewDidLoad() {
2426
view?.setBackColorAble(color: "backColor")
25-
27+
// view?.prepareCollectionView()
2628
}
2729
}
2830

2931
extension TvShowDetailPresenter: ViewToPresenterTvShowDetailProtocol {
3032
func layout(for sectionIndex: Int) -> LayoutSource {
31-
33+
guard let sectionType = SectionType(rawValue: sectionIndex) else {
34+
return LayoutSourceTeamplate.none.template
35+
}
36+
37+
switch sectionType {
38+
case .cast:
39+
return LayoutSource(
40+
groupOrientation: .horizontal,
41+
itemSize: .init(
42+
width: (type: .fractional, value: 1.0 ),
43+
height: (type: .fractional, value: 0.7)),
44+
groupSize: .init(
45+
width: (type: .fractional, value: 0.4),
46+
height: (type: .fractional, value: 0.45)),
47+
sectionInsets: (top: 0, leading: 5, bottom: 0, trailing: 5),
48+
interItemSpacing: 0,
49+
interGroupSpacing: 0,
50+
scrollDirection: .horizontal
51+
)
52+
case .season:
53+
return LayoutSourceTeamplate.horizontalSingleRow.template
54+
}
3255
}
3356

3457
func getID(id: Int?) {
3558
guard let id = id else {return}
36-
59+
interactor.loadTvShowDetail(id: id)
60+
interactor.loadTvShowCasts(id: id)
3761

3862
}
3963

4064
func numberOfRowsInSection(in section: Int) -> Int {
41-
65+
guard let sectionType = SectionType(rawValue: section) else {return 0}
66+
switch sectionType {
67+
case .cast:
68+
return castList.count
69+
case .season:
70+
return seasonList.count
71+
}
4272
}
4373

4474
func numberOfSections() -> Int {
45-
75+
SectionType.allCases.count
4676
}
4777

4878
func cellForItem(section: Int,item:Int) -> CellItemType {
49-
79+
guard let sectionType = SectionType(rawValue: section) else { return .none }
80+
switch sectionType {
81+
case .cast:
82+
let data = castList[item]
83+
return .cast(data)
84+
case .season:
85+
let data = seasonList[item]
86+
return .season(data)
87+
}
5088
}
5189

5290

5391

5492
func titleForSection(at section: Int) -> (
5593
title: String, sizeType:SectionSizeType,
5694
buttonType: [TitleForSectionButtonType]?) {
57-
95+
guard let sectionType = SectionType(rawValue: section) else { return (title:"",sizeType:.small,buttonType:[]) }
96+
var item : (title: String, sizeType: SectionSizeType,buttonType: [TitleForSectionButtonType]?)
97+
switch sectionType {
98+
case .cast:
99+
item = (title:LocalizableUI.cast.localized,sizeType:.large,buttonType:[])
100+
case .season:
101+
item = (title:LocalizableUI.season.localized,sizeType:.large,buttonType:[])
102+
}
103+
return item
58104
}
59105

60106
func sectionType(at section: Int) -> SectionType {
61-
107+
return SectionType(rawValue: section) ?? .cast
62108
}
63109

64110
func cellIdentifier(at section: Int) -> String {
65-
111+
guard let section = SectionType(rawValue: section) else {return ""}
112+
switch section {
113+
case .cast: return CastCell.identifier
114+
case .season: return SeasonCell.identifier
115+
116+
}
66117
}
67118
}
68119

69120
extension TvShowDetailPresenter: InteractorToPresenterTvShowDetailProtocol {
70121
func onHandle(handle: TvShowInteractorResult) {
122+
switch handle {
123+
case .sendData(let detail):
124+
let detailPresentation = TvShowDetailPresentation(tvShowDetail: detail)
125+
view?.sendData(detail: detailPresentation, title: title)
126+
let seasons = detail.seasons
127+
guard let seasons = seasons else {return}
128+
seasonList = seasons.map { SeasonPresentation(season: $0) }
129+
130+
case .sendError(let error):
131+
switch error {
132+
case .detailError:
133+
break
134+
case .castError:
135+
castList = []
136+
}
137+
case .sendCast(let casts):
138+
castList = casts.map{CastPresentation(cast: $0) }
139+
140+
}
71141

142+
view?.prepareCollectionView()
143+
view?.relaodCollectionView()
72144
}
73145
}

ICTMDBDetailModule/DetailProtocols.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ protocol ViewToPresenterTvShowDetailProtocol:
2323

2424

2525
protocol PresenterToViewTvShowDetailProtocol : AnyObject,Ables{
26-
func sendData()
26+
func sendData(detail:TvShowDetailPresentation,title:TvShowDetailTitlePresentation)
2727
func relaodCollectionView()
2828
func prepareCollectionView()
2929
}
@@ -41,9 +41,9 @@ enum TvShowErrorType : Error{
4141
case castError
4242
}
4343
enum TvShowInteractorResult {
44-
case sendData
45-
case sendCast
46-
case sendError
44+
case sendData(_ data: TvShowDetail,)
45+
case sendCast(_ data:[Cast])
46+
case sendError(_ error:TvShowErrorType)
4747
}
4848

4949
protocol InteractorToPresenterTvShowDetailProtocol {
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
//
2+
// DetailRouter.swift
3+
// ICTMDBDetailModule
4+
//
5+
// Created by Engin Gülek on 12.11.2025.
6+
//
7+
8+
final class DetailRouter : PresenterToRouterTvShowDetailProtocol {
9+
10+
}

ICTMDBDetailModule/ICTMDBDetailModule.swift

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,25 @@
66
//
77

88
import Foundation
9+
import UIKit
10+
import ICTMDBModularProtocols
11+
import ICTMDBNetworkManagerKit
12+
13+
public class ICTMDBDetailModule : @preconcurrency TvShowDetailProtocol {
14+
15+
16+
public init() { }
17+
@MainActor
18+
public func createTvShowDetailModule(id: Int?) -> UIViewController {
19+
let viewController = DetailViewController()
20+
let _ = DetailRouter()
21+
let interactor = TvShowDetailInteractor(network: NetworkManager())
22+
let presenter : any ViewToPresenterTvShowDetailProtocol & InteractorToPresenterTvShowDetailProtocol
23+
= TvShowDetailPresenter(view: viewController, interactor: interactor)
24+
viewController.presenter = presenter
25+
interactor.presenter = presenter
26+
presenter.getID(id: id)
27+
return viewController
28+
}
29+
}
930

ICTMDBDetailModule/cells/cast/CastCell.swift

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ final class CastCell: UICollectionViewCell {
3333
containerView.addSubview(characterLabel)
3434

3535

36-
contentView.backgroundColor = .blue
36+
contentView.backgroundColor = .clear
3737
layer.shadowColor = UIColor.black.cgColor
3838
layer.shadowOpacity = 0.15
3939
layer.shadowOffset = CGSize(width: 0, height: 4)
@@ -88,8 +88,10 @@ final class CastCell: UICollectionViewCell {
8888
}
8989
}
9090

91-
func configure() {
92-
91+
func configure(cast:CastPresentation) {
92+
castImageView.setImageWithKigfisher(with: cast.image)
93+
nameLabel.text = cast.name
94+
characterLabel.text = cast.character
9395
}
9496

9597

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
//
2+
// CastPresentation.swift
3+
// ICTMDBDetailModule
4+
//
5+
// Created by Engin Gülek on 12.11.2025.
6+
//
7+
8+
struct CastPresentation : Equatable {
9+
let id:Int
10+
let image:String
11+
let name:String
12+
let character:String
13+
14+
init(id: Int, image: String, name: String, character: String) {
15+
self.id = id
16+
self.image = image
17+
self.name = name
18+
self.character = character
19+
}
20+
}
21+
extension CastPresentation {
22+
public init(cast:Cast) {
23+
self.id = cast.id
24+
self.image = "https://image.tmdb.org/t/p/w500\(cast.profilePath ?? "")"
25+
self.name = cast.name
26+
self.character = cast.character ?? "-"
27+
}
28+
}

ICTMDBDetailModule/cells/season/SeasonCell.swift

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ final class SeasonCell: UICollectionViewCell {
3737
containerView.addSubview(ratingLabel)
3838

3939

40-
contentView.backgroundColor = .red
40+
contentView.backgroundColor = .clear
4141
layer.shadowColor = UIColor.black.cgColor
4242
layer.shadowOpacity = 0.15
4343
layer.shadowOffset = CGSize(width: 0, height: 4)
@@ -100,7 +100,12 @@ final class SeasonCell: UICollectionViewCell {
100100
}
101101
}
102102

103-
func configure() {
103+
func configure(season:SeasonPresentation) {
104+
posterImageView.setImageWithKigfisher(with: season.poster)
105+
seasonLabel.text = season.seasonTitle
106+
airDateLabel.text = season.airdate
107+
episodeCountLabel.text = season.episodeCount
108+
ratingLabel.text = season.rating
104109
}
105110
}
106111

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
//
2+
// SeasonPresentation.swift
3+
// ICTMDBDetailModule
4+
//
5+
// Created by Engin Gülek on 12.11.2025.
6+
//
7+
import ICTMDBViewKit
8+
struct SeasonPresentation : Equatable {
9+
let poster : String
10+
let seasonTitle : String
11+
let airdate : String
12+
let episodeCount : String
13+
let rating:String
14+
15+
init(poster: String, seasonTitle: String, airdate: String, episodeCount: String, rating: String) {
16+
self.poster = poster
17+
self.seasonTitle = seasonTitle
18+
self.airdate = airdate
19+
self.episodeCount = episodeCount
20+
self.rating = rating
21+
}
22+
}
23+
24+
extension SeasonPresentation {
25+
public init ( season:Season) {
26+
self.poster = "https://image.tmdb.org/t/p/w500\(season.posterPath ?? "")"
27+
self.seasonTitle = season.name ?? ""
28+
self.airdate = "\(LocalizableUI.firstAirDate.localized) : \( season.airDate?.toLongDateString() ?? "N/A")"
29+
self.episodeCount = "\(LocalizableUI.season.localized) \( season.episodeCount == 0 ? "-" : "\(season.episodeCount ?? 0)")"
30+
31+
self.rating = "⭐️ \(season.voteAverage ?? 0)"
32+
}
33+
}

0 commit comments

Comments
 (0)