Skip to content

Commit e27ffb2

Browse files
committed
add DataSources and Presenters
1 parent 21c42a8 commit e27ffb2

22 files changed

+776
-410
lines changed

Images/MVC/Views.key

-46.8 KB
Binary file not shown.

Images/MVP/Views.key

1.55 MB
Binary file not shown.

Images/favorite.png

179 KB
Loading

Images/repository.png

105 KB
Loading

Images/search.png

192 KB
Loading

Images/structure.png

15.4 KB
Loading

Images/user_reposiroty.png

158 KB
Loading

README.md

+40-6
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,51 @@
1-
# iOSDesignPatternSamples (MVC)
1+
# iOSDesignPatternSamples (MVP)
22

3-
This is Github user search demo app that made with MVC design pattern.
3+
This is Github user search demo app that made with MVP design pattern.
44

55
## Application Structure
66

77
![](./Images/structure.png)
88

99
## ViewControllers
1010

11-
- [SearchViewController](./iOSDesignPatternSamples/Sources/UI/Search/SearchViewController.swift) -> Search Github user and show user result list
12-
- [FavoriteViewController](./iOSDesignPatternSamples/Sources/UI/Favorite/FavoriteViewController.swift) -> Show local on memory favorite repositories
13-
- [UserRepositoryViewController](./iOSDesignPatternSamples/Sources/UI/UserRepository/UserRepositoryViewController.swift) -> Show Github user's repositories
14-
- [RepositoryViewController](./iOSDesignPatternSamples/Sources/UI/Repository/RepositoryViewController.swift) -> Show a repository and add / remove local on memory favorites
11+
### [SearchViewController](./iOSDesignPatternSamples/Sources/UI/Search/SearchViewController.swift)
12+
Search Github user and show user result list
13+
14+
![](./Images/search.png)
15+
16+
- [SearchView](./iOSDesignPatternSamples/Sources/UI/Search/SearchViewController.swift)
17+
- [SearchPresenter](./iOSDesignPatternSamples/Sources/UI/Search/SearchViewPresenter.swift)
18+
- [SearchViewPresenter](./iOSDesignPatternSamples/Sources/UI/Search/SearchViewPresenter.swift) <- Adapt SearchPresenter
19+
- [SearchViewDataSource](./iOSDesignPatternSamples/Sources/UI/Search/SearchViewDataSource.swift) <- Adapt UITableViewDataSource and UITableViewDelegate
20+
21+
### [FavoriteViewController](./iOSDesignPatternSamples/Sources/UI/Favorite/FavoriteViewController.swift)
22+
Show local on memory favorite repositories
23+
24+
![](./Images/favorite.png)
25+
26+
- [FavoriteView](./iOSDesignPatternSamples/Sources/UI/Favorite/FavoriteViewController.swift)
27+
- [FavoritePresenter](./iOSDesignPatternSamples/Sources/UI/Favorite/FavoriteViewPresenter.swift)
28+
- [FavoriteViewPresenter](./iOSDesignPatternSamples/Sources/UI/Favorite/FavoriteViewPresenter.swift) <- Adapt FavoritePresenter
29+
- [FavoriteViewDataSource](./iOSDesignPatternSamples/Sources/UI/Favorite/FavoriteViewDataSource.swift) <- Adapt UITableViewDataSource and UITableViewDelegate
30+
31+
### [UserRepositoryViewController](./iOSDesignPatternSamples/Sources/UI/UserRepository/UserRepositoryViewController.swift)
32+
Show Github user's repositories
33+
34+
![](./Images/user_reposiroty.png)
35+
36+
- [UserRepositoryView](./iOSDesignPatternSamples/Sources/UI/UserRepository/UserRepositoryViewController.swift)
37+
- [UserRepositoryPresenter](./iOSDesignPatternSamples/Sources/UI/UserRepository/UserRepositoryViewPresenter.swift)
38+
- [UserRepositoryViewPresenter](./iOSDesignPatternSamples/Sources/UI/UserRepository/UserRepositoryViewPresenter.swift) <- Adapt UserRepositoryPresenter
39+
- [UserRepositoryViewDataSource](./iOSDesignPatternSamples/Sources/UI/UserRepository/UserRepositoryViewDataSource.swift) <- Adapt UITableViewDataSource and UITableViewDelegate
40+
41+
### [RepositoryViewController](./iOSDesignPatternSamples/Sources/UI/Repository/RepositoryViewController.swift)
42+
Show a repository and add / remove local on memory favorites
43+
44+
![](./Images/repository.png)
45+
46+
- [RepositoryView](./iOSDesignPatternSamples/Sources/UI/Repository/RepositoryViewController.swift)
47+
- [RepositoryPresenter](./iOSDesignPatternSamples/Sources/UI/Repository/RepositoryViewPresenter.swift)
48+
- [RepositoryViewPresenter](./iOSDesignPatternSamples/Sources/UI/Repository/RepositoryViewPresenter.swift) <- Adapt RepositoryPresenter
1549

