├── Screenshots ├── github_logo.jpg └── starter-kit.png ├── Swift Vietnam Presentation ├── MobileMeetup.key ├── CleanSwift │ ├── CleanSwift.xcodeproj │ │ └── project.xcworkspace │ │ │ └── contents.xcworkspacedata │ ├── CleanSwift │ │ ├── UserObj.swift │ │ ├── SettingPresentor.swift │ │ ├── SettingInteractor.swift │ │ ├── SettingConfiguration.swift │ │ ├── MainAppState.swift │ │ ├── _RepoPresenter.swift │ │ ├── RepoObj.swift │ │ ├── Worker.swift │ │ ├── SettingState.swift │ │ ├── RepoCell.swift │ │ ├── Assets.xcassets │ │ │ └── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ ├── _RepoInteractor.swift │ │ ├── RepoConfiguration.swift │ │ ├── _RepoConfiguration.swift │ │ ├── MainReducer.swift │ │ ├── FetchRepoWorker.swift │ │ ├── RepoState.swift │ │ ├── RepoPresenter.swift │ │ ├── SettingController.swift │ │ ├── _RepoController.swift │ │ ├── RepoInteractor.swift │ │ ├── Info.plist │ │ ├── Base.lproj │ │ │ ├── LaunchScreen.storyboard │ │ │ └── Main.storyboard │ │ ├── AppDelegate.swift │ │ ├── RepoController.swift │ │ ├── RepoCell.xib │ │ └── Networking.swift │ ├── CleanSwift.xcworkspace │ │ └── contents.xcworkspacedata │ ├── Podfile │ └── Podfile.lock └── MVC-Repo-Github │ ├── MVC-Repo-Github.xcodeproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── project.pbxproj │ ├── MVC-Repo-Github │ ├── UserObj.swift │ ├── RepoObj.swift │ ├── RepoCell.swift │ ├── Assets.xcassets │ │ └── AppIcon.appiconset │ │ │ └── Contents.json │ ├── SettingViewController.swift │ ├── Info.plist │ ├── Base.lproj │ │ ├── LaunchScreen.storyboard │ │ └── Main.storyboard │ ├── AppDelegate.swift │ ├── RepoCell.xib │ ├── RepoViewController.swift │ └── Networking.swift │ ├── MVC-Repo-Github.xcworkspace │ └── contents.xcworkspacedata │ ├── Podfile.lock │ └── Podfile ├── iOS-Starter-Kit.xcodeproj ├── project.xcworkspace │ └── contents.xcworkspacedata └── xcuserdata │ └── nghiatran.xcuserdatad │ └── xcschemes │ ├── xcschememanagement.plist │ └── iOS-Starter-Kit.xcscheme ├── iOS-Starter-Kit.xcworkspace └── contents.xcworkspacedata ├── iOS-Starter-Kit ├── Shared │ ├── App │ │ ├── Store │ │ │ ├── State │ │ │ │ ├── MainAppState.swift │ │ │ │ └── RepoState.swift │ │ │ └── Reducer │ │ │ │ └── MainReducer.swift │ │ ├── Obj Model │ │ │ ├── RepoObj.swift │ │ │ ├── UserObj.swift │ │ │ └── BaseObj.swift │ │ └── Worker │ │ │ └── Worker.swift │ ├── Extensions │ │ ├── Register │ │ │ ├── UITableView+Register.swift │ │ │ └── UICollectionView+Register.swift │ │ ├── Identifier │ │ │ └── Identifier.swift │ │ ├── Error │ │ │ └── Error+Default.swift │ │ ├── Storyboard+Initialization.swift │ │ ├── Xib+Initialization.swift │ │ └── Ability │ │ │ └── BaseAbility.swift │ ├── Router │ │ ├── SettingRepoRoute.swift │ │ ├── Router.swift │ │ └── RouterManager.swift │ ├── Common │ │ ├── Application │ │ │ └── ApplicationManager.swift │ │ ├── SwiftSafer.swift │ │ └── Logger │ │ │ ├── Logger.swift │ │ │ └── SlackReporter.swift │ └── Base │ │ ├── BaseViewController.swift │ │ └── BaseView.swift ├── Source Code │ ├── Scenes │ │ ├── SettingInteractor.swift │ │ ├── SettingPresenter.swift │ │ ├── SettingConfiguration.swift │ │ ├── Example - Repo │ │ │ ├── Controller │ │ │ │ ├── RepoConfiguration.swift │ │ │ │ ├── RepoInteractor.swift │ │ │ │ ├── RepoPresenter.swift │ │ │ │ └── RepoController.swift │ │ │ ├── View │ │ │ │ ├── RepoCell.swift │ │ │ │ └── RepoCell.xib │ │ │ ├── Worker │ │ │ │ └── FetchRepoWorker.swift │ │ │ └── DataSource │ │ │ │ └── RepoDataSource.swift │ │ ├── SettingViewController.swift │ │ └── SettingViewController.xib │ └── Model │ │ ├── Networking │ │ ├── QueueManager │ │ │ └── QueueManager.swift │ │ ├── Networking.swift │ │ └── Request │ │ │ ├── FetchRepoRequest.swift │ │ │ └── Requestable.swift │ │ └── DiskManager.swift ├── Resource │ ├── Storyboards │ │ └── Base.lproj │ │ │ ├── LaunchScreen.storyboard │ │ │ └── Main.storyboard │ └── Assets │ │ └── Assets.xcassets │ │ └── AppIcon.appiconset │ │ └── Contents.json ├── Configuration │ ├── Info.plist │ └── Constants.swift └── App Delegate │ └── AppDelegate.swift ├── Podfile ├── iOS-Starter-KitTests ├── Info.plist └── iOS_Starter_KitTests.swift ├── Podfile.lock ├── .gitignore └── README.md /Screenshots/github_logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NghiaTranUIT/iOS-Awesome-Starter-Kit/HEAD/Screenshots/github_logo.jpg -------------------------------------------------------------------------------- /Screenshots/starter-kit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NghiaTranUIT/iOS-Awesome-Starter-Kit/HEAD/Screenshots/starter-kit.png -------------------------------------------------------------------------------- /Swift Vietnam Presentation/MobileMeetup.key: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/NghiaTranUIT/iOS-Awesome-Starter-Kit/HEAD/Swift Vietnam Presentation/MobileMeetup.key -------------------------------------------------------------------------------- /iOS-Starter-Kit.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Swift Vietnam Presentation/CleanSwift/CleanSwift.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Swift Vietnam Presentation/MVC-Repo-Github/MVC-Repo-Github.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Swift Vietnam Presentation/CleanSwift/CleanSwift/UserObj.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UserObj.swift 3 | // MVC-Repo-Github 4 | // 5 | // Created by Nghia Tran on 12/17/16. 6 | // Copyright © 2016 nghiatran. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class UserObj { 12 | 13 | } 14 | -------------------------------------------------------------------------------- /Swift Vietnam Presentation/MVC-Repo-Github/MVC-Repo-Github/UserObj.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UserObj.swift 3 | // MVC-Repo-Github 4 | // 5 | // Created by Nghia Tran on 12/17/16. 6 | // Copyright © 2016 nghiatran. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | class UserObj { 12 | 13 | } 14 | -------------------------------------------------------------------------------- /Swift Vietnam Presentation/CleanSwift/CleanSwift/SettingPresentor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SettingPresentor.swift 3 | // CleanSwift 4 | // 5 | // Created by Nghia Tran on 12/17/16. 6 | // Copyright © 2016 nghiatran. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class SettingPresentor: NSObject { 12 | 13 | } 14 | -------------------------------------------------------------------------------- /Swift Vietnam Presentation/CleanSwift/CleanSwift/SettingInteractor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SettingInteractor.swift 3 | // CleanSwift 4 | // 5 | // Created by Nghia Tran on 12/17/16. 6 | // Copyright © 2016 nghiatran. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class SettingInteractor: NSObject { 12 | 13 | } 14 | -------------------------------------------------------------------------------- /iOS-Starter-Kit.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Swift Vietnam Presentation/CleanSwift/CleanSwift/SettingConfiguration.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SettingConfiguration.swift 3 | // CleanSwift 4 | // 5 | // Created by Nghia Tran on 12/17/16. 6 | // Copyright © 2016 nghiatran. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class SettingConfiguration: NSObject { 12 | 13 | } 14 | -------------------------------------------------------------------------------- /Swift Vietnam Presentation/CleanSwift/CleanSwift.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /Swift Vietnam Presentation/MVC-Repo-Github/MVC-Repo-Github.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /iOS-Starter-Kit/Shared/App/Store/State/MainAppState.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MainAppState.swift 3 | // CleanSwift 4 | // 5 | // Created by Nghia Tran on 12/17/16. 6 | // Copyright © 2016 nghiatran. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import ReSwift 11 | 12 | struct MainAppState: StateType { 13 | 14 | // 15 | // MARK: - States 16 | 17 | /// Repo State 18 | let repoState: RepoState? 19 | 20 | } 21 | -------------------------------------------------------------------------------- /Swift Vietnam Presentation/MVC-Repo-Github/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Alamofire (4.2.0) 3 | - ObjectMapper (2.2.2) 4 | 5 | DEPENDENCIES: 6 | - Alamofire (~> 4.0) 7 | - ObjectMapper (~> 2.2) 8 | 9 | SPEC CHECKSUMS: 10 | Alamofire: aa2e09d871c9160ac53c90e83c68064a94e3dfbe 11 | ObjectMapper: 9e385c2295bcc4e16eabbcfc85db801442bba545 12 | 13 | PODFILE CHECKSUM: 54e2e6efcc3546b5f88f69ee5db02acf3d9a3533 14 | 15 | COCOAPODS: 1.1.1 16 | -------------------------------------------------------------------------------- /Swift Vietnam Presentation/CleanSwift/CleanSwift/MainAppState.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MainAppState.swift 3 | // CleanSwift 4 | // 5 | // Created by Nghia Tran on 12/17/16. 6 | // Copyright © 2016 nghiatran. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import ReSwift 11 | 12 | struct MainAppState: StateType { 13 | 14 | // 15 | // MARK: - States 16 | 17 | /// Repo State 18 | let repoState: RepoState? 19 | 20 | 21 | /// Setting State 22 | let settingState: SettingState? 23 | } 24 | -------------------------------------------------------------------------------- /iOS-Starter-Kit/Shared/App/Obj Model/RepoObj.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RepoObj.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 ObjectMapper 11 | 12 | class RepoObj: BaseObj { 13 | 14 | // 15 | // MARK: - Variable 16 | var name: String? 17 | 18 | // Mappable 19 | override func mapping(map: Map) { 20 | super.mapping(map: map) 21 | 22 | name <- map[Constants.Obj.Repo.Name] 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | # 2 | # Created by : Nghia Tran 3 | # Sun, 25th Sept 2016, Vietnam 4 | # 5 | 6 | 7 | source 'https://github.com/CocoaPods/Specs.git' 8 | platform :ios, '10.0' 9 | use_frameworks! 10 | 11 | 12 | # Pods 13 | def important_pods 14 | 15 | # Core 16 | pod 'Alamofire', '~> 4.0' 17 | pod 'ObjectMapper', '~> 2.2' 18 | pod 'ReSwift' 19 | pod 'PromiseKit', '~> 4.0' 20 | pod 'SwiftyBeaver' 21 | 22 | # Observable 23 | pod 'RxSwift', '~> 3.0' 24 | end 25 | 26 | 27 | target "iOS-Starter-Kit" do 28 | important_pods 29 | end 30 | -------------------------------------------------------------------------------- /Swift Vietnam Presentation/CleanSwift/CleanSwift/_RepoPresenter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // _RepoPresenter.swift 3 | // CleanSwift 4 | // 5 | // Created by Nghia Tran on 12/17/16. 6 | // Copyright © 2016 nghiatran. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol _RepoPresenterOutput: class { 12 | 13 | func showAlerView() 14 | } 15 | 16 | class _RepoPresenter: NSObject { 17 | 18 | weak var output: _RepoPresenterOutput? 19 | } 20 | 21 | extension _RepoPresenter: _RepoInteractorOutput { 22 | 23 | func showError() { 24 | 25 | } 26 | } 27 | 28 | -------------------------------------------------------------------------------- /iOS-Starter-Kit/Shared/App/Worker/Worker.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Worker.swift 3 | // Titan 4 | // 5 | // Created by Nghia Tran on 11/17/16. 6 | // Copyright © 2016 fe. All rights reserved. 7 | // 8 | 9 | import ReSwift 10 | import PromiseKit 11 | 12 | // 13 | // MARK: - Worker 14 | protocol Worker { 15 | 16 | } 17 | 18 | 19 | // 20 | // MARK: - Async Worker 21 | protocol AsyncWorker: Worker { 22 | 23 | associatedtype T 24 | 25 | func execute() -> Promise 26 | } 27 | 28 | 29 | // 30 | // MARK: - Sync Worker 31 | protocol SyncWorker: Worker { 32 | func execute() 33 | } 34 | -------------------------------------------------------------------------------- /iOS-Starter-Kit/Source Code/Scenes/SettingInteractor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SettingInteractor.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 UIKit 10 | 11 | protocol SettingInteractorOutput { 12 | 13 | } 14 | 15 | // 16 | // MARK: - SettingInteractor 17 | class SettingInteractor { 18 | 19 | // 20 | // MARK: - Variable 21 | var output: SettingInteractorOutput? 22 | } 23 | 24 | // 25 | // MARK: - SettingViewControllerOutput 26 | extension SettingInteractor: SettingViewControllerOutput {} 27 | -------------------------------------------------------------------------------- /iOS-Starter-Kit/Source Code/Scenes/SettingPresenter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SettingPresenter.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 UIKit 10 | 11 | protocol SettingPresenterOutput: class { 12 | 13 | } 14 | 15 | // 16 | // MARK: - SettingPresenter 17 | class SettingPresenter: NSObject { 18 | 19 | // 20 | // MARK: - Variable 21 | weak var output: SettingPresenterOutput? 22 | } 23 | 24 | // 25 | // MARK: - SettingInteractorOutput 26 | extension SettingPresenter: SettingInteractorOutput {} 27 | -------------------------------------------------------------------------------- /iOS-Starter-Kit/Shared/Extensions/Register/UITableView+Register.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSTableView.swift 3 | // Titan 4 | // 5 | // Created by Nghia Tran on 11/13/16. 6 | // Copyright © 2016 fe. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | 13 | // 14 | // MARK: - Register View 15 | extension UITableView { 16 | 17 | 18 | /// Helper register cell 19 | /// The View must conform Identifier protocol 20 | func registerCell(_ viewType: T.Type) { 21 | self.register(viewType.xib(), forCellReuseIdentifier: viewType.identifier) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Swift Vietnam Presentation/MVC-Repo-Github/Podfile: -------------------------------------------------------------------------------- 1 | # 2 | # Created by : Nghia Tran 3 | # Sun, 25th Sept 2016, Vietnam 4 | # 5 | 6 | source 'https://github.com/CocoaPods/Specs.git' 7 | platform :ios, '10.0' 8 | use_frameworks! 9 | 10 | # Pods 11 | def important_pods 12 | pod 'Alamofire', '~> 4.0' 13 | pod 'ObjectMapper', '~> 2.2' 14 | end 15 | 16 | # macOS 17 | target "MVC-Repo-Github" do 18 | important_pods 19 | end 20 | 21 | 22 | post_install do |installer| 23 | installer.pods_project.targets.each do |target| 24 | target.build_configurations.each do |config| 25 | config.build_settings['SWIFT_VERSION'] = '3.0' 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /iOS-Starter-Kit/Shared/App/Store/Reducer/MainReducer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MainReducer.swift 3 | // Titan 4 | // 5 | // Created by Nghia Tran on 10/11/16. 6 | // Copyright © 2016 fe. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import ReSwift 11 | 12 | struct MainReducer { 13 | 14 | } 15 | 16 | // MARK - 17 | // MARK: Reducer Protocol 18 | extension MainReducer: Reducer { 19 | func handleAction(action: Action, state: MainAppState?) -> MainAppState { 20 | 21 | // Repo state 22 | let repoState = RepoState.reducer(action: action, state: state?.repoState) 23 | 24 | // return 25 | return MainAppState(repoState: repoState) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Swift Vietnam Presentation/CleanSwift/CleanSwift/RepoObj.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RepoObj.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 ObjectMapper 11 | 12 | class RepoObj: Mappable { 13 | 14 | // 15 | // MARK: - Variable 16 | var name: String? 17 | var createdAt: Date? 18 | 19 | // 20 | // MARK: - Mapping 21 | required init?(map: Map) { 22 | 23 | } 24 | 25 | // Mappable 26 | func mapping(map: Map) { 27 | name <- map["name"] 28 | createdAt <- (map["createdAt"], DateTransform()) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Swift Vietnam Presentation/CleanSwift/CleanSwift/Worker.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Worker.swift 3 | // Titan 4 | // 5 | // Created by Nghia Tran on 11/17/16. 6 | // Copyright © 2016 fe. All rights reserved. 7 | // 8 | 9 | import ReSwift 10 | import PromiseKit 11 | 12 | // 13 | // MARK: - Worker 14 | protocol Worker { 15 | 16 | } 17 | 18 | class BaseWorker { 19 | 20 | } 21 | 22 | 23 | // 24 | // MARK: - Async Worker 25 | protocol AsyncWorker: Worker { 26 | 27 | associatedtype T 28 | 29 | func execute() -> Promise 30 | } 31 | 32 | 33 | // 34 | // MARK: - Sync Worker 35 | protocol SyncWorker: Worker { 36 | func execute() 37 | } 38 | 39 | 40 | class BaseWorker { 41 | 42 | } 43 | -------------------------------------------------------------------------------- /iOS-Starter-Kit/Resource/Storyboards/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /iOS-Starter-Kit/Shared/Router/SettingRepoRoute.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SettingRepoRoute.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 UIKit 11 | 12 | class SettingRouter: Router { 13 | 14 | typealias Element = SettingViewController 15 | 16 | var routerType: RouterType { 17 | return .push 18 | } 19 | 20 | fileprivate lazy var _viewController: SettingViewController = { 21 | return SettingViewController.xibInstance() 22 | }() 23 | 24 | var viewController: SettingViewController { 25 | return self._viewController 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Swift Vietnam Presentation/MVC-Repo-Github/MVC-Repo-Github/RepoObj.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RepoObj.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 ObjectMapper 11 | 12 | class RepoObj: Mappable { 13 | 14 | // 15 | // MARK: - Variable 16 | var name: String? 17 | var createdAt: Date? 18 | 19 | // 20 | // MARK: - Mapping 21 | required init?(map: Map) { 22 | 23 | } 24 | 25 | // Mappable 26 | func mapping(map: Map) { 27 | name <- map["name"] 28 | createdAt <- (map["createdAt"], DateTransform()) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /iOS-Starter-Kit/Shared/App/Obj Model/UserObj.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UserObj.swift 3 | // MVC-Repo-Github 4 | // 5 | // Created by Nghia Tran on 12/17/16. 6 | // Copyright © 2016 nghiatran. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import ObjectMapper 11 | 12 | class UserObj: BaseObj { 13 | 14 | // 15 | // MARK: - Variable 16 | var name: String? 17 | var username: String? 18 | var email: String? 19 | 20 | // Mappable 21 | override func mapping(map: Map) { 22 | super.mapping(map: map) 23 | 24 | name <- map[Constants.Obj.User.Name] 25 | email <- map[Constants.Obj.User.Email] 26 | username <- map[Constants.Obj.User.Username] 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /iOS-Starter-Kit/Source Code/Model/Networking/QueueManager/QueueManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // QueueManager.swift 3 | // Titan 4 | // 5 | // Created by Nghia Tran on 10/12/16. 6 | // Copyright © 2016 fe. All rights reserved. 7 | // 8 | 9 | import RxSwift 10 | 11 | class QueueManager { 12 | 13 | 14 | /// Share instance 15 | static let shared = QueueManager() 16 | 17 | 18 | /// Main queue 19 | lazy var mainQueue: MainScheduler = { 20 | return MainScheduler.instance 21 | }() 22 | 23 | 24 | /// Background Queue 25 | lazy var backgroundQueue: ConcurrentDispatchQueueScheduler = { 26 | return ConcurrentDispatchQueueScheduler(qos: DispatchQoS.background) 27 | }() 28 | } 29 | -------------------------------------------------------------------------------- /iOS-Starter-Kit/Shared/Extensions/Identifier/Identifier.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Identifier.swift 3 | // iOS-Starter-Kit 4 | // 5 | // Created by Nghia Tran on 12/19/16. 6 | // Copyright © 2016 nghiatran. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | // 13 | // MARK: - Identitifer protocol 14 | protocol Identifier { 15 | 16 | // Static vartiable to get id of object 17 | static var identifier: String {get} 18 | } 19 | 20 | // 21 | // MARK: - Exntension 22 | extension Identifier { 23 | 24 | static var identifier: String { 25 | return String(describing: self) 26 | } 27 | } 28 | 29 | // 30 | // MARK: - Conform automatically 31 | extension UIViewController: Identifier {} 32 | extension UIView: Identifier {} 33 | -------------------------------------------------------------------------------- /iOS-Starter-Kit.xcodeproj/xcuserdata/nghiatran.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | iOS-Starter-Kit.xcscheme 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | SuppressBuildableAutocreation 14 | 15 | BA96387B1E07800200D16B73 16 | 17 | primary 18 | 19 | 20 | BA96388F1E07800300D16B73 21 | 22 | primary 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /iOS-Starter-KitTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /iOS-Starter-Kit/Source Code/Scenes/SettingConfiguration.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SettingConfiguration.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 UIKit 10 | 11 | class SettingConfiguration { 12 | 13 | static let shared = SettingConfiguration() 14 | 15 | func configure(viewController: SettingViewController) { 16 | 17 | // Presenter 18 | let presenter = SettingPresenter() 19 | presenter.output = viewController 20 | 21 | // Interactor 22 | let interactor = SettingInteractor() 23 | interactor.output = presenter 24 | 25 | // View controller 26 | viewController.output = interactor 27 | } 28 | 29 | } 30 | -------------------------------------------------------------------------------- /Swift Vietnam Presentation/CleanSwift/CleanSwift/SettingState.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SettingState.swift 3 | // CleanSwift 4 | // 5 | // Created by Nghia Tran on 12/17/16. 6 | // Copyright © 2016 nghiatran. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import ReSwift 11 | 12 | 13 | // 14 | // MARK: - Setting State 15 | struct SettingState: StateType { 16 | 17 | } 18 | 19 | 20 | // 21 | // MARK: - Reducer 22 | extension SettingState { 23 | static func reducer(action: Action, state: SettingState?) -> SettingState { 24 | 25 | // Get state 26 | var state = state ?? SettingState() 27 | 28 | // Doing 29 | switch action { 30 | default: 31 | break 32 | } 33 | 34 | // Return 35 | return state 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Swift Vietnam Presentation/CleanSwift/Podfile: -------------------------------------------------------------------------------- 1 | # 2 | # Created by : Nghia Tran 3 | # Sun, 25th Sept 2016, Vietnam 4 | # 5 | 6 | 7 | source 'https://github.com/CocoaPods/Specs.git' 8 | platform :ios, '10.0' 9 | use_frameworks! 10 | 11 | 12 | # Pods 13 | def important_pods 14 | pod 'Alamofire', '~> 4.0' 15 | pod 'ObjectMapper', '~> 2.2' 16 | pod 'ReSwift' 17 | pod 'PromiseKit', '~> 4.0' 18 | 19 | pod 'RxSwift', '~> 3.0' 20 | pod 'RxCocoa', '~> 3.0' 21 | end 22 | 23 | 24 | target "CleanSwift" do 25 | important_pods 26 | end 27 | 28 | 29 | # iOS 30 | post_install do |installer| 31 | installer.pods_project.targets.each do |target| 32 | target.build_configurations.each do |config| 33 | config.build_settings['SWIFT_VERSION'] = '3.0' 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /Swift Vietnam Presentation/CleanSwift/CleanSwift/RepoCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RepoCell.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 RepoCell: UITableViewCell { 12 | 13 | override func awakeFromNib() { 14 | super.awakeFromNib() 15 | // Initialization code 16 | } 17 | 18 | override func setSelected(_ selected: Bool, animated: Bool) { 19 | super.setSelected(selected, animated: animated) 20 | 21 | // Configure the view for the selected state 22 | } 23 | 24 | // 25 | // MARK: - Public 26 | func configureCellWithRepo(repo: RepoObj) { 27 | 28 | // Name 29 | self.textLabel?.text = repo.name 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /iOS-Starter-Kit/Shared/Extensions/Error/Error+Default.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Error+Default.swift 3 | // iOS-Starter-Kit 4 | // 5 | // Created by Nghia Tran on 12/19/16. 6 | // Copyright © 2016 nghiatran. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | 12 | extension NSError { 13 | 14 | /// Unknow error 15 | static func unknowError() -> NSError { 16 | let userInfo = [NSLocalizedDescriptionKey: "Unknow error"] 17 | return NSError(domain: "com.fe.defaultError", code: 999, userInfo: userInfo) 18 | } 19 | 20 | 21 | /// JSON Mapper Error 22 | static func jsonMapperError() -> NSError { 23 | let userInfo = [NSLocalizedDescriptionKey: "JSON Mapper error"] 24 | return NSError(domain: "com.fe.defaultError", code: 998, userInfo: userInfo) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Swift Vietnam Presentation/MVC-Repo-Github/MVC-Repo-Github/RepoCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RepoCell.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 RepoCell: UITableViewCell { 12 | 13 | override func awakeFromNib() { 14 | super.awakeFromNib() 15 | // Initialization code 16 | } 17 | 18 | override func setSelected(_ selected: Bool, animated: Bool) { 19 | super.setSelected(selected, animated: animated) 20 | 21 | // Configure the view for the selected state 22 | } 23 | 24 | // 25 | // MARK: - Public 26 | func configureCellWithRepo(repo: RepoObj) { 27 | 28 | // Name 29 | self.textLabel?.text = repo.name 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Swift Vietnam Presentation/CleanSwift/CleanSwift/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "29x29", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "29x29", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "40x40", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "40x40", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "60x60", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "60x60", 31 | "scale" : "3x" 32 | } 33 | ], 34 | "info" : { 35 | "version" : 1, 36 | "author" : "xcode" 37 | } 38 | } -------------------------------------------------------------------------------- /Swift Vietnam Presentation/CleanSwift/CleanSwift/_RepoInteractor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // _RepoInteractor.swift 3 | // CleanSwift 4 | // 5 | // Created by Nghia Tran on 12/17/16. 6 | // Copyright © 2016 nghiatran. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol _RepoInteractorOutput { 12 | 13 | func showError() 14 | } 15 | 16 | 17 | class _RepoInteractor { 18 | 19 | var output: _RepoInteractorOutput? 20 | let fetchRepoWorker = FetchRepoWorker() 21 | } 22 | 23 | 24 | extension _RepoInteractor: _RepoControllerOutput { 25 | 26 | func fetchRepo() { 27 | 28 | self.fetchRepoWorker.execute() 29 | .then { _ -> Void in 30 | // Nothing 31 | } 32 | .catch { error in 33 | self.output?.presentError(error) 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Swift Vietnam Presentation/CleanSwift/CleanSwift/RepoConfiguration.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RepoConfiguration.swift 3 | // CleanSwift 4 | // 5 | // Created by Nghia Tran on 12/17/16. 6 | // Copyright © 2016 nghiatran. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class RepoConfiguration { 12 | 13 | static let shared = RepoConfiguration() 14 | 15 | func configure(viewController: RepoController) { 16 | 17 | // Presenter 18 | let presenter = RepoPresenter() 19 | presenter.output = viewController 20 | 21 | // Interactor 22 | let interactor = RepoInteractor() 23 | interactor.output = presenter 24 | 25 | // View controller 26 | viewController.output = interactor 27 | viewController.dataSource = presenter 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /iOS-Starter-Kit/Source Code/Scenes/Example - Repo/Controller/RepoConfiguration.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RepoConfiguration.swift 3 | // CleanSwift 4 | // 5 | // Created by Nghia Tran on 12/17/16. 6 | // Copyright © 2016 nghiatran. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class RepoConfiguration { 12 | 13 | static let shared = RepoConfiguration() 14 | 15 | func configure(viewController: RepoController) { 16 | 17 | // Presenter 18 | let presenter = RepoPresenter() 19 | presenter.output = viewController 20 | 21 | // Interactor 22 | let interactor = RepoInteractor() 23 | interactor.output = presenter 24 | 25 | // View controller 26 | viewController.output = interactor 27 | viewController.dataSource = presenter 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Swift Vietnam Presentation/CleanSwift/CleanSwift/_RepoConfiguration.swift: -------------------------------------------------------------------------------- 1 | // 2 | // _RepoConfiguration.swift 3 | // CleanSwift 4 | // 5 | // Created by Nghia Tran on 12/17/16. 6 | // Copyright © 2016 nghiatran. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class _RepoConfiguration: NSObject { 12 | 13 | static let shared = _RepoConfiguration() 14 | 15 | func configure(viewController: _RepoController) { 16 | 17 | // Presenter 18 | let presenter = _RepoPresenter() 19 | presenter.output = viewController 20 | 21 | // Interactor 22 | let interactor = _NewRepoInteractor() 23 | interactor.output = presenter 24 | 25 | // View controller 26 | viewController.output = interactor 27 | // viewController.dataSource = presenter 28 | } 29 | 30 | } 31 | -------------------------------------------------------------------------------- /Swift Vietnam Presentation/CleanSwift/CleanSwift/MainReducer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MainReducer.swift 3 | // Titan 4 | // 5 | // Created by Nghia Tran on 10/11/16. 6 | // Copyright © 2016 fe. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import ReSwift 11 | 12 | struct MainReducer { 13 | 14 | } 15 | 16 | // MARK - 17 | // MARK: Reducer Protocol 18 | extension MainReducer: Reducer { 19 | func handleAction(action: Action, state: MainAppState?) -> MainAppState { 20 | 21 | // Repo state 22 | let repoState = RepoState.reducer(action: action, state: state?.repoState) 23 | 24 | 25 | // Setting 26 | let settingState = SettingState.reducer(action: action, state: state?.settingState) 27 | 28 | // return 29 | return MainAppState(repoState: repoState, settingState: settingState) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /iOS-Starter-Kit/Source Code/Model/Networking/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 | // Repo request 35 | let repoRequest = FetchRepoRequest(param: ["q": text]) 36 | return repoRequest.toPromise() 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /iOS-Starter-Kit/Shared/Extensions/Register/UICollectionView+Register.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NSCollectionView+Register.swift 3 | // Titan 4 | // 5 | // Created by Nghia Tran on 12/2/16. 6 | // Copyright © 2016 fe. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | // 13 | // MARK: - Register View 14 | extension UICollectionView { 15 | 16 | // Helper register cell 17 | // The View must conform Identifier protocol 18 | func registerCell(_ viewType: T.Type) { 19 | self.register(viewType.xib(), forCellWithReuseIdentifier: viewType.identifier) 20 | } 21 | 22 | // Register Supplementary 23 | func registerSupplementary(_ supplementaryType: T.Type, kind: String) { 24 | self.register(supplementaryType.xib(), forSupplementaryViewOfKind: kind, withReuseIdentifier: supplementaryType.identifier) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /iOS-Starter-Kit/Source Code/Scenes/Example - Repo/View/RepoCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RepoCell.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 RepoCell: UITableViewCell { 12 | 13 | override func awakeFromNib() { 14 | super.awakeFromNib() 15 | // Initialization code 16 | } 17 | 18 | override func setSelected(_ selected: Bool, animated: Bool) { 19 | super.setSelected(selected, animated: animated) 20 | 21 | // Configure the view for the selected state 22 | } 23 | 24 | // 25 | // MARK: - Public 26 | func configureCellWithRepo(repo: RepoObj) { 27 | 28 | // Name 29 | self.textLabel?.text = repo.name 30 | } 31 | } 32 | 33 | extension RepoCell: XibInitialization { 34 | typealias Element = RepoCell 35 | } 36 | -------------------------------------------------------------------------------- /iOS-Starter-Kit/Source Code/Model/Networking/Request/FetchRepoRequest.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FetchRepoRequest.swift 3 | // Titan 4 | // 5 | // Created by Nghia Tran on 10/12/16. 6 | // Copyright © 2016 fe. All rights reserved. 7 | // 8 | 9 | import Alamofire 10 | import ObjectMapper 11 | 12 | 13 | struct FetchRepoRequest: Requestable { 14 | 15 | typealias T = [RepoObj] 16 | 17 | var param: Parameters? 18 | 19 | var httpMethod: HTTPMethod { 20 | get { return .get } 21 | } 22 | 23 | var endpoint: String { 24 | get { return Constants.APIEndPoint.RepoList } 25 | } 26 | 27 | var parameterEncoding: ParameterEncoding { 28 | get { return URLEncoding.default } 29 | } 30 | 31 | func decode(data: Any) -> Array { 32 | return Mapper().mapArray(JSONObject: data) ?? [] 33 | } 34 | 35 | // Init 36 | init(param: Parameters?) { 37 | self.param = param 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Swift Vietnam Presentation/CleanSwift/CleanSwift/FetchRepoWorker.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FetchRepoWorker.swift 3 | // CleanSwift 4 | // 5 | // Created by Nghia Tran on 12/17/16. 6 | // Copyright © 2016 nghiatran. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import ReSwift 11 | import PromiseKit 12 | 13 | // 14 | // MARK: - Action 15 | struct UpdateRepoListAction: Action { 16 | 17 | var repos: [RepoObj]? 18 | } 19 | 20 | 21 | // 22 | // MARK: - FetchRepoWorker 23 | class FetchRepoWorker: AsyncWorker { 24 | 25 | typealias T = [RepoObj] 26 | 27 | func execute() -> Promise { 28 | 29 | // Return 30 | return Networking.shared.fetchRepoWithText(text: "titan") 31 | .then { (repoObjs) -> Promise in 32 | 33 | // Disaptch action 34 | let action = UpdateRepoListAction(repos: repoObjs) 35 | mainStore.dispatch(action) 36 | 37 | return Promise(value: repoObjs) 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Swift Vietnam Presentation/CleanSwift/CleanSwift/RepoState.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RepoState.swift 3 | // CleanSwift 4 | // 5 | // Created by Nghia Tran on 12/17/16. 6 | // Copyright © 2016 nghiatran. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import ReSwift 11 | 12 | // 13 | // MARK: - Repo 14 | struct RepoState: StateType { 15 | 16 | 17 | /// Repos list 18 | var repos: [RepoObj] = [] 19 | } 20 | 21 | 22 | // 23 | // MARK: - Reducer 24 | extension RepoState { 25 | static func reducer(action: Action, state: RepoState?) -> RepoState { 26 | 27 | // Get state 28 | var state = state ?? RepoState() 29 | 30 | // Doing 31 | switch action { 32 | case let action as UpdateRepoListAction: 33 | 34 | /// Update here 35 | state.repos = action.repos ?? [] 36 | 37 | break 38 | default: 39 | break 40 | } 41 | 42 | return state 43 | } 44 | } 45 | 46 | -------------------------------------------------------------------------------- /iOS-Starter-Kit/Shared/Router/Router.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Router.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 UIKit 11 | 12 | // 13 | // MARK: - Router Type 14 | enum RouterType { 15 | case root 16 | case push 17 | case present 18 | } 19 | 20 | // 21 | // MARK: - Basic Router protocol 22 | protocol Router { 23 | 24 | // Associatedtype for kind of router 25 | associatedtype Element: UIViewController 26 | 27 | // Type 28 | var routerType: RouterType {get} 29 | 30 | // View controller 31 | var viewController: Element {get} 32 | 33 | // Passing data between to router 34 | func handleData(_ block: (Element) -> Void) 35 | } 36 | 37 | // 38 | // MARK: - Extend Router 39 | extension Router { 40 | 41 | // Passing data 42 | func handleData(_ block: (Element) -> Void) { 43 | block(self.viewController) 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /iOS-Starter-Kit/Source Code/Scenes/Example - Repo/Worker/FetchRepoWorker.swift: -------------------------------------------------------------------------------- 1 | // 2 | // FetchRepoWorker.swift 3 | // CleanSwift 4 | // 5 | // Created by Nghia Tran on 12/17/16. 6 | // Copyright © 2016 nghiatran. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import ReSwift 11 | import PromiseKit 12 | 13 | // 14 | // MARK: - Action 15 | struct UpdateRepoListAction: Action { 16 | 17 | var repos: [RepoObj]? 18 | } 19 | 20 | 21 | // 22 | // MARK: - FetchRepoWorker 23 | class FetchRepoWorker: AsyncWorker { 24 | 25 | typealias T = [RepoObj] 26 | 27 | func execute() -> Promise { 28 | 29 | // Return 30 | return Networking.shared.fetchRepoWithText(text: "titan") 31 | .then { (repoObjs) -> Promise in 32 | 33 | // Disaptch action 34 | let action = UpdateRepoListAction(repos: repoObjs) 35 | mainStore.dispatch(action) 36 | 37 | return Promise(value: repoObjs) 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /iOS-Starter-Kit/Shared/App/Store/State/RepoState.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RepoState.swift 3 | // CleanSwift 4 | // 5 | // Created by Nghia Tran on 12/17/16. 6 | // Copyright © 2016 nghiatran. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import ReSwift 11 | import RxSwift 12 | 13 | // 14 | // MARK: - Repo 15 | struct RepoState: StateType { 16 | 17 | 18 | /// Repos list 19 | var repos = Variable<[RepoObj]>([]) 20 | } 21 | 22 | 23 | // 24 | // MARK: - Reducer 25 | extension RepoState { 26 | static func reducer(action: Action, state: RepoState?) -> RepoState { 27 | 28 | // Get state 29 | let state = state ?? RepoState() 30 | 31 | // Doing 32 | switch action { 33 | case let action as UpdateRepoListAction: 34 | 35 | /// Update here 36 | state.repos.value = action.repos ?? [] 37 | 38 | break 39 | default: 40 | break 41 | } 42 | 43 | return state 44 | } 45 | } 46 | 47 | -------------------------------------------------------------------------------- /Swift Vietnam Presentation/MVC-Repo-Github/MVC-Repo-Github/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | } 43 | ], 44 | "info" : { 45 | "version" : 1, 46 | "author" : "xcode" 47 | } 48 | } -------------------------------------------------------------------------------- /Swift Vietnam Presentation/CleanSwift/CleanSwift/RepoPresenter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RepoPresenter.swift 3 | // CleanSwift 4 | // 5 | // Created by Nghia Tran on 12/17/16. 6 | // Copyright © 2016 nghiatran. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | protocol RepoPresenterOutput: class { 12 | 13 | /// Error 14 | func presentError(_ error: Error) 15 | 16 | 17 | /// Reload data 18 | func reloadData(repos: [RepoObj]) 19 | } 20 | 21 | class RepoPresenter { 22 | 23 | 24 | // 25 | // MARK: - Variable 26 | 27 | /// Output 28 | weak var output: RepoPresenterOutput? 29 | } 30 | 31 | 32 | extension RepoPresenter: RepoInteractorOutput { 33 | 34 | func presentError(_ error: Error) { 35 | self.output?.presentError(error) 36 | } 37 | } 38 | 39 | // 40 | // MARK: - Data Source 41 | extension RepoPresenter: RepoControllerDataSource { 42 | 43 | func numberOfRepo() -> Int { 44 | return 0 45 | } 46 | 47 | func repoAtRow(row: Int) -> RepoObj { 48 | 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /Swift Vietnam Presentation/CleanSwift/CleanSwift/SettingController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SettingController.swift 3 | // CleanSwift 4 | // 5 | // Created by Nghia Tran on 12/17/16. 6 | // Copyright © 2016 nghiatran. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class SettingController: UIViewController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | 16 | // Do any additional setup after loading the view. 17 | } 18 | 19 | override func didReceiveMemoryWarning() { 20 | super.didReceiveMemoryWarning() 21 | // Dispose of any resources that can be recreated. 22 | } 23 | 24 | 25 | /* 26 | // MARK: - Navigation 27 | 28 | // In a storyboard-based application, you will often want to do a little preparation before navigation 29 | override func prepare(for segue: UIStoryboardSegue, sender: Any?) { 30 | // Get the new view controller using segue.destinationViewController. 31 | // Pass the selected object to the new view controller. 32 | } 33 | */ 34 | 35 | } 36 | -------------------------------------------------------------------------------- /iOS-Starter-Kit/Shared/Extensions/Storyboard+Initialization.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Storyboard+Initialization.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 UIKit 11 | 12 | // 13 | // MARK: - StoryboardInitialization 14 | protocol StoryboardInitialization: Identifier { 15 | 16 | // Associated type: Must adopt Identifier 17 | associatedtype Element: Identifier 18 | 19 | // Get view controller from storyboard 20 | static var storyboardViewController: Element {get} 21 | } 22 | 23 | // 24 | // MARK: - Extend StoryboardInitialization if Element is UIViewController 25 | extension StoryboardInitialization where Element: UIViewController { 26 | 27 | // Get view controller 28 | static var storyboardViewController: Element { 29 | let storybroad = UIStoryboard(name: Element.identifier, bundle: nil) 30 | return storybroad.instantiateViewController(withIdentifier: Element.identifier) as! Element 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /iOS-Starter-KitTests/iOS_Starter_KitTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // iOS_Starter_KitTests.swift 3 | // iOS-Starter-KitTests 4 | // 5 | // Created by Nghia Tran on 12/19/16. 6 | // Copyright © 2016 nghiatran. All rights reserved. 7 | // 8 | 9 | import XCTest 10 | @testable import iOS_Starter_Kit 11 | 12 | class iOS_Starter_KitTests: XCTestCase { 13 | 14 | override func setUp() { 15 | super.setUp() 16 | // Put setup code here. This method is called before the invocation of each test method in the class. 17 | } 18 | 19 | override func tearDown() { 20 | // Put teardown code here. This method is called after the invocation of each test method in the class. 21 | super.tearDown() 22 | } 23 | 24 | func testExample() { 25 | // This is an example of a functional test case. 26 | // Use XCTAssert and related functions to verify your tests produce the correct results. 27 | } 28 | 29 | func testPerformanceExample() { 30 | // This is an example of a performance test case. 31 | self.measure { 32 | // Put the code you want to measure the time of here. 33 | } 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /Swift Vietnam Presentation/CleanSwift/CleanSwift/_RepoController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // _RepoController.swift 3 | // CleanSwift 4 | // 5 | // Created by Nghia Tran on 12/17/16. 6 | // Copyright © 2016 nghiatran. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | 12 | protocol _RepoControllerOutput { 13 | 14 | func fetchRepo() 15 | } 16 | 17 | class _RepoController: UIViewController { 18 | 19 | 20 | // 21 | // MARK: - OUTPUT 22 | var output: _RepoControllerOutput? 23 | 24 | 25 | override func viewDidLoad() { 26 | super.viewDidLoad() 27 | 28 | // Do any additional setup after loading the view. 29 | 30 | 31 | self.fetchRepo() 32 | } 33 | 34 | override func didReceiveMemoryWarning() { 35 | super.didReceiveMemoryWarning() 36 | // Dispose of any resources that can be recreated. 37 | } 38 | 39 | func fetchRepo() { 40 | 41 | // MaGIC 42 | self.output?.fetchRepo() 43 | } 44 | } 45 | 46 | 47 | 48 | extension _RepoController: _RepoPresenterOutput { 49 | 50 | func showAlerView() { 51 | // API 52 | // ALertView 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Alamofire (4.2.0) 3 | - ObjectMapper (2.2.2) 4 | - PromiseKit (4.1.0): 5 | - PromiseKit/Foundation (= 4.1.0) 6 | - PromiseKit/QuartzCore (= 4.1.0) 7 | - PromiseKit/UIKit (= 4.1.0) 8 | - PromiseKit/CorePromise (4.1.0) 9 | - PromiseKit/Foundation (4.1.0): 10 | - PromiseKit/CorePromise 11 | - PromiseKit/QuartzCore (4.1.0): 12 | - PromiseKit/CorePromise 13 | - PromiseKit/UIKit (4.1.0): 14 | - PromiseKit/CorePromise 15 | - ReSwift (3.0.0) 16 | - RxSwift (3.0.1) 17 | - SwiftyBeaver (1.1.1) 18 | 19 | DEPENDENCIES: 20 | - Alamofire (~> 4.0) 21 | - ObjectMapper (~> 2.2) 22 | - PromiseKit (~> 4.0) 23 | - ReSwift 24 | - RxSwift (~> 3.0) 25 | - SwiftyBeaver 26 | 27 | SPEC CHECKSUMS: 28 | Alamofire: aa2e09d871c9160ac53c90e83c68064a94e3dfbe 29 | ObjectMapper: 9e385c2295bcc4e16eabbcfc85db801442bba545 30 | PromiseKit: c98453ff78544c0ad5bb0ea980782e6875f47978 31 | ReSwift: f0255f4b4f2dacd12c5d97d34d13fc5dad6d6371 32 | RxSwift: af5680055c4ad04480189c52d28385b1029493a6 33 | SwiftyBeaver: 2b68694df862381235e32bcfc4ec991e26bf131d 34 | 35 | PODFILE CHECKSUM: dd816bd7e18a4bd24769f358c6079b8e658f79e2 36 | 37 | COCOAPODS: 1.2.0.beta.1 38 | -------------------------------------------------------------------------------- /iOS-Starter-Kit/Source Code/Scenes/SettingViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SettingViewController.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 UIKit 10 | 11 | protocol SettingViewControllerOutput { 12 | 13 | } 14 | 15 | class SettingViewController: UIViewController { 16 | 17 | // 18 | // MARK: - Variable 19 | var output: SettingViewControllerOutput? 20 | var searchText: String! 21 | 22 | 23 | // 24 | // MARK: - View Cycle 25 | override func viewDidLoad() { 26 | super.viewDidLoad() 27 | 28 | // Configuration 29 | SettingConfiguration.shared.configure(viewController: self) 30 | } 31 | 32 | override func didReceiveMemoryWarning() { 33 | super.didReceiveMemoryWarning() 34 | // Dispose of any resources that can be recreated. 35 | } 36 | 37 | } 38 | 39 | // 40 | // MARK: - XibInitialization 41 | extension SettingViewController: XibInitialization { 42 | typealias Element = SettingViewController 43 | } 44 | 45 | // 46 | // MARK: - SettingPresenterOutput 47 | extension SettingViewController: SettingPresenterOutput {} 48 | -------------------------------------------------------------------------------- /Swift Vietnam Presentation/MVC-Repo-Github/MVC-Repo-Github/SettingViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SettingViewController.swift 3 | // MVC-Repo-Github 4 | // 5 | // Created by Nghia Tran on 12/17/16. 6 | // Copyright © 2016 nghiatran. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class SettingViewController: UIViewController { 12 | 13 | // Count 14 | @IBOutlet weak var reposCount: UILabel! 15 | 16 | 17 | override func viewDidLoad() { 18 | super.viewDidLoad() 19 | 20 | // Do any additional setup after loading the view. 21 | 22 | // 23 | NotificationCenter.default.addObserver(self, selector: #selector(SettingViewController.reloadSettingViewNotification), name: NSNotification.Name(rawValue: "Reload"), object: nil) 24 | } 25 | 26 | override func didReceiveMemoryWarning() { 27 | super.didReceiveMemoryWarning() 28 | // Dispose of any resources that can be recreated. 29 | } 30 | 31 | 32 | @objc func reloadSettingViewNotification(sender: Notification) { 33 | 34 | let count = sender.userInfo?["count"] as? Int ?? 0 35 | 36 | // Reload here 37 | self.reposCount.text = "\(count)" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Swift Vietnam Presentation/CleanSwift/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Alamofire (4.2.0) 3 | - ObjectMapper (2.2.2) 4 | - PromiseKit (4.1.0): 5 | - PromiseKit/Foundation (= 4.1.0) 6 | - PromiseKit/QuartzCore (= 4.1.0) 7 | - PromiseKit/UIKit (= 4.1.0) 8 | - PromiseKit/CorePromise (4.1.0) 9 | - PromiseKit/Foundation (4.1.0): 10 | - PromiseKit/CorePromise 11 | - PromiseKit/QuartzCore (4.1.0): 12 | - PromiseKit/CorePromise 13 | - PromiseKit/UIKit (4.1.0): 14 | - PromiseKit/CorePromise 15 | - ReSwift (3.0.0) 16 | - RxCocoa (3.0.1): 17 | - RxSwift (~> 3.0) 18 | - RxSwift (3.0.1) 19 | 20 | DEPENDENCIES: 21 | - Alamofire (~> 4.0) 22 | - ObjectMapper (~> 2.2) 23 | - PromiseKit (~> 4.0) 24 | - ReSwift 25 | - RxCocoa (~> 3.0) 26 | - RxSwift (~> 3.0) 27 | 28 | SPEC CHECKSUMS: 29 | Alamofire: aa2e09d871c9160ac53c90e83c68064a94e3dfbe 30 | ObjectMapper: 9e385c2295bcc4e16eabbcfc85db801442bba545 31 | PromiseKit: c98453ff78544c0ad5bb0ea980782e6875f47978 32 | ReSwift: f0255f4b4f2dacd12c5d97d34d13fc5dad6d6371 33 | RxCocoa: 15a52fc590dcc700cb4a690a633b5c5184ce3a78 34 | RxSwift: af5680055c4ad04480189c52d28385b1029493a6 35 | 36 | PODFILE CHECKSUM: 26fba9a9240f6ac5d330a8e607ad9afe3de9f501 37 | 38 | COCOAPODS: 1.1.1 39 | -------------------------------------------------------------------------------- /Swift Vietnam Presentation/CleanSwift/CleanSwift/RepoInteractor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RepoInteractor.swift 3 | // CleanSwift 4 | // 5 | // Created by Nghia Tran on 12/17/16. 6 | // Copyright © 2016 nghiatran. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import PromiseKit 11 | 12 | // 13 | // MARK: - OUTPUT 14 | protocol RepoInteractorOutput { 15 | 16 | 17 | /// Show Error 18 | func presentError(_ error: Error) 19 | } 20 | 21 | 22 | // 23 | // MARK: - RepoInteractor 24 | class RepoInteractor { 25 | 26 | 27 | // 28 | // MARK: - Variable 29 | 30 | /// Output 31 | var output: RepoInteractorOutput? 32 | 33 | 34 | /// Workers 35 | fileprivate lazy var fetchRepoWorker: FetchRepoWorker = { 36 | return FetchRepoWorker() 37 | }() 38 | } 39 | 40 | 41 | // 42 | // MARK: - Controller's output 43 | extension RepoInteractor: RepoControllerOutput { 44 | 45 | 46 | /// Fetch 47 | func fetchList() { 48 | 49 | /// Get worker 50 | self.fetchRepoWorker.execute() 51 | .then { _ -> Void in 52 | // Nothing 53 | } 54 | .catch { error in 55 | self.output?.presentError(error) 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /iOS-Starter-Kit/Source Code/Scenes/Example - Repo/Controller/RepoInteractor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RepoInteractor.swift 3 | // CleanSwift 4 | // 5 | // Created by Nghia Tran on 12/17/16. 6 | // Copyright © 2016 nghiatran. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import PromiseKit 11 | 12 | 13 | // 14 | // MARK: - OUTPUT 15 | protocol RepoInteractorOutput { 16 | 17 | 18 | /// Show Error 19 | func presentError(_ error: Error) 20 | } 21 | 22 | 23 | // 24 | // MARK: - RepoInteractor 25 | class RepoInteractor { 26 | 27 | 28 | // 29 | // MARK: - Variable 30 | 31 | /// Output 32 | var output: RepoInteractorOutput? 33 | 34 | 35 | /// Workers 36 | fileprivate lazy var fetchRepoWorker: FetchRepoWorker = { 37 | return FetchRepoWorker() 38 | }() 39 | } 40 | 41 | 42 | // 43 | // MARK: - Controller's output 44 | extension RepoInteractor: RepoControllerOutput { 45 | 46 | 47 | /// Fetch 48 | func fetchList() { 49 | 50 | /// Get worker 51 | self.fetchRepoWorker.execute() 52 | .then { _ -> Void in 53 | // Nothing 54 | } 55 | .catch { error in 56 | self.output?.presentError(error) 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /iOS-Starter-Kit/Shared/Extensions/Xib+Initialization.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Xib+Initialization.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 UIKit 11 | 12 | // 13 | // MARK: - XibInitialization 14 | // Helper protocol to get xib generic 15 | protocol XibInitialization: Identifier { 16 | 17 | associatedtype Element: Identifier 18 | 19 | static func xib() -> UINib 20 | 21 | static func xibInstance() -> Element 22 | } 23 | 24 | // 25 | // MARK: - Default 26 | extension XibInitialization { 27 | 28 | static func xib() -> UINib { 29 | return UINib(nibName: Element.identifier, bundle: nil) 30 | } 31 | } 32 | 33 | // 34 | // MARK: - Extend UIView 35 | extension XibInitialization where Element: UIView { 36 | 37 | static func xibInstance() -> Element { 38 | let xib = self.xib() 39 | return xib.instantiate(withOwner: self, options: nil).first! as! Element 40 | } 41 | } 42 | 43 | // 44 | // MARK: - Extend UIViewController 45 | extension XibInitialization where Element: UIViewController { 46 | 47 | static func xibInstance() -> Element { 48 | return Element(nibName: Element.identifier, bundle: nil) 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /Swift Vietnam Presentation/CleanSwift/CleanSwift/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /Swift Vietnam Presentation/MVC-Repo-Github/MVC-Repo-Github/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /iOS-Starter-Kit/Source Code/Scenes/Example - Repo/Controller/RepoPresenter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RepoPresenter.swift 3 | // CleanSwift 4 | // 5 | // Created by Nghia Tran on 12/17/16. 6 | // Copyright © 2016 nghiatran. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import RxSwift 11 | 12 | protocol RepoPresenterOutput: class { 13 | 14 | /// Error 15 | func presentError(_ error: Error) 16 | 17 | 18 | /// Reload data 19 | func reloadTableView() 20 | } 21 | 22 | class RepoPresenter { 23 | 24 | /// Data Source 25 | fileprivate lazy var repoDataSource: RepoDataSource = { 26 | let repo = RepoDataSource() 27 | repo.delegate = self 28 | return repo 29 | }() 30 | 31 | 32 | /// Output 33 | weak var output: RepoPresenterOutput? 34 | } 35 | 36 | 37 | extension RepoPresenter: RepoInteractorOutput { 38 | 39 | func presentError(_ error: Error) { 40 | self.output?.presentError(error) 41 | } 42 | } 43 | 44 | // 45 | // MARK: - Data Source 46 | extension RepoPresenter: RepoControllerDataSource { 47 | 48 | func dataSource() -> UITableViewDataSource { 49 | return self.repoDataSource 50 | } 51 | } 52 | 53 | 54 | // 55 | // MARK: - Delegate 56 | extension RepoPresenter: RepoDataSourceDelegate { 57 | 58 | func reloadTableView() { 59 | self.output?.reloadTableView() 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /iOS-Starter-Kit/Shared/Common/Application/ApplicationManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ApplicationManager.swift 3 | // Titan 4 | // 5 | // Created by Nghia Tran on 10/14/16. 6 | // Copyright © 2016 fe. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ApplicationManager { 12 | 13 | // 14 | // MARK: - Variable 15 | static let sharedInstance = ApplicationManager() 16 | 17 | 18 | // Global Date formatter 19 | lazy var globalDateFormatter: DateFormatter = { 20 | let dateFormatter = DateFormatter() 21 | dateFormatter.timeZone = NSTimeZone(name: "UTC") as TimeZone! 22 | dateFormatter.locale = NSLocale.init(localeIdentifier: "en_US_POSIX") as Locale! 23 | dateFormatter.dateFormat = "yyyy'-'MM'-'dd'T'HH':'mm':'ss'.'zzz'Z'" 24 | return dateFormatter 25 | }() 26 | 27 | 28 | // 29 | // MARK: Public 30 | 31 | /// SDK 32 | func initAllSDKs() { 33 | 34 | } 35 | 36 | 37 | /// Common 38 | func initCommon(window: UIWindow?) { 39 | 40 | // Logger 41 | self.initLogger() 42 | 43 | // Global Appearance 44 | self.initGlobalAppearance() 45 | } 46 | 47 | } 48 | 49 | 50 | // 51 | // MARK: - Private 52 | extension ApplicationManager { 53 | 54 | // Logger 55 | fileprivate func initGlobalAppearance() { 56 | 57 | } 58 | } 59 | 60 | 61 | // MARK: 62 | // MARK: Logger 63 | extension ApplicationManager { 64 | fileprivate func initLogger() { 65 | Logger.initLogger() 66 | } 67 | } 68 | 69 | -------------------------------------------------------------------------------- /iOS-Starter-Kit/Configuration/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | en 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UILaunchStoryboardName 24 | LaunchScreen 25 | UIMainStoryboardFile 26 | Main 27 | UIRequiredDeviceCapabilities 28 | 29 | armv7 30 | 31 | UISupportedInterfaceOrientations 32 | 33 | UIInterfaceOrientationPortrait 34 | UIInterfaceOrientationLandscapeLeft 35 | UIInterfaceOrientationLandscapeRight 36 | 37 | UISupportedInterfaceOrientations~ipad 38 | 39 | UIInterfaceOrientationPortrait 40 | UIInterfaceOrientationPortraitUpsideDown 41 | UIInterfaceOrientationLandscapeLeft 42 | UIInterfaceOrientationLandscapeRight 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /iOS-Starter-Kit/Shared/Base/BaseViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BaseViewController.swift 3 | // Titan 4 | // 5 | // Created by Nghia Tran on 10/11/16. 6 | // Copyright © 2016 fe. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | // 13 | // MARK: - Confrom BaseAbility 14 | /// The main purpose is we don't perfer BaseViewController 15 | /// If create BaseViewController, we also need to create BaseSplitViewController, BaseNavigationController 16 | /// -> Redundancy code 17 | 18 | 19 | /* 20 | # Tried 1 21 | 22 | - Extension default implement for all BaseAbility's methods 23 | -> Problem: Subclass of NSViewController can't override method from protocol 24 | 25 | 26 | # Tried 2 27 | 28 | - Convert it's to optional 29 | - So we don't need to implement BaseAbility's protocol 30 | -> Problem: optional only support in @objs protocl, and @objc method. 31 | But the real struggle is, @objc protocol doesn't support Extension Protocol =,= 32 | Ref: http://stackoverflow.com/questions/39487168/non-objc-method-does-not-satisfy-optional-requirement-of-objc-protocol 33 | 34 | # Tried 3 35 | - Conform BaseAbility protocol 36 | - Implement default methods 37 | -> Results: 'Override method' worked 38 | 'Don't need BaseView, BaseCollectionView, Base ....' 39 | 40 | */ 41 | 42 | extension UIViewController: BaseAbility { 43 | 44 | 45 | /// Common 46 | func initCommon() { 47 | 48 | } 49 | 50 | /// Appearance 51 | func initUIs() {} 52 | 53 | 54 | /// Binding 55 | func initBinding() {} 56 | 57 | 58 | /// Action 59 | func initActions() {} 60 | 61 | } 62 | -------------------------------------------------------------------------------- /iOS-Starter-Kit/Shared/Base/BaseView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BaseView.swift 3 | // Titan 4 | // 5 | // Created by Nghia Tran on 12/8/16. 6 | // Copyright © 2016 fe. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | // 13 | // MARK: - Override awakeFromNib 14 | /// We shouldn't create too many BaseView like what we did before. 15 | /// It's redundancy if we create many BaseView, ex: BaseView, BaseCollectionViewCell, BaseTableViewCell, ... 16 | /// Just conform BaseAbility 17 | /// Less code - less bug 18 | 19 | /* 20 | # Tried 1 21 | 22 | - Extension default implement for all BaseAbility's methods 23 | -> Problem: Subclass of NSViewController can't override method from protocol 24 | 25 | 26 | # Tried 2 27 | 28 | - Convert it's to optional 29 | - So we don't need to implement BaseAbility's protocol 30 | -> Problem: optional only support in @objs protocl, and @objc method. 31 | But the real struggle is, @objc protocol doesn't support Extension Protocol =,= 32 | Ref: http://stackoverflow.com/questions/39487168/non-objc-method-does-not-satisfy-optional-requirement-of-objc-protocol 33 | 34 | # Tried 3 35 | - Conform BaseAbility protocol 36 | - Implement default methods 37 | -> Results: 'Override method' worked 38 | 'Don't need BaseView, BaseCollectionView, Base ....' 39 | 40 | */ 41 | 42 | 43 | extension UIView: BaseAbility { 44 | 45 | 46 | /// Common 47 | func initCommon() { 48 | 49 | } 50 | 51 | 52 | /// UIs 53 | func initUIs() {} 54 | 55 | 56 | /// Binding 57 | func initBinding() {} 58 | 59 | 60 | /// Action 61 | func initActions() {} 62 | 63 | } 64 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Xcode 2 | # 3 | # gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore 4 | 5 | ## Build generated 6 | build/ 7 | DerivedData/ 8 | 9 | ## Various settings 10 | *.pbxuser 11 | !default.pbxuser 12 | *.mode1v3 13 | !default.mode1v3 14 | *.mode2v3 15 | !default.mode2v3 16 | *.perspectivev3 17 | !default.perspectivev3 18 | xcuserdata/ 19 | 20 | ## Other 21 | *.moved-aside 22 | *.xcuserstate 23 | 24 | ## Obj-C/Swift specific 25 | *.hmap 26 | *.ipa 27 | *.dSYM.zip 28 | *.dSYM 29 | 30 | ## Playgrounds 31 | timeline.xctimeline 32 | playground.xcworkspace 33 | 34 | # Swift Package Manager 35 | # 36 | # Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. 37 | 38 | Packages/ 39 | .build/ 40 | 41 | # CocoaPods 42 | # 43 | # We recommend against adding the Pods directory to your .gitignore. However 44 | # you should judge for yourself, the pros and cons are mentioned at: 45 | # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control 46 | # 47 | 48 | Pods/ 49 | 50 | # Carthage 51 | # 52 | # Add this line if you want to avoid checking in source code from Carthage dependencies. 53 | 54 | Carthage/Checkouts 55 | Carthage/Build 56 | 57 | # fastlane 58 | # 59 | # It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the 60 | # screenshots whenever they are needed. 61 | # For more information about the recommended setup visit: 62 | # https://github.com/fastlane/fastlane/blob/master/fastlane/docs/Gitignore.md 63 | 64 | fastlane/report.xml 65 | fastlane/Preview.html 66 | fastlane/screenshots 67 | fastlane/test_output 68 | -------------------------------------------------------------------------------- /iOS-Starter-Kit/Shared/Extensions/Ability/BaseAbility.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BaseViewAbility.swift 3 | // Titan 4 | // 5 | // Created by Nghia Tran on 12/8/16. 6 | // Copyright © 2016 fe. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | 12 | // 13 | // MARK: - BaseAbility 14 | /// I don't prefer to way to subclass BaseClass 15 | /// So BaseAbility will contain all things BaseClass does, but it's flexible 16 | /// We don't need create BaseView, BaseCollectionViewCell, Base ..... 17 | /// Just conform this protocol 18 | protocol BaseAbility: class { 19 | 20 | 21 | /// Init all relative methods below 22 | /// An helper method 23 | func initBaseAbility() 24 | 25 | 26 | /// Init Common 27 | /// Intent to do everything common here 28 | func initCommon() 29 | 30 | 31 | /// UIs 32 | /// To do all things about UIs 33 | func initUIs() 34 | 35 | 36 | /// Binding 37 | func initBinding() 38 | 39 | 40 | /// Action 41 | func initActions() 42 | } 43 | 44 | 45 | // 46 | // MARK: - Default Extension 47 | /* 48 | The reason we why can't override ViewController's ViewDidLoad in extenstion, 'cause Swift protocol don't allow we do that 49 | So we need create seperate method to call it manaully 50 | */ 51 | extension BaseAbility { 52 | 53 | 54 | /// Make default implement of initBaseAbility to call all relative methods. 55 | /// We must call method in where we need. Ex: ViewController's ViewDidLoad 56 | /// NSView's AwakeFromNib 57 | func initBaseAbility() { 58 | 59 | self.initCommon() 60 | 61 | self.initUIs() 62 | 63 | self.initBinding() 64 | 65 | self.initActions() 66 | } 67 | } 68 | 69 | 70 | -------------------------------------------------------------------------------- /Swift Vietnam Presentation/CleanSwift/CleanSwift/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /iOS-Starter-Kit/Shared/Common/SwiftSafer.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SwiftSafer.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 | 11 | typealias SafeAccessBlock = () -> () 12 | 13 | // Protocol 14 | protocol Safe { 15 | 16 | func read(_ accessBlock: SafeAccessBlock) 17 | 18 | func writeSync(_ accessBlock: SafeAccessBlock) 19 | 20 | func writeAync(_ accessBlock: @escaping SafeAccessBlock) 21 | } 22 | 23 | let EREWQueueName = "com.fe.feels.EREW" 24 | let CREWQueueName = "com.fe.feels.CREW" 25 | 26 | class EREW: Safe { 27 | 28 | var queue: DispatchQueue! 29 | 30 | init(queue: DispatchQueue = DispatchQueue(label: EREWQueueName, attributes: [])) { 31 | self.queue = queue 32 | } 33 | 34 | func read(_ accessBlock: SafeAccessBlock) { 35 | self.queue.sync(execute: accessBlock) 36 | } 37 | 38 | internal func writeAync(_ accessBlock: @escaping () -> ()) { 39 | self.queue.async(execute: accessBlock) 40 | } 41 | 42 | func writeSync(_ accessBlock: SafeAccessBlock) { 43 | self.queue.sync(execute: accessBlock) 44 | } 45 | } 46 | 47 | class CREW: Safe { 48 | 49 | var queue: DispatchQueue! 50 | 51 | init(queue: DispatchQueue = DispatchQueue(label: CREWQueueName, attributes: DispatchQueue.Attributes.concurrent)) { 52 | self.queue = queue 53 | } 54 | 55 | func read(_ accessBlock: SafeAccessBlock) { 56 | self.queue.sync(execute: accessBlock) 57 | } 58 | 59 | internal func writeAync(_ accessBlock: @escaping () -> ()) { 60 | self.queue.async(flags: .barrier, execute: accessBlock) 61 | } 62 | 63 | func writeSync(_ accessBlock: SafeAccessBlock) { 64 | self.queue.sync(flags: .barrier, execute: accessBlock) 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /Swift Vietnam Presentation/MVC-Repo-Github/MVC-Repo-Github/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | -------------------------------------------------------------------------------- /iOS-Starter-Kit/Shared/App/Obj Model/BaseObj.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BaseObj.swift 3 | // iOS-Starter-Kit 4 | // 5 | // Created by Nghia Tran on 12/19/16. 6 | // Copyright © 2016 nghiatran. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import ObjectMapper 11 | 12 | class BaseObj: Mappable { 13 | 14 | // 15 | // MARK: - Variable 16 | var objectId: String! 17 | var createdAt: Date! 18 | var updatedAt: Date! 19 | var className: String! 20 | 21 | // 22 | // MARK: - Init 23 | required init?(map: Map) { 24 | 25 | guard map.JSON[Constants.Obj.ObjectId] != nil else { 26 | Logger.error("Can't create obj in BaseObj. Missing ObjectId") 27 | return nil 28 | } 29 | } 30 | 31 | // Mapping function 32 | func mapping(map: Map) { 33 | self.objectId <- map[Constants.Obj.ObjectId] 34 | self.createdAt <- (map[Constants.Obj.CreatedAt], APIDateTransform()) 35 | self.updatedAt <- (map[Constants.Obj.UpdatedAt], APIDateTransform()) 36 | } 37 | } 38 | 39 | 40 | // 41 | // MARK: - Date Transform 42 | public class APIDateTransform: TransformType { 43 | 44 | public typealias Object = Date 45 | public typealias JSON = String 46 | 47 | public init() {} 48 | 49 | public func transformFromJSON(_ value: Any?) -> Date? { 50 | if let value = value as? String { 51 | return ApplicationManager.sharedInstance.globalDateFormatter.date(from: value) as Date? 52 | } 53 | 54 | if let value = value as? Date { 55 | return value 56 | } 57 | 58 | return nil 59 | } 60 | 61 | public func transformToJSON(_ value: Date?) -> String? { 62 | if let value = value { 63 | return ApplicationManager.sharedInstance.globalDateFormatter.string(from: value as Date) 64 | } 65 | 66 | return nil 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /iOS-Starter-Kit/Source Code/Scenes/Example - Repo/DataSource/RepoDataSource.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RepoDataSource.swift 3 | // iOS-Starter-Kit 4 | // 5 | // Created by Nghia Tran on 12/19/16. 6 | // Copyright © 2016 nghiatran. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | import RxSwift 12 | 13 | 14 | protocol RepoDataSourceDelegate: class { 15 | 16 | // Reload 17 | func reloadTableView() 18 | } 19 | 20 | class RepoDataSource: NSObject { 21 | 22 | // 23 | // MARK: - Variable 24 | weak var delegate: RepoDataSourceDelegate? 25 | private let disposeBag = DisposeBag() 26 | fileprivate var repos: Variable<[RepoObj]> { 27 | return mainStore.state.repoState!.repos 28 | } 29 | 30 | 31 | // 32 | // MARK: - Init 33 | override init() { 34 | super.init() 35 | 36 | // Obser 37 | self.repos.asObservable().subscribe {[unowned self] (repo) in 38 | 39 | // Reload 40 | self.delegate?.reloadTableView() 41 | 42 | }.addDisposableTo(self.disposeBag) 43 | } 44 | } 45 | 46 | // 47 | // MARK: - TableView 48 | extension RepoDataSource: UITableViewDataSource { 49 | 50 | func numberOfSections(in tableView: UITableView) -> Int { 51 | return 1 52 | } 53 | 54 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 55 | return self.repos.value.count 56 | } 57 | 58 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 59 | 60 | let cell = tableView.dequeueReusableCell(withIdentifier: "RepoCell", for: indexPath) as! RepoCell 61 | 62 | let repo = self.repos.value[indexPath.row] 63 | cell.configureCellWithRepo(repo: repo) 64 | 65 | return cell 66 | } 67 | } 68 | 69 | extension RepoController: UITableViewDelegate { 70 | 71 | } 72 | -------------------------------------------------------------------------------- /iOS-Starter-Kit/Resource/Assets/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "size" : "20x20", 6 | "scale" : "2x" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "size" : "20x20", 11 | "scale" : "3x" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "size" : "29x29", 16 | "scale" : "2x" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "size" : "29x29", 21 | "scale" : "3x" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "size" : "40x40", 26 | "scale" : "2x" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "size" : "40x40", 31 | "scale" : "3x" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "size" : "60x60", 36 | "scale" : "2x" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "size" : "60x60", 41 | "scale" : "3x" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "size" : "20x20", 46 | "scale" : "1x" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "size" : "20x20", 51 | "scale" : "2x" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "size" : "29x29", 56 | "scale" : "1x" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "size" : "29x29", 61 | "scale" : "2x" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "size" : "40x40", 66 | "scale" : "1x" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "size" : "40x40", 71 | "scale" : "2x" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "size" : "76x76", 76 | "scale" : "1x" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "size" : "76x76", 81 | "scale" : "2x" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "size" : "83.5x83.5", 86 | "scale" : "2x" 87 | } 88 | ], 89 | "info" : { 90 | "version" : 1, 91 | "author" : "xcode" 92 | } 93 | } -------------------------------------------------------------------------------- /iOS-Starter-Kit/Shared/Router/RouterManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RouterManager.swift 3 | // iOS-Starter-Kit 4 | // 5 | // Created by Nghia Tran on 12/21/16. 6 | // Copyright © 2016 nghiatran. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | class RouterManager { 13 | 14 | // 15 | // MARK: - Variable 16 | static let shared = RouterManager() 17 | 18 | // 19 | // MARK: - Initialize 20 | init() { 21 | 22 | } 23 | 24 | // 25 | // MARK: - helper 26 | var rootViewController: UIViewController? { 27 | let app = UIApplication.shared.delegate as! AppDelegate 28 | return app.window?.rootViewController 29 | } 30 | 31 | var visibleNavigationController: UINavigationController? { 32 | return self.visibleViewController?.navigationController 33 | } 34 | 35 | var visibleViewController: UIViewController? { 36 | guard let rootViewController = self.rootViewController else {return nil} 37 | 38 | // Present 39 | if let presentedVC = rootViewController.presentedViewController { 40 | return presentedVC 41 | } 42 | 43 | // Navi 44 | if let navi = rootViewController as? UINavigationController { 45 | return navi.visibleViewController 46 | } 47 | 48 | return rootViewController 49 | } 50 | 51 | func handleRouter(_ router: A) { 52 | 53 | // Get view controller 54 | let viewController = router.viewController 55 | 56 | // Handle 57 | switch router.routerType { 58 | case .root: 59 | let app = UIApplication.shared.delegate as! AppDelegate 60 | app.window = UIWindow(frame: UIScreen.main.bounds) 61 | app.window?.rootViewController = viewController 62 | app.window?.makeKeyAndVisible() 63 | 64 | case .push: 65 | self.visibleNavigationController?.pushViewController(viewController, animated: true) 66 | 67 | case .present: 68 | self.visibleViewController?.present(viewController, animated: true, completion: nil) 69 | } 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /Swift Vietnam Presentation/MVC-Repo-Github/MVC-Repo-Github/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.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 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | 17 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool { 18 | // Override point for customization after application launch. 19 | return true 20 | } 21 | 22 | func applicationWillResignActive(_ application: UIApplication) { 23 | // 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. 24 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 25 | } 26 | 27 | func applicationDidEnterBackground(_ application: UIApplication) { 28 | // 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. 29 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 30 | } 31 | 32 | func applicationWillEnterForeground(_ application: UIApplication) { 33 | // 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. 34 | } 35 | 36 | func applicationDidBecomeActive(_ application: UIApplication) { 37 | // 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. 38 | } 39 | 40 | func applicationWillTerminate(_ application: UIApplication) { 41 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 42 | } 43 | 44 | 45 | } 46 | 47 | -------------------------------------------------------------------------------- /iOS-Starter-Kit/Source Code/Scenes/Example - Repo/Controller/RepoController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RepoController.swift 3 | // CleanSwift 4 | // 5 | // Created by Nghia Tran on 12/17/16. 6 | // Copyright © 2016 nghiatran. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | 12 | // 13 | // MARK: - OUTPUT 14 | protocol RepoControllerOutput { 15 | 16 | /// Fetch 17 | func fetchList() 18 | } 19 | 20 | 21 | // 22 | // MARK: - DataSource 23 | protocol RepoControllerDataSource: class { 24 | 25 | // Repo 26 | func dataSource() -> UITableViewDataSource 27 | } 28 | 29 | // 30 | // MARK: - Repo 31 | class RepoController: UIViewController { 32 | 33 | 34 | // 35 | // MARK: - Output 36 | var output: RepoControllerOutput? 37 | weak var input: RepoPresenterOutput? 38 | weak var dataSource: RepoControllerDataSource? 39 | 40 | 41 | // 42 | // MARK: - OUTLET 43 | @IBOutlet weak var searchBar: UISearchBar! 44 | @IBOutlet weak var tableView: UITableView! 45 | 46 | 47 | // 48 | // MARK: - View Cycle 49 | override func viewDidLoad() { 50 | super.viewDidLoad() 51 | 52 | // Setup Clean 53 | RepoConfiguration.shared.configure(viewController: self) 54 | 55 | // Init 56 | self.initBaseAbility() 57 | 58 | // Fetch 59 | self.output?.fetchList() 60 | } 61 | 62 | override func didReceiveMemoryWarning() { 63 | super.didReceiveMemoryWarning() 64 | // Dispose of any resources that can be recreated. 65 | } 66 | 67 | override func initUIs() { 68 | self.tableView.registerCell(RepoCell.self) 69 | self.tableView.dataSource = self.dataSource?.dataSource() 70 | } 71 | 72 | func openSettingPate() { 73 | 74 | // Init Setting Router 75 | let router = SettingRouter() 76 | router.handleData { (settingVC) in 77 | settingVC.searchText = "Passed from Repo viewcontroller" 78 | } 79 | 80 | // Handle route 81 | RouterManager.shared.handleRouter(router) 82 | } 83 | } 84 | 85 | 86 | extension RepoController: RepoPresenterOutput { 87 | 88 | func presentError(_ error: Error) { 89 | 90 | /// Show error view 91 | } 92 | 93 | 94 | func reloadTableView() { 95 | self.tableView.reloadData() 96 | } 97 | } 98 | 99 | -------------------------------------------------------------------------------- /iOS-Starter-Kit/Source Code/Scenes/SettingViewController.xib: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /Swift Vietnam Presentation/CleanSwift/CleanSwift/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // CleanSwift 4 | // 5 | // Created by Nghia Tran on 12/17/16. 6 | // Copyright © 2016 nghiatran. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import ReSwift 11 | 12 | 13 | // Main State 14 | let mainStore = Store(reducer: MainReducer(), state: nil, middleware: []) 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 | // Override point for customization after application launch. 24 | return true 25 | } 26 | 27 | func applicationWillResignActive(_ application: UIApplication) { 28 | // 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. 29 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 30 | } 31 | 32 | func applicationDidEnterBackground(_ application: UIApplication) { 33 | // 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. 34 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 35 | } 36 | 37 | func applicationWillEnterForeground(_ application: UIApplication) { 38 | // 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. 39 | } 40 | 41 | func applicationDidBecomeActive(_ application: UIApplication) { 42 | // 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. 43 | } 44 | 45 | func applicationWillTerminate(_ application: UIApplication) { 46 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 47 | } 48 | 49 | 50 | } 51 | 52 | -------------------------------------------------------------------------------- /Swift Vietnam Presentation/CleanSwift/CleanSwift/RepoController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RepoController.swift 3 | // CleanSwift 4 | // 5 | // Created by Nghia Tran on 12/17/16. 6 | // Copyright © 2016 nghiatran. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | 12 | // 13 | // MARK: - OUTPUT 14 | protocol RepoControllerOutput { 15 | 16 | /// Fetch 17 | func fetchList() 18 | } 19 | 20 | 21 | // 22 | // MARK: - DataSource 23 | protocol RepoControllerDataSource: class { 24 | 25 | func numberOfRepo() -> Int 26 | func repoAtRow(row: Int) -> RepoObj 27 | } 28 | 29 | // 30 | // MARK: - Repo 31 | class RepoController: UIViewController { 32 | 33 | // 34 | // MARK: - Output 35 | var output: RepoControllerOutput? 36 | weak var input: RepoPresenterOutput? 37 | weak var dataSource: RepoControllerDataSource! 38 | 39 | // 40 | // MARK: - OUTLET 41 | @IBOutlet weak var searchBar: UISearchBar! 42 | @IBOutlet weak var tableView: UITableView! 43 | 44 | // 45 | // MARK: - View Cycle 46 | override func viewDidLoad() { 47 | super.viewDidLoad() 48 | 49 | // Fetch 50 | self.output?.fetchList() 51 | } 52 | 53 | override func didReceiveMemoryWarning() { 54 | super.didReceiveMemoryWarning() 55 | // Dispose of any resources that can be recreated. 56 | } 57 | 58 | } 59 | 60 | 61 | extension RepoController: RepoPresenterOutput { 62 | 63 | func presentError(_ error: Error) { 64 | 65 | /// Show error view 66 | } 67 | 68 | 69 | func reloadData(repos: [RepoObj]) { 70 | self.tableView.reloadData() 71 | } 72 | } 73 | 74 | 75 | // 76 | // MARK: - TableView 77 | extension RepoController: UITableViewDataSource { 78 | 79 | func numberOfSections(in tableView: UITableView) -> Int { 80 | return 1 81 | } 82 | 83 | func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { 84 | return self.dataSource.numberOfRepo() 85 | } 86 | 87 | func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { 88 | 89 | let cell = tableView.dequeueReusableCell(withIdentifier: "RepoCell", for: indexPath) as! RepoCell 90 | 91 | let repo = self.dataSource.repoAtRow(row: indexPath.row) 92 | cell.configureCellWithRepo(repo: repo) 93 | 94 | return cell 95 | } 96 | } 97 | 98 | extension RepoController: UITableViewDelegate { 99 | 100 | } 101 | -------------------------------------------------------------------------------- /iOS-Starter-Kit/App Delegate/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // iOS-Starter-Kit 4 | // 5 | // Created by Nghia Tran on 12/19/16. 6 | // Copyright © 2016 nghiatran. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import ReSwift 11 | 12 | // Main State 13 | let mainStore = Store(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 | ![](https://img.shields.io/badge/Swift-3.0-blue.svg?style=flat) 24 | ![License](https://img.shields.io/npm/l/express.svg?style=flat) 25 | ![Platform](https://img.shields.io/badge/platform-ios-green.svg?style=flat) 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 | 28 | 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 | 28 | 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 | 28 | 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 | 72 | 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 | 72 | 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 | --------------------------------------------------------------------------------