(reducer: MainReducer(), state: nil, middleware: [])
14 |
15 |
16 | @UIApplicationMain
17 | class AppDelegate: UIResponder, UIApplicationDelegate {
18 |
19 | var window: UIWindow?
20 |
21 |
22 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
23 |
24 | // Configure SDKs
25 | ApplicationManager.sharedInstance.initAllSDKs()
26 |
27 | // Init Common things
28 | ApplicationManager.sharedInstance.initCommon(window: self.window)
29 |
30 | return true
31 | }
32 |
33 | func applicationWillResignActive(_ application: UIApplication) {
34 | // Sent when the application is about to move from active to inactive state. This can occur for certain types of temporary interruptions (such as an incoming phone call or SMS message) or when the user quits the application and it begins the transition to the background state.
35 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game.
36 | }
37 |
38 | func applicationDidEnterBackground(_ application: UIApplication) {
39 | // Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
40 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.
41 | }
42 |
43 | func applicationWillEnterForeground(_ application: UIApplication) {
44 | // Called as part of the transition from the background to the active state; here you can undo many of the changes made on entering the background.
45 | }
46 |
47 | func applicationDidBecomeActive(_ application: UIApplication) {
48 | // Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
49 | }
50 |
51 | func applicationWillTerminate(_ application: UIApplication) {
52 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
53 | }
54 |
55 |
56 | }
57 |
58 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Mad lab •
8 | TITAN •
9 | FeSpinner
10 |
11 | iOS Awesome Starter Kit •
12 | FeSlideFilter •
13 | Responsive Interaction Control
14 |
15 |
16 | Awesome iOS Starter Kit
17 | ------------
18 |
19 | The starter kit is designed to help iOS develop can implement their app quickly and resolve common problem easily.
20 | It includes bunch of framework at top of technologies, inlcude new Clean Swift Architecture.
21 | For further infomation, please check out my keynote below.
22 |
23 | 
24 | 
25 | 
26 |
27 | What's inside
28 | ------------
29 |
30 |
31 |
32 |
33 |
34 | + ReSwift
35 | + Clean Swift template
36 | + Promise Kit
37 | + Userful classes: Networking, BaseObj, Logger, ApplicationManager, Identifier, Registerable, BaseAbility, Worker, Slack Report,....
38 | + ...
39 |
40 | Roadmap
41 | ------------
42 |
43 | - [x] Base Foundation
44 | - [x] Clean Swift Example
45 | - [x] Networking + Request Protocol
46 | - [x] Worker Protocol
47 | - [x] Object Mapping
48 | - [ ] Caching Manager
49 | - [ ] Authentication
50 | - [ ] Realm Driver
51 | - [x] Router
52 | - [x] Disk Manager
53 | - [ ] Transition Manager
54 | - [ ] Test
55 |
56 | Presentation at Swift Vietnam
57 | ------------
58 | ✏️[Keynote](https://github.com/NghiaTranUIT/iOS-Awesome-Starter-Kit/blob/master/Swift%20Vietnam%20Presentation/MobileMeetup.key)
59 | ✏️[Video Facebook](https://www.facebook.com/swift.org.vn/videos/537074413148880/) 🔴 59:00
60 |
61 | Reference
62 | ------------
63 | 1. http://blog.benjamin-encz.de/post/real-world-flux-ios/
64 | 2. http://clean-swift.com/clean-swift-ios-architecture/
65 |
66 | Question 🤔
67 | ------------
68 | If you have any problem, feels free to shot me an message in `ios-starter-kit` group at [SwiftVietnam](http://chat.swift.org.vn)
69 |
70 | Contact
71 | ------------
72 |
73 | Vinh Nghia Tran
74 |
75 | http://github.com/NghiaTranUIT
76 | http://www.nghiatran.me
77 | vinhnghiatran@gmail.com
78 |
79 | Contributor
80 | ------------
81 |
82 | It would be greatly appreciated when you make a pull-quest 🤗
83 |
84 |
85 | License
86 | ------------
87 | * [Clean Swift](http://clean-swift.com) by *Raymond Law*
88 | * [ReSwift](https://github.com/ReSwift/ReSwift) by @benjaminencz and his team.
89 | * [PromiseKit](https://github.com/mxcl/PromiseKit) by @mxcl
90 |
91 | iOS Awesome Starter Kit is available under the MIT license. See the LICENSE file for more info.
92 |
93 |
--------------------------------------------------------------------------------
/iOS-Starter-Kit/Shared/Common/Logger/Logger.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Logger.swift
3 | // Titan
4 | //
5 | // Created by Nghia Tran on 10/12/16.
6 | // Copyright © 2016 fe. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import SwiftyBeaver
11 |
12 | // MARK:
13 | // MARK: Log Instace
14 | class Log: NSObject {
15 |
16 | let log = SwiftyBeaver.self
17 |
18 | // Share instance
19 | static var shareInstance = Log()
20 |
21 | override init() {
22 | super.init()
23 |
24 | // Log console
25 | let console = ConsoleDestination() // log to Xcode Console
26 | self.log.addDestination(console)
27 | }
28 |
29 | // MARK:
30 | // MARK: Public
31 | func error(_ error:Any, fileName: String, functionName: String, line: Int) {
32 | self.log.error(error, fileName, functionName, line: line)
33 | }
34 |
35 | func warning(_ warning:Any, _ file: String, _ function: String, _ line: Int) {
36 | self.log.warning(warning)
37 | }
38 |
39 | func debug(_ debug:Any, _ file: String, _ function: String, _ line: Int) {
40 | self.log.debug(debug)
41 | }
42 |
43 | func info(_ info:Any, _ file: String, _ function: String, _ line: Int) {
44 | self.log.info(info, file, function, line: line)
45 | }
46 |
47 | func verbose(_ verbose:Any, _ file: String, _ function: String, _ line: Int) {
48 | self.log.verbose(verbose)
49 | }
50 | }
51 |
52 |
53 | // MARK:
54 | // MARK: Helper
55 | class Logger {
56 |
57 | // Helper
58 | // MARK: Public
59 | class func initLogger() {
60 | _ = Log.shareInstance
61 | }
62 |
63 | class func error(_ error:Any, toSlack:Bool = true, fileName: String = #file, functionName: String = #function, line: Int = #line) {
64 |
65 | // Console
66 | Log.shareInstance.error(error, fileName: fileName, functionName: functionName, line: line)
67 |
68 | if toSlack {
69 | let errorObj = NSError.errorWithMessage(message: "\(error)")
70 | SlackReporter.shareInstance.reportErrorData(SlackReporterData(error: errorObj, fileName: fileName, functionName: functionName, line: line))
71 | }
72 | }
73 |
74 | class func warning(_ warning: Any, _ file: String = #file, _ function: String = #function, _ line: Int = #line) {
75 | Log.shareInstance.warning(warning, file, function, line)
76 | }
77 |
78 | class func debug(_ debug: Any, _ file: String = #file, _ function: String = #function, _ line: Int = #line) {
79 | Log.shareInstance.debug(debug, file, function, line)
80 | }
81 |
82 | class func info(_ info: Any, _ file: String = #file, _ function: String = #function, _ line: Int = #line) {
83 | Log.shareInstance.info(info, file, function, line)
84 | }
85 |
86 | class func verbose(_ verbose: Any, _ file: String = #file, _ function: String = #function, _ line: Int = #line) {
87 | Log.shareInstance.verbose(verbose, file, function, line)
88 | }
89 | }
90 |
91 |
92 | extension NSError {
93 | class func errorWithMessage(message: String) -> NSError {
94 | return NSError(domain: "com.fe.feels", code: 999, userInfo: [NSLocalizedDescriptionKey: message])
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/Swift Vietnam Presentation/CleanSwift/CleanSwift/RepoCell.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/iOS-Starter-Kit/Source Code/Scenes/Example - Repo/View/RepoCell.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/Swift Vietnam Presentation/MVC-Repo-Github/MVC-Repo-Github/RepoCell.xib:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
--------------------------------------------------------------------------------
/Swift Vietnam Presentation/MVC-Repo-Github/MVC-Repo-Github/RepoViewController.swift:
--------------------------------------------------------------------------------
1 | //
2 | // RepoViewController.swift
3 | // MVC-Repo-Github
4 | //
5 | // Created by Nghia Tran on 12/16/16.
6 | // Copyright © 2016 nghiatran. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | class RepoViewController: UIViewController {
12 |
13 | //
14 | // MARK: - Variable
15 | fileprivate var repos: [RepoObj] = []
16 |
17 | //
18 | // MARK: - OUTLET
19 |
20 | @IBOutlet weak var searchBar: UISearchBar!
21 | @IBOutlet weak var tableView: UITableView!
22 |
23 | //
24 | // MARK: - View Cycle
25 | override func viewDidLoad() {
26 | super.viewDidLoad()
27 |
28 | //
29 | self.initCommon()
30 | self.initTableView()
31 |
32 | // Load data
33 | self.fetchData()
34 | self.fetchUserInfo()
35 | }
36 |
37 | override func didReceiveMemoryWarning() {
38 | super.didReceiveMemoryWarning()
39 | // Dispose of any resources that can be recreated.
40 | }
41 |
42 | //
43 | // MARK: - Private
44 | private func initCommon() {
45 |
46 | }
47 |
48 | private func initTableView() {
49 |
50 | self.tableView.delegate = self
51 | self.tableView.dataSource = self
52 |
53 | self.tableView.register(UINib(nibName: "RepoCell", bundle: nil), forCellReuseIdentifier: "RepoCell")
54 | }
55 |
56 | private func fetchData() {
57 |
58 | guard let text = self.searchBar.text else {return}
59 |
60 | // Fetch
61 | Networking.shared.fetchRepoWithText(text: text) { (result) in
62 |
63 | // Handle here
64 | switch result {
65 | case .success(let repos):
66 |
67 | // Update
68 | self.repos = repos as! [RepoObj]
69 | self.tableView.reloadData()
70 | case .failed(let error):
71 |
72 | // Show error
73 | print(error)
74 | break
75 | }
76 | }
77 | }
78 |
79 | private func fetchUserInfo() {
80 |
81 | // Fetch Current User
82 | Networking.shared.fetchCurrentUser()
83 |
84 | // Fetch Setting
85 | Networking.shared.fetchSetting
86 |
87 | // Fetch ...
88 | }
89 | }
90 |
91 |
92 | //
93 | // MARK: - TableView
94 | extension RepoViewController: UITableViewDataSource {
95 |
96 | func numberOfSections(in tableView: UITableView) -> Int {
97 | return 1
98 | }
99 |
100 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
101 | return self.repos.count
102 | }
103 |
104 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
105 |
106 | let cell = tableView.dequeueReusableCell(withIdentifier: "RepoCell", for: indexPath) as! RepoCell
107 |
108 | let repo = self.repos[indexPath.row]
109 | cell.configureCellWithRepo(repo: repo)
110 |
111 | return cell
112 | }
113 | }
114 |
115 | extension RepoViewController: UITableViewDelegate {
116 |
117 | }
118 |
--------------------------------------------------------------------------------
/iOS-Starter-Kit/Configuration/Constants.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Constants.swift
3 | // feels
4 | //
5 | // Created by Nghia Tran Vinh on 5/24/16.
6 | // Copyright © 2016 fe. All rights reserved.
7 | //
8 |
9 | import UIKit
10 |
11 | struct Constants {
12 |
13 |
14 | // APPLICATION
15 | struct App {
16 |
17 |
18 | // Main
19 | static let isDebugJSON = true
20 | static let isHTTPS = false
21 |
22 |
23 | // Base
24 | static let BaseURL: String = {
25 | if Constants.App.isHTTPS {
26 | return "https://"
27 | }
28 | else {
29 | return "http://"
30 | }
31 | }()
32 |
33 |
34 | // Key
35 | struct Key {
36 |
37 | // Key Chain
38 | struct KeyChain {
39 |
40 | }
41 |
42 | // Twitter
43 | struct Twitter {
44 |
45 | }
46 |
47 | // Instagram
48 | struct Instagram {
49 |
50 | }
51 |
52 | // S3 amazon
53 | struct S3Amazon {
54 |
55 | }
56 |
57 | // Youtube
58 | struct Youtube {
59 |
60 | }
61 | }
62 |
63 | #if DEBUG // -> Development
64 |
65 | static let BaseAPIURL = "your.base.endpoint.development"
66 |
67 | #else // -> Production
68 |
69 | static let BaseAPIURL = "your.base.endpoint.production"
70 |
71 | #endif
72 |
73 | }
74 |
75 |
76 | // MARK:
77 | // MARK: Feels
78 | struct APIEndPoint {
79 |
80 | static let RepoList = "/search/repositories"
81 |
82 | }
83 |
84 |
85 | // MARK:
86 | // MARK: KeyAPI
87 | struct APIKey {
88 |
89 | }
90 |
91 |
92 | // MARK:
93 | // MARK: Feels Object
94 | struct Obj {
95 |
96 |
97 | // MARK:
98 | // MARK: BASE
99 | static let CreatedAt = "created_at"
100 | static let UpdatedAt = "updated_at"
101 | static let ObjectId = "id"
102 |
103 |
104 | // MARK:
105 | // MARK: USER
106 | struct User {
107 | static let Name = "name"
108 | static let Username = "username"
109 | static let Email = "email"
110 | }
111 |
112 |
113 | // MARK:
114 | // MARK: Repo
115 | struct Repo {
116 | static let Name = "name"
117 | }
118 | }
119 |
120 | //
121 | // MARK: - Logger
122 | struct Logger {
123 |
124 | // Slack Report
125 | struct Slack {
126 |
127 | // Base
128 | static let Token = "your.token.slack"
129 | static let ErrorChannel = "name.error.slack.channel"
130 | static let ResponseChannel = "name.response.slack.channel"
131 |
132 |
133 | // Webhook integration
134 | static let ErrorChannel_Webhook = "webhook.error.channel"
135 | static let ResponseChannel_Webhook = "webhook.response.channel"
136 | }
137 | }
138 | }
139 |
--------------------------------------------------------------------------------
/Swift Vietnam Presentation/MVC-Repo-Github/MVC-Repo-Github/Networking.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Networking.swift
3 | // MVC-Repo-Github
4 | //
5 | // Created by Nghia Tran on 12/16/16.
6 | // Copyright © 2016 nghiatran. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Alamofire
11 | import ObjectMapper
12 |
13 | typealias NetworkCompletionBlock = (NetworkResult)->()
14 |
15 | //
16 | // MARK: - Result warpper
17 | enum NetworkResult {
18 | case success(T)
19 | case failed(Error)
20 | }
21 |
22 |
23 | //
24 | // MARK: - Networking
25 | class Networking {
26 |
27 |
28 | /// Singleton
29 | static let shared = Networking()
30 |
31 |
32 | /// Fetch Repo
33 | func fetchRepoWithText(text: String, completion: NetworkCompletionBlock?) {
34 |
35 | // Encoding
36 | let escapedQuery = text.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) ?? ""
37 |
38 | // url
39 | let url = URL(string: "https://api.github.com/search/repositories?q=\(escapedQuery)")!
40 |
41 | // Query
42 | Alamofire.request(url)
43 | .validate()
44 | .responseJSON { (response) in
45 |
46 | let result = response.result
47 |
48 | switch result {
49 | case .success(let value):
50 |
51 | // Parse
52 | let repoObjs = Mapper().mapArray(JSONObject: value) ?? []
53 |
54 | // Complete
55 | completion?(NetworkResult.success(repoObjs))
56 |
57 | case .failure(let error):
58 | completion?(NetworkResult.failed(error))
59 | }
60 | }
61 | }
62 |
63 |
64 | // Fetch current user
65 | func fetchCurrentUser() {
66 |
67 | // Fetch current user
68 | let urlCurrentUser = URL(string: "/user")!
69 | Alamofire.request(urlCurrentUser)
70 | .validate()
71 | .responseJSON { (response) in
72 |
73 | // Check
74 | switch response.result {
75 | case .success:
76 |
77 | // Check
78 | switch response.result {
79 | case .success:
80 |
81 | // Fetch Setting
82 | let urlSetting = URL(string: "/setting")!
83 | Alamofire.request(urlSetting)
84 | .validate()
85 | .responseJSON(completionHandler: { (response) in
86 |
87 | // Fetch notification
88 | let urlNotification = URL(string: "/notification")!
89 | Alamofire.request(urlNotification)
90 | .validate()
91 | .responseJSON(completionHandler: { (response) in
92 |
93 | // Fetch more here
94 | // -> 😡 Too much call-back
95 |
96 | })
97 | })
98 | case .failure(let error):
99 |
100 | // Handle error
101 | print("fail \(error)") // -> 😡 Duplicated
102 | break
103 | }
104 |
105 | case .failure(let error):
106 |
107 | // Handle error
108 | print("fail \(error)") // -> 😡 Duplicated
109 | break
110 | }
111 | }
112 | }
113 | }
114 |
--------------------------------------------------------------------------------
/Swift Vietnam Presentation/CleanSwift/CleanSwift/Networking.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Networking.swift
3 | // MVC-Repo-Github
4 | //
5 | // Created by Nghia Tran on 12/16/16.
6 | // Copyright © 2016 nghiatran. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Alamofire
11 | import ObjectMapper
12 | import PromiseKit
13 |
14 | //
15 | // MARK: - Result warpper
16 | enum NetworkResult {
17 | case success(T)
18 | case failed(Error)
19 | }
20 |
21 |
22 | //
23 | // MARK: - Networking
24 | class Networking {
25 |
26 |
27 | /// Singleton
28 | static let shared = Networking()
29 |
30 |
31 | /// Fetch Repo
32 | func fetchRepoWithText(text: String) -> Promise<[RepoObj]> {
33 |
34 | return Promise { fulfill, reject in
35 |
36 | // Encoding
37 | let escapedQuery = text.addingPercentEncoding(withAllowedCharacters: .urlHostAllowed) ?? ""
38 |
39 | // url
40 | let url = URL(string: "https://api.github.com/search/repositories?q=\(escapedQuery)")!
41 |
42 | // Query
43 | Alamofire.request(url)
44 | .validate()
45 | .responseJSON { (response) in
46 |
47 | let result = response.result
48 |
49 | switch result {
50 | case .success(let value):
51 |
52 | // Parse
53 | let repoObjs = Mapper().mapArray(JSONObject: value) ?? []
54 |
55 | // Full fill
56 | fulfill(repoObjs)
57 |
58 | case .failure(let error):
59 |
60 | // Reject
61 | reject(error)
62 | }
63 | }
64 | }
65 | }
66 |
67 |
68 | // Fetch current user
69 | func fetchCurrentUser() {
70 |
71 | // Fetch current user
72 | let urlCurrentUser = URL(string: "/user")!
73 | Alamofire.request(urlCurrentUser)
74 | .validate()
75 | .responseJSON { (response) in
76 |
77 | // Check
78 | switch response.result {
79 | case .success:
80 |
81 | // Check
82 | switch response.result {
83 | case .success:
84 |
85 | // Fetch Setting
86 | let urlSetting = URL(string: "/setting")!
87 | Alamofire.request(urlSetting)
88 | .validate()
89 | .responseJSON(completionHandler: { (response) in
90 |
91 | // Fetch notification
92 | let urlNotification = URL(string: "/notification")!
93 | Alamofire.request(urlNotification)
94 | .validate()
95 | .responseJSON(completionHandler: { (response) in
96 |
97 | // Fetch more here
98 | // -> 😡 Too much call-back
99 |
100 | })
101 | })
102 | case .failure(let error):
103 |
104 | // Handle error
105 | print("fail \(error)") // -> 😡 Duplicated
106 | break
107 | }
108 |
109 | case .failure(let error):
110 |
111 | // Handle error
112 | print("fail \(error)") // -> 😡 Duplicated
113 | break
114 | }
115 | }
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/iOS-Starter-Kit.xcodeproj/xcuserdata/nghiatran.xcuserdatad/xcschemes/iOS-Starter-Kit.xcscheme:
--------------------------------------------------------------------------------
1 |
2 |
5 |
8 |
9 |
15 |
21 |
22 |
23 |
24 |
25 |
30 |
31 |
33 |
39 |
40 |
41 |
42 |
43 |
49 |
50 |
51 |
52 |
53 |
54 |
64 |
66 |
72 |
73 |
74 |
75 |
76 |
77 |
83 |
85 |
91 |
92 |
93 |
94 |
96 |
97 |
100 |
101 |
102 |
--------------------------------------------------------------------------------
/iOS-Starter-Kit/Source Code/Model/Networking/Request/Requestable.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Requestable.swift
3 | // Titan
4 | //
5 | // Created by Nghia Tran on 10/12/16.
6 | // Copyright © 2016 fe. All rights reserved.
7 | //
8 |
9 | import ReSwift
10 | import Alamofire
11 | import ObjectMapper
12 | import PromiseKit
13 |
14 |
15 | //
16 | // MARK: - Requestable protocol
17 | protocol Requestable: Action, URLRequestConvertible {
18 |
19 | associatedtype T
20 |
21 | var basePath: String {get}
22 |
23 | var endpoint: String {get}
24 |
25 | var httpMethod: HTTPMethod {get}
26 |
27 | var param: Parameters? {get}
28 |
29 | var addionalHeader: HeaderParameter? {get}
30 |
31 | var parameterEncoding: ParameterEncoding {get}
32 |
33 | func toPromise() -> Promise
34 |
35 | func decode(data: Any) -> T
36 |
37 | init(param: Parameters?)
38 | }
39 |
40 |
41 | //
42 | // MARK: - Conform URLConvitible from Alamofire
43 | extension Requestable {
44 | func asURLRequest() -> URLRequest {
45 | return self.buildURLRequest()
46 | }
47 | }
48 |
49 |
50 | //
51 | // MARK: - Default implementation
52 | extension Requestable {
53 |
54 | // Variable
55 | typealias Parameters = [String: Any]
56 | typealias HeaderParameter = [String: String]
57 | typealias JSONDictionary = [String: Any]
58 |
59 |
60 | // Base
61 | var basePath: String {
62 | get { return Constants.App.BaseURL }
63 | }
64 |
65 |
66 | // Param
67 | var param: Parameters? {
68 | get { return nil }
69 | }
70 |
71 |
72 | // Additional Header
73 | var addionalHeader: HeaderParameter? {
74 | get { return nil }
75 | }
76 |
77 |
78 | // Default
79 | var defaultHeader: HeaderParameter {
80 | get { return ["Accept": "application/json"] }
81 | }
82 |
83 |
84 | // Path
85 | var urlPath: String {
86 | return basePath + endpoint
87 | }
88 |
89 |
90 | // URL
91 | var url: URL {
92 | return URL(string: urlPath)!
93 | }
94 |
95 |
96 | // Encoode
97 | var parameterEncoding: ParameterEncoding {
98 | get { return JSONEncoding.default }
99 | }
100 |
101 |
102 | // Promise
103 | func toPromise() -> Promise {
104 |
105 | return Promise { fulfill, reject in
106 |
107 | guard let urlRequest = try? self.asURLRequest() else {
108 | reject(NSError.unknowError())
109 | return
110 | }
111 |
112 | Alamofire.request(urlRequest)
113 | .validate(statusCode: 200..<300)
114 | .validate(contentType: ["application/json"])
115 | .responseJSON(completionHandler: { (response) in
116 |
117 | // Check error
118 | if let error = response.result.error {
119 | reject(error as NSError)
120 | return
121 | }
122 |
123 | // Check Response
124 | guard let data = response.result.value else {
125 | reject(NSError.jsonMapperError())
126 | return
127 | }
128 |
129 | // Parse here
130 | let result = self.decode(data: data)
131 |
132 | // Fill
133 | fulfill(result)
134 | })
135 | }
136 | }
137 |
138 | // Build URL Request
139 | func buildURLRequest() -> URLRequest {
140 |
141 | // Init
142 | var urlRequest = URLRequest(url: self.url)
143 | urlRequest.httpMethod = self.httpMethod.rawValue
144 | urlRequest.timeoutInterval = TimeInterval(10 * 1000)
145 |
146 | // Encode param
147 | var request = try! self.parameterEncoding.encode(urlRequest, with: self.param)
148 |
149 | // Add addional Header if need
150 | if let additinalHeaders = self.addionalHeader {
151 | for (key, value) in additinalHeaders {
152 | request.addValue(value, forHTTPHeaderField: key)
153 | }
154 | }
155 |
156 | return request
157 | }
158 | }
159 |
--------------------------------------------------------------------------------
/iOS-Starter-Kit/Source Code/Model/DiskManager.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DiskManager.swift
3 | // iOS-Starter-Kit
4 | //
5 | // Created by Nghia Tran on 3/20/17.
6 | // Copyright © 2017 nghiatran. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import ObjectMapper
11 |
12 | //
13 | // MARK: - FileType
14 | enum FileType: String {
15 |
16 | case userFile = "currentUser.feels"
17 | case installationFile = "currentInstallation.feels"
18 |
19 | func toFilePath() -> String {
20 | switch self {
21 | case .userFile:
22 | return DiskManager.shareInstance.pathForCurrentUserFile
23 | case .installationFile:
24 | return DiskManager.shareInstance.pathForInstallationFile
25 | }
26 | }
27 |
28 | static let allValue: [FileType] = [.userFile, .installationFile]
29 | }
30 |
31 | class DiskManager {
32 |
33 | // MARK:
34 | // MARK: Vairable
35 | fileprivate let safe = EREW()
36 | static let shareInstance = DiskManager()
37 |
38 | // MARK:
39 | // MARK: Private
40 | lazy var pathForCurrentUserFile: String = {return (self.pathForDocumentFolder as NSString).appendingPathComponent(FileType.userFile.rawValue)}()
41 | lazy var pathForInstallationFile: String = {return (self.pathForDocumentFolder as NSString).appendingPathComponent(FileType.installationFile.rawValue)}()
42 |
43 | fileprivate lazy var pathForDocumentFolder: String = {
44 | let path: String = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true).first!
45 | return path
46 | }()
47 |
48 | fileprivate lazy var urlForDocumentFolder: URL = {
49 | let url = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
50 | return url
51 | }()
52 |
53 | fileprivate lazy var pathForDirectioryFolder: String = {
54 | let path = FileManager.default.currentDirectoryPath
55 | return path
56 | }()
57 |
58 |
59 | // MARK:
60 | // MARK: Method
61 | fileprivate func saveToFile(_ object: BaseObj, type: FileType) {
62 | // Exclusive Write
63 | safe.writeSync {
64 | let dict = object.toJSON()
65 |
66 | // Try to cast JSON
67 | let jsonData = try? JSONSerialization.data(withJSONObject: dict, options: JSONSerialization.WritingOptions.prettyPrinted)
68 | guard let json = jsonData else {
69 | return
70 | }
71 |
72 | // Convert
73 | if let stringJSON = String(data: json, encoding: String.Encoding.utf8) {
74 | let path = type.toFilePath()
75 | _ = try? stringJSON.write(toFile: path, atomically: true, encoding: String.Encoding.utf8)
76 | path.removeICloudBackup()
77 | }
78 | }
79 | }
80 |
81 | fileprivate func loadModelFromFileWithType(_ type: FileType) -> N? {
82 | var map: N? = nil
83 | self.safe.read {
84 |
85 | // Get file path
86 | let filePath = type.toFilePath()
87 | guard FileManager.default.fileExists(atPath: filePath) else {return}
88 | guard let jsonString = try? String(contentsOfFile: filePath, encoding: String.Encoding.utf8) else {return}
89 | if let _map = Mapper().map(JSONString: jsonString) {
90 | map = _map
91 | } else {
92 | self.unsafeDeleteAllFile()
93 | }
94 |
95 | }
96 | return map
97 | }
98 |
99 | func deleteAllFile() {
100 |
101 | // Safe if warp to sync queue
102 | self.safe.writeSync {
103 | self.unsafeDeleteAllFile()
104 | }
105 | }
106 |
107 | fileprivate func unsafeDeleteAllFile() {
108 | let fileManager = FileManager.default
109 | for fileType in FileType.allValue {
110 | let path = fileType.toFilePath()
111 | guard fileManager.fileExists(atPath: path) else {continue}
112 | do {
113 | let _ = try? fileManager.removeItem(atPath: path)
114 | }
115 | }
116 | }
117 | }
118 |
119 | // MARK:
120 | // MARK: iCloud Logic
121 | extension URL {
122 | func removeICloudBackup() {
123 | do {
124 | let _ = try (self as NSURL).setResourceValue(true, forKey: URLResourceKey.isExcludedFromBackupKey)
125 | }
126 | catch (let error) {
127 | Logger.error("Can't setResourceValue Image to path \(self), error = \(error)")
128 | }
129 | }
130 | }
131 |
132 | extension String {
133 | func removeICloudBackup() {
134 | do {
135 | guard let URL = URL(string: self) else {return}
136 | let _ = try (URL as NSURL).setResourceValue(true, forKey: URLResourceKey.isExcludedFromBackupKey)
137 | }
138 | catch (let error) {
139 | Logger.error("Can't setResourceValue Image to path \(self), error = \(error)")
140 | }
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/iOS-Starter-Kit/Resource/Storyboards/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
--------------------------------------------------------------------------------
/iOS-Starter-Kit/Shared/Common/Logger/SlackReporter.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SlackReport.swift
3 | // Titan
4 | //
5 | // Created by Nghia Tran on 10/12/16.
6 | // Copyright © 2016 fe. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import Alamofire
11 |
12 | enum SlackReporterDataType {
13 | case Error
14 | case Response
15 | }
16 |
17 | struct SlackReporterData {
18 |
19 | let type: SlackReporterDataType
20 |
21 | // Slack
22 | private lazy var usernameSlack: String = {
23 | switch self.type {
24 | case .Error:
25 | return "Susu"
26 | case .Response:
27 | return "Rolex"
28 | }
29 | }()
30 |
31 | private lazy var icon_emoji: String = {
32 | switch self.type {
33 | case .Error:
34 | return ":dog:"
35 | case .Response:
36 | return ":stopwatch:"
37 | }
38 | }()
39 |
40 | // Data
41 | private var error: NSError?
42 | private var responseTime: CGFloat? = 0
43 | private var apiName: String = ""
44 | private var fileName: String = ""
45 | private var functionName: String = ""
46 | private var line: String = ""
47 | private var additionInfo: String = ""
48 | private lazy var buildNumber: String? = {
49 | guard let appInfo = Bundle.main.infoDictionary else {return nil}
50 | let appVersion = appInfo[kCFBundleVersionKey as String] as? String
51 | return appVersion
52 | }()
53 |
54 | init(error: NSError?, fileName: String, functionName: String, line: Int) {
55 | self.type = .Error
56 | self.error = error
57 | self.functionName = functionName
58 | self.line = String(line)
59 |
60 | // Filename
61 | let componment = fileName.components(separatedBy: "/")
62 | if let _fileName = componment.last {
63 | self.fileName = _fileName
64 | }
65 | else {
66 | self.fileName = "Unknow"
67 | }
68 | }
69 |
70 | init(responseTime: CGFloat?, apiName: String, response: Alamofire.DataResponse?) {
71 | self.type = .Response
72 | self.responseTime = responseTime ?? 0
73 | self.apiName = apiName
74 | self.additionInfo = self.infoTextFromResponse(response: response)
75 | }
76 |
77 | mutating func toParam() -> [String: String] {
78 | let text = self.toLog()
79 | let username = self.usernameSlack
80 | let icon = self.icon_emoji
81 |
82 | let param: [String: String] = ["username": username,
83 | "icon_emoji": icon,
84 | "text": text]
85 | return param
86 | }
87 |
88 | private func infoTextFromResponse(response: Alamofire.DataResponse?) -> String {
89 | guard let response = response else {return ""}
90 |
91 | var text: String = ""
92 | if let URL = response.request?.url?.absoluteString {
93 | text += " *URL* = \(URL)"
94 | }
95 |
96 | return text
97 | }
98 |
99 | private mutating func toLog() -> String {
100 |
101 | // Current User first
102 | var text: String = ""
103 | text += ":dark_sunglasses: "
104 |
105 | // Build version
106 | if let buildVersion = self.buildNumber {
107 | text += " :macOS: \(buildVersion)"
108 | }
109 |
110 | // Info
111 | switch self.type {
112 | case .Error:
113 | text += ":round_pushpin:\(fileName):\(line) :mag_right:\(functionName)"
114 | if let error = self.error {
115 | text += " 👉 \(error.localizedDescription)"
116 | }
117 | else {
118 | text += " 👉 Unknow"
119 | }
120 | return text
121 | case .Response:
122 | text += ":round_pushpin:\(self.apiName):"
123 | if let responseTime = self.responseTime {
124 | text += " 👉 \(responseTime)"
125 | }
126 | else {
127 | text += " 👉 Unknow"
128 | }
129 | text += " :rocket: \(self.additionInfo)"
130 |
131 | return text
132 | }
133 |
134 | }
135 | }
136 |
137 | class SlackReporter: NSObject {
138 |
139 | // MARK:
140 | // MARK: Variable
141 | private let Token = Constants.Logger.Slack.Token
142 | private let ErrorChannel = Constants.Logger.Slack.ErrorChannel
143 | private let ResponseChannel = Constants.Logger.Slack.ResponseChannel
144 | private lazy var URLErrorChannel: String = {
145 | return Constants.Logger.Slack.ErrorChannel_Webhook
146 | }()
147 | private lazy var URLResponseChannel: String = {
148 | return Constants.Logger.Slack.ResponseChannel_Webhook
149 | }()
150 |
151 | // MARK:
152 | // MARK: Public
153 | func reportErrorData(_ data: SlackReporterData) {
154 |
155 | // Build param
156 | var data = data
157 | let param = data.toParam()
158 |
159 | Alamofire.request(self.URLErrorChannel, method: .post, parameters: param, encoding: JSONEncoding.default).responseJSON { (_) in
160 |
161 | }
162 | }
163 |
164 | func reportResponseData(data: SlackReporterData) {
165 |
166 | // Build param
167 | var data = data
168 | let param = data.toParam()
169 |
170 | Alamofire.request(self.self.URLResponseChannel, method: .post, parameters: param, encoding: JSONEncoding.default).responseJSON { (_) in
171 |
172 | }
173 | }
174 |
175 | // MARK:
176 | // MARK: Private
177 | static let shareInstance = SlackReporter()
178 | }
179 |
180 | extension SlackReporter {
181 |
182 | // Test
183 | func testSlackReport() {
184 | let error = NSError.errorWithMessage(message: "Hi, I'm from Error Report")
185 | let data = SlackReporterData(error: error, fileName: #file, functionName: #function, line:#line)
186 | self.reportErrorData(data)
187 | }
188 |
189 | // Test
190 | func testSlackResponseReport() {
191 | let data = SlackReporterData(responseTime: 0.2, apiName: "TestAPIName", response: nil)
192 | self.reportResponseData(data: data)
193 | }
194 | }
195 |
--------------------------------------------------------------------------------
/Swift Vietnam Presentation/CleanSwift/CleanSwift/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
--------------------------------------------------------------------------------
/Swift Vietnam Presentation/MVC-Repo-Github/MVC-Repo-Github/Base.lproj/Main.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 |
146 |
147 |
148 |
149 |
150 |
151 |
152 |
153 |
154 |
--------------------------------------------------------------------------------
/Swift Vietnam Presentation/MVC-Repo-Github/MVC-Repo-Github.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 6F361E689558CD329DAC5807 /* Pods_MVC_Repo_Github.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 40E273305A4CF021632EDD60 /* Pods_MVC_Repo_Github.framework */; };
11 | BAD9E31B1E044D05005C07B6 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAD9E31A1E044D05005C07B6 /* AppDelegate.swift */; };
12 | BAD9E3201E044D05005C07B6 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BAD9E31E1E044D05005C07B6 /* Main.storyboard */; };
13 | BAD9E3221E044D05005C07B6 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = BAD9E3211E044D05005C07B6 /* Assets.xcassets */; };
14 | BAD9E3251E044D05005C07B6 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = BAD9E3231E044D05005C07B6 /* LaunchScreen.storyboard */; };
15 | BAD9E3341E044D48005C07B6 /* RepoViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAD9E3331E044D48005C07B6 /* RepoViewController.swift */; };
16 | BAD9E3391E044D67005C07B6 /* RepoCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAD9E3371E044D67005C07B6 /* RepoCell.swift */; };
17 | BAD9E33A1E044D67005C07B6 /* RepoCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = BAD9E3381E044D67005C07B6 /* RepoCell.xib */; };
18 | BAD9E33F1E044D97005C07B6 /* Networking.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAD9E33E1E044D97005C07B6 /* Networking.swift */; };
19 | BAD9E3411E044DA0005C07B6 /* RepoObj.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAD9E3401E044DA0005C07B6 /* RepoObj.swift */; };
20 | BAD9E3451E0455C6005C07B6 /* SettingViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAD9E3441E0455C6005C07B6 /* SettingViewController.swift */; };
21 | BAD9E3471E045913005C07B6 /* UserObj.swift in Sources */ = {isa = PBXBuildFile; fileRef = BAD9E3461E045913005C07B6 /* UserObj.swift */; };
22 | /* End PBXBuildFile section */
23 |
24 | /* Begin PBXFileReference section */
25 | 0935D223B4CCFC4885054B5B /* Pods-MVC-Repo-Github.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MVC-Repo-Github.debug.xcconfig"; path = "Pods/Target Support Files/Pods-MVC-Repo-Github/Pods-MVC-Repo-Github.debug.xcconfig"; sourceTree = ""; };
26 | 40E273305A4CF021632EDD60 /* Pods_MVC_Repo_Github.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_MVC_Repo_Github.framework; sourceTree = BUILT_PRODUCTS_DIR; };
27 | BAD9E3171E044D05005C07B6 /* MVC-Repo-Github.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "MVC-Repo-Github.app"; sourceTree = BUILT_PRODUCTS_DIR; };
28 | BAD9E31A1E044D05005C07B6 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
29 | BAD9E31F1E044D05005C07B6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; };
30 | BAD9E3211E044D05005C07B6 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
31 | BAD9E3241E044D05005C07B6 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
32 | BAD9E3261E044D05005C07B6 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
33 | BAD9E3331E044D48005C07B6 /* RepoViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RepoViewController.swift; sourceTree = ""; };
34 | BAD9E3371E044D67005C07B6 /* RepoCell.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RepoCell.swift; sourceTree = ""; };
35 | BAD9E3381E044D67005C07B6 /* RepoCell.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = RepoCell.xib; sourceTree = ""; };
36 | BAD9E33E1E044D97005C07B6 /* Networking.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Networking.swift; sourceTree = ""; };
37 | BAD9E3401E044DA0005C07B6 /* RepoObj.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RepoObj.swift; sourceTree = ""; };
38 | BAD9E3441E0455C6005C07B6 /* SettingViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingViewController.swift; sourceTree = ""; };
39 | BAD9E3461E045913005C07B6 /* UserObj.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = UserObj.swift; sourceTree = ""; };
40 | EC2FC633D7E66F98319FDA6E /* Pods-MVC-Repo-Github.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MVC-Repo-Github.release.xcconfig"; path = "Pods/Target Support Files/Pods-MVC-Repo-Github/Pods-MVC-Repo-Github.release.xcconfig"; sourceTree = ""; };
41 | /* End PBXFileReference section */
42 |
43 | /* Begin PBXFrameworksBuildPhase section */
44 | BAD9E3141E044D05005C07B6 /* Frameworks */ = {
45 | isa = PBXFrameworksBuildPhase;
46 | buildActionMask = 2147483647;
47 | files = (
48 | 6F361E689558CD329DAC5807 /* Pods_MVC_Repo_Github.framework in Frameworks */,
49 | );
50 | runOnlyForDeploymentPostprocessing = 0;
51 | };
52 | /* End PBXFrameworksBuildPhase section */
53 |
54 | /* Begin PBXGroup section */
55 | 71AE818A1EF519F1F8DE1A61 /* Pods */ = {
56 | isa = PBXGroup;
57 | children = (
58 | 0935D223B4CCFC4885054B5B /* Pods-MVC-Repo-Github.debug.xcconfig */,
59 | EC2FC633D7E66F98319FDA6E /* Pods-MVC-Repo-Github.release.xcconfig */,
60 | );
61 | name = Pods;
62 | sourceTree = "";
63 | };
64 | 919361789350DA987E783884 /* Frameworks */ = {
65 | isa = PBXGroup;
66 | children = (
67 | 40E273305A4CF021632EDD60 /* Pods_MVC_Repo_Github.framework */,
68 | );
69 | name = Frameworks;
70 | sourceTree = "";
71 | };
72 | BAD9E30E1E044D04005C07B6 = {
73 | isa = PBXGroup;
74 | children = (
75 | BAD9E3191E044D05005C07B6 /* MVC-Repo-Github */,
76 | BAD9E3181E044D05005C07B6 /* Products */,
77 | 71AE818A1EF519F1F8DE1A61 /* Pods */,
78 | 919361789350DA987E783884 /* Frameworks */,
79 | );
80 | sourceTree = "";
81 | };
82 | BAD9E3181E044D05005C07B6 /* Products */ = {
83 | isa = PBXGroup;
84 | children = (
85 | BAD9E3171E044D05005C07B6 /* MVC-Repo-Github.app */,
86 | );
87 | name = Products;
88 | sourceTree = "";
89 | };
90 | BAD9E3191E044D05005C07B6 /* MVC-Repo-Github */ = {
91 | isa = PBXGroup;
92 | children = (
93 | BAD9E32E1E044D19005C07B6 /* AppDelegate */,
94 | BAD9E32D1E044D13005C07B6 /* Resource */,
95 | BAD9E32C1E044D0C005C07B6 /* Source Code */,
96 | BAD9E31E1E044D05005C07B6 /* Main.storyboard */,
97 | BAD9E3211E044D05005C07B6 /* Assets.xcassets */,
98 | BAD9E3231E044D05005C07B6 /* LaunchScreen.storyboard */,
99 | BAD9E3261E044D05005C07B6 /* Info.plist */,
100 | );
101 | path = "MVC-Repo-Github";
102 | sourceTree = "";
103 | };
104 | BAD9E32C1E044D0C005C07B6 /* Source Code */ = {
105 | isa = PBXGroup;
106 | children = (
107 | BAD9E3311E044D30005C07B6 /* Model */,
108 | BAD9E3301E044D22005C07B6 /* View */,
109 | BAD9E32F1E044D1E005C07B6 /* Controller */,
110 | );
111 | name = "Source Code";
112 | sourceTree = "";
113 | };
114 | BAD9E32D1E044D13005C07B6 /* Resource */ = {
115 | isa = PBXGroup;
116 | children = (
117 | );
118 | name = Resource;
119 | sourceTree = "";
120 | };
121 | BAD9E32E1E044D19005C07B6 /* AppDelegate */ = {
122 | isa = PBXGroup;
123 | children = (
124 | BAD9E31A1E044D05005C07B6 /* AppDelegate.swift */,
125 | );
126 | name = AppDelegate;
127 | sourceTree = "";
128 | };
129 | BAD9E32F1E044D1E005C07B6 /* Controller */ = {
130 | isa = PBXGroup;
131 | children = (
132 | BAD9E3431E0455BA005C07B6 /* Setting */,
133 | BAD9E3321E044D37005C07B6 /* Repo */,
134 | );
135 | name = Controller;
136 | sourceTree = "";
137 | };
138 | BAD9E3301E044D22005C07B6 /* View */ = {
139 | isa = PBXGroup;
140 | children = (
141 | BAD9E3351E044D4C005C07B6 /* Repo */,
142 | );
143 | name = View;
144 | sourceTree = "";
145 | };
146 | BAD9E3311E044D30005C07B6 /* Model */ = {
147 | isa = PBXGroup;
148 | children = (
149 | BAD9E33C1E044D78005C07B6 /* Networking */,
150 | BAD9E33B1E044D6E005C07B6 /* Obj */,
151 | );
152 | name = Model;
153 | sourceTree = "";
154 | };
155 | BAD9E3321E044D37005C07B6 /* Repo */ = {
156 | isa = PBXGroup;
157 | children = (
158 | BAD9E3331E044D48005C07B6 /* RepoViewController.swift */,
159 | );
160 | name = Repo;
161 | sourceTree = "";
162 | };
163 | BAD9E3351E044D4C005C07B6 /* Repo */ = {
164 | isa = PBXGroup;
165 | children = (
166 | BAD9E3361E044D55005C07B6 /* Cell */,
167 | );
168 | name = Repo;
169 | sourceTree = "";
170 | };
171 | BAD9E3361E044D55005C07B6 /* Cell */ = {
172 | isa = PBXGroup;
173 | children = (
174 | BAD9E3371E044D67005C07B6 /* RepoCell.swift */,
175 | BAD9E3381E044D67005C07B6 /* RepoCell.xib */,
176 | );
177 | name = Cell;
178 | sourceTree = "";
179 | };
180 | BAD9E33B1E044D6E005C07B6 /* Obj */ = {
181 | isa = PBXGroup;
182 | children = (
183 | BAD9E3401E044DA0005C07B6 /* RepoObj.swift */,
184 | BAD9E3461E045913005C07B6 /* UserObj.swift */,
185 | );
186 | name = Obj;
187 | sourceTree = "";
188 | };
189 | BAD9E33C1E044D78005C07B6 /* Networking */ = {
190 | isa = PBXGroup;
191 | children = (
192 | BAD9E33E1E044D97005C07B6 /* Networking.swift */,
193 | );
194 | name = Networking;
195 | sourceTree = "";
196 | };
197 | BAD9E3431E0455BA005C07B6 /* Setting */ = {
198 | isa = PBXGroup;
199 | children = (
200 | BAD9E3441E0455C6005C07B6 /* SettingViewController.swift */,
201 | );
202 | name = Setting;
203 | sourceTree = "";
204 | };
205 | /* End PBXGroup section */
206 |
207 | /* Begin PBXNativeTarget section */
208 | BAD9E3161E044D05005C07B6 /* MVC-Repo-Github */ = {
209 | isa = PBXNativeTarget;
210 | buildConfigurationList = BAD9E3291E044D05005C07B6 /* Build configuration list for PBXNativeTarget "MVC-Repo-Github" */;
211 | buildPhases = (
212 | B48268229E89C6DEDFA5DA83 /* [CP] Check Pods Manifest.lock */,
213 | BAD9E3131E044D05005C07B6 /* Sources */,
214 | BAD9E3141E044D05005C07B6 /* Frameworks */,
215 | BAD9E3151E044D05005C07B6 /* Resources */,
216 | CF65328F802796BEC67C5D12 /* [CP] Embed Pods Frameworks */,
217 | 0C7D43757AECB80966C42079 /* [CP] Copy Pods Resources */,
218 | );
219 | buildRules = (
220 | );
221 | dependencies = (
222 | );
223 | name = "MVC-Repo-Github";
224 | productName = "MVC-Repo-Github";
225 | productReference = BAD9E3171E044D05005C07B6 /* MVC-Repo-Github.app */;
226 | productType = "com.apple.product-type.application";
227 | };
228 | /* End PBXNativeTarget section */
229 |
230 | /* Begin PBXProject section */
231 | BAD9E30F1E044D04005C07B6 /* Project object */ = {
232 | isa = PBXProject;
233 | attributes = {
234 | LastSwiftUpdateCheck = 0820;
235 | LastUpgradeCheck = 0820;
236 | ORGANIZATIONNAME = nghiatran;
237 | TargetAttributes = {
238 | BAD9E3161E044D05005C07B6 = {
239 | CreatedOnToolsVersion = 8.2;
240 | DevelopmentTeam = D54Y88Y9CY;
241 | ProvisioningStyle = Automatic;
242 | };
243 | };
244 | };
245 | buildConfigurationList = BAD9E3121E044D04005C07B6 /* Build configuration list for PBXProject "MVC-Repo-Github" */;
246 | compatibilityVersion = "Xcode 3.2";
247 | developmentRegion = English;
248 | hasScannedForEncodings = 0;
249 | knownRegions = (
250 | en,
251 | Base,
252 | );
253 | mainGroup = BAD9E30E1E044D04005C07B6;
254 | productRefGroup = BAD9E3181E044D05005C07B6 /* Products */;
255 | projectDirPath = "";
256 | projectRoot = "";
257 | targets = (
258 | BAD9E3161E044D05005C07B6 /* MVC-Repo-Github */,
259 | );
260 | };
261 | /* End PBXProject section */
262 |
263 | /* Begin PBXResourcesBuildPhase section */
264 | BAD9E3151E044D05005C07B6 /* Resources */ = {
265 | isa = PBXResourcesBuildPhase;
266 | buildActionMask = 2147483647;
267 | files = (
268 | BAD9E3251E044D05005C07B6 /* LaunchScreen.storyboard in Resources */,
269 | BAD9E3221E044D05005C07B6 /* Assets.xcassets in Resources */,
270 | BAD9E3201E044D05005C07B6 /* Main.storyboard in Resources */,
271 | BAD9E33A1E044D67005C07B6 /* RepoCell.xib in Resources */,
272 | );
273 | runOnlyForDeploymentPostprocessing = 0;
274 | };
275 | /* End PBXResourcesBuildPhase section */
276 |
277 | /* Begin PBXShellScriptBuildPhase section */
278 | 0C7D43757AECB80966C42079 /* [CP] Copy Pods Resources */ = {
279 | isa = PBXShellScriptBuildPhase;
280 | buildActionMask = 2147483647;
281 | files = (
282 | );
283 | inputPaths = (
284 | );
285 | name = "[CP] Copy Pods Resources";
286 | outputPaths = (
287 | );
288 | runOnlyForDeploymentPostprocessing = 0;
289 | shellPath = /bin/sh;
290 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-MVC-Repo-Github/Pods-MVC-Repo-Github-resources.sh\"\n";
291 | showEnvVarsInLog = 0;
292 | };
293 | B48268229E89C6DEDFA5DA83 /* [CP] Check Pods Manifest.lock */ = {
294 | isa = PBXShellScriptBuildPhase;
295 | buildActionMask = 2147483647;
296 | files = (
297 | );
298 | inputPaths = (
299 | );
300 | name = "[CP] Check Pods Manifest.lock";
301 | outputPaths = (
302 | );
303 | runOnlyForDeploymentPostprocessing = 0;
304 | shellPath = /bin/sh;
305 | shellScript = "diff \"${PODS_ROOT}/../Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n";
306 | showEnvVarsInLog = 0;
307 | };
308 | CF65328F802796BEC67C5D12 /* [CP] Embed Pods Frameworks */ = {
309 | isa = PBXShellScriptBuildPhase;
310 | buildActionMask = 2147483647;
311 | files = (
312 | );
313 | inputPaths = (
314 | );
315 | name = "[CP] Embed Pods Frameworks";
316 | outputPaths = (
317 | );
318 | runOnlyForDeploymentPostprocessing = 0;
319 | shellPath = /bin/sh;
320 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-MVC-Repo-Github/Pods-MVC-Repo-Github-frameworks.sh\"\n";
321 | showEnvVarsInLog = 0;
322 | };
323 | /* End PBXShellScriptBuildPhase section */
324 |
325 | /* Begin PBXSourcesBuildPhase section */
326 | BAD9E3131E044D05005C07B6 /* Sources */ = {
327 | isa = PBXSourcesBuildPhase;
328 | buildActionMask = 2147483647;
329 | files = (
330 | BAD9E3411E044DA0005C07B6 /* RepoObj.swift in Sources */,
331 | BAD9E3471E045913005C07B6 /* UserObj.swift in Sources */,
332 | BAD9E33F1E044D97005C07B6 /* Networking.swift in Sources */,
333 | BAD9E31B1E044D05005C07B6 /* AppDelegate.swift in Sources */,
334 | BAD9E3341E044D48005C07B6 /* RepoViewController.swift in Sources */,
335 | BAD9E3391E044D67005C07B6 /* RepoCell.swift in Sources */,
336 | BAD9E3451E0455C6005C07B6 /* SettingViewController.swift in Sources */,
337 | );
338 | runOnlyForDeploymentPostprocessing = 0;
339 | };
340 | /* End PBXSourcesBuildPhase section */
341 |
342 | /* Begin PBXVariantGroup section */
343 | BAD9E31E1E044D05005C07B6 /* Main.storyboard */ = {
344 | isa = PBXVariantGroup;
345 | children = (
346 | BAD9E31F1E044D05005C07B6 /* Base */,
347 | );
348 | name = Main.storyboard;
349 | sourceTree = "";
350 | };
351 | BAD9E3231E044D05005C07B6 /* LaunchScreen.storyboard */ = {
352 | isa = PBXVariantGroup;
353 | children = (
354 | BAD9E3241E044D05005C07B6 /* Base */,
355 | );
356 | name = LaunchScreen.storyboard;
357 | sourceTree = "";
358 | };
359 | /* End PBXVariantGroup section */
360 |
361 | /* Begin XCBuildConfiguration section */
362 | BAD9E3271E044D05005C07B6 /* Debug */ = {
363 | isa = XCBuildConfiguration;
364 | buildSettings = {
365 | ALWAYS_SEARCH_USER_PATHS = NO;
366 | CLANG_ANALYZER_NONNULL = YES;
367 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
368 | CLANG_CXX_LIBRARY = "libc++";
369 | CLANG_ENABLE_MODULES = YES;
370 | CLANG_ENABLE_OBJC_ARC = YES;
371 | CLANG_WARN_BOOL_CONVERSION = YES;
372 | CLANG_WARN_CONSTANT_CONVERSION = YES;
373 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
374 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
375 | CLANG_WARN_EMPTY_BODY = YES;
376 | CLANG_WARN_ENUM_CONVERSION = YES;
377 | CLANG_WARN_INFINITE_RECURSION = YES;
378 | CLANG_WARN_INT_CONVERSION = YES;
379 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
380 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
381 | CLANG_WARN_UNREACHABLE_CODE = YES;
382 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
383 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
384 | COPY_PHASE_STRIP = NO;
385 | DEBUG_INFORMATION_FORMAT = dwarf;
386 | ENABLE_STRICT_OBJC_MSGSEND = YES;
387 | ENABLE_TESTABILITY = YES;
388 | GCC_C_LANGUAGE_STANDARD = gnu99;
389 | GCC_DYNAMIC_NO_PIC = NO;
390 | GCC_NO_COMMON_BLOCKS = YES;
391 | GCC_OPTIMIZATION_LEVEL = 0;
392 | GCC_PREPROCESSOR_DEFINITIONS = (
393 | "DEBUG=1",
394 | "$(inherited)",
395 | );
396 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
397 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
398 | GCC_WARN_UNDECLARED_SELECTOR = YES;
399 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
400 | GCC_WARN_UNUSED_FUNCTION = YES;
401 | GCC_WARN_UNUSED_VARIABLE = YES;
402 | IPHONEOS_DEPLOYMENT_TARGET = 10.2;
403 | MTL_ENABLE_DEBUG_INFO = YES;
404 | ONLY_ACTIVE_ARCH = YES;
405 | SDKROOT = iphoneos;
406 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
407 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
408 | };
409 | name = Debug;
410 | };
411 | BAD9E3281E044D05005C07B6 /* Release */ = {
412 | isa = XCBuildConfiguration;
413 | buildSettings = {
414 | ALWAYS_SEARCH_USER_PATHS = NO;
415 | CLANG_ANALYZER_NONNULL = YES;
416 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
417 | CLANG_CXX_LIBRARY = "libc++";
418 | CLANG_ENABLE_MODULES = YES;
419 | CLANG_ENABLE_OBJC_ARC = YES;
420 | CLANG_WARN_BOOL_CONVERSION = YES;
421 | CLANG_WARN_CONSTANT_CONVERSION = YES;
422 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
423 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
424 | CLANG_WARN_EMPTY_BODY = YES;
425 | CLANG_WARN_ENUM_CONVERSION = YES;
426 | CLANG_WARN_INFINITE_RECURSION = YES;
427 | CLANG_WARN_INT_CONVERSION = YES;
428 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
429 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
430 | CLANG_WARN_UNREACHABLE_CODE = YES;
431 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
432 | "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
433 | COPY_PHASE_STRIP = NO;
434 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
435 | ENABLE_NS_ASSERTIONS = NO;
436 | ENABLE_STRICT_OBJC_MSGSEND = YES;
437 | GCC_C_LANGUAGE_STANDARD = gnu99;
438 | GCC_NO_COMMON_BLOCKS = YES;
439 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
440 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
441 | GCC_WARN_UNDECLARED_SELECTOR = YES;
442 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
443 | GCC_WARN_UNUSED_FUNCTION = YES;
444 | GCC_WARN_UNUSED_VARIABLE = YES;
445 | IPHONEOS_DEPLOYMENT_TARGET = 10.2;
446 | MTL_ENABLE_DEBUG_INFO = NO;
447 | SDKROOT = iphoneos;
448 | SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
449 | VALIDATE_PRODUCT = YES;
450 | };
451 | name = Release;
452 | };
453 | BAD9E32A1E044D05005C07B6 /* Debug */ = {
454 | isa = XCBuildConfiguration;
455 | baseConfigurationReference = 0935D223B4CCFC4885054B5B /* Pods-MVC-Repo-Github.debug.xcconfig */;
456 | buildSettings = {
457 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
458 | DEVELOPMENT_TEAM = D54Y88Y9CY;
459 | INFOPLIST_FILE = "MVC-Repo-Github/Info.plist";
460 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
461 | PRODUCT_BUNDLE_IDENTIFIER = "com.fe.nghiatran.MVC-Repo-Github";
462 | PRODUCT_NAME = "$(TARGET_NAME)";
463 | SWIFT_VERSION = 3.0;
464 | };
465 | name = Debug;
466 | };
467 | BAD9E32B1E044D05005C07B6 /* Release */ = {
468 | isa = XCBuildConfiguration;
469 | baseConfigurationReference = EC2FC633D7E66F98319FDA6E /* Pods-MVC-Repo-Github.release.xcconfig */;
470 | buildSettings = {
471 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
472 | DEVELOPMENT_TEAM = D54Y88Y9CY;
473 | INFOPLIST_FILE = "MVC-Repo-Github/Info.plist";
474 | LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks";
475 | PRODUCT_BUNDLE_IDENTIFIER = "com.fe.nghiatran.MVC-Repo-Github";
476 | PRODUCT_NAME = "$(TARGET_NAME)";
477 | SWIFT_VERSION = 3.0;
478 | };
479 | name = Release;
480 | };
481 | /* End XCBuildConfiguration section */
482 |
483 | /* Begin XCConfigurationList section */
484 | BAD9E3121E044D04005C07B6 /* Build configuration list for PBXProject "MVC-Repo-Github" */ = {
485 | isa = XCConfigurationList;
486 | buildConfigurations = (
487 | BAD9E3271E044D05005C07B6 /* Debug */,
488 | BAD9E3281E044D05005C07B6 /* Release */,
489 | );
490 | defaultConfigurationIsVisible = 0;
491 | defaultConfigurationName = Release;
492 | };
493 | BAD9E3291E044D05005C07B6 /* Build configuration list for PBXNativeTarget "MVC-Repo-Github" */ = {
494 | isa = XCConfigurationList;
495 | buildConfigurations = (
496 | BAD9E32A1E044D05005C07B6 /* Debug */,
497 | BAD9E32B1E044D05005C07B6 /* Release */,
498 | );
499 | defaultConfigurationIsVisible = 0;
500 | defaultConfigurationName = Release;
501 | };
502 | /* End XCConfigurationList section */
503 | };
504 | rootObject = BAD9E30F1E044D04005C07B6 /* Project object */;
505 | }
506 |
--------------------------------------------------------------------------------