├── .gitignore ├── RxSwiftMVVM ├── Assets.xcassets │ ├── Contents.json │ └── AppIcon.appiconset │ │ └── Contents.json ├── Resource │ └── Marvel_logo.jpg ├── Const │ └── AppDefinition.swift ├── Extension │ └── ObservableType-Extension.swift ├── Navigator │ └── MarvelSearchNavigator.swift ├── NavigationController.swift ├── Model │ └── MarvelHeroModel.swift ├── ViewModel │ ├── MarvelSearchTVCellViewModel.swift │ ├── MarvelSearchViewModel.swift │ └── MarvelDescriptionViewModel.swift ├── ViewController.swift ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard ├── Info.plist ├── Service │ └── MarvelService.swift ├── AppDelegate.swift ├── View │ └── MarvelSearchTVCell.swift └── ViewController │ ├── MarvelSearchVC.swift │ └── MarvelDescriptionVC.swift ├── Podfile ├── RxSwiftMVVM.xcworkspace ├── contents.xcworkspacedata └── xcshareddata │ └── IDEWorkspaceChecks.plist ├── LICENSE ├── Podfile.lock ├── README.md └── RxSwiftMVVM.xcodeproj └── project.pbxproj /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | *.xcuserstate 3 | project.xcworkspace/ 4 | xcuserdata/ 5 | 6 | Pods/ -------------------------------------------------------------------------------- /RxSwiftMVVM/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /RxSwiftMVVM/Resource/Marvel_logo.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/dokgi88/rxswift-mvvm-example/HEAD/RxSwiftMVVM/Resource/Marvel_logo.jpg -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | use_frameworks! 2 | 3 | target 'RxSwiftMVVM' do 4 | pod 'RxSwift', '~> 4.0' 5 | pod 'RxCocoa', '~> 4.0' 6 | pod 'SwiftyJSON', '~> 4.0' 7 | pod 'Then' 8 | pod 'Alamofire' 9 | pod 'Kingfisher' 10 | pod 'CryptoSwift' 11 | pod 'Result', '~> 4.0.0' 12 | end 13 | -------------------------------------------------------------------------------- /RxSwiftMVVM.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /RxSwiftMVVM.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /RxSwiftMVVM/Const/AppDefinition.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDefinition.swift 3 | // RxSwiftMVVM 4 | // 5 | // Created by cashwalk on 13/12/2018. 6 | // Copyright © 2018 cashwalk. All rights reserved. 7 | // 8 | 9 | let MARVEL_API = "https://gateway.marvel.com/" 10 | let MARVEL_API_CHARACTERS = "v1/public/characters" 11 | 12 | let MARVEL_PUBLIC_KEY = "0fb4a9b7adfe6ecc0df9de15a983989a" 13 | let MARVEL_PRIVATE_KEY = "87f2630587cd5e08c144d503c6742c6b6e6e91a0" 14 | 15 | let MARVEL_HERO_KEY = "MARVEL_HERO_KEY" 16 | -------------------------------------------------------------------------------- /RxSwiftMVVM/Extension/ObservableType-Extension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ObservableType-Extension.swift 3 | // RxSwiftMVVM 4 | // 5 | // Created by soom on 07/04/2019. 6 | // Copyright © 2019 cashwalk. All rights reserved. 7 | // 8 | 9 | import RxCocoa 10 | import RxSwift 11 | 12 | extension ObservableType { 13 | 14 | func asDriverComplete() -> SharedSequence { 15 | return asDriver(onErrorRecover: { (error) in 16 | return Driver.empty() 17 | }) 18 | } 19 | 20 | func mapToVoid() -> Observable { 21 | return map { _ in } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /RxSwiftMVVM/Navigator/MarvelSearchNavigator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MarvelSearchNavigator.swift 3 | // RxSwiftMVVM 4 | // 5 | // Created by soom on 07/04/2019. 6 | // 7 | 8 | import UIKit 9 | 10 | final class MarvelSearchNavigator { 11 | 12 | private let navigationController: UINavigationController? 13 | 14 | init(_ navigationController: UINavigationController?) { 15 | self.navigationController = navigationController 16 | } 17 | 18 | // MARK: - Internal Method 19 | 20 | func showDescription(model: MarvelHeroModel) { 21 | let controller = MarvelDescriptionVC(viewModel: .init(model: model)) 22 | navigationController?.show(controller, sender: nil) 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /RxSwiftMVVM/NavigationController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NavigationController.swift 3 | // RxSwiftMVVM 4 | // 5 | // Created by soom on 07/04/2019. 6 | // 7 | 8 | import UIKit 9 | 10 | class NavigationController: UINavigationController { 11 | 12 | override func viewDidLoad() { 13 | super.viewDidLoad() 14 | 15 | let logoView = UIImageView(frame: CGRect(x: 0, y: 0, width: 100, height: 20)) 16 | logoView.image = UIImage(named: "Marvel_logo.jpg") 17 | logoView.contentMode = .scaleAspectFit 18 | 19 | navigationBar.tintColor = .white 20 | navigationBar.barTintColor = .black 21 | navigationBar.topItem?.titleView = logoView 22 | navigationBar.titleTextAttributes = [NSAttributedString.Key.foregroundColor: UIColor.white] 23 | navigationItem.backBarButtonItem = UIBarButtonItem(title: "", style: .plain, target: nil, action: nil) 24 | } 25 | 26 | } 27 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 dokgi88 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /RxSwiftMVVM/Model/MarvelHeroModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MarvelHeroModel.swift 3 | // RxSwiftMVVM 4 | // 5 | // Created by soom on 21/12/2018. 6 | // 7 | 8 | import SwiftyJSON 9 | 10 | class MarvelHeroModel { 11 | 12 | private(set) var name: String? 13 | private(set) var description: String? 14 | private(set) var thumbnail: String? 15 | 16 | init(json: JSON) { 17 | name = json["name"].string 18 | description = json["description"].string 19 | if let dicThumnail = json["thumbnail"].dictionary { 20 | guard let path = dicThumnail["path"]?.string else {return} 21 | guard let thumbnailEx = dicThumnail["extension"]?.string else {return} 22 | thumbnail = "\(path).\(thumbnailEx)" 23 | } 24 | } 25 | } 26 | 27 | struct MarvelHeroData: Decodable { 28 | let data: MarvelHeroResult 29 | } 30 | 31 | struct MarvelHeroResult: Decodable { 32 | let results: [HeroModel] 33 | } 34 | 35 | struct HeroModel: Decodable { 36 | let name: String 37 | let description: String 38 | let thumbnail: HeroThumbnail 39 | } 40 | 41 | struct HeroThumbnail: Decodable { 42 | let path: String 43 | let `extension`: String 44 | } 45 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - Alamofire (4.8.0) 3 | - CryptoSwift (0.13.1) 4 | - Kingfisher (5.0.0) 5 | - Result (4.0.1) 6 | - RxAtomic (4.4.0) 7 | - RxCocoa (4.4.0): 8 | - RxSwift (~> 4.0) 9 | - RxSwift (4.4.0): 10 | - RxAtomic (~> 4.4) 11 | - SwiftyJSON (4.2.0) 12 | - Then (2.4.0) 13 | 14 | DEPENDENCIES: 15 | - Alamofire 16 | - CryptoSwift 17 | - Kingfisher 18 | - Result (~> 4.0.0) 19 | - RxCocoa (~> 4.0) 20 | - RxSwift (~> 4.0) 21 | - SwiftyJSON (~> 4.0) 22 | - Then 23 | 24 | SPEC REPOS: 25 | https://github.com/cocoapods/specs.git: 26 | - Alamofire 27 | - CryptoSwift 28 | - Kingfisher 29 | - Result 30 | - RxAtomic 31 | - RxCocoa 32 | - RxSwift 33 | - SwiftyJSON 34 | - Then 35 | 36 | SPEC CHECKSUMS: 37 | Alamofire: 3ec537f71edc9804815215393ae2b1a8ea33a844 38 | CryptoSwift: ff68c35a53e03177d31aaa2c1e8b3e010aeed6cb 39 | Kingfisher: 2b0dd651e4fe5a07305b8242fde56f8e2d410f58 40 | Result: a6e784bebf48b471d59cddc1adb81f41f678c487 41 | RxAtomic: eacf60db868c96bfd63320e28619fe29c179656f 42 | RxCocoa: df63ebf7b9a70d6b4eeea407ed5dd4efc8979749 43 | RxSwift: 5976ecd04fc2fefd648827c23de5e11157faa973 44 | SwiftyJSON: c4bcba26dd9ec7a027fc8eade48e2c911f229e96 45 | Then: 71866660c7af35a7343831f7668e7cd2b62ee0f2 46 | 47 | PODFILE CHECKSUM: 38af498490a4857a1955491e50555af89801c852 48 | 49 | COCOAPODS: 1.5.2 50 | -------------------------------------------------------------------------------- /RxSwiftMVVM/ViewModel/MarvelSearchTVCellViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MarvelSearchTVCellViewModel.swift 3 | // RxSwiftMVVM 4 | // 5 | // Created by soom on 07/04/2019. 6 | // 7 | 8 | import RxCocoa 9 | import RxSwift 10 | 11 | final class MarvelSearchTVCellViewModel { 12 | 13 | let model: MarvelHeroModel 14 | 15 | init(model: MarvelHeroModel) { 16 | self.model = model 17 | } 18 | 19 | struct Input { 20 | let trigger: Driver 21 | } 22 | struct Output { 23 | let name: Driver 24 | let thumbnail: Driver 25 | } 26 | 27 | // MARK: - Internal Method 28 | 29 | func transform(input: Input) -> Output { 30 | let name: Driver = input.trigger 31 | .flatMapLatest { [weak self] (_) in 32 | guard let self = self, let name = self.model.name else {return Driver.empty()} 33 | return Driver.just(name) 34 | } 35 | let thumbnail: Driver = input.trigger 36 | .flatMapLatest { [weak self] (_) in 37 | guard let self = self else {return Driver.empty()} 38 | guard let thumnail = self.model.thumbnail, let url = URL(string: thumnail) else {return Driver.empty()} 39 | return Driver.just(url) 40 | } 41 | 42 | return Output(name: name, thumbnail: thumbnail) 43 | } 44 | 45 | } 46 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # RxSwift MVVM example 2 | 3 | ![Swift](https://img.shields.io/badge/Swift-4.2-orange.svg) 4 | [![GitHub license](https://img.shields.io/badge/license-MIT-lightgrey.svg?style=flat)](https://raw.githubusercontent.com/dokgi88/rxswift-mvvm-example/master/LICENSE) 5 | 6 | Marvel API example with MVVM Architecture pattern using RxSwift.
7 | [Clean Architecture](https://github.com/sergdort/CleanArchitectureRxSwift) partial application 8 | 9 | ## Example 10 | 11 |

12 | 13 | 14 | 15 | 16 |

17 | 18 | 19 | ## Getting Started 20 | 21 | 1. run pod install 22 | ```console 23 | $ pod install 24 | ``` 25 | 26 | 2. open RxSwiftMVVM.xcworkspace 27 | ```console 28 | $ open RxSwiftMVVM.xcworkspace 29 | ``` 30 | 31 | 3. run RxSwiftMVVM 32 | 33 | 34 | ## LICENSE 35 | 36 | These works are available under the MIT license. See the [LICENSE][license] file 37 | for more info. 38 | 39 | [license]: LICENSE 40 | -------------------------------------------------------------------------------- /RxSwiftMVVM/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // RxSwiftMVVM 4 | // 5 | // Created by soom on 13/12/2018. 6 | // 7 | 8 | import UIKit 9 | 10 | class ViewController: UIViewController { 11 | 12 | private let indicatorView = UIActivityIndicatorView().then { 13 | $0.translatesAutoresizingMaskIntoConstraints = false 14 | $0.style = .whiteLarge 15 | $0.backgroundColor = UIColor.black.withAlphaComponent(0.5) 16 | $0.startAnimating() 17 | $0.alpha = 0 18 | } 19 | 20 | override func viewDidLoad() { 21 | super.viewDidLoad() 22 | 23 | setProperties() 24 | } 25 | 26 | public func setTitle(_ text: String) { 27 | title = text 28 | } 29 | 30 | public func showLoading() { 31 | view.addSubview(indicatorView) 32 | 33 | indicatorView.topAnchor.constraint(equalTo: view.topAnchor).isActive = true 34 | indicatorView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true 35 | indicatorView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true 36 | indicatorView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true 37 | 38 | indicatorView.alpha = 1 39 | } 40 | 41 | public func hideLoading() { 42 | indicatorView.alpha = 0 43 | 44 | indicatorView.removeFromSuperview() 45 | } 46 | 47 | private func getClassName(_ anyClass: AnyObject) -> String { 48 | return String(describing: type(of: anyClass)) 49 | } 50 | 51 | private func setProperties() { 52 | view.backgroundColor = .black 53 | } 54 | 55 | } 56 | 57 | -------------------------------------------------------------------------------- /RxSwiftMVVM/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 | -------------------------------------------------------------------------------- /RxSwiftMVVM/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 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 | NSAppTransportSecurity 45 | 46 | NSAllowsArbitraryLoads 47 | 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /RxSwiftMVVM/ViewModel/MarvelSearchViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MarvelSearchViewModel.swift 3 | // RxSwiftMVVM 4 | // 5 | // Created by soom on 21/12/2018. 6 | // 7 | 8 | import RxCocoa 9 | import RxSwift 10 | 11 | final class MarvelSearchViewModel { 12 | 13 | // MARK: - Properties 14 | 15 | private var disposeBag = DisposeBag() 16 | private var heros = PublishRelay<[MarvelHeroModel]>() 17 | private let navigator: MarvelSearchNavigator 18 | 19 | init(navigator: MarvelSearchNavigator) { 20 | self.navigator = navigator 21 | } 22 | 23 | struct Input { 24 | let searchText: Driver 25 | let modelSelected: ControlEvent 26 | } 27 | struct Output { 28 | let heros: Observable<[MarvelHeroModel]> 29 | } 30 | 31 | // MARK: - Internal methods 32 | 33 | func transform(input: Input) -> Output { 34 | input.searchText 35 | .drive(onNext: { [weak self] (searchText) in 36 | guard let self = self else {return} 37 | self.requestHero(searchText: searchText) 38 | }) 39 | .disposed(by: disposeBag) 40 | input.modelSelected 41 | .bind { [weak self] (model) in 42 | guard let self = self else {return} 43 | self.navigator.showDescription(model: model) 44 | } 45 | .disposed(by: disposeBag) 46 | 47 | return Output(heros: heros.asObservable()) 48 | } 49 | 50 | // MARK: - Private Method 51 | 52 | private func requestHero(searchText: String) { 53 | MarvelService().getCharacters(name: searchText, offset: 20) { [weak self] (error, result) in 54 | guard let self = self else {return} 55 | guard error == nil, let result = result, result.count > 0 else {return self.heros.accept([])} 56 | self.heros.accept(result) 57 | } 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /RxSwiftMVVM/Service/MarvelService.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MarvelService.swift 3 | // RxSwiftMVVM 4 | // 5 | // Created by soom on 21/12/2018. 6 | // 7 | 8 | import Alamofire 9 | import CryptoSwift 10 | import RxCocoa 11 | import SwiftyJSON 12 | 13 | class MarvelService { 14 | 15 | @discardableResult 16 | public func getCharacters(name: String, offset: Int, completion: @escaping (NSError?, [MarvelHeroModel]?) -> Void) -> URLSessionTask? { 17 | 18 | let url = "\(MARVEL_API)\(MARVEL_API_CHARACTERS)" 19 | var param = [String: Any]() 20 | let timeStamp = Int(Date().timeIntervalSince1970) 21 | param["ts"] = timeStamp 22 | param["apikey"] = MARVEL_PUBLIC_KEY 23 | param["hash"] = getHash(ts: timeStamp) 24 | if name.count > 0 { 25 | param["nameStartsWith"] = name 26 | } 27 | param["limit"] = 20 28 | param["offset"] = offset 29 | let headers = ["Content-Type":"application/json", "Accept":"application/json"] 30 | 31 | return Alamofire.request(url, method: HTTPMethod.get, parameters: param, encoding: URLEncoding.default, headers: headers).responseJSON { (response) in 32 | 33 | switch response.result { 34 | case .success(let value): 35 | let json = JSON(value) 36 | let error = json["error"] 37 | guard error.isEmpty else {return} 38 | guard let data = json["data"].dictionary else {return} 39 | guard let result = data["results"]?.array else {return} 40 | 41 | completion(nil, result.compactMap({MarvelHeroModel(json: $0)})) 42 | case .failure(let error): 43 | completion(error as NSError, nil) 44 | } 45 | }.task 46 | } 47 | 48 | private func getHash(ts: Int) -> String { 49 | let hash = "\(ts)\(MARVEL_PRIVATE_KEY)\(MARVEL_PUBLIC_KEY)" 50 | return hash.md5() 51 | } 52 | } 53 | 54 | -------------------------------------------------------------------------------- /RxSwiftMVVM/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 | "idiom" : "ios-marketing", 90 | "size" : "1024x1024", 91 | "scale" : "1x" 92 | } 93 | ], 94 | "info" : { 95 | "version" : 1, 96 | "author" : "xcode" 97 | } 98 | } -------------------------------------------------------------------------------- /RxSwiftMVVM/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // RxSwiftMVVM 4 | // 5 | // Created by cashwalk on 13/12/2018. 6 | // Copyright © 2018 cashwalk. 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: [UIApplication.LaunchOptionsKey: 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 | -------------------------------------------------------------------------------- /RxSwiftMVVM/ViewModel/MarvelDescriptionViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MarvelDescriptionViewModel.swift 3 | // RxSwiftMVVM 4 | // 5 | // Created by soom on 21/12/2018. 6 | // 7 | 8 | import RxCocoa 9 | import RxSwift 10 | 11 | final class MarvelDescriptionViewModel { 12 | 13 | private let model: MarvelHeroModel 14 | 15 | init(model: MarvelHeroModel) { 16 | self.model = model 17 | } 18 | 19 | struct Input { 20 | let trigger: Driver 21 | } 22 | struct Output { 23 | let thumbnail: Driver 24 | let name: Driver 25 | let description: Driver 26 | } 27 | 28 | // MARK: - Internal Method 29 | 30 | func transform(input: Input) -> Output { 31 | let thumbnail: Driver = input.trigger 32 | .flatMapLatest { [weak self] (_) in 33 | guard let self = self else {return Driver.empty()} 34 | guard let thumbnail = self.model.thumbnail, let url = URL(string: thumbnail) else {return Driver.empty()} 35 | return Driver.just(url) 36 | } 37 | let name: Driver = input.trigger 38 | .flatMapLatest { [weak self] (_) in 39 | guard let self = self, let name = self.model.name else {return Driver.empty()} 40 | return Driver.just(name) 41 | } 42 | let description: Driver = input.trigger 43 | .flatMapLatest { [weak self] (_) in 44 | guard let self = self else {return Driver.empty()} 45 | return Driver.just(self.getDescription()) 46 | } 47 | 48 | return Output(thumbnail: thumbnail, name: name, description: description) 49 | } 50 | 51 | // MARK: - Private Method 52 | 53 | private func getDescription() -> NSAttributedString { 54 | guard let name = model.name, let desc = model.description else {return NSAttributedString(string: "Not Description")} 55 | let description = desc.count > 0 ? desc:"Not Description" 56 | let originText = "\(name)\n\n\(description)" 57 | let range = originText.lowercased().range(of: name.lowercased()) 58 | let attributeString = NSMutableAttributedString(string: originText, 59 | attributes: [NSAttributedString.Key.font: UIFont.systemFont(ofSize: 16, weight: .semibold), NSAttributedString.Key.foregroundColor:UIColor.black]) 60 | attributeString.addAttribute(NSAttributedString.Key.font, value: UIFont.systemFont(ofSize: 30, weight: .black), range: NSRange(location: (range?.lowerBound.encodedOffset)!, length: name.count)) 61 | return attributeString 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /RxSwiftMVVM/View/MarvelSearchTVCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MarvelSearchTVCell.swift 3 | // RxSwiftMVVM 4 | // 5 | // Created by soom on 21/12/2018. 6 | // 7 | 8 | import UIKit 9 | 10 | import Kingfisher 11 | import RxCocoa 12 | import RxSwift 13 | import Then 14 | 15 | class MarvelSearchTVCell: UITableViewCell { 16 | 17 | // MARK: - Constants 18 | 19 | static let reuseIdentifier = "MarvelSearchTVCell" 20 | 21 | // MARK: - Properties 22 | 23 | private var disposeBag = DisposeBag() 24 | private var viewModel: MarvelSearchTVCellViewModel! 25 | 26 | // MARK: - UI Components 27 | 28 | private let heroImageView = UIImageView().then { 29 | $0.translatesAutoresizingMaskIntoConstraints = false 30 | $0.contentMode = .scaleAspectFill 31 | $0.layer.cornerRadius = 6 32 | $0.clipsToBounds = true 33 | } 34 | private let heroTitleLabel = UILabel().then { 35 | $0.translatesAutoresizingMaskIntoConstraints = false 36 | $0.backgroundColor = UIColor.black.withAlphaComponent(0.5) 37 | $0.font = .systemFont(ofSize: 20, weight: .black) 38 | $0.textColor = .white 39 | $0.textAlignment = .center 40 | } 41 | 42 | override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { 43 | super.init(style: style, reuseIdentifier: reuseIdentifier) 44 | 45 | setProperties() 46 | contentView.addSubview(heroImageView) 47 | contentView.addSubview(heroTitleLabel) 48 | layout() 49 | } 50 | 51 | required init?(coder aDecoder: NSCoder) { 52 | fatalError("init(coder:) has not been implemented") 53 | } 54 | 55 | override func prepareForReuse() { 56 | heroImageView.image = nil 57 | heroTitleLabel.text = nil 58 | } 59 | 60 | func configure(viewModel: MarvelSearchTVCellViewModel) { 61 | self.viewModel = viewModel 62 | bindViewModel() 63 | } 64 | 65 | private func bindViewModel() { 66 | let layoutSubviews = rx.sentMessage(#selector(UITableViewCell.layoutSubviews)).take(1).mapToVoid().asDriverComplete() 67 | let input = MarvelSearchTVCellViewModel.Input(trigger: layoutSubviews) 68 | 69 | let output = viewModel.transform(input: input) 70 | output.thumbnail 71 | .drive(onNext: { [weak self] (url) in 72 | guard let self = self else {return} 73 | self.heroImageView.kf.setImage(with: url) 74 | }) 75 | .disposed(by: disposeBag) 76 | output.name 77 | .drive(heroTitleLabel.rx.text) 78 | .disposed(by: disposeBag) 79 | } 80 | 81 | private func setProperties() { 82 | backgroundColor = .clear 83 | selectionStyle = .none 84 | } 85 | } 86 | 87 | // MARK: - Layout 88 | 89 | extension MarvelSearchTVCell { 90 | 91 | private func layout() { 92 | heroImageView.topAnchor.constraint(equalTo: contentView.topAnchor).isActive = true 93 | heroImageView.leadingAnchor.constraint(equalTo: contentView.leadingAnchor).isActive = true 94 | heroImageView.trailingAnchor.constraint(equalTo: contentView.trailingAnchor).isActive = true 95 | heroImageView.bottomAnchor.constraint(equalTo: contentView.bottomAnchor, constant: -10).isActive = true 96 | 97 | heroTitleLabel.topAnchor.constraint(equalTo: heroImageView.topAnchor).isActive = true 98 | heroTitleLabel.leadingAnchor.constraint(equalTo: heroImageView.leadingAnchor).isActive = true 99 | heroTitleLabel.trailingAnchor.constraint(equalTo: heroImageView.trailingAnchor).isActive = true 100 | heroTitleLabel.bottomAnchor.constraint(equalTo: heroImageView.bottomAnchor).isActive = true 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /RxSwiftMVVM/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 | -------------------------------------------------------------------------------- /RxSwiftMVVM/ViewController/MarvelSearchVC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MarvelSearchVC.swift 3 | // RxSwiftMVVM 4 | // 5 | // Created by soom on 21/12/2018. 6 | // 7 | 8 | import RxCocoa 9 | import RxSwift 10 | 11 | final class MarvelSearchVC: ViewController { 12 | 13 | // MARK: - Properties 14 | 15 | private let disposeBag = DisposeBag() 16 | private var searchText = PublishRelay() 17 | private lazy var viewModel = MarvelSearchViewModel(navigator: .init(self.navigationController)) 18 | 19 | // MARK: - UI Components 20 | 21 | private let searchTextField = UITextField().then { 22 | let paddingView = UIView(frame: CGRect(x: 0, y: 0, width: 15, height: 48)) 23 | $0.leftView = paddingView 24 | $0.leftViewMode = .always 25 | $0.translatesAutoresizingMaskIntoConstraints = false 26 | $0.borderStyle = UITextField.BorderStyle.roundedRect 27 | $0.placeholder = "ex. Spider-Man" 28 | $0.backgroundColor = .white 29 | } 30 | private let tableView = UITableView(frame: .zero, style: .plain).then { 31 | $0.translatesAutoresizingMaskIntoConstraints = false 32 | } 33 | private let emptyLabel = UILabel().then { 34 | $0.translatesAutoresizingMaskIntoConstraints = false 35 | $0.backgroundColor = .black 36 | $0.text = "no heros :(" 37 | $0.textColor = .white 38 | $0.textAlignment = .center 39 | $0.font = .boldSystemFont(ofSize: 30) 40 | $0.isHidden = false 41 | } 42 | 43 | // MARK: - Overridden: BackViewController 44 | 45 | override func viewDidLoad() { 46 | super.viewDidLoad() 47 | 48 | bindView() 49 | bindViewModel() 50 | setProperties() 51 | setupUI() 52 | } 53 | 54 | // MARK: - Private methods 55 | 56 | private func bindView() { 57 | searchTextField.rx.controlEvent(.editingDidEndOnExit) 58 | .bind { [weak self] (event) in 59 | guard let self = self, let text = self.searchTextField.text else {return} 60 | 61 | self.showLoading() 62 | UIView.animate(withDuration: 0.3, animations: { 63 | self.tableView.contentOffset = .zero 64 | }, completion: { (_) in 65 | self.searchText.accept(text) 66 | }) 67 | } 68 | .disposed(by: disposeBag) 69 | } 70 | 71 | private func bindViewModel() { 72 | let input = MarvelSearchViewModel.Input(searchText: searchText.asDriverComplete(), 73 | modelSelected: tableView.rx.modelSelected(MarvelHeroModel.self)) 74 | 75 | let output = viewModel.transform(input: input) 76 | output.heros 77 | .do(onNext: { [weak self] (heros) in 78 | guard let self = self else {return} 79 | self.hideLoading() 80 | self.emptyLabel.isHidden = heros.count > 0 ? true:false 81 | }) 82 | .bind(to: tableView.rx.items(cellIdentifier: MarvelSearchTVCell.reuseIdentifier, cellType: MarvelSearchTVCell.self)) { (row, hero, cell) in 83 | cell.configure(viewModel: .init(model: hero)) 84 | }.disposed(by: disposeBag) 85 | } 86 | 87 | private func setProperties() { 88 | tableView.rx.setDelegate(self).disposed(by: disposeBag) 89 | tableView.backgroundColor = .clear 90 | tableView.separatorStyle = .none 91 | tableView.register(MarvelSearchTVCell.self, forCellReuseIdentifier: MarvelSearchTVCell.reuseIdentifier) 92 | } 93 | 94 | private func setupUI() { 95 | view.addSubview(tableView) 96 | view.addSubview(emptyLabel) 97 | view.addSubview(searchTextField) 98 | layout() 99 | } 100 | 101 | } 102 | 103 | // MARK: - Layout 104 | 105 | extension MarvelSearchVC { 106 | 107 | private func layout() { 108 | searchTextField.topAnchor.constraint(equalTo: topLayoutGuide.bottomAnchor, constant: 10).isActive = true 109 | searchTextField.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 10).isActive = true 110 | searchTextField.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -10).isActive = true 111 | searchTextField.heightAnchor.constraint(equalToConstant: 60).isActive = true 112 | 113 | tableView.topAnchor.constraint(equalTo: searchTextField.bottomAnchor, constant: 14).isActive = true 114 | tableView.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 14).isActive = true 115 | tableView.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -14).isActive = true 116 | tableView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true 117 | 118 | emptyLabel.topAnchor.constraint(equalTo: topLayoutGuide.bottomAnchor).isActive = true 119 | emptyLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true 120 | emptyLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true 121 | emptyLabel.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true 122 | } 123 | } 124 | 125 | // MARK: - UITableViewDelegate 126 | 127 | extension MarvelSearchVC: UITableViewDelegate { 128 | 129 | func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { 130 | return 320 131 | } 132 | } 133 | -------------------------------------------------------------------------------- /RxSwiftMVVM/ViewController/MarvelDescriptionVC.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MarvelDescriptionVC.swift 3 | // RxSwiftMVVM 4 | // 5 | // Created by soom on 21/12/2018. 6 | // 7 | 8 | import RxCocoa 9 | import RxSwift 10 | import Kingfisher 11 | 12 | final class MarvelDescriptionVC: ViewController { 13 | 14 | // MARK: - NSLayoutConstraints 15 | 16 | private var containerViewTop: NSLayoutConstraint! 17 | 18 | // MARK: - Properties 19 | 20 | private let disposeBag = DisposeBag() 21 | private var viewModel: MarvelDescriptionViewModel! 22 | 23 | // MARK: - UI Components 24 | 25 | private let heroImageView = UIImageView().then { 26 | $0.translatesAutoresizingMaskIntoConstraints = false 27 | $0.contentMode = .scaleAspectFit 28 | } 29 | private let containerView = UIView().then { 30 | $0.translatesAutoresizingMaskIntoConstraints = false 31 | $0.backgroundColor = UIColor.white.withAlphaComponent(0.7) 32 | $0.clipsToBounds = true 33 | } 34 | private let descriptionButton = UIButton().then { 35 | $0.translatesAutoresizingMaskIntoConstraints = false 36 | $0.setTitle("DESCRIPTION", for: .normal) 37 | $0.setTitleColor(.black, for: .normal) 38 | $0.titleLabel?.font = .boldSystemFont(ofSize: 16) 39 | $0.backgroundColor = UIColor.white.withAlphaComponent(0.7) 40 | $0.isSelected = true 41 | } 42 | private let heroDescTextView = UITextView().then { 43 | $0.translatesAutoresizingMaskIntoConstraints = false 44 | $0.isEditable = false 45 | $0.backgroundColor = .clear 46 | $0.clipsToBounds = true 47 | } 48 | 49 | init(viewModel: MarvelDescriptionViewModel) { 50 | self.viewModel = viewModel 51 | super.init(nibName: nil, bundle: nil) 52 | showLoading() 53 | } 54 | 55 | required init?(coder aDecoder: NSCoder) { 56 | fatalError() 57 | } 58 | 59 | // MARK: - Overridden: ParentClass 60 | 61 | override func viewDidLoad() { 62 | super.viewDidLoad() 63 | 64 | bindView() 65 | bindViewModel() 66 | setupUI() 67 | } 68 | 69 | // MARK: - Private methods 70 | 71 | private func bindView() { 72 | descriptionButton.rx.tap 73 | .bind { [weak self] in 74 | guard let self = self else {return} 75 | let height = self.descriptionButton.isSelected ? 0:-(self.view.bounds.width-100) 76 | 77 | UIView.animate(withDuration: 0.3, animations: { 78 | self.containerViewTop.constant = height 79 | self.view.layoutIfNeeded() 80 | }) { (_) in 81 | self.descriptionButton.isSelected = !self.descriptionButton.isSelected 82 | } 83 | } 84 | .disposed(by: disposeBag) 85 | } 86 | 87 | private func bindViewModel() { 88 | let viewDidAppear = rx.sentMessage(#selector(UIViewController.viewDidAppear(_:))).take(1).mapToVoid().asDriverComplete() 89 | let input = MarvelDescriptionViewModel.Input(trigger: viewDidAppear) 90 | 91 | let output = viewModel.transform(input: input) 92 | output.thumbnail 93 | .drive(onNext: { [weak self] (url) in 94 | guard let self = self else {return} 95 | self.heroImageView.kf.setImage(with: url) 96 | self.hideLoading() 97 | }) 98 | .disposed(by: disposeBag) 99 | output.name 100 | .drive(rx.title) 101 | .disposed(by: disposeBag) 102 | output.description 103 | .drive(heroDescTextView.rx.attributedText) 104 | .disposed(by: disposeBag) 105 | } 106 | 107 | private func setupUI() { 108 | view.addSubview(heroImageView) 109 | view.addSubview(containerView) 110 | view.addSubview(descriptionButton) 111 | containerView.addSubview(heroDescTextView) 112 | layout() 113 | } 114 | 115 | } 116 | 117 | // MARK: - Layout 118 | 119 | extension MarvelDescriptionVC { 120 | 121 | private func layout() { 122 | heroImageView.topAnchor.constraint(equalTo: topLayoutGuide.bottomAnchor).isActive = true 123 | heroImageView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true 124 | heroImageView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true 125 | heroImageView.bottomAnchor.constraint(equalTo: view.bottomAnchor).isActive = true 126 | 127 | containerViewTop = containerView.topAnchor.constraint(equalTo: view.bottomAnchor, constant: -(view.bounds.width-100)) 128 | containerViewTop.isActive = true 129 | containerView.leadingAnchor.constraint(equalTo: view.leadingAnchor).isActive = true 130 | containerView.trailingAnchor.constraint(equalTo: view.trailingAnchor).isActive = true 131 | containerView.heightAnchor.constraint(equalToConstant: view.bounds.width-100).isActive = true 132 | 133 | descriptionButton.bottomAnchor.constraint(equalTo: containerView.topAnchor, constant: 0).isActive = true 134 | descriptionButton.centerXAnchor.constraint(equalTo: containerView.centerXAnchor).isActive = true 135 | descriptionButton.widthAnchor.constraint(equalToConstant: 130).isActive = true 136 | descriptionButton.heightAnchor.constraint(equalToConstant: 40).isActive = true 137 | 138 | heroDescTextView.topAnchor.constraint(equalTo: containerView.topAnchor, constant: 8).isActive = true 139 | heroDescTextView.leadingAnchor.constraint(equalTo: containerView.leadingAnchor, constant: 8).isActive = true 140 | heroDescTextView.trailingAnchor.constraint(equalTo: containerView.trailingAnchor, constant: -8).isActive = true 141 | heroDescTextView.bottomAnchor.constraint(equalTo: containerView.bottomAnchor, constant: -8).isActive = true 142 | } 143 | } 144 | -------------------------------------------------------------------------------- /RxSwiftMVVM.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 3A4834CF21CC757F00D28416 /* Marvel_logo.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 3A4834CE21CC757F00D28416 /* Marvel_logo.jpg */; }; 11 | 3A4834D121CC75A300D28416 /* MarvelService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A4834D021CC75A300D28416 /* MarvelService.swift */; }; 12 | 3A4834D321CC75CC00D28416 /* MarvelHeroModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A4834D221CC75CC00D28416 /* MarvelHeroModel.swift */; }; 13 | 3A4834D521CC761B00D28416 /* MarvelSearchTVCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A4834D421CC761B00D28416 /* MarvelSearchTVCell.swift */; }; 14 | 3A4834D721CC772400D28416 /* MarvelDescriptionVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A4834D621CC772400D28416 /* MarvelDescriptionVC.swift */; }; 15 | 3A4834D921CC774F00D28416 /* MarvelSearchVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A4834D821CC774F00D28416 /* MarvelSearchVC.swift */; }; 16 | 3A4834DB21CC776B00D28416 /* MarvelDescriptionViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A4834DA21CC776B00D28416 /* MarvelDescriptionViewModel.swift */; }; 17 | 3A4834DD21CC778700D28416 /* MarvelSearchViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3A4834DC21CC778700D28416 /* MarvelSearchViewModel.swift */; }; 18 | 3AF9B59D21C1DDEF0096D1C3 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF9B59C21C1DDEF0096D1C3 /* AppDelegate.swift */; }; 19 | 3AF9B59F21C1DDEF0096D1C3 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF9B59E21C1DDEF0096D1C3 /* ViewController.swift */; }; 20 | 3AF9B5A221C1DDEF0096D1C3 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3AF9B5A021C1DDEF0096D1C3 /* Main.storyboard */; }; 21 | 3AF9B5A421C1DDF00096D1C3 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 3AF9B5A321C1DDF00096D1C3 /* Assets.xcassets */; }; 22 | 3AF9B5A721C1DDF00096D1C3 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 3AF9B5A521C1DDF00096D1C3 /* LaunchScreen.storyboard */; }; 23 | 3AF9B5B721C1E0C70096D1C3 /* AppDefinition.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3AF9B5B621C1E0C70096D1C3 /* AppDefinition.swift */; }; 24 | 9005716B9B42E868A65FF0AC /* Pods_RxSwiftMVVM.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EABD88FABAB27129E8DD339D /* Pods_RxSwiftMVVM.framework */; }; 25 | ED8583442259C6F000CACD59 /* MarvelSearchTVCellViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED8583432259C6F000CACD59 /* MarvelSearchTVCellViewModel.swift */; }; 26 | ED8583472259DE9500CACD59 /* MarvelSearchNavigator.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED8583462259DE9500CACD59 /* MarvelSearchNavigator.swift */; }; 27 | ED858349225A384500CACD59 /* NavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED858348225A384500CACD59 /* NavigationController.swift */; }; 28 | ED85834C225A3EF900CACD59 /* ObservableType-Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED85834B225A3EF900CACD59 /* ObservableType-Extension.swift */; }; 29 | /* End PBXBuildFile section */ 30 | 31 | /* Begin PBXFileReference section */ 32 | 3A4834CE21CC757F00D28416 /* Marvel_logo.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = Marvel_logo.jpg; sourceTree = ""; }; 33 | 3A4834D021CC75A300D28416 /* MarvelService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarvelService.swift; sourceTree = ""; }; 34 | 3A4834D221CC75CC00D28416 /* MarvelHeroModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarvelHeroModel.swift; sourceTree = ""; }; 35 | 3A4834D421CC761B00D28416 /* MarvelSearchTVCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarvelSearchTVCell.swift; sourceTree = ""; }; 36 | 3A4834D621CC772400D28416 /* MarvelDescriptionVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarvelDescriptionVC.swift; sourceTree = ""; }; 37 | 3A4834D821CC774F00D28416 /* MarvelSearchVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarvelSearchVC.swift; sourceTree = ""; }; 38 | 3A4834DA21CC776B00D28416 /* MarvelDescriptionViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarvelDescriptionViewModel.swift; sourceTree = ""; }; 39 | 3A4834DC21CC778700D28416 /* MarvelSearchViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarvelSearchViewModel.swift; sourceTree = ""; }; 40 | 3AF9B59921C1DDEF0096D1C3 /* RxSwiftMVVM.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = RxSwiftMVVM.app; sourceTree = BUILT_PRODUCTS_DIR; }; 41 | 3AF9B59C21C1DDEF0096D1C3 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 42 | 3AF9B59E21C1DDEF0096D1C3 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 43 | 3AF9B5A121C1DDEF0096D1C3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 44 | 3AF9B5A321C1DDF00096D1C3 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 45 | 3AF9B5A621C1DDF00096D1C3 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 46 | 3AF9B5A821C1DDF00096D1C3 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 47 | 3AF9B5B621C1E0C70096D1C3 /* AppDefinition.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDefinition.swift; sourceTree = ""; }; 48 | 67571DE271057B2FA35AE281 /* Pods-RxSwiftMVVM.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RxSwiftMVVM.release.xcconfig"; path = "Pods/Target Support Files/Pods-RxSwiftMVVM/Pods-RxSwiftMVVM.release.xcconfig"; sourceTree = ""; }; 49 | 6D930388EAA07CACC8BBAF61 /* Pods-RxSwiftMVVM.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RxSwiftMVVM.debug.xcconfig"; path = "Pods/Target Support Files/Pods-RxSwiftMVVM/Pods-RxSwiftMVVM.debug.xcconfig"; sourceTree = ""; }; 50 | EABD88FABAB27129E8DD339D /* Pods_RxSwiftMVVM.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RxSwiftMVVM.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 51 | ED8583432259C6F000CACD59 /* MarvelSearchTVCellViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarvelSearchTVCellViewModel.swift; sourceTree = ""; }; 52 | ED8583462259DE9500CACD59 /* MarvelSearchNavigator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarvelSearchNavigator.swift; sourceTree = ""; }; 53 | ED858348225A384500CACD59 /* NavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavigationController.swift; sourceTree = ""; }; 54 | ED85834B225A3EF900CACD59 /* ObservableType-Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "ObservableType-Extension.swift"; sourceTree = ""; }; 55 | /* End PBXFileReference section */ 56 | 57 | /* Begin PBXFrameworksBuildPhase section */ 58 | 3AF9B59621C1DDEF0096D1C3 /* Frameworks */ = { 59 | isa = PBXFrameworksBuildPhase; 60 | buildActionMask = 2147483647; 61 | files = ( 62 | 9005716B9B42E868A65FF0AC /* Pods_RxSwiftMVVM.framework in Frameworks */, 63 | ); 64 | runOnlyForDeploymentPostprocessing = 0; 65 | }; 66 | /* End PBXFrameworksBuildPhase section */ 67 | 68 | /* Begin PBXGroup section */ 69 | 08F57457C4198AD88556BBBC /* Pods */ = { 70 | isa = PBXGroup; 71 | children = ( 72 | 6D930388EAA07CACC8BBAF61 /* Pods-RxSwiftMVVM.debug.xcconfig */, 73 | 67571DE271057B2FA35AE281 /* Pods-RxSwiftMVVM.release.xcconfig */, 74 | ); 75 | name = Pods; 76 | sourceTree = ""; 77 | }; 78 | 3A4834CD21CC750800D28416 /* Resource */ = { 79 | isa = PBXGroup; 80 | children = ( 81 | 3A4834CE21CC757F00D28416 /* Marvel_logo.jpg */, 82 | ); 83 | path = Resource; 84 | sourceTree = ""; 85 | }; 86 | 3AF9B59021C1DDEF0096D1C3 = { 87 | isa = PBXGroup; 88 | children = ( 89 | 3AF9B59B21C1DDEF0096D1C3 /* RxSwiftMVVM */, 90 | 3AF9B59A21C1DDEF0096D1C3 /* Products */, 91 | 08F57457C4198AD88556BBBC /* Pods */, 92 | 916E1CE61F54C2E98E75B742 /* Frameworks */, 93 | ); 94 | sourceTree = ""; 95 | }; 96 | 3AF9B59A21C1DDEF0096D1C3 /* Products */ = { 97 | isa = PBXGroup; 98 | children = ( 99 | 3AF9B59921C1DDEF0096D1C3 /* RxSwiftMVVM.app */, 100 | ); 101 | name = Products; 102 | sourceTree = ""; 103 | }; 104 | 3AF9B59B21C1DDEF0096D1C3 /* RxSwiftMVVM */ = { 105 | isa = PBXGroup; 106 | children = ( 107 | 3AF9B5A821C1DDF00096D1C3 /* Info.plist */, 108 | 3AF9B59C21C1DDEF0096D1C3 /* AppDelegate.swift */, 109 | ED858348225A384500CACD59 /* NavigationController.swift */, 110 | 3AF9B59E21C1DDEF0096D1C3 /* ViewController.swift */, 111 | 3AF9B5A321C1DDF00096D1C3 /* Assets.xcassets */, 112 | 3AF9B5B321C1DF220096D1C3 /* Const */, 113 | ED85834A225A3ED800CACD59 /* Extension */, 114 | 3AF9B5A521C1DDF00096D1C3 /* LaunchScreen.storyboard */, 115 | 3AF9B5A021C1DDEF0096D1C3 /* Main.storyboard */, 116 | 3AF9B5B121C1DF0A0096D1C3 /* Model */, 117 | ED8583452259DE5F00CACD59 /* Navigator */, 118 | 3A4834CD21CC750800D28416 /* Resource */, 119 | 3AF9B5B221C1DF150096D1C3 /* Service */, 120 | 3AF9B5B021C1DF050096D1C3 /* View */, 121 | 3AF9B5AE21C1DEF40096D1C3 /* ViewController */, 122 | 3AF9B5AF21C1DEFD0096D1C3 /* ViewModel */, 123 | ); 124 | path = RxSwiftMVVM; 125 | sourceTree = ""; 126 | }; 127 | 3AF9B5AE21C1DEF40096D1C3 /* ViewController */ = { 128 | isa = PBXGroup; 129 | children = ( 130 | 3A4834D621CC772400D28416 /* MarvelDescriptionVC.swift */, 131 | 3A4834D821CC774F00D28416 /* MarvelSearchVC.swift */, 132 | ); 133 | path = ViewController; 134 | sourceTree = ""; 135 | }; 136 | 3AF9B5AF21C1DEFD0096D1C3 /* ViewModel */ = { 137 | isa = PBXGroup; 138 | children = ( 139 | 3A4834DA21CC776B00D28416 /* MarvelDescriptionViewModel.swift */, 140 | 3A4834DC21CC778700D28416 /* MarvelSearchViewModel.swift */, 141 | ED8583432259C6F000CACD59 /* MarvelSearchTVCellViewModel.swift */, 142 | ); 143 | path = ViewModel; 144 | sourceTree = ""; 145 | }; 146 | 3AF9B5B021C1DF050096D1C3 /* View */ = { 147 | isa = PBXGroup; 148 | children = ( 149 | 3A4834D421CC761B00D28416 /* MarvelSearchTVCell.swift */, 150 | ); 151 | path = View; 152 | sourceTree = ""; 153 | }; 154 | 3AF9B5B121C1DF0A0096D1C3 /* Model */ = { 155 | isa = PBXGroup; 156 | children = ( 157 | 3A4834D221CC75CC00D28416 /* MarvelHeroModel.swift */, 158 | ); 159 | path = Model; 160 | sourceTree = ""; 161 | }; 162 | 3AF9B5B221C1DF150096D1C3 /* Service */ = { 163 | isa = PBXGroup; 164 | children = ( 165 | 3A4834D021CC75A300D28416 /* MarvelService.swift */, 166 | ); 167 | path = Service; 168 | sourceTree = ""; 169 | }; 170 | 3AF9B5B321C1DF220096D1C3 /* Const */ = { 171 | isa = PBXGroup; 172 | children = ( 173 | 3AF9B5B621C1E0C70096D1C3 /* AppDefinition.swift */, 174 | ); 175 | path = Const; 176 | sourceTree = ""; 177 | }; 178 | 916E1CE61F54C2E98E75B742 /* Frameworks */ = { 179 | isa = PBXGroup; 180 | children = ( 181 | EABD88FABAB27129E8DD339D /* Pods_RxSwiftMVVM.framework */, 182 | ); 183 | name = Frameworks; 184 | sourceTree = ""; 185 | }; 186 | ED8583452259DE5F00CACD59 /* Navigator */ = { 187 | isa = PBXGroup; 188 | children = ( 189 | ED8583462259DE9500CACD59 /* MarvelSearchNavigator.swift */, 190 | ); 191 | path = Navigator; 192 | sourceTree = ""; 193 | }; 194 | ED85834A225A3ED800CACD59 /* Extension */ = { 195 | isa = PBXGroup; 196 | children = ( 197 | ED85834B225A3EF900CACD59 /* ObservableType-Extension.swift */, 198 | ); 199 | path = Extension; 200 | sourceTree = ""; 201 | }; 202 | /* End PBXGroup section */ 203 | 204 | /* Begin PBXNativeTarget section */ 205 | 3AF9B59821C1DDEF0096D1C3 /* RxSwiftMVVM */ = { 206 | isa = PBXNativeTarget; 207 | buildConfigurationList = 3AF9B5AB21C1DDF00096D1C3 /* Build configuration list for PBXNativeTarget "RxSwiftMVVM" */; 208 | buildPhases = ( 209 | A787573E3CFE222944E0F04D /* [CP] Check Pods Manifest.lock */, 210 | 3AF9B59521C1DDEF0096D1C3 /* Sources */, 211 | 3AF9B59621C1DDEF0096D1C3 /* Frameworks */, 212 | 3AF9B59721C1DDEF0096D1C3 /* Resources */, 213 | 49C3794F2E75DB18311E7CC7 /* [CP] Embed Pods Frameworks */, 214 | ); 215 | buildRules = ( 216 | ); 217 | dependencies = ( 218 | ); 219 | name = RxSwiftMVVM; 220 | productName = RxSwiftMVVM; 221 | productReference = 3AF9B59921C1DDEF0096D1C3 /* RxSwiftMVVM.app */; 222 | productType = "com.apple.product-type.application"; 223 | }; 224 | /* End PBXNativeTarget section */ 225 | 226 | /* Begin PBXProject section */ 227 | 3AF9B59121C1DDEF0096D1C3 /* Project object */ = { 228 | isa = PBXProject; 229 | attributes = { 230 | LastSwiftUpdateCheck = 1010; 231 | LastUpgradeCheck = 1010; 232 | ORGANIZATIONNAME = cashwalk; 233 | TargetAttributes = { 234 | 3AF9B59821C1DDEF0096D1C3 = { 235 | CreatedOnToolsVersion = 10.1; 236 | }; 237 | }; 238 | }; 239 | buildConfigurationList = 3AF9B59421C1DDEF0096D1C3 /* Build configuration list for PBXProject "RxSwiftMVVM" */; 240 | compatibilityVersion = "Xcode 9.3"; 241 | developmentRegion = en; 242 | hasScannedForEncodings = 0; 243 | knownRegions = ( 244 | en, 245 | Base, 246 | ); 247 | mainGroup = 3AF9B59021C1DDEF0096D1C3; 248 | productRefGroup = 3AF9B59A21C1DDEF0096D1C3 /* Products */; 249 | projectDirPath = ""; 250 | projectRoot = ""; 251 | targets = ( 252 | 3AF9B59821C1DDEF0096D1C3 /* RxSwiftMVVM */, 253 | ); 254 | }; 255 | /* End PBXProject section */ 256 | 257 | /* Begin PBXResourcesBuildPhase section */ 258 | 3AF9B59721C1DDEF0096D1C3 /* Resources */ = { 259 | isa = PBXResourcesBuildPhase; 260 | buildActionMask = 2147483647; 261 | files = ( 262 | 3AF9B5A721C1DDF00096D1C3 /* LaunchScreen.storyboard in Resources */, 263 | 3A4834CF21CC757F00D28416 /* Marvel_logo.jpg in Resources */, 264 | 3AF9B5A421C1DDF00096D1C3 /* Assets.xcassets in Resources */, 265 | 3AF9B5A221C1DDEF0096D1C3 /* Main.storyboard in Resources */, 266 | ); 267 | runOnlyForDeploymentPostprocessing = 0; 268 | }; 269 | /* End PBXResourcesBuildPhase section */ 270 | 271 | /* Begin PBXShellScriptBuildPhase section */ 272 | 49C3794F2E75DB18311E7CC7 /* [CP] Embed Pods Frameworks */ = { 273 | isa = PBXShellScriptBuildPhase; 274 | buildActionMask = 2147483647; 275 | files = ( 276 | ); 277 | inputPaths = ( 278 | "${SRCROOT}/Pods/Target Support Files/Pods-RxSwiftMVVM/Pods-RxSwiftMVVM-frameworks.sh", 279 | "${BUILT_PRODUCTS_DIR}/Alamofire/Alamofire.framework", 280 | "${BUILT_PRODUCTS_DIR}/CryptoSwift/CryptoSwift.framework", 281 | "${BUILT_PRODUCTS_DIR}/Kingfisher/Kingfisher.framework", 282 | "${BUILT_PRODUCTS_DIR}/Result/Result.framework", 283 | "${BUILT_PRODUCTS_DIR}/RxAtomic/RxAtomic.framework", 284 | "${BUILT_PRODUCTS_DIR}/RxCocoa/RxCocoa.framework", 285 | "${BUILT_PRODUCTS_DIR}/RxSwift/RxSwift.framework", 286 | "${BUILT_PRODUCTS_DIR}/SwiftyJSON/SwiftyJSON.framework", 287 | "${BUILT_PRODUCTS_DIR}/Then/Then.framework", 288 | ); 289 | name = "[CP] Embed Pods Frameworks"; 290 | outputPaths = ( 291 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Alamofire.framework", 292 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/CryptoSwift.framework", 293 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Kingfisher.framework", 294 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Result.framework", 295 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxAtomic.framework", 296 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxCocoa.framework", 297 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/RxSwift.framework", 298 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/SwiftyJSON.framework", 299 | "${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/Then.framework", 300 | ); 301 | runOnlyForDeploymentPostprocessing = 0; 302 | shellPath = /bin/sh; 303 | shellScript = "\"${SRCROOT}/Pods/Target Support Files/Pods-RxSwiftMVVM/Pods-RxSwiftMVVM-frameworks.sh\"\n"; 304 | showEnvVarsInLog = 0; 305 | }; 306 | A787573E3CFE222944E0F04D /* [CP] Check Pods Manifest.lock */ = { 307 | isa = PBXShellScriptBuildPhase; 308 | buildActionMask = 2147483647; 309 | files = ( 310 | ); 311 | inputPaths = ( 312 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 313 | "${PODS_ROOT}/Manifest.lock", 314 | ); 315 | name = "[CP] Check Pods Manifest.lock"; 316 | outputPaths = ( 317 | "$(DERIVED_FILE_DIR)/Pods-RxSwiftMVVM-checkManifestLockResult.txt", 318 | ); 319 | runOnlyForDeploymentPostprocessing = 0; 320 | shellPath = /bin/sh; 321 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/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# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; 322 | showEnvVarsInLog = 0; 323 | }; 324 | /* End PBXShellScriptBuildPhase section */ 325 | 326 | /* Begin PBXSourcesBuildPhase section */ 327 | 3AF9B59521C1DDEF0096D1C3 /* Sources */ = { 328 | isa = PBXSourcesBuildPhase; 329 | buildActionMask = 2147483647; 330 | files = ( 331 | 3A4834DB21CC776B00D28416 /* MarvelDescriptionViewModel.swift in Sources */, 332 | ED8583442259C6F000CACD59 /* MarvelSearchTVCellViewModel.swift in Sources */, 333 | ED85834C225A3EF900CACD59 /* ObservableType-Extension.swift in Sources */, 334 | 3A4834D521CC761B00D28416 /* MarvelSearchTVCell.swift in Sources */, 335 | 3AF9B5B721C1E0C70096D1C3 /* AppDefinition.swift in Sources */, 336 | 3A4834DD21CC778700D28416 /* MarvelSearchViewModel.swift in Sources */, 337 | 3A4834D721CC772400D28416 /* MarvelDescriptionVC.swift in Sources */, 338 | ED8583472259DE9500CACD59 /* MarvelSearchNavigator.swift in Sources */, 339 | 3AF9B59F21C1DDEF0096D1C3 /* ViewController.swift in Sources */, 340 | ED858349225A384500CACD59 /* NavigationController.swift in Sources */, 341 | 3A4834D121CC75A300D28416 /* MarvelService.swift in Sources */, 342 | 3A4834D921CC774F00D28416 /* MarvelSearchVC.swift in Sources */, 343 | 3AF9B59D21C1DDEF0096D1C3 /* AppDelegate.swift in Sources */, 344 | 3A4834D321CC75CC00D28416 /* MarvelHeroModel.swift in Sources */, 345 | ); 346 | runOnlyForDeploymentPostprocessing = 0; 347 | }; 348 | /* End PBXSourcesBuildPhase section */ 349 | 350 | /* Begin PBXVariantGroup section */ 351 | 3AF9B5A021C1DDEF0096D1C3 /* Main.storyboard */ = { 352 | isa = PBXVariantGroup; 353 | children = ( 354 | 3AF9B5A121C1DDEF0096D1C3 /* Base */, 355 | ); 356 | name = Main.storyboard; 357 | sourceTree = ""; 358 | }; 359 | 3AF9B5A521C1DDF00096D1C3 /* LaunchScreen.storyboard */ = { 360 | isa = PBXVariantGroup; 361 | children = ( 362 | 3AF9B5A621C1DDF00096D1C3 /* Base */, 363 | ); 364 | name = LaunchScreen.storyboard; 365 | sourceTree = ""; 366 | }; 367 | /* End PBXVariantGroup section */ 368 | 369 | /* Begin XCBuildConfiguration section */ 370 | 3AF9B5A921C1DDF00096D1C3 /* Debug */ = { 371 | isa = XCBuildConfiguration; 372 | buildSettings = { 373 | ALWAYS_SEARCH_USER_PATHS = NO; 374 | CLANG_ANALYZER_NONNULL = YES; 375 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 376 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 377 | CLANG_CXX_LIBRARY = "libc++"; 378 | CLANG_ENABLE_MODULES = YES; 379 | CLANG_ENABLE_OBJC_ARC = YES; 380 | CLANG_ENABLE_OBJC_WEAK = YES; 381 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 382 | CLANG_WARN_BOOL_CONVERSION = YES; 383 | CLANG_WARN_COMMA = YES; 384 | CLANG_WARN_CONSTANT_CONVERSION = YES; 385 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 386 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 387 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 388 | CLANG_WARN_EMPTY_BODY = YES; 389 | CLANG_WARN_ENUM_CONVERSION = YES; 390 | CLANG_WARN_INFINITE_RECURSION = YES; 391 | CLANG_WARN_INT_CONVERSION = YES; 392 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 393 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 394 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 395 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 396 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 397 | CLANG_WARN_STRICT_PROTOTYPES = YES; 398 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 399 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 400 | CLANG_WARN_UNREACHABLE_CODE = YES; 401 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 402 | CODE_SIGN_IDENTITY = "iPhone Developer"; 403 | COPY_PHASE_STRIP = NO; 404 | DEBUG_INFORMATION_FORMAT = dwarf; 405 | ENABLE_STRICT_OBJC_MSGSEND = YES; 406 | ENABLE_TESTABILITY = YES; 407 | GCC_C_LANGUAGE_STANDARD = gnu11; 408 | GCC_DYNAMIC_NO_PIC = NO; 409 | GCC_NO_COMMON_BLOCKS = YES; 410 | GCC_OPTIMIZATION_LEVEL = 0; 411 | GCC_PREPROCESSOR_DEFINITIONS = ( 412 | "DEBUG=1", 413 | "$(inherited)", 414 | ); 415 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 416 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 417 | GCC_WARN_UNDECLARED_SELECTOR = YES; 418 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 419 | GCC_WARN_UNUSED_FUNCTION = YES; 420 | GCC_WARN_UNUSED_VARIABLE = YES; 421 | IPHONEOS_DEPLOYMENT_TARGET = 12.1; 422 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 423 | MTL_FAST_MATH = YES; 424 | ONLY_ACTIVE_ARCH = YES; 425 | SDKROOT = iphoneos; 426 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 427 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 428 | }; 429 | name = Debug; 430 | }; 431 | 3AF9B5AA21C1DDF00096D1C3 /* Release */ = { 432 | isa = XCBuildConfiguration; 433 | buildSettings = { 434 | ALWAYS_SEARCH_USER_PATHS = NO; 435 | CLANG_ANALYZER_NONNULL = YES; 436 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 437 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 438 | CLANG_CXX_LIBRARY = "libc++"; 439 | CLANG_ENABLE_MODULES = YES; 440 | CLANG_ENABLE_OBJC_ARC = YES; 441 | CLANG_ENABLE_OBJC_WEAK = YES; 442 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 443 | CLANG_WARN_BOOL_CONVERSION = YES; 444 | CLANG_WARN_COMMA = YES; 445 | CLANG_WARN_CONSTANT_CONVERSION = YES; 446 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 447 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 448 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 449 | CLANG_WARN_EMPTY_BODY = YES; 450 | CLANG_WARN_ENUM_CONVERSION = YES; 451 | CLANG_WARN_INFINITE_RECURSION = YES; 452 | CLANG_WARN_INT_CONVERSION = YES; 453 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 454 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 455 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 456 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 457 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 458 | CLANG_WARN_STRICT_PROTOTYPES = YES; 459 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 460 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 461 | CLANG_WARN_UNREACHABLE_CODE = YES; 462 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 463 | CODE_SIGN_IDENTITY = "iPhone Developer"; 464 | COPY_PHASE_STRIP = NO; 465 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 466 | ENABLE_NS_ASSERTIONS = NO; 467 | ENABLE_STRICT_OBJC_MSGSEND = YES; 468 | GCC_C_LANGUAGE_STANDARD = gnu11; 469 | GCC_NO_COMMON_BLOCKS = YES; 470 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 471 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 472 | GCC_WARN_UNDECLARED_SELECTOR = YES; 473 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 474 | GCC_WARN_UNUSED_FUNCTION = YES; 475 | GCC_WARN_UNUSED_VARIABLE = YES; 476 | IPHONEOS_DEPLOYMENT_TARGET = 12.1; 477 | MTL_ENABLE_DEBUG_INFO = NO; 478 | MTL_FAST_MATH = YES; 479 | SDKROOT = iphoneos; 480 | SWIFT_COMPILATION_MODE = wholemodule; 481 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 482 | VALIDATE_PRODUCT = YES; 483 | }; 484 | name = Release; 485 | }; 486 | 3AF9B5AC21C1DDF00096D1C3 /* Debug */ = { 487 | isa = XCBuildConfiguration; 488 | baseConfigurationReference = 6D930388EAA07CACC8BBAF61 /* Pods-RxSwiftMVVM.debug.xcconfig */; 489 | buildSettings = { 490 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 491 | CODE_SIGN_STYLE = Automatic; 492 | DEVELOPMENT_TEAM = ""; 493 | INFOPLIST_FILE = RxSwiftMVVM/Info.plist; 494 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 495 | LD_RUNPATH_SEARCH_PATHS = ( 496 | "$(inherited)", 497 | "@executable_path/Frameworks", 498 | ); 499 | PRODUCT_BUNDLE_IDENTIFIER = io.cashawalk.RxSwiftMVVM; 500 | PRODUCT_NAME = "$(TARGET_NAME)"; 501 | SWIFT_VERSION = 4.2; 502 | TARGETED_DEVICE_FAMILY = "1,2"; 503 | }; 504 | name = Debug; 505 | }; 506 | 3AF9B5AD21C1DDF00096D1C3 /* Release */ = { 507 | isa = XCBuildConfiguration; 508 | baseConfigurationReference = 67571DE271057B2FA35AE281 /* Pods-RxSwiftMVVM.release.xcconfig */; 509 | buildSettings = { 510 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 511 | CODE_SIGN_STYLE = Automatic; 512 | DEVELOPMENT_TEAM = ""; 513 | INFOPLIST_FILE = RxSwiftMVVM/Info.plist; 514 | IPHONEOS_DEPLOYMENT_TARGET = 10.0; 515 | LD_RUNPATH_SEARCH_PATHS = ( 516 | "$(inherited)", 517 | "@executable_path/Frameworks", 518 | ); 519 | PRODUCT_BUNDLE_IDENTIFIER = io.cashawalk.RxSwiftMVVM; 520 | PRODUCT_NAME = "$(TARGET_NAME)"; 521 | SWIFT_VERSION = 4.2; 522 | TARGETED_DEVICE_FAMILY = "1,2"; 523 | }; 524 | name = Release; 525 | }; 526 | /* End XCBuildConfiguration section */ 527 | 528 | /* Begin XCConfigurationList section */ 529 | 3AF9B59421C1DDEF0096D1C3 /* Build configuration list for PBXProject "RxSwiftMVVM" */ = { 530 | isa = XCConfigurationList; 531 | buildConfigurations = ( 532 | 3AF9B5A921C1DDF00096D1C3 /* Debug */, 533 | 3AF9B5AA21C1DDF00096D1C3 /* Release */, 534 | ); 535 | defaultConfigurationIsVisible = 0; 536 | defaultConfigurationName = Release; 537 | }; 538 | 3AF9B5AB21C1DDF00096D1C3 /* Build configuration list for PBXNativeTarget "RxSwiftMVVM" */ = { 539 | isa = XCConfigurationList; 540 | buildConfigurations = ( 541 | 3AF9B5AC21C1DDF00096D1C3 /* Debug */, 542 | 3AF9B5AD21C1DDF00096D1C3 /* Release */, 543 | ); 544 | defaultConfigurationIsVisible = 0; 545 | defaultConfigurationName = Release; 546 | }; 547 | /* End XCConfigurationList section */ 548 | }; 549 | rootObject = 3AF9B59121C1DDEF0096D1C3 /* Project object */; 550 | } 551 | --------------------------------------------------------------------------------