1650
## How to add / remove favorites
1751

iOSDesignPatternSamples.xcodeproj/project.pbxproj

+38-14
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,14 @@
77
objects = {
88

99
/* Begin PBXBuildFile section */
10-
37086C1F2017848900D625CA /* NSObjectProtocol.extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37086C1E2017848900D625CA /* NSObjectProtocol.extension.swift */; };
11-
37086C212017850600D625CA /* ApiSession.extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37086C202017850600D625CA /* ApiSession.extension.swift */; };
12-
372936AE1F54538A00762D15 /* FavoriteModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 372936AD1F54538A00762D15 /* FavoriteModel.swift */; };
10+
37086C2620178F1C00D625CA /* ApiSession.extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37086C2320178F1C00D625CA /* ApiSession.extension.swift */; };
11+
37086C2820178F1C00D625CA /* NSObjectProtocol.extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37086C2520178F1C00D625CA /* NSObjectProtocol.extension.swift */; };
12+
375C54291F65073900310929 /* SearchViewDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 375C54281F65073900310929 /* SearchViewDataSource.swift */; };
13+
375C542B1F65079A00310929 /* FavoriteViewPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 375C542A1F65079A00310929 /* FavoriteViewPresenter.swift */; };
14+
375C542D1F6509E900310929 /* FavoriteViewDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 375C542C1F6509E900310929 /* FavoriteViewDataSource.swift */; };
15+
375C542F1F650EA900310929 /* SearchViewPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 375C542E1F650EA900310929 /* SearchViewPresenter.swift */; };
16+
375C54311F65454000310929 /* UserRepositoryViewPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 375C54301F65454000310929 /* UserRepositoryViewPresenter.swift */; };
17+
375C54331F65455700310929 /* UserRepositoryViewDataSource.swift in Sources */ = {isa = PBXBuildFile; fileRef = 375C54321F65455700310929 /* UserRepositoryViewDataSource.swift */; };
1318
37BE2ABB1F3745D0003DC1F8 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 37BE2AB31F3745D0003DC1F8 /* Assets.xcassets */; };
1419
37BE2ABC1F3745D0003DC1F8 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 37BE2AB41F3745D0003DC1F8 /* LaunchScreen.storyboard */; };
1520
37BE2ABD1F3745D0003DC1F8 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 37BE2AB61F3745D0003DC1F8 /* Main.storyboard */; };
@@ -30,12 +35,18 @@
3035
37BE2AEF1F3748EF003DC1F8 /* UIKeyboardWillShow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37BE2AEE1F3748EF003DC1F8 /* UIKeyboardWillShow.swift */; };
3136
37BE2AF51F3759E7003DC1F8 /* LoadingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37BE2AF41F3759E7003DC1F8 /* LoadingView.swift */; };
3237
37BE2AF71F3759F0003DC1F8 /* LoadingView.xib in Resources */ = {isa = PBXBuildFile; fileRef = 37BE2AF61F3759F0003DC1F8 /* LoadingView.xib */; };
38+
37D5D20B1F6599A900FA46DF /* RepositoryViewPresenter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 37D5D20A1F6599A900FA46DF /* RepositoryViewPresenter.swift */; };
3339
/* End PBXBuildFile section */
3440

3541
/* Begin PBXFileReference section */
36-
37086C1E2017848900D625CA /* NSObjectProtocol.extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSObjectProtocol.extension.swift; sourceTree = "<group>"; };
37-
37086C202017850600D625CA /* ApiSession.extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ApiSession.extension.swift; sourceTree = "<group>"; };
38-
372936AD1F54538A00762D15 /* FavoriteModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FavoriteModel.swift; sourceTree = "<group>"; };
42+
37086C2320178F1C00D625CA /* ApiSession.extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ApiSession.extension.swift; sourceTree = "<group>"; };
43+
37086C2520178F1C00D625CA /* NSObjectProtocol.extension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NSObjectProtocol.extension.swift; sourceTree = "<group>"; };
44+
375C54281F65073900310929 /* SearchViewDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchViewDataSource.swift; sourceTree = "<group>"; };
45+
375C542A1F65079A00310929 /* FavoriteViewPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FavoriteViewPresenter.swift; sourceTree = "<group>"; };
46+
375C542C1F6509E900310929 /* FavoriteViewDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FavoriteViewDataSource.swift; sourceTree = "<group>"; };
47+
375C542E1F650EA900310929 /* SearchViewPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SearchViewPresenter.swift; sourceTree = "<group>"; };
48+
375C54301F65454000310929 /* UserRepositoryViewPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserRepositoryViewPresenter.swift; sourceTree = "<group>"; };
49+
375C54321F65455700310929 /* UserRepositoryViewDataSource.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserRepositoryViewDataSource.swift; sourceTree = "<group>"; };
3950
37817D031F373F8B00EC69C6 /* iOSDesignPatternSamples.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = iOSDesignPatternSamples.app; sourceTree = BUILT_PRODUCTS_DIR; };
4051
37BE2AB31F3745D0003DC1F8 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
4152
37BE2AB51F3745D0003DC1F8 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
@@ -58,6 +69,7 @@
5869
37BE2AEE1F3748EF003DC1F8 /* UIKeyboardWillShow.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UIKeyboardWillShow.swift; sourceTree = "<group>"; };
5970
37BE2AF41F3759E7003DC1F8 /* LoadingView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LoadingView.swift; sourceTree = "<group>"; };
6071
37BE2AF61F3759F0003DC1F8 /* LoadingView.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = LoadingView.xib; sourceTree = "<group>"; };
72+
37D5D20A1F6599A900FA46DF /* RepositoryViewPresenter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RepositoryViewPresenter.swift; sourceTree = "<group>"; };
6173
/* End PBXFileReference section */
6274

6375
/* Begin PBXFrameworksBuildPhase section */
@@ -77,11 +89,11 @@
7789
/* End PBXFrameworksBuildPhase section */
7890

7991
/* Begin PBXGroup section */
80-
37086C1D2017847A00D625CA /* Extension */ = {
92+
37086C2220178F1C00D625CA /* Extension */ = {
8193
isa = PBXGroup;
8294
children = (
83-
37086C202017850600D625CA /* ApiSession.extension.swift */,
84-
37086C1E2017848900D625CA /* NSObjectProtocol.extension.swift */,
95+
37086C2320178F1C00D625CA /* ApiSession.extension.swift */,
96+
37086C2520178F1C00D625CA /* NSObjectProtocol.extension.swift */,
8597
);
8698
path = Extension;
8799
sourceTree = "<group>";
@@ -135,8 +147,7 @@
135147
isa = PBXGroup;
136148
children = (
137149
37BE2AC11F37460C003DC1F8 /* AppDelegate.swift */,
138-
372936AD1F54538A00762D15 /* FavoriteModel.swift */,
139-
37086C1D2017847A00D625CA /* Extension */,
150+
37086C2220178F1C00D625CA /* Extension */,
140151
37BE2AE91F374889003DC1F8 /* NotieObserver */,
141152
);
142153
path = Common;
@@ -159,6 +170,8 @@
159170
isa = PBXGroup;
160171
children = (
161172
37BE2AC81F37468C003DC1F8 /* FavoriteViewController.swift */,
173+
375C542A1F65079A00310929 /* FavoriteViewPresenter.swift */,
174+
375C542C1F6509E900310929 /* FavoriteViewDataSource.swift */,
162175
);
163176
path = Favorite;
164177
sourceTree = "<group>";
@@ -167,6 +180,7 @@
167180
isa = PBXGroup;
168181
children = (
169182
37BE2ACC1F3746E2003DC1F8 /* RepositoryViewController.swift */,
183+
37D5D20A1F6599A900FA46DF /* RepositoryViewPresenter.swift */,
170184
);
171185
path = Repository;
172186
sourceTree = "<group>";
@@ -175,6 +189,8 @@
175189
isa = PBXGroup;
176190
children = (
177191
37BE2ACA1F374699003DC1F8 /* SearchViewController.swift */,
192+
375C542E1F650EA900310929 /* SearchViewPresenter.swift */,
193+
375C54281F65073900310929 /* SearchViewDataSource.swift */,
178194
);
179195
path = Search;
180196
sourceTree = "<group>";
@@ -183,6 +199,8 @@
183199
isa = PBXGroup;
184200
children = (
185201
37BE2AD01F3746FA003DC1F8 /* UserRepositoryViewController.swift */,
202+
375C54301F65454000310929 /* UserRepositoryViewPresenter.swift */,
203+
375C54321F65455700310929 /* UserRepositoryViewDataSource.swift */,
186204
37BE2AD11F3746FA003DC1F8 /* UserRepositoryViewController.xib */,
187205
);
188206
path = UserRepository;
@@ -318,16 +336,22 @@
318336
buildActionMask = 2147483647;
319337
files = (
320338
37BE2AEB1F3748B6003DC1F8 /* UIKeyboardInfo.swift in Sources */,
321-
372936AE1F54538A00762D15 /* FavoriteModel.swift in Sources */,
322-
37086C212017850600D625CA /* ApiSession.extension.swift in Sources */,
339+
375C542B1F65079A00310929 /* FavoriteViewPresenter.swift in Sources */,
340+
375C542F1F650EA900310929 /* SearchViewPresenter.swift in Sources */,
323341
37BE2ACE1F3746E2003DC1F8 /* RepositoryViewController.swift in Sources */,
324342
37BE2AF51F3759E7003DC1F8 /* LoadingView.swift in Sources */,
343+
375C542D1F6509E900310929 /* FavoriteViewDataSource.swift in Sources */,
325344
37BE2ACB1F374699003DC1F8 /* SearchViewController.swift in Sources */,
345+
375C54291F65073900310929 /* SearchViewDataSource.swift in Sources */,
326346
37BE2AEF1F3748EF003DC1F8 /* UIKeyboardWillShow.swift in Sources */,
327347
37BE2AD21F3746FA003DC1F8 /* UserRepositoryViewController.swift in Sources */,
348+
37086C2620178F1C00D625CA /* ApiSession.extension.swift in Sources */,
349+
375C54311F65454000310929 /* UserRepositoryViewPresenter.swift in Sources */,
350+
37D5D20B1F6599A900FA46DF /* RepositoryViewPresenter.swift in Sources */,
328351
37BE2AED1F3748D9003DC1F8 /* UIKeyboardWillHide.swift in Sources */,
329-
37086C1F2017848900D625CA /* NSObjectProtocol.extension.swift in Sources */,
330352
37BE2AC91F37468C003DC1F8 /* FavoriteViewController.swift in Sources */,
353+
37086C2820178F1C00D625CA /* NSObjectProtocol.extension.swift in Sources */,
354+
375C54331F65455700310929 /* UserRepositoryViewDataSource.swift in Sources */,
331355
37BE2AC21F37460C003DC1F8 /* AppDelegate.swift in Sources */,
332356
);
333357
runOnlyForDeploymentPostprocessing = 0;

iOSDesignPatternSamples/Sources/Common/AppDelegate.swift

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
2424
let favoriteVC = viewControllers.flatMap({
2525
($0 as? UINavigationController)?.topViewController as? FavoriteViewController
2626
}).first {
27-
searchVC.favoriteModel = favoriteVC.favoriteModel
27+
searchVC.favoritePresenter = favoriteVC.presenter
2828
}
2929

3030
return true

iOSDesignPatternSamples/Sources/Common/FavoriteModel.swift

-37
This file was deleted.

iOSDesignPatternSamples/Sources/UI/Favorite/FavoriteViewController.swift

+13-42
Original file line numberDiff line numberDiff line change
@@ -9,61 +9,32 @@
99
import UIKit
1010
import GithubKit
1111

12-
final class FavoriteViewController: UIViewController {
12+
protocol FavoriteView: class {
13+
func reloadData()
14+
func showRepository(with repository: Repository)
15+
}
16+
17+
final class FavoriteViewController: UIViewController, FavoriteView {
1318
@IBOutlet weak var tableView: UITableView!
1419

15-
let favoriteModel = FavoriteModel()
20+
private(set) lazy var presenter: FavoritePresenter = FavoriteViewPresenter(view: self)
21+
private lazy var dataSource: FavoriteViewDataSource = .init(presenter: self.presenter)
1622

1723
override func viewDidLoad() {
1824
super.viewDidLoad()
1925

2026
title = "On Memory Favorite"
2127
automaticallyAdjustsScrollViewInsets = false
2228

23-
favoriteModel.delegate = self
24-
configure(with: tableView)
25-
}
26-
27-
private func configure(with tableView: UITableView) {
28-
tableView.dataSource = self
29-
tableView.delegate = self
30-
31-
tableView.register(RepositoryViewCell.self)
29+
dataSource.configure(with: tableView)
3230
}
3331

34-
fileprivate func showRepository(with repository: Repository) {
35-
let vc = RepositoryViewController(repository: repository, favoriteModel: favoriteModel)
32+
func showRepository(with repository: Repository) {
33+
let vc = RepositoryViewController(repository: repository, favoritePresenter: presenter)
3634
navigationController?.pushViewController(vc, animated: true)
3735
}
38-
}
39-
40-
extension FavoriteViewController: FavoriteModelDelegate {
41-
func favoriteDidChange() {
42-
tableView.reloadData()
43-
}
44-
}
45-
46-
extension FavoriteViewController: UITableViewDataSource {
47-
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
48-
return favoriteModel.favorites.count
49-
}
50-
51-
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
52-
let cell = tableView.dequeue(RepositoryViewCell.self, for: indexPath)
53-
cell.configure(with: favoriteModel.favorites[indexPath.row])
54-
return cell
55-
}
56-
}
57-
58-
extension FavoriteViewController: UITableViewDelegate {
59-
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
60-
tableView.deselectRow(at: indexPath, animated: false)
61-
62-
let repository = favoriteModel.favorites[indexPath.row]
63-
showRepository(with: repository)
64-
}
6536

66-
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
67-
return RepositoryViewCell.calculateHeight(with: favoriteModel.favorites[indexPath.row], and: tableView)
37+
func reloadData() {
38+
tableView?.reloadData()
6839
}
6940
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
//
2+
// FavoriteViewDataSource.swift
3+
// iOSDesignPatternSamples
4+
//
5+
// Created by marty-suzuki on 2017/09/10.
6+
// Copyright © 2017年 marty-suzuki. All rights reserved.
7+
//
8+
9+
import Foundation
10+
import UIKit
11+
import GithubKit
12+
13+
final class FavoriteViewDataSource: NSObject {
14+
fileprivate let presenter: FavoritePresenter
15+
16+
init(presenter: FavoritePresenter) {
17+
self.presenter = presenter
18+
}
19+
20+
func configure(with tableView: UITableView) {
21+
tableView.dataSource = self
22+
tableView.delegate = self
23+
24+
tableView.register(RepositoryViewCell.self)
25+
}
26+
}
27+
28+
extension FavoriteViewDataSource: UITableViewDataSource {
29+
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
30+
return presenter.numberOfFavorites
31+
}
32+
33+
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
34+
let cell = tableView.dequeue(RepositoryViewCell.self, for: indexPath)
35+
let repository = presenter.favoriteRepository(at: indexPath.row)
36+
cell.configure(with: repository)
37+
return cell
38+
}
39+
}
40+
41+
extension FavoriteViewDataSource: UITableViewDelegate {
42+
func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
43+
tableView.deselectRow(at: indexPath, animated: false)
44+
presenter.showFavoriteRepository(at: indexPath.row)
45+
}
46+
47+
func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat {
48+
let repository = presenter.favoriteRepository(at: indexPath.row)
49+
return RepositoryViewCell.calculateHeight(with: repository, and: tableView)
50+
}
51+
}

0 commit comments

Comments
 (0)