├── .github └── images │ ├── frame │ ├── dark_keyboard.png │ ├── dark_main.png │ ├── dark_setting.png │ ├── white_keyboard.png │ ├── white_main.png │ └── white_setting.png │ ├── header.png │ ├── no_frame │ ├── dark_keyboard.png │ ├── dark_main.png │ ├── dark_setting.png │ ├── white_keyboard.png │ ├── white_main.png │ └── white_setting.png │ └── preview.png ├── .gitignore ├── LICENSE ├── Podfile ├── Podfile.lock ├── README.md ├── keyboard ├── BaseInputViewController.swift ├── Extensions │ └── UIButton.swift ├── Info.plist ├── KeyboardViewController.swift ├── KeyboardViewReactor.swift ├── Sections │ └── ListSection.swift ├── Views │ ├── ListCell.swift │ └── ListCellReactor.swift ├── en.lproj │ └── InfoPlist.strings ├── keyboard.entitlements └── ko.lproj │ └── InfoPlist.strings ├── reactorkitKeyboardExample.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── xcuserdata │ │ └── fernando.xcuserdatad │ │ └── UserInterfaceState.xcuserstate ├── xcshareddata │ └── xcschemes │ │ ├── keyboard.xcscheme │ │ └── reactorkitKeyboardExample.xcscheme └── xcuserdata │ └── fernando.xcuserdatad │ └── xcschemes │ └── xcschememanagement.plist ├── reactorkitKeyboardExample.xcworkspace ├── contents.xcworkspacedata ├── xcshareddata │ └── IDEWorkspaceChecks.plist └── xcuserdata │ └── fernando.xcuserdatad │ ├── UserInterfaceState.xcuserstate │ └── xcdebugger │ └── Breakpoints_v2.xcbkptlist └── reactorkitKeyboardExample ├── AppDelegate.swift ├── Base.lproj ├── LaunchScreen.storyboard └── Main.storyboard ├── Base ├── BaseNavigationController.swift ├── BaseTableViewCell.swift └── BaseViewController.swift ├── Common ├── Constants.swift ├── GlobalColors.swift ├── UIColor.swift └── Utillity.swift ├── Extensions └── Rx │ └── UITableView+Rx.swift ├── Info.plist ├── Logging └── Logger.swift ├── Sections ├── MainSection.swift └── SettingSection.swift ├── Supported Files ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── Icon-App-20x20@1x.png │ │ ├── Icon-App-20x20@2x.png │ │ ├── Icon-App-20x20@3x.png │ │ ├── Icon-App-29x29@1x.png │ │ ├── Icon-App-29x29@2x.png │ │ ├── Icon-App-29x29@3x.png │ │ ├── Icon-App-40x40@1x.png │ │ ├── Icon-App-40x40@2x.png │ │ ├── Icon-App-40x40@3x.png │ │ ├── Icon-App-60x60@2x.png │ │ ├── Icon-App-60x60@3x.png │ │ ├── Icon-App-76x76@1x.png │ │ ├── Icon-App-76x76@2x.png │ │ ├── Icon-App-83.5x83.5@2x.png │ │ └── ItunesArtwork@2x.png │ ├── Contents.json │ ├── clipboard.imageset │ │ ├── Contents.json │ │ ├── clipboard@1x-1.png │ │ ├── clipboard@1x.png │ │ ├── clipboard@2x-1.png │ │ ├── clipboard@2x.png │ │ ├── clipboard@3x-1.png │ │ └── clipboard@3x.png │ ├── delete.imageset │ │ ├── Contents.json │ │ ├── delete@1x.png │ │ ├── delete@2x.png │ │ ├── delete@3x.png │ │ ├── delete_white@1x.png │ │ ├── delete_white@2x.png │ │ └── delete_white@3x.png │ ├── global.imageset │ │ ├── Contents.json │ │ ├── global-white@1x.png │ │ ├── global-white@2x.png │ │ ├── global-white@3x.png │ │ ├── global@1x.png │ │ ├── global@2x.png │ │ └── global@3x.png │ ├── plus.imageset │ │ ├── Contents.json │ │ ├── plus.png │ │ ├── plus@2x.png │ │ └── plus@3x.png │ └── return.imageset │ │ ├── Contents.json │ │ ├── return-white@1x.png │ │ ├── return-white@2x.png │ │ ├── return-white@3x.png │ │ ├── return@1x.png │ │ ├── return@2x.png │ │ └── return@3x.png ├── en.lproj │ ├── InfoPlist.strings │ └── Localizable.strings └── ko.lproj │ ├── InfoPlist.strings │ └── Localizable.strings ├── Types ├── SettingFooterType.swift └── SettingMenuTypes.swift ├── ViewController.swift ├── ViewControllers ├── MainViewController.swift ├── MainViewReactor.swift ├── SettingsViewController.swift ├── SettingsViewReactor.swift ├── WriteViewController.swift └── WriteViewReactor.swift ├── Views └── Cells │ ├── MainTableViewCell.swift │ ├── MainTableViewCellReactor.swift │ ├── SettingCell.swift │ ├── SettingCellReactor.swift │ ├── SettingSectionFooterView.swift │ └── SettingSectionFooterViewReactor.swift ├── ko.lproj ├── LaunchScreen.strings └── Main.strings └── reactorkitKeyboardExample.entitlements /.github/images/frame/dark_keyboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techinpark/reactorkit-keyboard-example/26d215b5832750fb5c347dca2d7ac3a954b279f0/.github/images/frame/dark_keyboard.png -------------------------------------------------------------------------------- /.github/images/frame/dark_main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techinpark/reactorkit-keyboard-example/26d215b5832750fb5c347dca2d7ac3a954b279f0/.github/images/frame/dark_main.png -------------------------------------------------------------------------------- /.github/images/frame/dark_setting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techinpark/reactorkit-keyboard-example/26d215b5832750fb5c347dca2d7ac3a954b279f0/.github/images/frame/dark_setting.png -------------------------------------------------------------------------------- /.github/images/frame/white_keyboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techinpark/reactorkit-keyboard-example/26d215b5832750fb5c347dca2d7ac3a954b279f0/.github/images/frame/white_keyboard.png -------------------------------------------------------------------------------- /.github/images/frame/white_main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techinpark/reactorkit-keyboard-example/26d215b5832750fb5c347dca2d7ac3a954b279f0/.github/images/frame/white_main.png -------------------------------------------------------------------------------- /.github/images/frame/white_setting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techinpark/reactorkit-keyboard-example/26d215b5832750fb5c347dca2d7ac3a954b279f0/.github/images/frame/white_setting.png -------------------------------------------------------------------------------- /.github/images/header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techinpark/reactorkit-keyboard-example/26d215b5832750fb5c347dca2d7ac3a954b279f0/.github/images/header.png -------------------------------------------------------------------------------- /.github/images/no_frame/dark_keyboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techinpark/reactorkit-keyboard-example/26d215b5832750fb5c347dca2d7ac3a954b279f0/.github/images/no_frame/dark_keyboard.png -------------------------------------------------------------------------------- /.github/images/no_frame/dark_main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techinpark/reactorkit-keyboard-example/26d215b5832750fb5c347dca2d7ac3a954b279f0/.github/images/no_frame/dark_main.png -------------------------------------------------------------------------------- /.github/images/no_frame/dark_setting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techinpark/reactorkit-keyboard-example/26d215b5832750fb5c347dca2d7ac3a954b279f0/.github/images/no_frame/dark_setting.png -------------------------------------------------------------------------------- /.github/images/no_frame/white_keyboard.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techinpark/reactorkit-keyboard-example/26d215b5832750fb5c347dca2d7ac3a954b279f0/.github/images/no_frame/white_keyboard.png -------------------------------------------------------------------------------- /.github/images/no_frame/white_main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techinpark/reactorkit-keyboard-example/26d215b5832750fb5c347dca2d7ac3a954b279f0/.github/images/no_frame/white_main.png -------------------------------------------------------------------------------- /.github/images/no_frame/white_setting.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techinpark/reactorkit-keyboard-example/26d215b5832750fb5c347dca2d7ac3a954b279f0/.github/images/no_frame/white_setting.png -------------------------------------------------------------------------------- /.github/images/preview.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techinpark/reactorkit-keyboard-example/26d215b5832750fb5c347dca2d7ac3a954b279f0/.github/images/preview.png -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | Pods/ 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Fernando. 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 | -------------------------------------------------------------------------------- /Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment the next line to define a global platform for your project 2 | platform :ios, '11.0' 3 | 4 | target 'reactorkitKeyboardExample' do 5 | # Comment the next line if you don't want to use dynamic frameworks 6 | use_frameworks! 7 | inhibit_all_warnings! 8 | # Pods for reactorkitKeyboardExample 9 | 10 | # Architecture 11 | pod 'ReactorKit' 12 | 13 | # UI 14 | pod 'Then' 15 | pod 'SnapKit' 16 | pod 'ReusableKit' 17 | pod 'AcknowList' 18 | 19 | # Rx 20 | pod 'RxSwift' 21 | pod 'RxCocoa' 22 | pod 'RxViewController' 23 | pod 'RxDataSources' 24 | pod 'RxOptional' 25 | 26 | # Logging 27 | pod 'CocoaLumberjack/Swift' 28 | 29 | target 'keyboard' do 30 | inherit! :search_paths 31 | end 32 | 33 | end 34 | # Uncomment the next line to define a global platform for your project 35 | 36 | 37 | 38 | 39 | 40 | -------------------------------------------------------------------------------- /Podfile.lock: -------------------------------------------------------------------------------- 1 | PODS: 2 | - AcknowList (1.9.4) 3 | - CocoaLumberjack/Core (3.6.1) 4 | - CocoaLumberjack/Swift (3.6.1): 5 | - CocoaLumberjack/Core 6 | - Differentiator (4.0.1) 7 | - ReactorKit (2.0.1): 8 | - RxSwift (~> 5.0) 9 | - ReusableKit (3.0.0): 10 | - ReusableKit/Core (= 3.0.0) 11 | - ReusableKit/Core (3.0.0) 12 | - RxCocoa (5.1.1): 13 | - RxRelay (~> 5) 14 | - RxSwift (~> 5) 15 | - RxDataSources (4.0.1): 16 | - Differentiator (~> 4.0) 17 | - RxCocoa (~> 5.0) 18 | - RxSwift (~> 5.0) 19 | - RxOptional (4.1.0): 20 | - RxCocoa (~> 5) 21 | - RxSwift (~> 5) 22 | - RxRelay (5.1.1): 23 | - RxSwift (~> 5) 24 | - RxSwift (5.1.1) 25 | - RxViewController (1.0.0): 26 | - RxCocoa (~> 5.0) 27 | - RxSwift (~> 5.0) 28 | - SnapKit (5.0.1) 29 | - Then (2.6.0) 30 | 31 | DEPENDENCIES: 32 | - AcknowList 33 | - CocoaLumberjack/Swift 34 | - ReactorKit 35 | - ReusableKit 36 | - RxCocoa 37 | - RxDataSources 38 | - RxOptional 39 | - RxSwift 40 | - RxViewController 41 | - SnapKit 42 | - Then 43 | 44 | SPEC REPOS: 45 | trunk: 46 | - AcknowList 47 | - CocoaLumberjack 48 | - Differentiator 49 | - ReactorKit 50 | - ReusableKit 51 | - RxCocoa 52 | - RxDataSources 53 | - RxOptional 54 | - RxRelay 55 | - RxSwift 56 | - RxViewController 57 | - SnapKit 58 | - Then 59 | 60 | SPEC CHECKSUMS: 61 | AcknowList: a236060b8c33141d7756814fd21d5dab477fc193 62 | CocoaLumberjack: b17ae15142558d08bbacf69775fa10c4abbebcc9 63 | Differentiator: 886080237d9f87f322641dedbc5be257061b0602 64 | ReactorKit: a63c5e11e0fd32d59d672e54178b0ed2876b764b 65 | ReusableKit: e5f853ad4652e411f96b6119b2488afa12929be6 66 | RxCocoa: 32065309a38d29b5b0db858819b5bf9ef038b601 67 | RxDataSources: efee07fa4de48477eca0a4611e6d11e2da9c1114 68 | RxOptional: b1fcd60856807a564c0215c2184b8d33e7826dc2 69 | RxRelay: d77f7d771495f43c556cbc43eebd1bb54d01e8e9 70 | RxSwift: 81470a2074fa8780320ea5fe4102807cb7118178 71 | RxViewController: 7330a46e5c31cd680db169da4c9fc8676e975a81 72 | SnapKit: 97b92857e3df3a0c71833cce143274bf6ef8e5eb 73 | Then: 90cd104fd951cec1980a03f57704ad8f784d4d79 74 | 75 | PODFILE CHECKSUM: fc1e5314b28d12c4e3b6c442f6ed724bc66eb1ef 76 | 77 | COCOAPODS: 1.8.4 78 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![](./.github/images/header.png) 2 | 3 | # Reactorkit-keyboard-example [![GitHub license](https://img.shields.io/badge/license-MIT-green.svg)](#) 4 | 5 | This project is ReactorKit based keyboard aplication example. 6 | 7 | # 🐒 Features 8 | - 🚀 ReactorKit based. 9 | - 🎉 iOS13 Dark mode support. 10 | - ➕ Using cocoapods 11 | 12 | # Quick Start 13 | 14 | ![](./.github/images/preview.png) 15 | 16 | # 📚 Skills 17 | 18 | - Architecture 19 | 20 | - [ReactorKit](https://github.com/ReactorKit/ReactorKit) 21 | 22 | - Reactive 23 | 24 | - [RxSwift](https://github.com/ReactiveX/RxSwift) 25 | - [RxDataSources](https://github.com/RxSwiftCommunity/RxDataSources) 26 | - [RxOptional](https://github.com/RxSwiftCommunity/RxOptional) 27 | 28 | - UI 29 | 30 | - [SnapKit](https://github.com/SnapKit/SnapKit) 31 | 32 | - etc 33 | - [AcknowList](https://github.com/vtourraine/AcknowList) 34 | 35 | # ❤️ Contributing 36 | This is an open source project, so feel free to contribute. How? 37 | 38 | - Open an issue. 39 | - Send feedback via email. 40 | - Propose your own fixes, suggestions and open a pull request with the changes. 41 | -------------------------------------------------------------------------------- /keyboard/BaseInputViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BaseInputViewController.swift 3 | // keyboard 4 | // 5 | // Created by Fernando on 2020/04/21. 6 | // Copyright © 2020 tmsae. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import RxSwift 11 | 12 | class BaseInputViewController: UIInputViewController { 13 | 14 | var disposeBag = DisposeBag() 15 | 16 | override func viewDidLoad() { 17 | super.viewDidLoad() 18 | 19 | // Do any additional setup after loading the view. 20 | self.addViews() 21 | self.updateViewConstraints() 22 | } 23 | 24 | override func updateViewConstraints() { 25 | super.updateViewConstraints() 26 | print("updateViewConstraints") 27 | } 28 | 29 | func addViews() { 30 | print("addViews") 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /keyboard/Extensions/UIButton.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIButton.swift 3 | // keyboard 4 | // 5 | // Created by Fernando on 2020/04/21. 6 | // Copyright © 2020 tmsae. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | extension UIButton { 12 | func setBackgroundColor(_ color: UIColor, for state: UIControl.State) { 13 | UIGraphicsBeginImageContext(CGSize(width: 1.0, height: 1.0)) 14 | guard let context = UIGraphicsGetCurrentContext() else { return } 15 | context.setFillColor(color.cgColor) 16 | context.fill(CGRect(x: 0.0, y: 0.0, width: 1.0, height: 1.0)) 17 | 18 | let backgroundImage = UIGraphicsGetImageFromCurrentImageContext() 19 | UIGraphicsEndImageContext() 20 | 21 | self.setBackgroundImage(backgroundImage, for: state) 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /keyboard/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleDisplayName 8 | keyboard 9 | CFBundleExecutable 10 | $(EXECUTABLE_NAME) 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 19 | CFBundleShortVersionString 20 | $(MARKETING_VERSION) 21 | CFBundleVersion 22 | 1 23 | NSExtension 24 | 25 | NSExtensionAttributes 26 | 27 | IsASCIICapable 28 | 29 | PrefersRightToLeft 30 | 31 | PrimaryLanguage 32 | en-US 33 | RequestsOpenAccess 34 | 35 | 36 | NSExtensionPointIdentifier 37 | com.apple.keyboard-service 38 | NSExtensionPrincipalClass 39 | $(PRODUCT_MODULE_NAME).KeyboardViewController 40 | 41 | 42 | 43 | -------------------------------------------------------------------------------- /keyboard/KeyboardViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KeyboardViewController.swift 3 | // keyboard 4 | // 5 | // Created by Fernando on 2020/04/21. 6 | // Copyright © 2020 tmsae. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import SnapKit 11 | import Then 12 | import RxSwift 13 | import RxCocoa 14 | import ReusableKit 15 | import ReactorKit 16 | import RxDataSources 17 | import RxViewController 18 | 19 | class KeyboardViewController: BaseInputViewController, ReactorKit.View { 20 | 21 | typealias Reactor = KeyboardViewReactor 22 | 23 | @IBOutlet var nextKeyboardButton: UIButton! 24 | 25 | private struct Metric { 26 | static let bottomButtonWidth: CGFloat = 80.0 27 | static let globalButtonWidth: CGFloat = 60.0 28 | static let deleteButtonWidth: CGFloat = 60.0 29 | static let enterButtonWidth: CGFloat = 80.0 30 | static let bottomButtonHeight: CGFloat = 45.0 31 | static let spaceButtonWidth: CGFloat = 100.0 32 | static let marginButtonLeftRight: CGFloat = 4.0 33 | static let buttonCornerRadius: CGFloat = 8.0 34 | 35 | static let tableViewCellHeight: CGFloat = 40.0 36 | static let tableViewHeight: CGFloat = 200.0 37 | static let textSize: CGFloat = 15.0 38 | 39 | static let emptyImageWidthHeight: CGFloat = 128.0 40 | static let emptyMessageTop: CGFloat = 20.0 41 | static let emptyMessageSize: CGFloat = 14.0 42 | } 43 | 44 | private struct Localized { 45 | static let space = NSLocalizedString("space", comment: "스페이스바") 46 | static let emptyMessage = NSLocalizedString("message_list_empty", comment: "등록된 내용이 없습니다") 47 | } 48 | 49 | private struct Reusable { 50 | static let listCell = ReusableCell() 51 | } 52 | 53 | required init?(coder: NSCoder) { 54 | super.init(coder: coder) 55 | } 56 | 57 | override init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) { 58 | defer { self.reactor = KeyboardViewReactor() } 59 | super.init(nibName: nibNameOrNil, bundle: nibBundleOrNil) 60 | self.dataSource = self.dataSourceFactory() 61 | } 62 | 63 | private var dataSource: RxTableViewSectionedReloadDataSource? 64 | 65 | private let tableView = UITableView().then { 66 | $0.backgroundColor = Color.keyboardBackground 67 | $0.rowHeight = Metric.tableViewCellHeight 68 | $0.tableHeaderView = UIView(frame: .zero) 69 | $0.tableFooterView = UIView(frame: .zero) 70 | $0.bounces = false 71 | 72 | if #available(iOS 11.0, *) { 73 | $0.contentInsetAdjustmentBehavior = .never 74 | } 75 | 76 | $0.register(Reusable.listCell) 77 | $0.separatorStyle = .singleLine 78 | $0.separatorInset.left = 0 79 | } 80 | 81 | private let bottomWrapView = UIView().then { 82 | $0.backgroundColor = Color.keyboardBackground 83 | } 84 | 85 | private let emptyView = UIView().then { 86 | $0.backgroundColor = Color.keyboardBackground 87 | $0.isHidden = true 88 | } 89 | 90 | private let emptyImageView = UIImageView().then { 91 | $0.image = #imageLiteral(resourceName: "clipboard") 92 | } 93 | 94 | private let emptyMessageLabel = UILabel().then { 95 | $0.text = Localized.emptyMessage 96 | $0.font = UIFont.systemFont(ofSize: Metric.emptyMessageSize) 97 | $0.textColor = Color.description 98 | $0.sizeToFit() 99 | } 100 | 101 | private let globalButton = UIButton().then { 102 | $0.layer.borderWidth = 0.3 103 | $0.layer.borderColor = UIColor.gray.cgColor 104 | $0.layer.masksToBounds = true 105 | $0.layer.cornerRadius = Metric.buttonCornerRadius 106 | $0.setImage(#imageLiteral(resourceName: "global"), for: .normal) 107 | $0.setBackgroundColor(Color.keyBackground, for: .normal) 108 | } 109 | 110 | private let spaceButton = UIButton().then { 111 | $0.layer.borderWidth = 0.3 112 | $0.layer.borderColor = UIColor.gray.cgColor 113 | $0.layer.masksToBounds = true 114 | $0.layer.cornerRadius = Metric.buttonCornerRadius 115 | $0.setTitle(Localized.space, for: .normal) 116 | $0.titleLabel?.font = UIFont.systemFont(ofSize: Metric.textSize) 117 | $0.setTitleColor(Color.title, for:.normal) 118 | $0.setBackgroundColor(Color.keySpaceBackground, for: .normal) 119 | } 120 | 121 | private let deleteButton = UIButton().then { 122 | $0.layer.borderWidth = 0.3 123 | $0.layer.borderColor = UIColor.gray.cgColor 124 | $0.layer.masksToBounds = true 125 | $0.layer.cornerRadius = Metric.buttonCornerRadius 126 | $0.setImage(#imageLiteral(resourceName: "delete"), for: .normal) 127 | $0.setBackgroundColor(Color.keyBackground, for: .normal) 128 | } 129 | 130 | private let enterButton = UIButton().then { 131 | $0.layer.borderWidth = 0.3 132 | $0.layer.borderColor = UIColor.gray.cgColor 133 | $0.layer.masksToBounds = true 134 | $0.layer.cornerRadius = Metric.buttonCornerRadius 135 | $0.setImage(#imageLiteral(resourceName: "return"), for: .normal) 136 | $0.setBackgroundColor(Color.keyBackground, for: .normal) 137 | } 138 | 139 | override func viewDidLoad() { 140 | super.viewDidLoad() 141 | } 142 | 143 | override func addViews() { 144 | super.addViews() 145 | 146 | if needsInputModeSwitchKey { 147 | self.bottomWrapView.addSubview(globalButton) 148 | } 149 | 150 | self.bottomWrapView.addSubview(spaceButton) 151 | self.bottomWrapView.addSubview(deleteButton) 152 | self.bottomWrapView.addSubview(enterButton) 153 | 154 | self.emptyView.addSubview(emptyImageView) 155 | self.emptyView.addSubview(emptyMessageLabel) 156 | 157 | self.view.addSubview(bottomWrapView) 158 | self.view.addSubview(tableView) 159 | self.view.addSubview(emptyView) 160 | } 161 | 162 | override func updateViewConstraints() { 163 | super.updateViewConstraints() 164 | 165 | self.view.snp.makeConstraints { make in 166 | make.height.equalTo(Metric.bottomButtonHeight + Metric.tableViewHeight) 167 | make.width.equalTo(UIScreen.main.bounds.width) 168 | } 169 | 170 | bottomWrapView.snp.makeConstraints { (make) in 171 | make.left.right.bottom.equalToSuperview() 172 | make.height.equalTo(Metric.bottomButtonHeight) 173 | } 174 | 175 | tableView.snp.makeConstraints { (make) in 176 | make.left.right.top.equalToSuperview() 177 | make.bottom.equalTo(bottomWrapView.snp.top) 178 | } 179 | 180 | emptyView.snp.makeConstraints { (make) in 181 | make.left.right.top.equalToSuperview() 182 | make.bottom.equalTo(bottomWrapView.snp.top) 183 | } 184 | 185 | emptyImageView.snp.makeConstraints { (make) in 186 | make.center.equalToSuperview() 187 | } 188 | 189 | emptyMessageLabel.snp.makeConstraints { (make) in 190 | make.top.equalTo(emptyImageView.snp.bottom).offset(Metric.emptyMessageTop) 191 | make.centerX.equalToSuperview() 192 | } 193 | 194 | if needsInputModeSwitchKey { 195 | globalButton.snp.makeConstraints { (make) in 196 | make.bottom.equalToSuperview().offset(-Metric.marginButtonLeftRight) 197 | make.top.equalToSuperview().offset(Metric.marginButtonLeftRight) 198 | make.left.equalToSuperview().offset(Metric.marginButtonLeftRight) 199 | make.width.equalTo(Metric.globalButtonWidth) 200 | } 201 | 202 | spaceButton.snp.makeConstraints { (make) in 203 | make.bottom.equalToSuperview().offset(-Metric.marginButtonLeftRight) 204 | make.top.equalToSuperview().offset(Metric.marginButtonLeftRight) 205 | make.left.equalTo(globalButton.snp.right).offset(Metric.marginButtonLeftRight) 206 | } 207 | } else { 208 | spaceButton.snp.makeConstraints { (make) in 209 | make.bottom.equalToSuperview().offset(-Metric.marginButtonLeftRight) 210 | make.top.equalToSuperview().offset(Metric.marginButtonLeftRight) 211 | make.left.equalToSuperview().offset(Metric.marginButtonLeftRight) 212 | } 213 | } 214 | 215 | deleteButton.snp.makeConstraints { (make) in 216 | make.bottom.equalToSuperview().offset(-Metric.marginButtonLeftRight) 217 | make.top.equalToSuperview().offset(Metric.marginButtonLeftRight) 218 | make.left.equalTo(spaceButton.snp.right).offset(Metric.marginButtonLeftRight) 219 | make.width.equalTo(Metric.deleteButtonWidth) 220 | } 221 | 222 | enterButton.snp.makeConstraints { (make) in 223 | make.bottom.equalToSuperview().offset(-Metric.marginButtonLeftRight) 224 | make.top.equalToSuperview().offset(Metric.marginButtonLeftRight) 225 | make.left.equalTo(deleteButton.snp.right).offset(Metric.marginButtonLeftRight) 226 | make.right.equalToSuperview().offset(-Metric.marginButtonLeftRight) 227 | make.width.equalTo(Metric.enterButtonWidth) 228 | } 229 | } 230 | 231 | func bind(reactor: KeyboardViewReactor) { 232 | 233 | // Action 234 | rx.viewDidLoad 235 | .map { _ in Reactor.Action.loadWords } 236 | .bind(to: reactor.action) 237 | .disposed(by: self.disposeBag) 238 | 239 | self.spaceButton.rx.tap 240 | .subscribe(onNext: { [weak self] _ in 241 | guard let self = self else { return } 242 | self.tapSpaceBar() 243 | }).disposed(by: self.disposeBag) 244 | 245 | self.globalButton.rx.tap 246 | .subscribe(onNext: { [weak self] _ in 247 | guard let self = self else { return } 248 | self.tapGlobal() 249 | }).disposed(by: self.disposeBag) 250 | 251 | self.deleteButton.rx.tap 252 | .subscribe(onNext: { [weak self] _ in 253 | guard let self = self else { return } 254 | self.tapDelete() 255 | }).disposed(by: self.disposeBag) 256 | 257 | self.enterButton.rx.tap 258 | .subscribe(onNext: { [weak self] _ in 259 | guard let self = self else { return } 260 | self.tapEnter() 261 | }).disposed(by: self.disposeBag) 262 | 263 | // State 264 | 265 | reactor.state.map { $0.sections } 266 | .bind(to: tableView.rx.items(dataSource: self.dataSource!)) 267 | .disposed(by: self.disposeBag) 268 | 269 | reactor.state.map { $0.sections.first?.items } 270 | .subscribe(onNext: { [weak self] items in 271 | guard let self = self else { return } 272 | if let items = items { 273 | if items.isEmpty { 274 | self.emptyView.isHidden = false 275 | } else { 276 | self.emptyView.isHidden = true 277 | } 278 | } 279 | }) 280 | .disposed(by: self.disposeBag) 281 | 282 | tableView.rx.itemSelected 283 | .subscribe(onNext: { [weak tableView] indexPath in 284 | logger.verbose("click") 285 | tableView?.deselectRow(at: indexPath, animated: true) 286 | }).disposed(by: self.disposeBag) 287 | 288 | tableView.rx.itemSelected(dataSource: self.dataSource!) 289 | .subscribe(onNext: { [weak self] sectionItem in 290 | guard let self = self else { return } 291 | switch sectionItem { 292 | case let .listItem(cellReactor): 293 | let message = cellReactor.currentState.list.message 294 | self.textDocumentProxy.insertText(message) 295 | } 296 | }) 297 | .disposed(by: self.disposeBag) 298 | } 299 | 300 | // MARK: DataSrouceFactory - configuration 301 | 302 | private func dataSourceFactory() -> RxTableViewSectionedReloadDataSource { 303 | return .init(configureCell: { (dataSource, tableView, indexPath, sectionItem) -> UITableViewCell in 304 | switch sectionItem { 305 | case .listItem(let cellReactor): 306 | let cell = tableView.dequeue(Reusable.listCell, for: indexPath) 307 | cell.reactor = cellReactor 308 | return cell 309 | } 310 | }) 311 | } 312 | 313 | override func viewWillLayoutSubviews() {} 314 | 315 | override func textWillChange(_ textInput: UITextInput?) { 316 | // The app is about to change the document's contents. Perform any preparation here. 317 | } 318 | 319 | override func textDidChange(_ textInput: UITextInput?) { 320 | // The app has just changed the document's contents, the document context has been updated. 321 | } 322 | 323 | // MARK: private function 324 | 325 | func tapGlobal() { 326 | logger.verbose("tapGlobal") 327 | self.advanceToNextInputMode() 328 | } 329 | 330 | func tapSpaceBar() { 331 | logger.verbose("tapSpaceBar") 332 | self.textDocumentProxy.insertText(" ") 333 | } 334 | 335 | func tapEnter() { 336 | self.textDocumentProxy.insertText("\n") 337 | } 338 | 339 | func tapDelete() { 340 | self.textDocumentProxy.deleteBackward() 341 | } 342 | } 343 | -------------------------------------------------------------------------------- /keyboard/KeyboardViewReactor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // KeyboardViewReactor.swift 3 | // keyboard 4 | // 5 | // Created by Fernando on 2020/04/21. 6 | // Copyright © 2020 tmsae. All rights reserved. 7 | // 8 | 9 | import ReactorKit 10 | import RxCocoa 11 | import RxSwift 12 | import UIKit 13 | 14 | final class KeyboardViewReactor: Reactor { 15 | 16 | enum Item { 17 | case word(String) 18 | 19 | var message: String { 20 | switch self { 21 | case let .word(message): 22 | return message 23 | } 24 | } 25 | } 26 | 27 | enum Action { 28 | case loadWords 29 | case getClipboard 30 | } 31 | 32 | enum Mutation { 33 | case loadWords 34 | case getClipboard 35 | } 36 | 37 | struct State { 38 | 39 | var words: [String] = [] 40 | var toastMessage: String? 41 | var clipboardString: String? 42 | 43 | var sections: [ListSection] { 44 | let sectionItems = words.map { word -> ListCellReactor in 45 | return ListCellReactor(list: Item.word(word)) 46 | }.map(ListSectionItem.listItem) 47 | return [.list(sectionItems)] 48 | } 49 | } 50 | 51 | let initialState: State 52 | 53 | // MARK: Initializing 54 | 55 | init() { 56 | initialState = State() 57 | } 58 | 59 | // MARK: Mutate 60 | 61 | func mutate(action: Action) -> Observable { 62 | switch action { 63 | case .loadWords: 64 | return .just(Mutation.loadWords) 65 | case .getClipboard: 66 | return .just(Mutation.getClipboard) 67 | } 68 | } 69 | 70 | // MARK: Reduce 71 | 72 | func reduce(state: State, mutation: Mutation) -> State { 73 | var state = state 74 | switch mutation { 75 | case .loadWords: 76 | let words = self.getWords() 77 | state.words = words 78 | return state 79 | case .getClipboard: 80 | let words = self.getPasteboard() 81 | state.clipboardString = words 82 | return state 83 | } 84 | } 85 | 86 | // MARK: Private 87 | private func getWords() -> [String] { 88 | guard let userDefault = UserDefaults(suiteName: Constants.AppConfig.groupID) else { return [] } 89 | if let data = userDefault.array(forKey: Constants.AppConfig.oldUserDefaultKey) as? [String] { 90 | logger.verbose(data) 91 | return data 92 | } 93 | 94 | return [] 95 | } 96 | 97 | private func setPasteboard(string: String) { 98 | UIPasteboard.general.string = string 99 | } 100 | 101 | private func getPasteboard() -> String? { 102 | if let string = UIPasteboard.general.string { 103 | return string 104 | } 105 | 106 | return nil 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /keyboard/Sections/ListSection.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ListSection.swift 3 | // keyboard 4 | // 5 | // Created by Fernando on 2020/04/21. 6 | // Copyright © 2020 tmsae. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import RxDataSources 11 | 12 | enum ListSection { 13 | case list([ListSectionItem]) 14 | } 15 | 16 | extension ListSection: SectionModelType { 17 | 18 | typealias Identify = String 19 | typealias Item = ListSectionItem 20 | var identify: String { 21 | return "" 22 | } 23 | 24 | var items: [ListSectionItem] { 25 | switch self { 26 | case .list(let items): 27 | return items 28 | } 29 | } 30 | 31 | init(original: ListSection, items: [Item]) { 32 | switch original { 33 | case .list: 34 | self = .list(items) 35 | } 36 | } 37 | } 38 | 39 | enum ListSectionItem { 40 | case listItem(ListCellReactor) 41 | } 42 | -------------------------------------------------------------------------------- /keyboard/Views/ListCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ListCell.swift 3 | // keyboard 4 | // 5 | // Created by Fernando on 2020/04/21. 6 | // Copyright © 2020 tmsae. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | import ReactorKit 12 | import RxCocoa 13 | import RxSwift 14 | 15 | final class ListCell: BaseTableViewCell, ReactorKit.View { 16 | 17 | // MARK: Properties 18 | typealias Reactor = ListCellReactor 19 | 20 | // MARK: Initializing 21 | override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { 22 | super.init(style: style, reuseIdentifier: reuseIdentifier) 23 | self.accessoryType = .none 24 | self.textLabel?.font = UIFont.systemFont(ofSize: 14.0) 25 | self.textLabel?.textColor = Color.title 26 | } 27 | 28 | required init?(coder aDecoder: NSCoder) { 29 | fatalError("init(coder:) has not been implemented") 30 | } 31 | 32 | // MARK: Initializing 33 | 34 | override func addViews() { 35 | super.addViews() 36 | self.contentView.backgroundColor = Color.keyboardListBackground 37 | self.selectionStyle = .none 38 | } 39 | 40 | override func setupConstraints() { 41 | super.setupConstraints() 42 | } 43 | 44 | // MARK: Binding 45 | 46 | func bind(reactor: ListCellReactor) { 47 | 48 | reactor.state.map { $0.list.message } 49 | .distinctUntilChanged() 50 | .bind(to: self.textLabel!.rx.text) 51 | .disposed(by: self.disposeBag) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /keyboard/Views/ListCellReactor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ListCellReactor.swift 3 | // keyboard 4 | // 5 | // Created by Fernando on 2020/04/21. 6 | // Copyright © 2020 tmsae. All rights reserved. 7 | // 8 | 9 | import ReactorKit 10 | import RxCocoa 11 | import RxSwift 12 | 13 | final class ListCellReactor: Reactor { 14 | typealias Action = NoAction 15 | typealias Mutaiton = NoMutation 16 | 17 | struct State { 18 | var list: KeyboardViewReactor.Item 19 | } 20 | 21 | let initialState: State 22 | 23 | init(list: KeyboardViewReactor.Item) { 24 | initialState = State(list: list) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /keyboard/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* 2 | infoPlist.strings 3 | reactorkitKeyboardExample 4 | 5 | Created by Fernando on 2020/04/21. 6 | Copyright © 2020 tmsae. All rights reserved. 7 | */ 8 | 9 | "CFBundleDisplayName" = "Keyboard-Example"; 10 | "CFBundleDevelopmentRegion" = "en_US"; 11 | -------------------------------------------------------------------------------- /keyboard/keyboard.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.application-groups 6 | 7 | group.com.tmsae.keyboardExample 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /keyboard/ko.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* 2 | infoPlist.strings 3 | reactorkitKeyboardExample 4 | 5 | Created by Fernando on 2020/04/21. 6 | Copyright © 2020 tmsae. All rights reserved. 7 | */ 8 | 9 | "CFBundleDisplayName" = "Keyboard-Example"; 10 | "CFBundleDevelopmentRegion" = "ko_KR"; 11 | -------------------------------------------------------------------------------- /reactorkitKeyboardExample.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 51; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 2325D6B6E78502333B03A1F1 /* Pods_keyboard.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 968073ACABAB3358884C79DC /* Pods_keyboard.framework */; }; 11 | CCBECB9BD5869123E121BC84 /* Pods_reactorkitKeyboardExample.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = F01FA2BC9C3FA2798570C834 /* Pods_reactorkitKeyboardExample.framework */; }; 12 | ED0EFE11244EC62D00608FDD /* Pods-reactorkitKeyboardExample-acknowledgements.plist in Resources */ = {isa = PBXBuildFile; fileRef = ED0EFE10244EC62D00608FDD /* Pods-reactorkitKeyboardExample-acknowledgements.plist */; }; 13 | ED0EFE14244ECBEF00608FDD /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = ED0EFE16244ECBEF00608FDD /* InfoPlist.strings */; }; 14 | ED15C026244EDA9000483E20 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = EDED22CE244E0CD3004503BB /* Localizable.strings */; }; 15 | ED15C029244EDABD00483E20 /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = ED15C02B244EDABD00483E20 /* InfoPlist.strings */; }; 16 | ED15C02E244EDB7A00483E20 /* UIColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED15C02D244EDB7A00483E20 /* UIColor.swift */; }; 17 | ED15C02F244EDBA700483E20 /* UIColor.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED15C02D244EDB7A00483E20 /* UIColor.swift */; }; 18 | ED8A0173244E03B100D0C796 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED8A0172244E03B100D0C796 /* AppDelegate.swift */; }; 19 | ED8A0177244E03B100D0C796 /* ViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = ED8A0176244E03B100D0C796 /* ViewController.swift */; }; 20 | ED8A017A244E03B100D0C796 /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = ED8A0178244E03B100D0C796 /* Main.storyboard */; }; 21 | ED8A017C244E03B300D0C796 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = ED8A017B244E03B300D0C796 /* Assets.xcassets */; }; 22 | ED8A017F244E03B300D0C796 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = ED8A017D244E03B300D0C796 /* LaunchScreen.storyboard */; }; 23 | EDED2298244E079A004503BB /* BaseViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDED2297244E079A004503BB /* BaseViewController.swift */; }; 24 | EDED229A244E07C7004503BB /* BaseNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDED2299244E07C7004503BB /* BaseNavigationController.swift */; }; 25 | EDED229C244E07E5004503BB /* BaseTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDED229B244E07E5004503BB /* BaseTableViewCell.swift */; }; 26 | EDED229E244E0807004503BB /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDED229D244E0807004503BB /* Logger.swift */; }; 27 | EDED22A0244E08A4004503BB /* MainViewReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDED229F244E08A4004503BB /* MainViewReactor.swift */; }; 28 | EDED22A2244E08C0004503BB /* MainViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDED22A1244E08C0004503BB /* MainViewController.swift */; }; 29 | EDED22A5244E08F2004503BB /* MainTableViewCellReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDED22A4244E08F2004503BB /* MainTableViewCellReactor.swift */; }; 30 | EDED22A7244E0900004503BB /* MainTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDED22A6244E0900004503BB /* MainTableViewCell.swift */; }; 31 | EDED22A9244E0926004503BB /* MainSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDED22A8244E0926004503BB /* MainSection.swift */; }; 32 | EDED22AC244E0956004503BB /* Utillity.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDED22AB244E0956004503BB /* Utillity.swift */; }; 33 | EDED22AE244E096E004503BB /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDED22AD244E096E004503BB /* Constants.swift */; }; 34 | EDED22B1244E09BD004503BB /* UITableView+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDED22B0244E09BD004503BB /* UITableView+Rx.swift */; }; 35 | EDED22B9244E09E6004503BB /* KeyboardViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDED22B8244E09E6004503BB /* KeyboardViewController.swift */; }; 36 | EDED22BD244E09E6004503BB /* keyboard.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = EDED22B6244E09E6004503BB /* keyboard.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; 37 | EDED22C3244E0A19004503BB /* GlobalColors.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDED22C2244E0A19004503BB /* GlobalColors.swift */; }; 38 | EDED22C5244E0B28004503BB /* WriteViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDED22C4244E0B28004503BB /* WriteViewController.swift */; }; 39 | EDED22C7244E0B38004503BB /* WriteViewReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDED22C6244E0B38004503BB /* WriteViewReactor.swift */; }; 40 | EDED22CC244E0CD3004503BB /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = EDED22CE244E0CD3004503BB /* Localizable.strings */; }; 41 | EDED22D1244E0E2A004503BB /* SettingsViewReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDED22D0244E0E2A004503BB /* SettingsViewReactor.swift */; }; 42 | EDED22D3244E0E3A004503BB /* SettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDED22D2244E0E3A004503BB /* SettingsViewController.swift */; }; 43 | EDED22D5244E0EF1004503BB /* SettingCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDED22D4244E0EF1004503BB /* SettingCell.swift */; }; 44 | EDED22D7244E0F0C004503BB /* SettingCellReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDED22D6244E0F0C004503BB /* SettingCellReactor.swift */; }; 45 | EDED22D9244E0F30004503BB /* SettingMenuTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDED22D8244E0F30004503BB /* SettingMenuTypes.swift */; }; 46 | EDED22DB244E0F4E004503BB /* SettingSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDED22DA244E0F4E004503BB /* SettingSection.swift */; }; 47 | EDED22DD244E0FC1004503BB /* SettingFooterType.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDED22DC244E0FC1004503BB /* SettingFooterType.swift */; }; 48 | EDED22DF244E1025004503BB /* SettingSectionFooterView.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDED22DE244E1025004503BB /* SettingSectionFooterView.swift */; }; 49 | EDED22E1244E1030004503BB /* SettingSectionFooterViewReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDED22E0244E1030004503BB /* SettingSectionFooterViewReactor.swift */; }; 50 | EDED22E5244E1168004503BB /* ListCellReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDED22E4244E1168004503BB /* ListCellReactor.swift */; }; 51 | EDED22E7244E1171004503BB /* ListCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDED22E6244E1171004503BB /* ListCell.swift */; }; 52 | EDED22E9244E1191004503BB /* ListSection.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDED22E8244E1191004503BB /* ListSection.swift */; }; 53 | EDED22EB244E11A9004503BB /* BaseInputViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDED22EA244E11A9004503BB /* BaseInputViewController.swift */; }; 54 | EDED22ED244E11CA004503BB /* KeyboardViewReactor.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDED22EC244E11CA004503BB /* KeyboardViewReactor.swift */; }; 55 | EDED22F0244E1218004503BB /* BaseTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDED229B244E07E5004503BB /* BaseTableViewCell.swift */; }; 56 | EDED22F1244E1220004503BB /* GlobalColors.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDED22C2244E0A19004503BB /* GlobalColors.swift */; }; 57 | EDED22F2244E1223004503BB /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDED22AD244E096E004503BB /* Constants.swift */; }; 58 | EDED22F5244E126B004503BB /* UIButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDED22F4244E126B004503BB /* UIButton.swift */; }; 59 | EDED22F6244E127E004503BB /* Logger.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDED229D244E0807004503BB /* Logger.swift */; }; 60 | EDED22F7244E12C1004503BB /* UITableView+Rx.swift in Sources */ = {isa = PBXBuildFile; fileRef = EDED22B0244E09BD004503BB /* UITableView+Rx.swift */; }; 61 | EDED22FA244E1504004503BB /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = ED8A017B244E03B300D0C796 /* Assets.xcassets */; }; 62 | /* End PBXBuildFile section */ 63 | 64 | /* Begin PBXContainerItemProxy section */ 65 | EDED22BB244E09E6004503BB /* PBXContainerItemProxy */ = { 66 | isa = PBXContainerItemProxy; 67 | containerPortal = ED8A0167244E03B100D0C796 /* Project object */; 68 | proxyType = 1; 69 | remoteGlobalIDString = EDED22B5244E09E6004503BB; 70 | remoteInfo = keyboard; 71 | }; 72 | /* End PBXContainerItemProxy section */ 73 | 74 | /* Begin PBXCopyFilesBuildPhase section */ 75 | EDED22C1244E09E6004503BB /* Embed App Extensions */ = { 76 | isa = PBXCopyFilesBuildPhase; 77 | buildActionMask = 2147483647; 78 | dstPath = ""; 79 | dstSubfolderSpec = 13; 80 | files = ( 81 | EDED22BD244E09E6004503BB /* keyboard.appex in Embed App Extensions */, 82 | ); 83 | name = "Embed App Extensions"; 84 | runOnlyForDeploymentPostprocessing = 0; 85 | }; 86 | /* End PBXCopyFilesBuildPhase section */ 87 | 88 | /* Begin PBXFileReference section */ 89 | 2038FF17147E219B043ABEAB /* Pods-reactorkitKeyboardExample.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-reactorkitKeyboardExample.release.xcconfig"; path = "Target Support Files/Pods-reactorkitKeyboardExample/Pods-reactorkitKeyboardExample.release.xcconfig"; sourceTree = ""; }; 90 | 6AFD01CE0785D20809E99E2B /* Pods-keyboard.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-keyboard.release.xcconfig"; path = "Target Support Files/Pods-keyboard/Pods-keyboard.release.xcconfig"; sourceTree = ""; }; 91 | 6BC67B005AA82AFD5DBBB4B5 /* Pods-keyboard.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-keyboard.debug.xcconfig"; path = "Target Support Files/Pods-keyboard/Pods-keyboard.debug.xcconfig"; sourceTree = ""; }; 92 | 968073ACABAB3358884C79DC /* Pods_keyboard.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_keyboard.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 93 | AA0AB43F661D32464E4E23C4 /* Pods-reactorkitKeyboardExample.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-reactorkitKeyboardExample.debug.xcconfig"; path = "Target Support Files/Pods-reactorkitKeyboardExample/Pods-reactorkitKeyboardExample.debug.xcconfig"; sourceTree = ""; }; 94 | ED0EFE10244EC62D00608FDD /* Pods-reactorkitKeyboardExample-acknowledgements.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; name = "Pods-reactorkitKeyboardExample-acknowledgements.plist"; path = "Pods/Target Support Files/Pods-reactorkitKeyboardExample/Pods-reactorkitKeyboardExample-acknowledgements.plist"; sourceTree = SOURCE_ROOT; }; 95 | ED0EFE15244ECBEF00608FDD /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 96 | ED0EFE17244ECBF000608FDD /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/InfoPlist.strings; sourceTree = ""; }; 97 | ED15C02A244EDABD00483E20 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; }; 98 | ED15C02C244EDABE00483E20 /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/InfoPlist.strings; sourceTree = ""; }; 99 | ED15C02D244EDB7A00483E20 /* UIColor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIColor.swift; sourceTree = ""; }; 100 | ED8A016F244E03B100D0C796 /* reactorkitKeyboardExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = reactorkitKeyboardExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 101 | ED8A0172244E03B100D0C796 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 102 | ED8A0176244E03B100D0C796 /* ViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewController.swift; sourceTree = ""; }; 103 | ED8A0179244E03B100D0C796 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 104 | ED8A017B244E03B300D0C796 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 105 | ED8A017E244E03B300D0C796 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; 106 | ED8A0180244E03B300D0C796 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 107 | EDED2297244E079A004503BB /* BaseViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseViewController.swift; sourceTree = ""; }; 108 | EDED2299244E07C7004503BB /* BaseNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseNavigationController.swift; sourceTree = ""; }; 109 | EDED229B244E07E5004503BB /* BaseTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseTableViewCell.swift; sourceTree = ""; }; 110 | EDED229D244E0807004503BB /* Logger.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Logger.swift; sourceTree = ""; }; 111 | EDED229F244E08A4004503BB /* MainViewReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainViewReactor.swift; sourceTree = ""; }; 112 | EDED22A1244E08C0004503BB /* MainViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainViewController.swift; sourceTree = ""; }; 113 | EDED22A4244E08F2004503BB /* MainTableViewCellReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainTableViewCellReactor.swift; sourceTree = ""; }; 114 | EDED22A6244E0900004503BB /* MainTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainTableViewCell.swift; sourceTree = ""; }; 115 | EDED22A8244E0926004503BB /* MainSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MainSection.swift; sourceTree = ""; }; 116 | EDED22AB244E0956004503BB /* Utillity.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Utillity.swift; sourceTree = ""; }; 117 | EDED22AD244E096E004503BB /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; 118 | EDED22B0244E09BD004503BB /* UITableView+Rx.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITableView+Rx.swift"; sourceTree = ""; }; 119 | EDED22B6244E09E6004503BB /* keyboard.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = keyboard.appex; sourceTree = BUILT_PRODUCTS_DIR; }; 120 | EDED22B8244E09E6004503BB /* KeyboardViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardViewController.swift; sourceTree = ""; }; 121 | EDED22BA244E09E6004503BB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 122 | EDED22C2244E0A19004503BB /* GlobalColors.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlobalColors.swift; sourceTree = ""; }; 123 | EDED22C4244E0B28004503BB /* WriteViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WriteViewController.swift; sourceTree = ""; }; 124 | EDED22C6244E0B38004503BB /* WriteViewReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WriteViewReactor.swift; sourceTree = ""; }; 125 | EDED22C8244E0C6A004503BB /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/Main.strings; sourceTree = ""; }; 126 | EDED22C9244E0C6A004503BB /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/LaunchScreen.strings; sourceTree = ""; }; 127 | EDED22CD244E0CD3004503BB /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = ""; }; 128 | EDED22CF244E0CD4004503BB /* ko */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ko; path = ko.lproj/Localizable.strings; sourceTree = ""; }; 129 | EDED22D0244E0E2A004503BB /* SettingsViewReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewReactor.swift; sourceTree = ""; }; 130 | EDED22D2244E0E3A004503BB /* SettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsViewController.swift; sourceTree = ""; }; 131 | EDED22D4244E0EF1004503BB /* SettingCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingCell.swift; sourceTree = ""; }; 132 | EDED22D6244E0F0C004503BB /* SettingCellReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingCellReactor.swift; sourceTree = ""; }; 133 | EDED22D8244E0F30004503BB /* SettingMenuTypes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingMenuTypes.swift; sourceTree = ""; }; 134 | EDED22DA244E0F4E004503BB /* SettingSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingSection.swift; sourceTree = ""; }; 135 | EDED22DC244E0FC1004503BB /* SettingFooterType.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingFooterType.swift; sourceTree = ""; }; 136 | EDED22DE244E1025004503BB /* SettingSectionFooterView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingSectionFooterView.swift; sourceTree = ""; }; 137 | EDED22E0244E1030004503BB /* SettingSectionFooterViewReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingSectionFooterViewReactor.swift; sourceTree = ""; }; 138 | EDED22E4244E1168004503BB /* ListCellReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListCellReactor.swift; sourceTree = ""; }; 139 | EDED22E6244E1171004503BB /* ListCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListCell.swift; sourceTree = ""; }; 140 | EDED22E8244E1191004503BB /* ListSection.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListSection.swift; sourceTree = ""; }; 141 | EDED22EA244E11A9004503BB /* BaseInputViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BaseInputViewController.swift; sourceTree = ""; }; 142 | EDED22EC244E11CA004503BB /* KeyboardViewReactor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardViewReactor.swift; sourceTree = ""; }; 143 | EDED22F4244E126B004503BB /* UIButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIButton.swift; sourceTree = ""; }; 144 | EDED22F8244E138C004503BB /* reactorkitKeyboardExample.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = reactorkitKeyboardExample.entitlements; sourceTree = ""; }; 145 | EDED22F9244E1463004503BB /* keyboard.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = keyboard.entitlements; sourceTree = ""; }; 146 | F01FA2BC9C3FA2798570C834 /* Pods_reactorkitKeyboardExample.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_reactorkitKeyboardExample.framework; sourceTree = BUILT_PRODUCTS_DIR; }; 147 | /* End PBXFileReference section */ 148 | 149 | /* Begin PBXFrameworksBuildPhase section */ 150 | ED8A016C244E03B100D0C796 /* Frameworks */ = { 151 | isa = PBXFrameworksBuildPhase; 152 | buildActionMask = 2147483647; 153 | files = ( 154 | CCBECB9BD5869123E121BC84 /* Pods_reactorkitKeyboardExample.framework in Frameworks */, 155 | ); 156 | runOnlyForDeploymentPostprocessing = 0; 157 | }; 158 | EDED22B3244E09E6004503BB /* Frameworks */ = { 159 | isa = PBXFrameworksBuildPhase; 160 | buildActionMask = 2147483647; 161 | files = ( 162 | 2325D6B6E78502333B03A1F1 /* Pods_keyboard.framework in Frameworks */, 163 | ); 164 | runOnlyForDeploymentPostprocessing = 0; 165 | }; 166 | /* End PBXFrameworksBuildPhase section */ 167 | 168 | /* Begin PBXGroup section */ 169 | AC481367D8F21772CD5D51A1 /* Frameworks */ = { 170 | isa = PBXGroup; 171 | children = ( 172 | F01FA2BC9C3FA2798570C834 /* Pods_reactorkitKeyboardExample.framework */, 173 | 968073ACABAB3358884C79DC /* Pods_keyboard.framework */, 174 | ); 175 | name = Frameworks; 176 | sourceTree = ""; 177 | }; 178 | CC6DAB0B1CEA65BD340F2FCB /* Pods */ = { 179 | isa = PBXGroup; 180 | children = ( 181 | AA0AB43F661D32464E4E23C4 /* Pods-reactorkitKeyboardExample.debug.xcconfig */, 182 | 2038FF17147E219B043ABEAB /* Pods-reactorkitKeyboardExample.release.xcconfig */, 183 | 6BC67B005AA82AFD5DBBB4B5 /* Pods-keyboard.debug.xcconfig */, 184 | 6AFD01CE0785D20809E99E2B /* Pods-keyboard.release.xcconfig */, 185 | ); 186 | path = Pods; 187 | sourceTree = ""; 188 | }; 189 | ED8A0166244E03B100D0C796 = { 190 | isa = PBXGroup; 191 | children = ( 192 | ED8A0171244E03B100D0C796 /* reactorkitKeyboardExample */, 193 | EDED22B7244E09E6004503BB /* keyboard */, 194 | ED8A0170244E03B100D0C796 /* Products */, 195 | CC6DAB0B1CEA65BD340F2FCB /* Pods */, 196 | AC481367D8F21772CD5D51A1 /* Frameworks */, 197 | ); 198 | sourceTree = ""; 199 | }; 200 | ED8A0170244E03B100D0C796 /* Products */ = { 201 | isa = PBXGroup; 202 | children = ( 203 | ED8A016F244E03B100D0C796 /* reactorkitKeyboardExample.app */, 204 | EDED22B6244E09E6004503BB /* keyboard.appex */, 205 | ); 206 | name = Products; 207 | sourceTree = ""; 208 | }; 209 | ED8A0171244E03B100D0C796 /* reactorkitKeyboardExample */ = { 210 | isa = PBXGroup; 211 | children = ( 212 | EDED22F8244E138C004503BB /* reactorkitKeyboardExample.entitlements */, 213 | EDED22AA244E094A004503BB /* Common */, 214 | EDED2296244E0777004503BB /* Logging */, 215 | EDED2290244E069E004503BB /* Base */, 216 | EDED2291244E06A4004503BB /* ViewControllers */, 217 | EDED228F244E0697004503BB /* Views */, 218 | EDED2294244E06B9004503BB /* Extensions */, 219 | EDED2293244E06B5004503BB /* Types */, 220 | EDED2292244E06AF004503BB /* Sections */, 221 | ED8A0172244E03B100D0C796 /* AppDelegate.swift */, 222 | ED8A0176244E03B100D0C796 /* ViewController.swift */, 223 | ED8A0178244E03B100D0C796 /* Main.storyboard */, 224 | EDED2295244E0748004503BB /* Supported Files */, 225 | ED8A017D244E03B300D0C796 /* LaunchScreen.storyboard */, 226 | ED8A0180244E03B300D0C796 /* Info.plist */, 227 | ); 228 | path = reactorkitKeyboardExample; 229 | sourceTree = ""; 230 | }; 231 | EDED228F244E0697004503BB /* Views */ = { 232 | isa = PBXGroup; 233 | children = ( 234 | EDED22A3244E08E4004503BB /* Cells */, 235 | ); 236 | path = Views; 237 | sourceTree = ""; 238 | }; 239 | EDED2290244E069E004503BB /* Base */ = { 240 | isa = PBXGroup; 241 | children = ( 242 | EDED2297244E079A004503BB /* BaseViewController.swift */, 243 | EDED2299244E07C7004503BB /* BaseNavigationController.swift */, 244 | EDED229B244E07E5004503BB /* BaseTableViewCell.swift */, 245 | ); 246 | path = Base; 247 | sourceTree = ""; 248 | }; 249 | EDED2291244E06A4004503BB /* ViewControllers */ = { 250 | isa = PBXGroup; 251 | children = ( 252 | EDED229F244E08A4004503BB /* MainViewReactor.swift */, 253 | EDED22A1244E08C0004503BB /* MainViewController.swift */, 254 | EDED22C4244E0B28004503BB /* WriteViewController.swift */, 255 | EDED22C6244E0B38004503BB /* WriteViewReactor.swift */, 256 | EDED22D0244E0E2A004503BB /* SettingsViewReactor.swift */, 257 | EDED22D2244E0E3A004503BB /* SettingsViewController.swift */, 258 | ); 259 | path = ViewControllers; 260 | sourceTree = ""; 261 | }; 262 | EDED2292244E06AF004503BB /* Sections */ = { 263 | isa = PBXGroup; 264 | children = ( 265 | EDED22A8244E0926004503BB /* MainSection.swift */, 266 | EDED22DA244E0F4E004503BB /* SettingSection.swift */, 267 | ); 268 | path = Sections; 269 | sourceTree = ""; 270 | }; 271 | EDED2293244E06B5004503BB /* Types */ = { 272 | isa = PBXGroup; 273 | children = ( 274 | EDED22D8244E0F30004503BB /* SettingMenuTypes.swift */, 275 | EDED22DC244E0FC1004503BB /* SettingFooterType.swift */, 276 | ); 277 | path = Types; 278 | sourceTree = ""; 279 | }; 280 | EDED2294244E06B9004503BB /* Extensions */ = { 281 | isa = PBXGroup; 282 | children = ( 283 | EDED22AF244E09B0004503BB /* Rx */, 284 | ); 285 | path = Extensions; 286 | sourceTree = ""; 287 | }; 288 | EDED2295244E0748004503BB /* Supported Files */ = { 289 | isa = PBXGroup; 290 | children = ( 291 | ED0EFE10244EC62D00608FDD /* Pods-reactorkitKeyboardExample-acknowledgements.plist */, 292 | ED8A017B244E03B300D0C796 /* Assets.xcassets */, 293 | EDED22CE244E0CD3004503BB /* Localizable.strings */, 294 | ED15C02B244EDABD00483E20 /* InfoPlist.strings */, 295 | ); 296 | path = "Supported Files"; 297 | sourceTree = ""; 298 | }; 299 | EDED2296244E0777004503BB /* Logging */ = { 300 | isa = PBXGroup; 301 | children = ( 302 | EDED229D244E0807004503BB /* Logger.swift */, 303 | ); 304 | path = Logging; 305 | sourceTree = ""; 306 | }; 307 | EDED22A3244E08E4004503BB /* Cells */ = { 308 | isa = PBXGroup; 309 | children = ( 310 | EDED22A4244E08F2004503BB /* MainTableViewCellReactor.swift */, 311 | EDED22A6244E0900004503BB /* MainTableViewCell.swift */, 312 | EDED22D4244E0EF1004503BB /* SettingCell.swift */, 313 | EDED22D6244E0F0C004503BB /* SettingCellReactor.swift */, 314 | EDED22DE244E1025004503BB /* SettingSectionFooterView.swift */, 315 | EDED22E0244E1030004503BB /* SettingSectionFooterViewReactor.swift */, 316 | ); 317 | path = Cells; 318 | sourceTree = ""; 319 | }; 320 | EDED22AA244E094A004503BB /* Common */ = { 321 | isa = PBXGroup; 322 | children = ( 323 | EDED22AB244E0956004503BB /* Utillity.swift */, 324 | EDED22AD244E096E004503BB /* Constants.swift */, 325 | EDED22C2244E0A19004503BB /* GlobalColors.swift */, 326 | ED15C02D244EDB7A00483E20 /* UIColor.swift */, 327 | ); 328 | path = Common; 329 | sourceTree = ""; 330 | }; 331 | EDED22AF244E09B0004503BB /* Rx */ = { 332 | isa = PBXGroup; 333 | children = ( 334 | EDED22B0244E09BD004503BB /* UITableView+Rx.swift */, 335 | ); 336 | path = Rx; 337 | sourceTree = ""; 338 | }; 339 | EDED22B7244E09E6004503BB /* keyboard */ = { 340 | isa = PBXGroup; 341 | children = ( 342 | EDED22F9244E1463004503BB /* keyboard.entitlements */, 343 | EDED22F3244E1256004503BB /* Extensions */, 344 | EDED22E3244E1115004503BB /* Views */, 345 | EDED22E2244E10F8004503BB /* Sections */, 346 | EDED22EC244E11CA004503BB /* KeyboardViewReactor.swift */, 347 | EDED22B8244E09E6004503BB /* KeyboardViewController.swift */, 348 | EDED22BA244E09E6004503BB /* Info.plist */, 349 | EDED22EA244E11A9004503BB /* BaseInputViewController.swift */, 350 | ED0EFE16244ECBEF00608FDD /* InfoPlist.strings */, 351 | ); 352 | path = keyboard; 353 | sourceTree = ""; 354 | }; 355 | EDED22E2244E10F8004503BB /* Sections */ = { 356 | isa = PBXGroup; 357 | children = ( 358 | EDED22E8244E1191004503BB /* ListSection.swift */, 359 | ); 360 | path = Sections; 361 | sourceTree = ""; 362 | }; 363 | EDED22E3244E1115004503BB /* Views */ = { 364 | isa = PBXGroup; 365 | children = ( 366 | EDED22E4244E1168004503BB /* ListCellReactor.swift */, 367 | EDED22E6244E1171004503BB /* ListCell.swift */, 368 | ); 369 | path = Views; 370 | sourceTree = ""; 371 | }; 372 | EDED22F3244E1256004503BB /* Extensions */ = { 373 | isa = PBXGroup; 374 | children = ( 375 | EDED22F4244E126B004503BB /* UIButton.swift */, 376 | ); 377 | path = Extensions; 378 | sourceTree = ""; 379 | }; 380 | /* End PBXGroup section */ 381 | 382 | /* Begin PBXNativeTarget section */ 383 | ED8A016E244E03B100D0C796 /* reactorkitKeyboardExample */ = { 384 | isa = PBXNativeTarget; 385 | buildConfigurationList = ED8A0183244E03B300D0C796 /* Build configuration list for PBXNativeTarget "reactorkitKeyboardExample" */; 386 | buildPhases = ( 387 | 5F6A9D0E4A91C652AA66CB16 /* [CP] Check Pods Manifest.lock */, 388 | ED8A016B244E03B100D0C796 /* Sources */, 389 | ED8A016C244E03B100D0C796 /* Frameworks */, 390 | ED8A016D244E03B100D0C796 /* Resources */, 391 | B21E8754D643CAA2D1957BB5 /* [CP] Embed Pods Frameworks */, 392 | EDED22C1244E09E6004503BB /* Embed App Extensions */, 393 | ); 394 | buildRules = ( 395 | ); 396 | dependencies = ( 397 | EDED22BC244E09E6004503BB /* PBXTargetDependency */, 398 | ); 399 | name = reactorkitKeyboardExample; 400 | productName = reactorkitKeyboardExample; 401 | productReference = ED8A016F244E03B100D0C796 /* reactorkitKeyboardExample.app */; 402 | productType = "com.apple.product-type.application"; 403 | }; 404 | EDED22B5244E09E6004503BB /* keyboard */ = { 405 | isa = PBXNativeTarget; 406 | buildConfigurationList = EDED22BE244E09E6004503BB /* Build configuration list for PBXNativeTarget "keyboard" */; 407 | buildPhases = ( 408 | 479BFEF2E5A7E0CAFBAB9FFA /* [CP] Check Pods Manifest.lock */, 409 | EDED22B2244E09E6004503BB /* Sources */, 410 | EDED22B3244E09E6004503BB /* Frameworks */, 411 | EDED22B4244E09E6004503BB /* Resources */, 412 | ); 413 | buildRules = ( 414 | ); 415 | dependencies = ( 416 | ); 417 | name = keyboard; 418 | productName = keyboard; 419 | productReference = EDED22B6244E09E6004503BB /* keyboard.appex */; 420 | productType = "com.apple.product-type.app-extension"; 421 | }; 422 | /* End PBXNativeTarget section */ 423 | 424 | /* Begin PBXProject section */ 425 | ED8A0167244E03B100D0C796 /* Project object */ = { 426 | isa = PBXProject; 427 | attributes = { 428 | LastSwiftUpdateCheck = 1140; 429 | LastUpgradeCheck = 1130; 430 | ORGANIZATIONNAME = tmsae; 431 | TargetAttributes = { 432 | ED8A016E244E03B100D0C796 = { 433 | CreatedOnToolsVersion = 11.3.1; 434 | }; 435 | EDED22B5244E09E6004503BB = { 436 | CreatedOnToolsVersion = 11.4; 437 | }; 438 | }; 439 | }; 440 | buildConfigurationList = ED8A016A244E03B100D0C796 /* Build configuration list for PBXProject "reactorkitKeyboardExample" */; 441 | compatibilityVersion = "Xcode 9.3"; 442 | developmentRegion = en; 443 | hasScannedForEncodings = 0; 444 | knownRegions = ( 445 | en, 446 | Base, 447 | ko, 448 | ); 449 | mainGroup = ED8A0166244E03B100D0C796; 450 | productRefGroup = ED8A0170244E03B100D0C796 /* Products */; 451 | projectDirPath = ""; 452 | projectRoot = ""; 453 | targets = ( 454 | ED8A016E244E03B100D0C796 /* reactorkitKeyboardExample */, 455 | EDED22B5244E09E6004503BB /* keyboard */, 456 | ); 457 | }; 458 | /* End PBXProject section */ 459 | 460 | /* Begin PBXResourcesBuildPhase section */ 461 | ED8A016D244E03B100D0C796 /* Resources */ = { 462 | isa = PBXResourcesBuildPhase; 463 | buildActionMask = 2147483647; 464 | files = ( 465 | ED8A017F244E03B300D0C796 /* LaunchScreen.storyboard in Resources */, 466 | ED15C029244EDABD00483E20 /* InfoPlist.strings in Resources */, 467 | ED0EFE11244EC62D00608FDD /* Pods-reactorkitKeyboardExample-acknowledgements.plist in Resources */, 468 | EDED22CC244E0CD3004503BB /* Localizable.strings in Resources */, 469 | ED8A017C244E03B300D0C796 /* Assets.xcassets in Resources */, 470 | ED8A017A244E03B100D0C796 /* Main.storyboard in Resources */, 471 | ); 472 | runOnlyForDeploymentPostprocessing = 0; 473 | }; 474 | EDED22B4244E09E6004503BB /* Resources */ = { 475 | isa = PBXResourcesBuildPhase; 476 | buildActionMask = 2147483647; 477 | files = ( 478 | EDED22FA244E1504004503BB /* Assets.xcassets in Resources */, 479 | ED15C026244EDA9000483E20 /* Localizable.strings in Resources */, 480 | ED0EFE14244ECBEF00608FDD /* InfoPlist.strings in Resources */, 481 | ); 482 | runOnlyForDeploymentPostprocessing = 0; 483 | }; 484 | /* End PBXResourcesBuildPhase section */ 485 | 486 | /* Begin PBXShellScriptBuildPhase section */ 487 | 479BFEF2E5A7E0CAFBAB9FFA /* [CP] Check Pods Manifest.lock */ = { 488 | isa = PBXShellScriptBuildPhase; 489 | buildActionMask = 2147483647; 490 | files = ( 491 | ); 492 | inputFileListPaths = ( 493 | ); 494 | inputPaths = ( 495 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 496 | "${PODS_ROOT}/Manifest.lock", 497 | ); 498 | name = "[CP] Check Pods Manifest.lock"; 499 | outputFileListPaths = ( 500 | ); 501 | outputPaths = ( 502 | "$(DERIVED_FILE_DIR)/Pods-keyboard-checkManifestLockResult.txt", 503 | ); 504 | runOnlyForDeploymentPostprocessing = 0; 505 | shellPath = /bin/sh; 506 | 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"; 507 | showEnvVarsInLog = 0; 508 | }; 509 | 5F6A9D0E4A91C652AA66CB16 /* [CP] Check Pods Manifest.lock */ = { 510 | isa = PBXShellScriptBuildPhase; 511 | buildActionMask = 2147483647; 512 | files = ( 513 | ); 514 | inputFileListPaths = ( 515 | ); 516 | inputPaths = ( 517 | "${PODS_PODFILE_DIR_PATH}/Podfile.lock", 518 | "${PODS_ROOT}/Manifest.lock", 519 | ); 520 | name = "[CP] Check Pods Manifest.lock"; 521 | outputFileListPaths = ( 522 | ); 523 | outputPaths = ( 524 | "$(DERIVED_FILE_DIR)/Pods-reactorkitKeyboardExample-checkManifestLockResult.txt", 525 | ); 526 | runOnlyForDeploymentPostprocessing = 0; 527 | shellPath = /bin/sh; 528 | 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"; 529 | showEnvVarsInLog = 0; 530 | }; 531 | B21E8754D643CAA2D1957BB5 /* [CP] Embed Pods Frameworks */ = { 532 | isa = PBXShellScriptBuildPhase; 533 | buildActionMask = 2147483647; 534 | files = ( 535 | ); 536 | inputFileListPaths = ( 537 | "${PODS_ROOT}/Target Support Files/Pods-reactorkitKeyboardExample/Pods-reactorkitKeyboardExample-frameworks-${CONFIGURATION}-input-files.xcfilelist", 538 | ); 539 | name = "[CP] Embed Pods Frameworks"; 540 | outputFileListPaths = ( 541 | "${PODS_ROOT}/Target Support Files/Pods-reactorkitKeyboardExample/Pods-reactorkitKeyboardExample-frameworks-${CONFIGURATION}-output-files.xcfilelist", 542 | ); 543 | runOnlyForDeploymentPostprocessing = 0; 544 | shellPath = /bin/sh; 545 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-reactorkitKeyboardExample/Pods-reactorkitKeyboardExample-frameworks.sh\"\n"; 546 | showEnvVarsInLog = 0; 547 | }; 548 | /* End PBXShellScriptBuildPhase section */ 549 | 550 | /* Begin PBXSourcesBuildPhase section */ 551 | ED8A016B244E03B100D0C796 /* Sources */ = { 552 | isa = PBXSourcesBuildPhase; 553 | buildActionMask = 2147483647; 554 | files = ( 555 | EDED22C7244E0B38004503BB /* WriteViewReactor.swift in Sources */, 556 | EDED229A244E07C7004503BB /* BaseNavigationController.swift in Sources */, 557 | EDED22B1244E09BD004503BB /* UITableView+Rx.swift in Sources */, 558 | EDED22A9244E0926004503BB /* MainSection.swift in Sources */, 559 | EDED229E244E0807004503BB /* Logger.swift in Sources */, 560 | EDED22D3244E0E3A004503BB /* SettingsViewController.swift in Sources */, 561 | ED8A0177244E03B100D0C796 /* ViewController.swift in Sources */, 562 | ED8A0173244E03B100D0C796 /* AppDelegate.swift in Sources */, 563 | EDED22AC244E0956004503BB /* Utillity.swift in Sources */, 564 | EDED22DD244E0FC1004503BB /* SettingFooterType.swift in Sources */, 565 | EDED22DB244E0F4E004503BB /* SettingSection.swift in Sources */, 566 | EDED22C3244E0A19004503BB /* GlobalColors.swift in Sources */, 567 | EDED22A7244E0900004503BB /* MainTableViewCell.swift in Sources */, 568 | EDED22E1244E1030004503BB /* SettingSectionFooterViewReactor.swift in Sources */, 569 | EDED22AE244E096E004503BB /* Constants.swift in Sources */, 570 | EDED22DF244E1025004503BB /* SettingSectionFooterView.swift in Sources */, 571 | EDED22A0244E08A4004503BB /* MainViewReactor.swift in Sources */, 572 | EDED22D9244E0F30004503BB /* SettingMenuTypes.swift in Sources */, 573 | EDED22D1244E0E2A004503BB /* SettingsViewReactor.swift in Sources */, 574 | EDED229C244E07E5004503BB /* BaseTableViewCell.swift in Sources */, 575 | EDED22A5244E08F2004503BB /* MainTableViewCellReactor.swift in Sources */, 576 | EDED2298244E079A004503BB /* BaseViewController.swift in Sources */, 577 | ED15C02E244EDB7A00483E20 /* UIColor.swift in Sources */, 578 | EDED22C5244E0B28004503BB /* WriteViewController.swift in Sources */, 579 | EDED22D5244E0EF1004503BB /* SettingCell.swift in Sources */, 580 | EDED22D7244E0F0C004503BB /* SettingCellReactor.swift in Sources */, 581 | EDED22A2244E08C0004503BB /* MainViewController.swift in Sources */, 582 | ); 583 | runOnlyForDeploymentPostprocessing = 0; 584 | }; 585 | EDED22B2244E09E6004503BB /* Sources */ = { 586 | isa = PBXSourcesBuildPhase; 587 | buildActionMask = 2147483647; 588 | files = ( 589 | EDED22F0244E1218004503BB /* BaseTableViewCell.swift in Sources */, 590 | EDED22EB244E11A9004503BB /* BaseInputViewController.swift in Sources */, 591 | EDED22E7244E1171004503BB /* ListCell.swift in Sources */, 592 | EDED22E9244E1191004503BB /* ListSection.swift in Sources */, 593 | EDED22E5244E1168004503BB /* ListCellReactor.swift in Sources */, 594 | EDED22F2244E1223004503BB /* Constants.swift in Sources */, 595 | EDED22ED244E11CA004503BB /* KeyboardViewReactor.swift in Sources */, 596 | EDED22F6244E127E004503BB /* Logger.swift in Sources */, 597 | EDED22F5244E126B004503BB /* UIButton.swift in Sources */, 598 | ED15C02F244EDBA700483E20 /* UIColor.swift in Sources */, 599 | EDED22F7244E12C1004503BB /* UITableView+Rx.swift in Sources */, 600 | EDED22F1244E1220004503BB /* GlobalColors.swift in Sources */, 601 | EDED22B9244E09E6004503BB /* KeyboardViewController.swift in Sources */, 602 | ); 603 | runOnlyForDeploymentPostprocessing = 0; 604 | }; 605 | /* End PBXSourcesBuildPhase section */ 606 | 607 | /* Begin PBXTargetDependency section */ 608 | EDED22BC244E09E6004503BB /* PBXTargetDependency */ = { 609 | isa = PBXTargetDependency; 610 | target = EDED22B5244E09E6004503BB /* keyboard */; 611 | targetProxy = EDED22BB244E09E6004503BB /* PBXContainerItemProxy */; 612 | }; 613 | /* End PBXTargetDependency section */ 614 | 615 | /* Begin PBXVariantGroup section */ 616 | ED0EFE16244ECBEF00608FDD /* InfoPlist.strings */ = { 617 | isa = PBXVariantGroup; 618 | children = ( 619 | ED0EFE15244ECBEF00608FDD /* en */, 620 | ED0EFE17244ECBF000608FDD /* ko */, 621 | ); 622 | name = InfoPlist.strings; 623 | sourceTree = ""; 624 | }; 625 | ED15C02B244EDABD00483E20 /* InfoPlist.strings */ = { 626 | isa = PBXVariantGroup; 627 | children = ( 628 | ED15C02A244EDABD00483E20 /* en */, 629 | ED15C02C244EDABE00483E20 /* ko */, 630 | ); 631 | name = InfoPlist.strings; 632 | sourceTree = ""; 633 | }; 634 | ED8A0178244E03B100D0C796 /* Main.storyboard */ = { 635 | isa = PBXVariantGroup; 636 | children = ( 637 | ED8A0179244E03B100D0C796 /* Base */, 638 | EDED22C8244E0C6A004503BB /* ko */, 639 | ); 640 | name = Main.storyboard; 641 | sourceTree = ""; 642 | }; 643 | ED8A017D244E03B300D0C796 /* LaunchScreen.storyboard */ = { 644 | isa = PBXVariantGroup; 645 | children = ( 646 | ED8A017E244E03B300D0C796 /* Base */, 647 | EDED22C9244E0C6A004503BB /* ko */, 648 | ); 649 | name = LaunchScreen.storyboard; 650 | sourceTree = ""; 651 | }; 652 | EDED22CE244E0CD3004503BB /* Localizable.strings */ = { 653 | isa = PBXVariantGroup; 654 | children = ( 655 | EDED22CD244E0CD3004503BB /* en */, 656 | EDED22CF244E0CD4004503BB /* ko */, 657 | ); 658 | name = Localizable.strings; 659 | sourceTree = ""; 660 | }; 661 | /* End PBXVariantGroup section */ 662 | 663 | /* Begin XCBuildConfiguration section */ 664 | ED8A0181244E03B300D0C796 /* Debug */ = { 665 | isa = XCBuildConfiguration; 666 | buildSettings = { 667 | ALWAYS_SEARCH_USER_PATHS = NO; 668 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 669 | CLANG_ANALYZER_NONNULL = YES; 670 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 671 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 672 | CLANG_CXX_LIBRARY = "libc++"; 673 | CLANG_ENABLE_MODULES = YES; 674 | CLANG_ENABLE_OBJC_ARC = YES; 675 | CLANG_ENABLE_OBJC_WEAK = YES; 676 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 677 | CLANG_WARN_BOOL_CONVERSION = YES; 678 | CLANG_WARN_COMMA = YES; 679 | CLANG_WARN_CONSTANT_CONVERSION = YES; 680 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 681 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 682 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 683 | CLANG_WARN_EMPTY_BODY = YES; 684 | CLANG_WARN_ENUM_CONVERSION = YES; 685 | CLANG_WARN_INFINITE_RECURSION = YES; 686 | CLANG_WARN_INT_CONVERSION = YES; 687 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 688 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 689 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 690 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 691 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 692 | CLANG_WARN_STRICT_PROTOTYPES = YES; 693 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 694 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 695 | CLANG_WARN_UNREACHABLE_CODE = YES; 696 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 697 | COPY_PHASE_STRIP = NO; 698 | DEBUG_INFORMATION_FORMAT = dwarf; 699 | ENABLE_STRICT_OBJC_MSGSEND = YES; 700 | ENABLE_TESTABILITY = YES; 701 | GCC_C_LANGUAGE_STANDARD = gnu11; 702 | GCC_DYNAMIC_NO_PIC = NO; 703 | GCC_NO_COMMON_BLOCKS = YES; 704 | GCC_OPTIMIZATION_LEVEL = 0; 705 | GCC_PREPROCESSOR_DEFINITIONS = ( 706 | "DEBUG=1", 707 | "$(inherited)", 708 | ); 709 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 710 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 711 | GCC_WARN_UNDECLARED_SELECTOR = YES; 712 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 713 | GCC_WARN_UNUSED_FUNCTION = YES; 714 | GCC_WARN_UNUSED_VARIABLE = YES; 715 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 716 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 717 | MTL_FAST_MATH = YES; 718 | ONLY_ACTIVE_ARCH = YES; 719 | SDKROOT = iphoneos; 720 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 721 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 722 | }; 723 | name = Debug; 724 | }; 725 | ED8A0182244E03B300D0C796 /* Release */ = { 726 | isa = XCBuildConfiguration; 727 | buildSettings = { 728 | ALWAYS_SEARCH_USER_PATHS = NO; 729 | CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES; 730 | CLANG_ANALYZER_NONNULL = YES; 731 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 732 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 733 | CLANG_CXX_LIBRARY = "libc++"; 734 | CLANG_ENABLE_MODULES = YES; 735 | CLANG_ENABLE_OBJC_ARC = YES; 736 | CLANG_ENABLE_OBJC_WEAK = YES; 737 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 738 | CLANG_WARN_BOOL_CONVERSION = YES; 739 | CLANG_WARN_COMMA = YES; 740 | CLANG_WARN_CONSTANT_CONVERSION = YES; 741 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 742 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 743 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 744 | CLANG_WARN_EMPTY_BODY = YES; 745 | CLANG_WARN_ENUM_CONVERSION = YES; 746 | CLANG_WARN_INFINITE_RECURSION = YES; 747 | CLANG_WARN_INT_CONVERSION = YES; 748 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 749 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 750 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 751 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 752 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 753 | CLANG_WARN_STRICT_PROTOTYPES = YES; 754 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 755 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 756 | CLANG_WARN_UNREACHABLE_CODE = YES; 757 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 758 | COPY_PHASE_STRIP = NO; 759 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 760 | ENABLE_NS_ASSERTIONS = NO; 761 | ENABLE_STRICT_OBJC_MSGSEND = YES; 762 | GCC_C_LANGUAGE_STANDARD = gnu11; 763 | GCC_NO_COMMON_BLOCKS = YES; 764 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 765 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 766 | GCC_WARN_UNDECLARED_SELECTOR = YES; 767 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 768 | GCC_WARN_UNUSED_FUNCTION = YES; 769 | GCC_WARN_UNUSED_VARIABLE = YES; 770 | IPHONEOS_DEPLOYMENT_TARGET = 11.0; 771 | MTL_ENABLE_DEBUG_INFO = NO; 772 | MTL_FAST_MATH = YES; 773 | SDKROOT = iphoneos; 774 | SWIFT_COMPILATION_MODE = wholemodule; 775 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 776 | VALIDATE_PRODUCT = YES; 777 | }; 778 | name = Release; 779 | }; 780 | ED8A0184244E03B300D0C796 /* Debug */ = { 781 | isa = XCBuildConfiguration; 782 | baseConfigurationReference = AA0AB43F661D32464E4E23C4 /* Pods-reactorkitKeyboardExample.debug.xcconfig */; 783 | buildSettings = { 784 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 785 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 786 | CODE_SIGN_ENTITLEMENTS = reactorkitKeyboardExample/reactorkitKeyboardExample.entitlements; 787 | CODE_SIGN_STYLE = Automatic; 788 | DEVELOPMENT_TEAM = U6GQL8JQMT; 789 | INFOPLIST_FILE = reactorkitKeyboardExample/Info.plist; 790 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 791 | LD_RUNPATH_SEARCH_PATHS = ( 792 | "$(inherited)", 793 | "@executable_path/Frameworks", 794 | ); 795 | PRODUCT_BUNDLE_IDENTIFIER = com.tmsae.reactorkitKeyboardExample; 796 | PRODUCT_NAME = "$(TARGET_NAME)"; 797 | SWIFT_VERSION = 5.0; 798 | TARGETED_DEVICE_FAMILY = "1,2"; 799 | }; 800 | name = Debug; 801 | }; 802 | ED8A0185244E03B300D0C796 /* Release */ = { 803 | isa = XCBuildConfiguration; 804 | baseConfigurationReference = 2038FF17147E219B043ABEAB /* Pods-reactorkitKeyboardExample.release.xcconfig */; 805 | buildSettings = { 806 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 807 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 808 | CODE_SIGN_ENTITLEMENTS = reactorkitKeyboardExample/reactorkitKeyboardExample.entitlements; 809 | CODE_SIGN_STYLE = Automatic; 810 | DEVELOPMENT_TEAM = U6GQL8JQMT; 811 | INFOPLIST_FILE = reactorkitKeyboardExample/Info.plist; 812 | IPHONEOS_DEPLOYMENT_TARGET = 12.0; 813 | LD_RUNPATH_SEARCH_PATHS = ( 814 | "$(inherited)", 815 | "@executable_path/Frameworks", 816 | ); 817 | PRODUCT_BUNDLE_IDENTIFIER = com.tmsae.reactorkitKeyboardExample; 818 | PRODUCT_NAME = "$(TARGET_NAME)"; 819 | SWIFT_VERSION = 5.0; 820 | TARGETED_DEVICE_FAMILY = "1,2"; 821 | }; 822 | name = Release; 823 | }; 824 | EDED22BF244E09E6004503BB /* Debug */ = { 825 | isa = XCBuildConfiguration; 826 | baseConfigurationReference = 6BC67B005AA82AFD5DBBB4B5 /* Pods-keyboard.debug.xcconfig */; 827 | buildSettings = { 828 | CODE_SIGN_ENTITLEMENTS = keyboard/keyboard.entitlements; 829 | CODE_SIGN_STYLE = Automatic; 830 | DEVELOPMENT_TEAM = U6GQL8JQMT; 831 | INFOPLIST_FILE = keyboard/Info.plist; 832 | IPHONEOS_DEPLOYMENT_TARGET = 13.4; 833 | LD_RUNPATH_SEARCH_PATHS = ( 834 | "$(inherited)", 835 | "@executable_path/Frameworks", 836 | "@executable_path/../../Frameworks", 837 | ); 838 | PRODUCT_BUNDLE_IDENTIFIER = com.tmsae.reactorkitKeyboardExample.keyboard; 839 | PRODUCT_NAME = "$(TARGET_NAME)"; 840 | SKIP_INSTALL = YES; 841 | SWIFT_VERSION = 5.0; 842 | TARGETED_DEVICE_FAMILY = "1,2"; 843 | }; 844 | name = Debug; 845 | }; 846 | EDED22C0244E09E6004503BB /* Release */ = { 847 | isa = XCBuildConfiguration; 848 | baseConfigurationReference = 6AFD01CE0785D20809E99E2B /* Pods-keyboard.release.xcconfig */; 849 | buildSettings = { 850 | CODE_SIGN_ENTITLEMENTS = keyboard/keyboard.entitlements; 851 | CODE_SIGN_STYLE = Automatic; 852 | DEVELOPMENT_TEAM = U6GQL8JQMT; 853 | INFOPLIST_FILE = keyboard/Info.plist; 854 | IPHONEOS_DEPLOYMENT_TARGET = 13.4; 855 | LD_RUNPATH_SEARCH_PATHS = ( 856 | "$(inherited)", 857 | "@executable_path/Frameworks", 858 | "@executable_path/../../Frameworks", 859 | ); 860 | PRODUCT_BUNDLE_IDENTIFIER = com.tmsae.reactorkitKeyboardExample.keyboard; 861 | PRODUCT_NAME = "$(TARGET_NAME)"; 862 | SKIP_INSTALL = YES; 863 | SWIFT_VERSION = 5.0; 864 | TARGETED_DEVICE_FAMILY = "1,2"; 865 | }; 866 | name = Release; 867 | }; 868 | /* End XCBuildConfiguration section */ 869 | 870 | /* Begin XCConfigurationList section */ 871 | ED8A016A244E03B100D0C796 /* Build configuration list for PBXProject "reactorkitKeyboardExample" */ = { 872 | isa = XCConfigurationList; 873 | buildConfigurations = ( 874 | ED8A0181244E03B300D0C796 /* Debug */, 875 | ED8A0182244E03B300D0C796 /* Release */, 876 | ); 877 | defaultConfigurationIsVisible = 0; 878 | defaultConfigurationName = Release; 879 | }; 880 | ED8A0183244E03B300D0C796 /* Build configuration list for PBXNativeTarget "reactorkitKeyboardExample" */ = { 881 | isa = XCConfigurationList; 882 | buildConfigurations = ( 883 | ED8A0184244E03B300D0C796 /* Debug */, 884 | ED8A0185244E03B300D0C796 /* Release */, 885 | ); 886 | defaultConfigurationIsVisible = 0; 887 | defaultConfigurationName = Release; 888 | }; 889 | EDED22BE244E09E6004503BB /* Build configuration list for PBXNativeTarget "keyboard" */ = { 890 | isa = XCConfigurationList; 891 | buildConfigurations = ( 892 | EDED22BF244E09E6004503BB /* Debug */, 893 | EDED22C0244E09E6004503BB /* Release */, 894 | ); 895 | defaultConfigurationIsVisible = 0; 896 | defaultConfigurationName = Release; 897 | }; 898 | /* End XCConfigurationList section */ 899 | }; 900 | rootObject = ED8A0167244E03B100D0C796 /* Project object */; 901 | } 902 | -------------------------------------------------------------------------------- /reactorkitKeyboardExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /reactorkitKeyboardExample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /reactorkitKeyboardExample.xcodeproj/project.xcworkspace/xcuserdata/fernando.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techinpark/reactorkit-keyboard-example/26d215b5832750fb5c347dca2d7ac3a954b279f0/reactorkitKeyboardExample.xcodeproj/project.xcworkspace/xcuserdata/fernando.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /reactorkitKeyboardExample.xcodeproj/xcshareddata/xcschemes/keyboard.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 6 | 9 | 10 | 16 | 22 | 23 | 24 | 30 | 36 | 37 | 38 | 39 | 40 | 45 | 46 | 47 | 48 | 60 | 64 | 65 | 66 | 72 | 73 | 74 | 75 | 82 | 84 | 90 | 91 | 92 | 93 | 95 | 96 | 99 | 100 | 101 | -------------------------------------------------------------------------------- /reactorkitKeyboardExample.xcodeproj/xcshareddata/xcschemes/reactorkitKeyboardExample.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 32 | 33 | 43 | 45 | 51 | 52 | 53 | 54 | 60 | 62 | 68 | 69 | 70 | 71 | 73 | 74 | 77 | 78 | 79 | -------------------------------------------------------------------------------- /reactorkitKeyboardExample.xcodeproj/xcuserdata/fernando.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | keyboard.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 15 11 | 12 | reactorkitKeyboardExample.xcscheme_^#shared#^_ 13 | 14 | orderHint 15 | 16 16 | 17 | 18 | SuppressBuildableAutocreation 19 | 20 | ED8A016E244E03B100D0C796 21 | 22 | primary 23 | 24 | 25 | EDED22B5244E09E6004503BB 26 | 27 | primary 28 | 29 | 30 | 31 | 32 | 33 | -------------------------------------------------------------------------------- /reactorkitKeyboardExample.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /reactorkitKeyboardExample.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /reactorkitKeyboardExample.xcworkspace/xcuserdata/fernando.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techinpark/reactorkit-keyboard-example/26d215b5832750fb5c347dca2d7ac3a954b279f0/reactorkitKeyboardExample.xcworkspace/xcuserdata/fernando.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /reactorkitKeyboardExample.xcworkspace/xcuserdata/fernando.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 9 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /reactorkitKeyboardExample/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // reactorkitKeyboardExample 4 | // 5 | // Created by Fernando on 2020/04/21. 6 | // Copyright © 2020 tmsae. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | @UIApplicationMain 12 | class AppDelegate: UIResponder, UIApplicationDelegate { 13 | 14 | var window: UIWindow? 15 | 16 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 17 | // Override point for customization after application launch. 18 | 19 | 20 | let reactor = MainViewReactor() 21 | let controller = MainViewController(reactor: reactor) 22 | let navigationController = BaseNavigationController(rootViewController: controller) 23 | 24 | window?.rootViewController = navigationController 25 | window?.makeKeyAndVisible() 26 | 27 | return true 28 | } 29 | 30 | func applicationWillResignActive(_ application: UIApplication) { 31 | // 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. 32 | // Use this method to pause ongoing tasks, disable timers, and invalidate graphics rendering callbacks. Games should use this method to pause the game. 33 | } 34 | 35 | func applicationDidEnterBackground(_ application: UIApplication) { 36 | // 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. 37 | // If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits. 38 | } 39 | 40 | func applicationWillEnterForeground(_ application: UIApplication) { 41 | // 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. 42 | } 43 | 44 | func applicationDidBecomeActive(_ application: UIApplication) { 45 | // 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. 46 | } 47 | 48 | func applicationWillTerminate(_ application: UIApplication) { 49 | // Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:. 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /reactorkitKeyboardExample/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 | -------------------------------------------------------------------------------- /reactorkitKeyboardExample/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 | -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Base/BaseNavigationController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BaseNavigationController.swift 3 | // reactorkitKeyboardExample 4 | // 5 | // Created by Fernando on 2020/04/21. 6 | // Copyright © 2020 tmsae. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class BaseNavigationController: UINavigationController { 12 | 13 | lazy private(set) var className: String = { 14 | return type(of: self).description().components(separatedBy: ".").last ?? "" 15 | }() 16 | 17 | // MARK: - Properties 18 | 19 | // MARK: - Initialize 20 | 21 | // MARK: Rx 22 | 23 | // MARK: - Life Cycle 24 | 25 | override func viewDidLoad() { 26 | super.viewDidLoad() 27 | self.navigationBar.prefersLargeTitles = true 28 | self.navigationBar.largeTitleTextAttributes = [ 29 | NSAttributedString.Key.foregroundColor: Color.title 30 | ] 31 | } 32 | 33 | deinit { 34 | logger.verbose("DEINIT: \(self.className)") 35 | } 36 | 37 | // MARK: - Layout Constraints 38 | 39 | // MARK: - Configure 40 | } 41 | -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Base/BaseTableViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BaseTableViewCell.swift 3 | // reactorkitKeyboardExample 4 | // 5 | // Created by Fernando on 2020/04/21. 6 | // Copyright © 2020 tmsae. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import RxSwift 11 | 12 | class BaseTableViewCell: UITableViewCell { 13 | 14 | private(set) var didSetupConstraints = false 15 | 16 | var disposeBag = DisposeBag() 17 | 18 | override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { 19 | super.init(style: style, reuseIdentifier: reuseIdentifier) 20 | self.addViews() 21 | self.layoutSubviews() 22 | } 23 | 24 | required init?(coder aDecoder: NSCoder) { 25 | fatalError("init(coder:) has not been implemented") 26 | } 27 | 28 | override func layoutSubviews() { 29 | if !self.didSetupConstraints { 30 | self.setupConstraints() 31 | self.didSetupConstraints = true 32 | } 33 | super.layoutSubviews() 34 | } 35 | 36 | func addViews() {} 37 | 38 | func setupConstraints() {} 39 | } 40 | -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Base/BaseViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BaseViewController.swift 3 | // reactorkitKeyboardExample 4 | // 5 | // Created by Fernando on 2020/04/21. 6 | // Copyright © 2020 tmsae. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | import SnapKit 12 | import Then 13 | import ReusableKit 14 | import RxSwift 15 | import RxCocoa 16 | 17 | class BaseViewController: UIViewController { 18 | 19 | override var supportedInterfaceOrientations: UIInterfaceOrientationMask { 20 | return .portrait 21 | } 22 | 23 | override var shouldAutorotate: Bool { 24 | return true 25 | } 26 | 27 | override var prefersStatusBarHidden: Bool { 28 | return false 29 | } 30 | 31 | override var preferredStatusBarStyle: UIStatusBarStyle { 32 | return .lightContent 33 | } 34 | 35 | var disposeBag = DisposeBag() 36 | 37 | lazy private(set) var className: String = { 38 | return type(of: self).description().components(separatedBy: ".").last ?? "" 39 | }() 40 | 41 | /// There is a bug when trying to go back to previous view controller in a navigation controller 42 | /// on iOS 11, a scroll view in the previous screen scrolls weirdly. In order to get this fixed, 43 | /// we have to set the scrollView's `contentInsetAdjustmentBehavior` property to `.never` on 44 | /// `viewWillAppear()` and set back to the original value on `viewDidAppear()`. 45 | private var scrollViewOriginalContentInsetAdjustmentBehaviorRawValue: Int? 46 | 47 | init() { 48 | super.init(nibName: nil, bundle: nil) 49 | } 50 | 51 | required convenience init?(coder aDecoder: NSCoder) { 52 | self.init() 53 | } 54 | 55 | deinit { 56 | print("DEINIT: \(self.className)") 57 | } 58 | 59 | var safeAreaInsets: UIEdgeInsets { 60 | get { 61 | if #available(iOS 11.0, *) { 62 | guard let window = UIApplication.shared.windows.first else { return self.view.safeAreaInsets } 63 | return window.safeAreaInsets 64 | } else { 65 | return .zero 66 | } 67 | } 68 | } 69 | 70 | // MARK: View Lifecycle 71 | override func viewDidLoad() { 72 | super.viewDidLoad() 73 | self.view.backgroundColor = .white 74 | 75 | self.addViews() 76 | self.view.setNeedsUpdateConstraints() 77 | 78 | } 79 | 80 | override func viewWillAppear(_ animated: Bool) { 81 | super.viewWillAppear(animated) 82 | } 83 | 84 | override func viewDidAppear(_ animated: Bool) { 85 | super.viewDidAppear(animated) 86 | } 87 | 88 | // MARK: add Views,Layout Constraints 89 | private(set) var didSetupConstraints = false 90 | 91 | override func updateViewConstraints() { 92 | if !self.didSetupConstraints { 93 | self.setupConstraints() 94 | self.didSetupConstraints = true 95 | } 96 | super.updateViewConstraints() 97 | } 98 | 99 | func addViews() {} 100 | 101 | func setupConstraints() { 102 | // Override point 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Common/Constants.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Constants.swift 3 | // reactorkitKeyboardExample 4 | // 5 | // Created by Fernando on 2020/04/21. 6 | // Copyright © 2020 tmsae. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | enum Constants { 12 | enum AppConfig { 13 | static let groupID = "group.com.tmsae.keyboardExample" 14 | static let oldUserDefaultKey = "kWords" 15 | } 16 | 17 | enum ETC { 18 | static let repoURL = "https://github.com/techinpark/reactorkit-keyboard-example" 19 | } 20 | } 21 | 22 | 23 | // MARK: Web Links 24 | enum WebLinks { 25 | static let keyboardUseageURL = "https://www.notion.so/3024c4472caf459b97eb1b90fce49a26" 26 | static let keyboardUsageEngURL = "https://www.notion.so/How-to-use-PasteKeyboard-17694e8ade094b61ac9a2c5c2e6b43db" 27 | static let updateLogsURL = "" 28 | static let paymentPageForKor = "http://tmsae.kr/payment/donate.html" 29 | static let kakaoChat = "https://open.kakao.com/o/sWy6qMYb" 30 | static let SurveyFormGoogle = "https://forms.gle/28q5b7JMgXEwEeGt7" 31 | static let paymentPageForGlobal = "https://www.buymeacoffee.com/n93O2fZ" 32 | } 33 | -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Common/GlobalColors.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GlobalColors.swift 3 | // reactorkitKeyboardExample 4 | // 5 | // Created by Fernando on 2020/04/21. 6 | // Copyright © 2020 tmsae. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | struct Color { 12 | static let title: UIColor = { 13 | if #available(iOS 13, *) { 14 | return UIColor { (UITraitCollection: UITraitCollection) -> UIColor in 15 | if UITraitCollection.userInterfaceStyle == .dark { 16 | // Return color for Dark Mode 17 | return .white 18 | } else { 19 | // Return color for Light Mode 20 | return .black 21 | } 22 | } 23 | } else { 24 | // Return fallback color for iOS 12 and lower 25 | return .black 26 | } 27 | }() 28 | 29 | static let description: UIColor = { 30 | if #available(iOS 13, *) { 31 | return UIColor { (UITraitCollection: UITraitCollection) -> UIColor in 32 | if UITraitCollection.userInterfaceStyle == .dark { 33 | // Return color for Dark Mode 34 | return .gray 35 | } else { 36 | // Return color for Light Mode 37 | return .gray 38 | } 39 | } 40 | } else { 41 | // Return fallback color for iOS 12 and lower 42 | return .gray 43 | } 44 | }() 45 | 46 | 47 | 48 | 49 | static let background: UIColor = { 50 | if #available(iOS 13, *) { 51 | return UIColor { (UITraitCollection: UITraitCollection) -> UIColor in 52 | if UITraitCollection.userInterfaceStyle == .dark { 53 | // Return color for Dark Mode 54 | return .black 55 | } else { 56 | // Return color for Light Mode 57 | return .white 58 | } 59 | } 60 | } else { 61 | // Return fallback color for iOS 12 and lower 62 | return .white 63 | } 64 | }() 65 | 66 | static let textColor: UIColor = { 67 | if #available(iOS 13, *) { 68 | return UIColor { (UITraitCollection: UITraitCollection) -> UIColor in 69 | if UITraitCollection.userInterfaceStyle == .dark { 70 | // Return color for Dark Mode 71 | return .white 72 | } else { 73 | // Return color for Light Mode 74 | return .black 75 | } 76 | } 77 | } else { 78 | // Return fallback color for iOS 12 and lower 79 | return .black 80 | } 81 | }() 82 | 83 | static let red: UIColor = { 84 | if #available(iOS 13, *) { 85 | return UIColor { (UITraitCollection: UITraitCollection) -> UIColor in 86 | if UITraitCollection.userInterfaceStyle == .dark { 87 | // Return color for Dark Mode 88 | return UIColor.init(red: 248, green: 44, blue: 84, alpha: 1.0) 89 | } else { 90 | // Return color for Light Mode 91 | return UIColor.init(red: 248, green: 7, blue: 63, alpha: 1.0) 92 | } 93 | } 94 | } else { 95 | // Return fallback color for iOS 12 and lower 96 | return UIColor.init(red: 248, green: 7, blue: 63, alpha: 1.0) 97 | } 98 | }() 99 | 100 | static let keySpaceBackground: UIColor = { 101 | if #available(iOS 13, *) { 102 | return UIColor { (UITraitCollection: UITraitCollection) -> UIColor in 103 | if UITraitCollection.userInterfaceStyle == .dark { 104 | // Return color for Dark Mode 105 | return UIColor(red: 106, green: 106, blue: 106) 106 | } else { 107 | // Return color for Light Mode 108 | return .white 109 | } 110 | } 111 | } else { 112 | // Return fallback color for iOS 12 and lower 113 | return .white 114 | } 115 | }() 116 | 117 | static let keyBackground: UIColor = { 118 | if #available(iOS 13, *) { 119 | return UIColor { (UITraitCollection: UITraitCollection) -> UIColor in 120 | if UITraitCollection.userInterfaceStyle == .dark { 121 | // Return color for Dark Mode 122 | return UIColor(red: 71, green: 71, blue: 71) 123 | } else { 124 | // Return color for Light Mode 125 | return UIColor(red: 180, green: 184, blue: 192) 126 | } 127 | } 128 | } else { 129 | // Return fallback color for iOS 12 and lower 130 | return UIColor(red: 180, green: 184, blue: 192) 131 | } 132 | }() 133 | 134 | static let keyboardBackground: UIColor = { 135 | if #available(iOS 13, *) { 136 | return UIColor { (UITraitCollection: UITraitCollection) -> UIColor in 137 | if UITraitCollection.userInterfaceStyle == .dark { 138 | // Return color for Dark Mode 139 | return UIColor(red: 43, green: 43, blue: 43) 140 | } else { 141 | // Return color for Light Mode 142 | return UIColor(red: 214, green: 216, blue: 221) 143 | } 144 | } 145 | } else { 146 | // Return fallback color for iOS 12 and lower 147 | return UIColor(red: 214, green: 216, blue: 221) 148 | } 149 | }() 150 | 151 | static let keyboardListBackground: UIColor = { 152 | if #available(iOS 13, *) { 153 | return UIColor { (UITraitCollection: UITraitCollection) -> UIColor in 154 | if UITraitCollection.userInterfaceStyle == .dark { 155 | // Return color for Dark Mode 156 | return UIColor(red: 43, green: 43, blue: 43) 157 | } else { 158 | // Return color for Light Mode 159 | return .white 160 | } 161 | } 162 | } else { 163 | // Return fallback color for iOS 12 and lower 164 | return .white 165 | } 166 | }() 167 | } 168 | -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Common/UIColor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UIColor.swift 3 | // reactorkitKeyboardExample 4 | // 5 | // Created by Fernando on 2020/04/21. 6 | // Copyright © 2020 tmsae. All rights reserved. 7 | // 8 | import UIKit 9 | 10 | extension UIColor { 11 | convenience init(red: Int, green: Int, blue: Int) { 12 | assert(red >= 0 && red <= 255, "Invalid red component") 13 | assert(green >= 0 && green <= 255, "Invalid green component") 14 | assert(blue >= 0 && blue <= 255, "Invalid blue component") 15 | self.init(red: CGFloat(red) / 255.0, green: CGFloat(green) / 255.0, blue: CGFloat(blue) / 255.0, alpha: 1.0) 16 | } 17 | 18 | convenience init(netHex:Int) { 19 | self.init(red:(netHex >> 16) & 0xff, green:(netHex >> 8) & 0xff, blue:netHex & 0xff) 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Common/Utillity.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Utillity.swift 3 | // reactorkitKeyboardExample 4 | // 5 | // Created by Fernando on 2020/04/21. 6 | // Copyright © 2020 tmsae. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import UIKit 11 | 12 | class Utility { 13 | 14 | static let shared = Utility() 15 | private let userDefaults = UserDefaults.standard 16 | 17 | func getWords() -> [String] { 18 | guard let userDefault = UserDefaults(suiteName: Constants.AppConfig.groupID) else { return [] } 19 | if let data = userDefault.array(forKey: Constants.AppConfig.oldUserDefaultKey) as? [String] { 20 | return data 21 | } 22 | 23 | return [] 24 | } 25 | 26 | func getIndex(text: String) -> Int { 27 | guard let userDefault = UserDefaults(suiteName: Constants.AppConfig.groupID) else { return -1 } 28 | if let data = userDefault.array(forKey: Constants.AppConfig.oldUserDefaultKey) as? [String] { 29 | if let index = data.firstIndex(of: text) { 30 | return index 31 | } 32 | } 33 | 34 | return -1 35 | } 36 | 37 | func updateWord(index: Int, text:String) -> Bool { 38 | guard let userDefault = UserDefaults(suiteName: Constants.AppConfig.groupID) else { return false } 39 | if var data = userDefault.array(forKey: Constants.AppConfig.oldUserDefaultKey) as? [String] { 40 | data.remove(at: index) 41 | data.insert(text, at: index) 42 | userDefault.set(data, forKey: Constants.AppConfig.oldUserDefaultKey) 43 | userDefault.synchronize() 44 | return true 45 | } 46 | 47 | return false 48 | } 49 | 50 | func moveWords(source: Int, destination: Int) -> Bool { 51 | 52 | guard let userDefault = UserDefaults(suiteName: Constants.AppConfig.groupID) else { return false } 53 | if let original = userDefault.array(forKey: Constants.AppConfig.oldUserDefaultKey) as? [String] { 54 | var copy = original 55 | copy.remove(at: source) 56 | copy.insert(original[source], at: destination) 57 | userDefault.set(copy, forKey: Constants.AppConfig.oldUserDefaultKey) 58 | userDefault.synchronize() 59 | 60 | return true 61 | } 62 | 63 | return false 64 | } 65 | 66 | func deleteWord(index: Int) -> Bool { 67 | guard let userDefault = UserDefaults(suiteName: Constants.AppConfig.groupID) else { return false } 68 | if var data = userDefault.array(forKey: Constants.AppConfig.oldUserDefaultKey) as? [String] { 69 | data.remove(at: index) 70 | userDefault.set(data, forKey: Constants.AppConfig.oldUserDefaultKey) 71 | userDefault.synchronize() 72 | return true 73 | } 74 | 75 | return false 76 | } 77 | 78 | func saveWord(text: String) -> Bool { 79 | guard let userDefault = UserDefaults(suiteName: Constants.AppConfig.groupID) else { return false } 80 | if var data = userDefault.array(forKey: Constants.AppConfig.oldUserDefaultKey) as? [String] { 81 | data.append(text) 82 | userDefault.set(data, forKey: Constants.AppConfig.oldUserDefaultKey) 83 | userDefault.synchronize() 84 | return true 85 | } else { 86 | let array:[String] = [text] 87 | userDefault.set(array, forKey: Constants.AppConfig.oldUserDefaultKey) 88 | userDefault.synchronize() 89 | return true 90 | } 91 | } 92 | 93 | class func sharedInstance() -> Utility { 94 | return shared 95 | } 96 | 97 | func setBool(value: Bool, key: String) { 98 | userDefaults.set(value, forKey: key) 99 | userDefaults.synchronize() 100 | } 101 | 102 | func getBool(key: String) -> Bool { 103 | return userDefaults.bool(forKey: key) 104 | } 105 | 106 | func setInt(value:Int, key: String) { 107 | userDefaults.set(value, forKey: key) 108 | userDefaults.synchronize() 109 | } 110 | 111 | func getInt(key: String) -> Int { 112 | return userDefaults.integer(forKey: key) 113 | } 114 | 115 | func removeObject(key: String) { 116 | userDefaults.removeObject(forKey: key) 117 | userDefaults.synchronize() 118 | } 119 | } 120 | -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Extensions/Rx/UITableView+Rx.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UITableView+Rx.swift 3 | // reactorkitKeyboardExample 4 | // 5 | // Created by Fernando on 2020/04/21. 6 | // Copyright © 2020 tmsae. All rights reserved. 7 | // 8 | 9 | import RxCocoa 10 | import RxDataSources 11 | import RxSwift 12 | 13 | extension Reactive where Base: UITableView { 14 | func itemSelected(dataSource: TableViewSectionedDataSource) -> ControlEvent { 15 | let source = self.itemSelected.map { indexPath in 16 | dataSource[indexPath] 17 | } 18 | return ControlEvent(events: source) 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /reactorkitKeyboardExample/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 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 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 | -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Logging/Logger.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Logger.swift 3 | // reactorkitKeyboardExample 4 | // 5 | // Created by Fernando on 2020/04/21. 6 | // Copyright © 2020 tmsae. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import CocoaLumberjack 11 | import Then 12 | 13 | extension DDLogFlag { 14 | public var level: String { 15 | switch self { 16 | case DDLogFlag.error: return "🚫 ERROR" 17 | case DDLogFlag.warning: return "⚠️ WARNING" 18 | case DDLogFlag.info: return "✅ INFO" 19 | case DDLogFlag.debug: return "🐛 DEBUG" 20 | case DDLogFlag.verbose: return "💙 VERBOSE" 21 | default: return "☠️ UNKNOWN" 22 | } 23 | } 24 | } 25 | 26 | private class LogFormatter: NSObject, DDLogFormatter { 27 | 28 | static let dateFormatter = DateFormatter().then { 29 | $0.dateFormat = "yyyy-MM-dd HH:mm:ss.SSS" 30 | } 31 | 32 | public func format(message logMessage: DDLogMessage) -> String? { 33 | let timestamp = LogFormatter.dateFormatter.string(from: logMessage.timestamp) 34 | let level = logMessage.flag.level 35 | let filename = logMessage.fileName 36 | let function = logMessage.function ?? "" 37 | let line = logMessage.line 38 | let message = logMessage.message.components(separatedBy: "\n").joined(separator: "\n ") 39 | return "\(timestamp) \(level) \(filename).\(function):\(line) - \(message)" 40 | } 41 | 42 | private func formattedDate(from date: Date) -> String { 43 | return LogFormatter.dateFormatter.string(from: date) 44 | } 45 | } 46 | 47 | /// A shared instance of `Logger`. 48 | let logger = Logger() 49 | 50 | final class Logger { 51 | 52 | // MARK: Initialize 53 | 54 | init() { 55 | setenv("XcodeColors", "YES", 0) 56 | 57 | // TTY = Xcode console 58 | DDTTYLogger.sharedInstance?.do { 59 | $0.logFormatter = LogFormatter() 60 | $0.colorsEnabled = false /* true */ // Note: doesn't work in Xcode 8 61 | $0.setForegroundColor(DDMakeColor(30, 121, 214), backgroundColor: nil, for: .info) 62 | $0.setForegroundColor(DDMakeColor(50, 143, 72), backgroundColor: nil, for: .debug) 63 | DDLog.add($0) 64 | } 65 | 66 | // File logger 67 | DDFileLogger().do { 68 | $0.rollingFrequency = TimeInterval(60 * 60 * 24) // 24 hours 69 | $0.logFileManager.maximumNumberOfLogFiles = 7 70 | DDLog.add($0) 71 | } 72 | } 73 | 74 | // MARK: Logging 75 | func error( 76 | _ items: Any..., 77 | file: StaticString = #file, 78 | function: StaticString = #function, 79 | line: UInt = #line 80 | ) { 81 | let message = self.message(from: items) 82 | DDLogError(message, file: file, function: function, line: line) 83 | } 84 | 85 | func warning( 86 | _ items: Any..., 87 | file: StaticString = #file, 88 | function: StaticString = #function, 89 | line: UInt = #line 90 | ) { 91 | let message = self.message(from: items) 92 | DDLogWarn(message, file: file, function: function, line: line) 93 | } 94 | 95 | func info( 96 | _ items: Any..., 97 | file: StaticString = #file, 98 | function: StaticString = #function, 99 | line: UInt = #line 100 | ) { 101 | let message = self.message(from: items) 102 | DDLogInfo(message, file: file, function: function, line: line) 103 | } 104 | 105 | func debug( 106 | _ items: Any..., 107 | file: StaticString = #file, 108 | function: StaticString = #function, 109 | line: UInt = #line 110 | ) { 111 | let message = self.message(from: items) 112 | DDLogDebug(message, file: file, function: function, line: line) 113 | } 114 | 115 | func verbose( 116 | _ items: Any..., 117 | file: StaticString = #file, 118 | function: StaticString = #function, 119 | line: UInt = #line 120 | ) { 121 | let message = self.message(from: items) 122 | DDLogVerbose(message, file: file, function: function, line: line) 123 | } 124 | 125 | // MARK: Utils 126 | 127 | private func message(from items: [Any]) -> String { 128 | return items 129 | .map { String(describing: $0) } 130 | .joined(separator: " ") 131 | } 132 | 133 | } 134 | -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Sections/MainSection.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MainSection.swift 3 | // reactorkitKeyboardExample 4 | // 5 | // Created by Fernando on 2020/04/21. 6 | // Copyright © 2020 tmsae. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import RxDataSources 11 | 12 | enum MainSection { 13 | case list([MainSectionItem]) 14 | } 15 | 16 | extension MainSection: SectionModelType { 17 | 18 | typealias Identify = String 19 | typealias Item = MainSectionItem 20 | var identify: String { 21 | return "" 22 | } 23 | 24 | var items: [MainSectionItem] { 25 | switch self { 26 | case .list(let items): 27 | return items 28 | } 29 | } 30 | 31 | init(original: MainSection, items: [Item]) { 32 | switch original { 33 | case .list: 34 | self = .list(items) 35 | } 36 | } 37 | } 38 | 39 | enum MainSectionItem { 40 | case listItem(MainTableViewCellReactor) 41 | } 42 | -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Sections/SettingSection.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SettingSection.swift 3 | // reactorkitKeyboardExample 4 | // 5 | // Created by Fernando on 2020/04/21. 6 | // Copyright © 2020 tmsae. All rights reserved. 7 | // 8 | 9 | import RxDataSources 10 | 11 | enum SettingSection { 12 | case usage([SettingSectionItem]) 13 | } 14 | 15 | extension SettingSection: SectionModelType { 16 | 17 | var items: [SettingSectionItem] { 18 | switch self { 19 | case .usage(let items): 20 | return items 21 | } 22 | } 23 | 24 | init(original: SettingSection, items: [SettingSectionItem]) { 25 | switch original { 26 | case .usage: 27 | self = .usage(items) 28 | } 29 | } 30 | } 31 | 32 | enum SettingSectionItem { 33 | case openSource(SettingCellReactor) 34 | case githubRepo(SettingCellReactor) 35 | } 36 | 37 | -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Supported Files/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "Icon-App-20x20@2x.png", 5 | "idiom" : "iphone", 6 | "scale" : "2x", 7 | "size" : "20x20" 8 | }, 9 | { 10 | "filename" : "Icon-App-20x20@3x.png", 11 | "idiom" : "iphone", 12 | "scale" : "3x", 13 | "size" : "20x20" 14 | }, 15 | { 16 | "filename" : "Icon-App-29x29@1x.png", 17 | "idiom" : "iphone", 18 | "scale" : "1x", 19 | "size" : "29x29" 20 | }, 21 | { 22 | "filename" : "Icon-App-29x29@2x.png", 23 | "idiom" : "iphone", 24 | "scale" : "2x", 25 | "size" : "29x29" 26 | }, 27 | { 28 | "filename" : "Icon-App-29x29@3x.png", 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "29x29" 32 | }, 33 | { 34 | "filename" : "Icon-App-40x40@2x.png", 35 | "idiom" : "iphone", 36 | "scale" : "2x", 37 | "size" : "40x40" 38 | }, 39 | { 40 | "filename" : "Icon-App-40x40@3x.png", 41 | "idiom" : "iphone", 42 | "scale" : "3x", 43 | "size" : "40x40" 44 | }, 45 | { 46 | "filename" : "Icon-App-60x60@2x.png", 47 | "idiom" : "iphone", 48 | "scale" : "2x", 49 | "size" : "60x60" 50 | }, 51 | { 52 | "filename" : "Icon-App-60x60@3x.png", 53 | "idiom" : "iphone", 54 | "scale" : "3x", 55 | "size" : "60x60" 56 | }, 57 | { 58 | "filename" : "Icon-App-20x20@1x.png", 59 | "idiom" : "ipad", 60 | "scale" : "1x", 61 | "size" : "20x20" 62 | }, 63 | { 64 | "filename" : "Icon-App-20x20@2x.png", 65 | "idiom" : "ipad", 66 | "scale" : "2x", 67 | "size" : "20x20" 68 | }, 69 | { 70 | "filename" : "Icon-App-29x29@1x.png", 71 | "idiom" : "ipad", 72 | "scale" : "1x", 73 | "size" : "29x29" 74 | }, 75 | { 76 | "filename" : "Icon-App-29x29@2x.png", 77 | "idiom" : "ipad", 78 | "scale" : "2x", 79 | "size" : "29x29" 80 | }, 81 | { 82 | "filename" : "Icon-App-40x40@1x.png", 83 | "idiom" : "ipad", 84 | "scale" : "1x", 85 | "size" : "40x40" 86 | }, 87 | { 88 | "filename" : "Icon-App-40x40@2x.png", 89 | "idiom" : "ipad", 90 | "scale" : "2x", 91 | "size" : "40x40" 92 | }, 93 | { 94 | "filename" : "Icon-App-76x76@1x.png", 95 | "idiom" : "ipad", 96 | "scale" : "1x", 97 | "size" : "76x76" 98 | }, 99 | { 100 | "filename" : "Icon-App-76x76@2x.png", 101 | "idiom" : "ipad", 102 | "scale" : "2x", 103 | "size" : "76x76" 104 | }, 105 | { 106 | "filename" : "Icon-App-83.5x83.5@2x.png", 107 | "idiom" : "ipad", 108 | "scale" : "2x", 109 | "size" : "83.5x83.5" 110 | }, 111 | { 112 | "filename" : "ItunesArtwork@2x.png", 113 | "idiom" : "ios-marketing", 114 | "scale" : "1x", 115 | "size" : "1024x1024" 116 | }, 117 | { 118 | "filename" : "Icon-App-76x76@2x.png", 119 | "idiom" : "iphone", 120 | "scale" : "2x", 121 | "size" : "76x76" 122 | } 123 | ], 124 | "info" : { 125 | "author" : "xcode", 126 | "version" : 1 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Supported Files/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techinpark/reactorkit-keyboard-example/26d215b5832750fb5c347dca2d7ac3a954b279f0/reactorkitKeyboardExample/Supported Files/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Supported Files/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techinpark/reactorkit-keyboard-example/26d215b5832750fb5c347dca2d7ac3a954b279f0/reactorkitKeyboardExample/Supported Files/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Supported Files/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techinpark/reactorkit-keyboard-example/26d215b5832750fb5c347dca2d7ac3a954b279f0/reactorkitKeyboardExample/Supported Files/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Supported Files/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techinpark/reactorkit-keyboard-example/26d215b5832750fb5c347dca2d7ac3a954b279f0/reactorkitKeyboardExample/Supported Files/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Supported Files/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techinpark/reactorkit-keyboard-example/26d215b5832750fb5c347dca2d7ac3a954b279f0/reactorkitKeyboardExample/Supported Files/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Supported Files/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techinpark/reactorkit-keyboard-example/26d215b5832750fb5c347dca2d7ac3a954b279f0/reactorkitKeyboardExample/Supported Files/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Supported Files/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techinpark/reactorkit-keyboard-example/26d215b5832750fb5c347dca2d7ac3a954b279f0/reactorkitKeyboardExample/Supported Files/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Supported Files/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techinpark/reactorkit-keyboard-example/26d215b5832750fb5c347dca2d7ac3a954b279f0/reactorkitKeyboardExample/Supported Files/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Supported Files/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techinpark/reactorkit-keyboard-example/26d215b5832750fb5c347dca2d7ac3a954b279f0/reactorkitKeyboardExample/Supported Files/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Supported Files/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techinpark/reactorkit-keyboard-example/26d215b5832750fb5c347dca2d7ac3a954b279f0/reactorkitKeyboardExample/Supported Files/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Supported Files/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techinpark/reactorkit-keyboard-example/26d215b5832750fb5c347dca2d7ac3a954b279f0/reactorkitKeyboardExample/Supported Files/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Supported Files/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techinpark/reactorkit-keyboard-example/26d215b5832750fb5c347dca2d7ac3a954b279f0/reactorkitKeyboardExample/Supported Files/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Supported Files/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techinpark/reactorkit-keyboard-example/26d215b5832750fb5c347dca2d7ac3a954b279f0/reactorkitKeyboardExample/Supported Files/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Supported Files/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techinpark/reactorkit-keyboard-example/26d215b5832750fb5c347dca2d7ac3a954b279f0/reactorkitKeyboardExample/Supported Files/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Supported Files/Assets.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techinpark/reactorkit-keyboard-example/26d215b5832750fb5c347dca2d7ac3a954b279f0/reactorkitKeyboardExample/Supported Files/Assets.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Supported Files/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "xcode" 5 | } 6 | } -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Supported Files/Assets.xcassets/clipboard.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "clipboard@1x.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "appearances" : [ 10 | { 11 | "appearance" : "luminosity", 12 | "value" : "dark" 13 | } 14 | ], 15 | "filename" : "clipboard@1x-1.png", 16 | "idiom" : "universal", 17 | "scale" : "1x" 18 | }, 19 | { 20 | "filename" : "clipboard@2x.png", 21 | "idiom" : "universal", 22 | "scale" : "2x" 23 | }, 24 | { 25 | "appearances" : [ 26 | { 27 | "appearance" : "luminosity", 28 | "value" : "dark" 29 | } 30 | ], 31 | "filename" : "clipboard@2x-1.png", 32 | "idiom" : "universal", 33 | "scale" : "2x" 34 | }, 35 | { 36 | "filename" : "clipboard@3x.png", 37 | "idiom" : "universal", 38 | "scale" : "3x" 39 | }, 40 | { 41 | "appearances" : [ 42 | { 43 | "appearance" : "luminosity", 44 | "value" : "dark" 45 | } 46 | ], 47 | "filename" : "clipboard@3x-1.png", 48 | "idiom" : "universal", 49 | "scale" : "3x" 50 | } 51 | ], 52 | "info" : { 53 | "author" : "xcode", 54 | "version" : 1 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Supported Files/Assets.xcassets/clipboard.imageset/clipboard@1x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techinpark/reactorkit-keyboard-example/26d215b5832750fb5c347dca2d7ac3a954b279f0/reactorkitKeyboardExample/Supported Files/Assets.xcassets/clipboard.imageset/clipboard@1x-1.png -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Supported Files/Assets.xcassets/clipboard.imageset/clipboard@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techinpark/reactorkit-keyboard-example/26d215b5832750fb5c347dca2d7ac3a954b279f0/reactorkitKeyboardExample/Supported Files/Assets.xcassets/clipboard.imageset/clipboard@1x.png -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Supported Files/Assets.xcassets/clipboard.imageset/clipboard@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techinpark/reactorkit-keyboard-example/26d215b5832750fb5c347dca2d7ac3a954b279f0/reactorkitKeyboardExample/Supported Files/Assets.xcassets/clipboard.imageset/clipboard@2x-1.png -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Supported Files/Assets.xcassets/clipboard.imageset/clipboard@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techinpark/reactorkit-keyboard-example/26d215b5832750fb5c347dca2d7ac3a954b279f0/reactorkitKeyboardExample/Supported Files/Assets.xcassets/clipboard.imageset/clipboard@2x.png -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Supported Files/Assets.xcassets/clipboard.imageset/clipboard@3x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techinpark/reactorkit-keyboard-example/26d215b5832750fb5c347dca2d7ac3a954b279f0/reactorkitKeyboardExample/Supported Files/Assets.xcassets/clipboard.imageset/clipboard@3x-1.png -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Supported Files/Assets.xcassets/clipboard.imageset/clipboard@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techinpark/reactorkit-keyboard-example/26d215b5832750fb5c347dca2d7ac3a954b279f0/reactorkitKeyboardExample/Supported Files/Assets.xcassets/clipboard.imageset/clipboard@3x.png -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Supported Files/Assets.xcassets/delete.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "delete@1x.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "appearances" : [ 10 | { 11 | "appearance" : "luminosity", 12 | "value" : "dark" 13 | } 14 | ], 15 | "filename" : "delete_white@1x.png", 16 | "idiom" : "universal", 17 | "scale" : "1x" 18 | }, 19 | { 20 | "filename" : "delete@2x.png", 21 | "idiom" : "universal", 22 | "scale" : "2x" 23 | }, 24 | { 25 | "appearances" : [ 26 | { 27 | "appearance" : "luminosity", 28 | "value" : "dark" 29 | } 30 | ], 31 | "filename" : "delete_white@2x.png", 32 | "idiom" : "universal", 33 | "scale" : "2x" 34 | }, 35 | { 36 | "filename" : "delete@3x.png", 37 | "idiom" : "universal", 38 | "scale" : "3x" 39 | }, 40 | { 41 | "appearances" : [ 42 | { 43 | "appearance" : "luminosity", 44 | "value" : "dark" 45 | } 46 | ], 47 | "filename" : "delete_white@3x.png", 48 | "idiom" : "universal", 49 | "scale" : "3x" 50 | } 51 | ], 52 | "info" : { 53 | "author" : "xcode", 54 | "version" : 1 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Supported Files/Assets.xcassets/delete.imageset/delete@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techinpark/reactorkit-keyboard-example/26d215b5832750fb5c347dca2d7ac3a954b279f0/reactorkitKeyboardExample/Supported Files/Assets.xcassets/delete.imageset/delete@1x.png -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Supported Files/Assets.xcassets/delete.imageset/delete@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techinpark/reactorkit-keyboard-example/26d215b5832750fb5c347dca2d7ac3a954b279f0/reactorkitKeyboardExample/Supported Files/Assets.xcassets/delete.imageset/delete@2x.png -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Supported Files/Assets.xcassets/delete.imageset/delete@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techinpark/reactorkit-keyboard-example/26d215b5832750fb5c347dca2d7ac3a954b279f0/reactorkitKeyboardExample/Supported Files/Assets.xcassets/delete.imageset/delete@3x.png -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Supported Files/Assets.xcassets/delete.imageset/delete_white@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techinpark/reactorkit-keyboard-example/26d215b5832750fb5c347dca2d7ac3a954b279f0/reactorkitKeyboardExample/Supported Files/Assets.xcassets/delete.imageset/delete_white@1x.png -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Supported Files/Assets.xcassets/delete.imageset/delete_white@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techinpark/reactorkit-keyboard-example/26d215b5832750fb5c347dca2d7ac3a954b279f0/reactorkitKeyboardExample/Supported Files/Assets.xcassets/delete.imageset/delete_white@2x.png -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Supported Files/Assets.xcassets/delete.imageset/delete_white@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techinpark/reactorkit-keyboard-example/26d215b5832750fb5c347dca2d7ac3a954b279f0/reactorkitKeyboardExample/Supported Files/Assets.xcassets/delete.imageset/delete_white@3x.png -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Supported Files/Assets.xcassets/global.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "global@1x.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "appearances" : [ 10 | { 11 | "appearance" : "luminosity", 12 | "value" : "dark" 13 | } 14 | ], 15 | "filename" : "global-white@1x.png", 16 | "idiom" : "universal", 17 | "scale" : "1x" 18 | }, 19 | { 20 | "filename" : "global@2x.png", 21 | "idiom" : "universal", 22 | "scale" : "2x" 23 | }, 24 | { 25 | "appearances" : [ 26 | { 27 | "appearance" : "luminosity", 28 | "value" : "dark" 29 | } 30 | ], 31 | "filename" : "global-white@2x.png", 32 | "idiom" : "universal", 33 | "scale" : "2x" 34 | }, 35 | { 36 | "filename" : "global@3x.png", 37 | "idiom" : "universal", 38 | "scale" : "3x" 39 | }, 40 | { 41 | "appearances" : [ 42 | { 43 | "appearance" : "luminosity", 44 | "value" : "dark" 45 | } 46 | ], 47 | "filename" : "global-white@3x.png", 48 | "idiom" : "universal", 49 | "scale" : "3x" 50 | } 51 | ], 52 | "info" : { 53 | "author" : "xcode", 54 | "version" : 1 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Supported Files/Assets.xcassets/global.imageset/global-white@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techinpark/reactorkit-keyboard-example/26d215b5832750fb5c347dca2d7ac3a954b279f0/reactorkitKeyboardExample/Supported Files/Assets.xcassets/global.imageset/global-white@1x.png -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Supported Files/Assets.xcassets/global.imageset/global-white@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techinpark/reactorkit-keyboard-example/26d215b5832750fb5c347dca2d7ac3a954b279f0/reactorkitKeyboardExample/Supported Files/Assets.xcassets/global.imageset/global-white@2x.png -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Supported Files/Assets.xcassets/global.imageset/global-white@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techinpark/reactorkit-keyboard-example/26d215b5832750fb5c347dca2d7ac3a954b279f0/reactorkitKeyboardExample/Supported Files/Assets.xcassets/global.imageset/global-white@3x.png -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Supported Files/Assets.xcassets/global.imageset/global@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techinpark/reactorkit-keyboard-example/26d215b5832750fb5c347dca2d7ac3a954b279f0/reactorkitKeyboardExample/Supported Files/Assets.xcassets/global.imageset/global@1x.png -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Supported Files/Assets.xcassets/global.imageset/global@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techinpark/reactorkit-keyboard-example/26d215b5832750fb5c347dca2d7ac3a954b279f0/reactorkitKeyboardExample/Supported Files/Assets.xcassets/global.imageset/global@2x.png -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Supported Files/Assets.xcassets/global.imageset/global@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techinpark/reactorkit-keyboard-example/26d215b5832750fb5c347dca2d7ac3a954b279f0/reactorkitKeyboardExample/Supported Files/Assets.xcassets/global.imageset/global@3x.png -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Supported Files/Assets.xcassets/plus.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "plus.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "filename" : "plus@2x.png", 10 | "idiom" : "universal", 11 | "scale" : "2x" 12 | }, 13 | { 14 | "filename" : "plus@3x.png", 15 | "idiom" : "universal", 16 | "scale" : "3x" 17 | } 18 | ], 19 | "info" : { 20 | "author" : "xcode", 21 | "version" : 1 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Supported Files/Assets.xcassets/plus.imageset/plus.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techinpark/reactorkit-keyboard-example/26d215b5832750fb5c347dca2d7ac3a954b279f0/reactorkitKeyboardExample/Supported Files/Assets.xcassets/plus.imageset/plus.png -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Supported Files/Assets.xcassets/plus.imageset/plus@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techinpark/reactorkit-keyboard-example/26d215b5832750fb5c347dca2d7ac3a954b279f0/reactorkitKeyboardExample/Supported Files/Assets.xcassets/plus.imageset/plus@2x.png -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Supported Files/Assets.xcassets/plus.imageset/plus@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techinpark/reactorkit-keyboard-example/26d215b5832750fb5c347dca2d7ac3a954b279f0/reactorkitKeyboardExample/Supported Files/Assets.xcassets/plus.imageset/plus@3x.png -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Supported Files/Assets.xcassets/return.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "return@1x.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "appearances" : [ 10 | { 11 | "appearance" : "luminosity", 12 | "value" : "dark" 13 | } 14 | ], 15 | "filename" : "return-white@1x.png", 16 | "idiom" : "universal", 17 | "scale" : "1x" 18 | }, 19 | { 20 | "filename" : "return@2x.png", 21 | "idiom" : "universal", 22 | "scale" : "2x" 23 | }, 24 | { 25 | "appearances" : [ 26 | { 27 | "appearance" : "luminosity", 28 | "value" : "dark" 29 | } 30 | ], 31 | "filename" : "return-white@2x.png", 32 | "idiom" : "universal", 33 | "scale" : "2x" 34 | }, 35 | { 36 | "filename" : "return@3x.png", 37 | "idiom" : "universal", 38 | "scale" : "3x" 39 | }, 40 | { 41 | "appearances" : [ 42 | { 43 | "appearance" : "luminosity", 44 | "value" : "dark" 45 | } 46 | ], 47 | "filename" : "return-white@3x.png", 48 | "idiom" : "universal", 49 | "scale" : "3x" 50 | } 51 | ], 52 | "info" : { 53 | "author" : "xcode", 54 | "version" : 1 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Supported Files/Assets.xcassets/return.imageset/return-white@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techinpark/reactorkit-keyboard-example/26d215b5832750fb5c347dca2d7ac3a954b279f0/reactorkitKeyboardExample/Supported Files/Assets.xcassets/return.imageset/return-white@1x.png -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Supported Files/Assets.xcassets/return.imageset/return-white@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techinpark/reactorkit-keyboard-example/26d215b5832750fb5c347dca2d7ac3a954b279f0/reactorkitKeyboardExample/Supported Files/Assets.xcassets/return.imageset/return-white@2x.png -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Supported Files/Assets.xcassets/return.imageset/return-white@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techinpark/reactorkit-keyboard-example/26d215b5832750fb5c347dca2d7ac3a954b279f0/reactorkitKeyboardExample/Supported Files/Assets.xcassets/return.imageset/return-white@3x.png -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Supported Files/Assets.xcassets/return.imageset/return@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techinpark/reactorkit-keyboard-example/26d215b5832750fb5c347dca2d7ac3a954b279f0/reactorkitKeyboardExample/Supported Files/Assets.xcassets/return.imageset/return@1x.png -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Supported Files/Assets.xcassets/return.imageset/return@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techinpark/reactorkit-keyboard-example/26d215b5832750fb5c347dca2d7ac3a954b279f0/reactorkitKeyboardExample/Supported Files/Assets.xcassets/return.imageset/return@2x.png -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Supported Files/Assets.xcassets/return.imageset/return@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/techinpark/reactorkit-keyboard-example/26d215b5832750fb5c347dca2d7ac3a954b279f0/reactorkitKeyboardExample/Supported Files/Assets.xcassets/return.imageset/return@3x.png -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Supported Files/en.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* 2 | InfoPlist.strings 3 | reactorkitKeyboardExample 4 | 5 | Created by Fernando on 2020/04/21. 6 | Copyright © 2020 tmsae. All rights reserved. 7 | */ 8 | "CFBundleDisplayName" = "Keyboard-Example"; 9 | -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Supported Files/en.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | /* 2 | Localizable.strings 3 | reactorkitKeyboardExample 4 | 5 | Created by Fernando on 2020/04/21. 6 | Copyright © 2020 tmsae. All rights reserved. 7 | */ 8 | 9 | // WriteViewController 10 | "paste_alert_title" = "Notice"; 11 | "paste_alert_body" = "Detect Message in Clipboard"; 12 | "paste_alert_okay_button" = "Paste"; 13 | "paste_alert_cancel_button" = "Cancel"; 14 | "paste_alert_message" = "New Message Added sucessfully"; 15 | "write_title" = "Add New"; 16 | 17 | // SettingViewController 18 | "setting_title" = "Settings"; 19 | "edit_title" = "Edit"; 20 | "opensource_title" = "Open Source License"; 21 | "github_repo_title" = "Go to Github Repository"; 22 | "opensource_comment" = "Thanks for clone this project 🚀"; 23 | // Common Messages 24 | "app_title" = "Keyboard-Example"; 25 | "common_ok" = "Okay"; 26 | "common_loading" = "Loading"; 27 | "common_close" = "Close"; 28 | "common_save" = "Save"; 29 | "common_load_data" = "Sync Saved Data"; 30 | "message_list_empty" = "Now List is Empty."; 31 | 32 | // Keyboard 33 | "space" = "space"; 34 | -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Supported Files/ko.lproj/InfoPlist.strings: -------------------------------------------------------------------------------- 1 | /* 2 | InfoPlist.strings 3 | reactorkitKeyboardExample 4 | 5 | Created by Fernando on 2020/04/21. 6 | Copyright © 2020 tmsae. All rights reserved. 7 | */ 8 | "CFBundleDisplayName" = "Keyboard-Example"; 9 | -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Supported Files/ko.lproj/Localizable.strings: -------------------------------------------------------------------------------- 1 | /* 2 | Localizable.strings 3 | reactorkitKeyboardExample 4 | 5 | Created by Fernando on 2020/04/21. 6 | Copyright © 2020 tmsae. All rights reserved. 7 | */ 8 | 9 | // WriteViewController 10 | "paste_alert_title" = "알림"; 11 | "paste_alert_body" = "클립보드에 내용이 감지 되었습니다."; 12 | "paste_alert_okay_button" = "붙이기"; 13 | "paste_alert_cancel_button" = "취소하기"; 14 | "paste_alert_message" = "정상적으로 추가되었습니다"; 15 | "write_title" = "추가하기"; 16 | 17 | // SettingViewController 18 | "setting_title" = "설정"; 19 | "edit_title" = "편집"; 20 | "opensource_title" = "오픈소스 라이센스"; 21 | "github_repo_title" = "Github 레포지토리로 이동"; 22 | "opensource_comment" = "해당 예제를 clone 받아주셔서 감사합니다. 🚀"; 23 | 24 | // Common Messages 25 | "app_title" = "Keyboard-Example"; 26 | "common_ok" = "확인"; 27 | "common_loading" = "로딩중"; 28 | "common_close" = "닫기"; 29 | "common_save" = "저장"; 30 | "common_load_data" = "데이터 불러오기"; 31 | "message_list_empty" = "리스트가 비어있습니다 내용을 추가해주세요."; 32 | 33 | // Keyboard 34 | "space" = "space"; 35 | 36 | -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Types/SettingFooterType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SettingFooterType.swift 3 | // reactorkitKeyboardExample 4 | // 5 | // Created by Fernando on 2020/04/21. 6 | // Copyright © 2020 tmsae. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | enum SettingFooterType: Int { 12 | case dataComment 13 | 14 | var description: String { 15 | switch self { 16 | case .dataComment: 17 | return NSLocalizedString("opensource_comment", comment: "") 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Types/SettingMenuTypes.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SettingMenuTypes.swift 3 | // reactorkitKeyboardExample 4 | // 5 | // Created by Fernando on 2020/04/21. 6 | // Copyright © 2020 tmsae. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | enum SettingMenuTypes { 12 | case openSource 13 | case githubRepo 14 | 15 | var title: String { 16 | switch self { 17 | case .openSource: 18 | return NSLocalizedString("opensource_title", comment: "Open Source License") 19 | case .githubRepo: 20 | return NSLocalizedString("github_repo_title", comment: "Go to Github Repository") 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /reactorkitKeyboardExample/ViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // reactorkitKeyboardExample 4 | // 5 | // Created by Fernando on 2020/04/21. 6 | // Copyright © 2020 tmsae. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | class ViewController: UIViewController { 12 | 13 | override func viewDidLoad() { 14 | super.viewDidLoad() 15 | // Do any additional setup after loading the view. 16 | } 17 | 18 | 19 | } 20 | 21 | -------------------------------------------------------------------------------- /reactorkitKeyboardExample/ViewControllers/MainViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MainViewController.swift 3 | // reactorkitKeyboardExample 4 | // 5 | // Created by Fernando on 2020/04/21. 6 | // Copyright © 2020 tmsae. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | import ReactorKit 12 | import RxCocoa 13 | import RxDataSources 14 | import RxSwift 15 | import ReusableKit 16 | import RxViewController 17 | import Then 18 | 19 | final class MainViewController: BaseViewController, View { 20 | typealias Reactor = MainViewReactor 21 | 22 | // MARK: Properties 23 | 24 | private struct Metric { 25 | static let writeButtonWidthHeight: CGFloat = 60.0 26 | static let writeButtonMarginLeft: CGFloat = -30.0 27 | static let writeButtonMarginRight: CGFloat = -30.0 28 | static let writeButtonMarginBottom: CGFloat = -20.0 29 | static let writeButtonRadius: CGFloat = 30.0 30 | static let adViewHeight: CGFloat = 60.0 31 | static let adViewRadius: CGFloat = 20.0 32 | 33 | static let emptyImageWidthHeight: CGFloat = 128.0 34 | static let emptyMessageTop: CGFloat = 20.0 35 | static let emptyMessageSize: CGFloat = 14.0 36 | 37 | static let tableViewCellHeight: CGFloat = 55.0 38 | } 39 | 40 | private struct Localized { 41 | static let edit = NSLocalizedString("edit_title", comment: "편집") 42 | static let setting = NSLocalizedString("setting_title", comment: "설정") 43 | static let title = NSLocalizedString("app_title", comment: "복붙키보드") 44 | static let emptyMessage = NSLocalizedString("message_list_empty", comment: "등록된 내용이 없습니다") 45 | } 46 | 47 | private struct Reusable { 48 | static let mainCell = ReusableCell() 49 | } 50 | 51 | // MARK: Initializing 52 | 53 | private let tableView = UITableView().then { 54 | $0.backgroundColor = Color.background 55 | $0.rowHeight = Metric.tableViewCellHeight 56 | $0.tableHeaderView = UIView(frame: .zero) 57 | $0.tableFooterView = UIView(frame: .zero) 58 | 59 | if #available(iOS 11.0, *) { 60 | $0.contentInsetAdjustmentBehavior = .never 61 | } 62 | 63 | $0.register(Reusable.mainCell) 64 | $0.separatorStyle = .singleLine 65 | $0.separatorInset.left = 0 66 | } 67 | 68 | private let writeButton = UIButton().then { 69 | $0.backgroundColor = .red 70 | $0.layer.masksToBounds = true 71 | $0.layer.cornerRadius = Metric.writeButtonRadius 72 | $0.setImage(#imageLiteral(resourceName: "plus"), for: .normal) 73 | } 74 | 75 | private let emptyView = UIView().then { 76 | $0.backgroundColor = Color.background 77 | $0.isHidden = true 78 | } 79 | 80 | private let emptyImageView = UIImageView().then { 81 | $0.image = #imageLiteral(resourceName: "clipboard") 82 | } 83 | 84 | private let emptyMessageLabel = UILabel().then { 85 | $0.text = Localized.emptyMessage 86 | $0.font = UIFont.systemFont(ofSize: Metric.emptyMessageSize) 87 | $0.textColor = Color.description 88 | $0.sizeToFit() 89 | } 90 | 91 | 92 | private let leftBarButtonItem = UIBarButtonItem(title: Localized.edit, style: .plain, target: nil, action: nil) 93 | private let rightBarButtonItem = UIBarButtonItem(title: Localized.setting, style: .plain, target: nil, action: nil) 94 | private var dataSource: RxTableViewSectionedReloadDataSource? 95 | 96 | convenience override init() { 97 | let reactor = MainViewReactor() 98 | self.init(reactor: reactor) 99 | } 100 | 101 | init(reactor: MainViewReactor) { 102 | defer { self.reactor = reactor } 103 | super.init() 104 | self.dataSource = self.dataSourceFactory() 105 | } 106 | 107 | required init?(coder _: NSCoder) { 108 | fatalError("init(coder:) has not been implemented") 109 | } 110 | 111 | // MARK: View Life Cycle 112 | 113 | override func viewDidLoad() { 114 | super.viewDidLoad() 115 | logger.debug("viewDidLoad") 116 | } 117 | 118 | override func viewWillAppear(_ animated: Bool) { 119 | super.viewWillAppear(animated) 120 | logger.debug("viewWillAppear") 121 | } 122 | 123 | override func addViews() { 124 | super.addViews() 125 | self.setupView() 126 | } 127 | 128 | private func setupView() { 129 | 130 | 131 | self.view.backgroundColor = Color.background 132 | self.navigationItem.leftBarButtonItem = leftBarButtonItem 133 | self.navigationItem.rightBarButtonItem = rightBarButtonItem 134 | 135 | self.emptyView.addSubview(emptyImageView) 136 | self.emptyView.addSubview(emptyMessageLabel) 137 | 138 | 139 | self.view.addSubview(tableView) 140 | self.view.addSubview(emptyView) 141 | self.view.addSubview(writeButton) 142 | } 143 | 144 | override func setupConstraints() { 145 | super.setupConstraints() 146 | 147 | 148 | tableView.snp.makeConstraints { (make) in 149 | make.top.equalTo(self.view.safeAreaLayoutGuide.snp.topMargin) 150 | make.left.right.equalToSuperview() 151 | make.bottom.equalToSuperview() 152 | } 153 | 154 | writeButton.snp.makeConstraints { make in 155 | make.right.equalTo(Metric.writeButtonMarginRight) 156 | make.width.height.equalTo(Metric.writeButtonWidthHeight) 157 | make.bottom.equalTo(self.tableView.snp.bottom).offset(Metric.writeButtonMarginBottom) 158 | } 159 | 160 | emptyView.snp.makeConstraints { (make) in 161 | make.top.equalTo(self.view.safeAreaLayoutGuide.snp.topMargin) 162 | make.left.right.equalToSuperview() 163 | make.bottom.equalToSuperview() 164 | } 165 | 166 | emptyImageView.snp.makeConstraints { (make) in 167 | make.center.equalToSuperview() 168 | } 169 | 170 | emptyMessageLabel.snp.makeConstraints { (make) in 171 | make.top.equalTo(emptyImageView.snp.bottom).offset(Metric.emptyMessageTop) 172 | make.centerX.equalToSuperview() 173 | 174 | } 175 | } 176 | 177 | // MARK: Binding 178 | 179 | func bind(reactor: MainViewReactor) { 180 | // Action 181 | rx.viewDidLoad 182 | .map { Localized.title } 183 | .bind(to: rx.title) 184 | .disposed(by: disposeBag) 185 | 186 | 187 | rx.viewWillAppear 188 | .map { _ in Reactor.Action.loadWords } 189 | .bind(to: reactor.action) 190 | .disposed(by: self.disposeBag) 191 | 192 | leftBarButtonItem.rx.tap 193 | .subscribe(onNext: { [weak self] _ in 194 | guard let self = self else { return } 195 | self.reactor?.action.onNext(.editing) 196 | }) 197 | .disposed(by: self.disposeBag) 198 | 199 | rightBarButtonItem.rx.tap 200 | .subscribe(onNext: { [weak self] _ in 201 | guard let self = self else { return } 202 | let reactor = SettingViewReactor() 203 | let settingViewController = SettingViewController(reactor: reactor) 204 | self.navigationController?.pushViewController(settingViewController, animated: true) 205 | }) 206 | .disposed(by: disposeBag) 207 | 208 | writeButton.rx.tap 209 | .subscribe(onNext: { [weak self] _ in 210 | guard let self = self else { return } 211 | 212 | let reactor = WriteViewReactor(mode: .write, 213 | text: nil) 214 | let writeViewController = WriteViewController(reactor: reactor) 215 | 216 | let navigationController = UINavigationController(rootViewController: writeViewController) 217 | navigationController.modalPresentationStyle = .fullScreen 218 | self.present(navigationController, animated: true, completion: nil) 219 | 220 | }) 221 | .disposed(by: disposeBag) 222 | 223 | // State 224 | reactor.state.map { $0.sections } 225 | .bind(to: tableView.rx.items(dataSource: self.dataSource!)) 226 | .disposed(by: self.disposeBag) 227 | 228 | reactor.state.map { !($0.sections.first?.items.isEmpty ?? false) } 229 | .bind(to: leftBarButtonItem.rx.isEnabled) 230 | .disposed(by: self.disposeBag) 231 | 232 | reactor.state.map { $0.sections.first?.items } 233 | .subscribe(onNext: { [weak self] items in 234 | guard let self = self else { return } 235 | if let items = items { 236 | if items.isEmpty { 237 | self.emptyView.isHidden = false 238 | } else { 239 | self.emptyView.isHidden = true 240 | } 241 | } 242 | }) 243 | .disposed(by: self.disposeBag) 244 | 245 | reactor.state.map { $0.isEditing } 246 | .distinctUntilChanged() 247 | .subscribe(onNext: { [weak self] isEditing in 248 | guard let self = self else { return } 249 | logger.debug(isEditing) 250 | self.tableView.setEditing(isEditing, animated: true) 251 | }) 252 | .disposed(by: self.disposeBag) 253 | 254 | tableView.rx.itemSelected 255 | .subscribe(onNext: { [weak tableView] indexPath in 256 | tableView?.deselectRow(at: indexPath, animated: true) 257 | }).disposed(by: self.disposeBag) 258 | 259 | tableView.rx.itemDeleted 260 | .subscribe(onNext: { [weak self] indexPath in 261 | guard let self = self else { return } 262 | self.reactor?.action.onNext(.deleteWord(indexPath)) 263 | logger.debug("item deleted - \(indexPath)") 264 | }).disposed(by: self.disposeBag) 265 | 266 | tableView.rx.itemMoved 267 | .subscribe(onNext: { [weak self] moveItem in 268 | guard let self = self else { return } 269 | self.reactor?.action.onNext(.moveWord(sourceIndex: moveItem.sourceIndex.row, 270 | destinationIndex: moveItem.destinationIndex.row)) 271 | logger.debug("item moved - \(moveItem)") 272 | }).disposed(by: self.disposeBag) 273 | 274 | tableView.rx.itemSelected(dataSource: self.dataSource!) 275 | .subscribe(onNext: { [weak self] sectionItem in 276 | guard let self = self else { return } 277 | switch sectionItem { 278 | case let .listItem(cellReactor): 279 | let message = cellReactor.currentState.list.message 280 | let reactor = WriteViewReactor(mode: .edit, 281 | text: message) 282 | let writeViewController = WriteViewController(reactor: reactor) 283 | 284 | let navigationController = UINavigationController(rootViewController: writeViewController) 285 | navigationController.modalPresentationStyle = .fullScreen 286 | self.present(navigationController, animated: true, completion: nil) 287 | } 288 | }) 289 | .disposed(by: self.disposeBag) 290 | } 291 | 292 | // MARK: DataSrouceFactory - configuration 293 | 294 | private func dataSourceFactory() -> RxTableViewSectionedReloadDataSource { 295 | return .init(configureCell: { (dataSource, tableView, indexPath, sectionItem) -> UITableViewCell in 296 | switch sectionItem { 297 | case .listItem(let cellReactor): 298 | let cell = tableView.dequeue(Reusable.mainCell, for: indexPath) 299 | cell.reactor = cellReactor 300 | return cell 301 | } 302 | }, canEditRowAtIndexPath: { _, _ in 303 | return true 304 | }, 305 | canMoveRowAtIndexPath: { _, _ in 306 | return true 307 | }) 308 | } 309 | 310 | } 311 | -------------------------------------------------------------------------------- /reactorkitKeyboardExample/ViewControllers/MainViewReactor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MainViewReactor.swift 3 | // reactorkitKeyboardExample 4 | // 5 | // Created by Fernando on 2020/04/21. 6 | // Copyright © 2020 tmsae. All rights reserved. 7 | // 8 | 9 | 10 | import UIKit 11 | import ReactorKit 12 | import RxCocoa 13 | import RxSwift 14 | 15 | final class MainViewReactor: Reactor { 16 | 17 | typealias SectionItem = MainSectionItem 18 | 19 | enum Item { 20 | case word(String) 21 | 22 | var message: String { 23 | switch self { 24 | case let .word(message): 25 | return message 26 | } 27 | } 28 | } 29 | 30 | enum Action { 31 | case loadWords 32 | case copyWord(String) 33 | case setToastMessage(String) 34 | case editing 35 | case moveWord(sourceIndex: Int, destinationIndex: Int) 36 | case deleteWord(IndexPath) 37 | } 38 | 39 | enum Mutation { 40 | case loadWords 41 | case moveWord(sourceIndex: Int, destinationIndex: Int) 42 | case deleteWord(Int) 43 | case setClipboard(String) 44 | case setToastMessage(String) 45 | case editing(Bool) 46 | } 47 | 48 | struct State { 49 | 50 | var words: [String] = [] 51 | var toastMessage: String? 52 | var isEditing: Bool = false 53 | var isUpdated: Bool = false 54 | var isMoved: Bool = false 55 | 56 | var sections: [MainSection] { 57 | let sectionItems = words.map { word -> MainTableViewCellReactor in 58 | return MainTableViewCellReactor(list: Item.word(word)) 59 | }.map(MainSectionItem.listItem) 60 | return [.list(sectionItems)] 61 | } 62 | } 63 | 64 | let initialState: State 65 | let utility = Utility() 66 | // MARK: Initializing 67 | 68 | init() { 69 | initialState = State() 70 | } 71 | 72 | // MARK: Mutate 73 | 74 | func mutate(action: Action) -> Observable { 75 | switch action { 76 | case .loadWords: 77 | return .just(Mutation.loadWords) 78 | case let .copyWord(word): 79 | return .just(Mutation.setClipboard(word)) 80 | case let .setToastMessage(message): 81 | return .just(Mutation.setToastMessage(message)) 82 | case .editing: 83 | if false == self.currentState.isEditing { 84 | return .just(Mutation.editing(true)) 85 | } else { 86 | return .just(Mutation.editing(false)) 87 | } 88 | case let .moveWord(moveEvent): 89 | 90 | let moveWordMutation = Mutation.moveWord(sourceIndex: moveEvent.sourceIndex, 91 | destinationIndex: moveEvent.destinationIndex) 92 | 93 | return .concat([.just(moveWordMutation), 94 | .just(Mutation.loadWords)]) 95 | 96 | case let .deleteWord(indexPath): 97 | return .just(.deleteWord(indexPath.row)) 98 | } 99 | } 100 | 101 | // MARK: Reduce 102 | 103 | func reduce(state: State, mutation: Mutation) -> State { 104 | var state = state 105 | switch mutation { 106 | case .loadWords: 107 | var originWords = state.words 108 | originWords.removeAll() 109 | originWords = utility.getWords() 110 | 111 | state.words = originWords 112 | return state 113 | case let .moveWord(sourceIndex, destinationIndex): 114 | let isMoved = utility.moveWords(source: sourceIndex, 115 | destination: destinationIndex) 116 | state.isMoved = isMoved 117 | return state 118 | case let .deleteWord(index): 119 | let isUpdated = utility.deleteWord(index: index) 120 | state.isUpdated = isUpdated 121 | state.words = utility.getWords() 122 | return state 123 | case let .setClipboard(word): 124 | self.setPasteboard(string: word) 125 | return state 126 | case let .setToastMessage(message): 127 | state.toastMessage = message 128 | return state 129 | case let .editing(isEditing): 130 | state.isEditing = isEditing 131 | return state 132 | 133 | } 134 | } 135 | 136 | private func setPasteboard(string: String) { 137 | UIPasteboard.general.string = string 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /reactorkitKeyboardExample/ViewControllers/SettingsViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SettingsViewController.swift 3 | // reactorkitKeyboardExample 4 | // 5 | // Created by Fernando on 2020/04/21. 6 | // Copyright © 2020 tmsae. All rights reserved. 7 | // 8 | 9 | import SafariServices 10 | import UIKit 11 | 12 | import ReactorKit 13 | import ReusableKit 14 | import RxCocoa 15 | import RxDataSources 16 | import RxSwift 17 | import AcknowList 18 | 19 | final class SettingViewController: BaseViewController, View { 20 | // MARK: Properties 21 | 22 | private struct Metric { 23 | static let tableViewCellHeight: CGFloat = 44.0 24 | static let sectionFooterHeight: CGFloat = 30.0 25 | static let sectionThanksFooterHeight: CGFloat = 60.0 26 | } 27 | 28 | private struct Font {} 29 | 30 | private struct Reusable { 31 | static let settingCell = ReusableCell() 32 | } 33 | 34 | private struct Localized { 35 | static let setting = NSLocalizedString("setting_title", comment: "Settings") 36 | } 37 | 38 | private var dataSource: RxTableViewSectionedReloadDataSource! 39 | 40 | // MARK: UI Views 41 | 42 | private let tableView = UITableView(frame: .zero, style: .grouped).then { 43 | $0.rowHeight = Metric.tableViewCellHeight 44 | $0.register(Reusable.settingCell) 45 | $0.separatorStyle = .singleLine 46 | } 47 | 48 | // MARK: Initializing 49 | 50 | init(reactor: SettingViewReactor) { 51 | defer { self.reactor = reactor } 52 | super.init() 53 | dataSource = dataSourceFactory() 54 | } 55 | 56 | required init?(coder _: NSCoder) { 57 | fatalError("init(coder:) has not been implemented") 58 | } 59 | 60 | // MARK: View Life Cycle 61 | 62 | override func viewDidLoad() { 63 | super.viewDidLoad() 64 | } 65 | 66 | override func addViews() { 67 | super.addViews() 68 | view.addSubview(tableView) 69 | } 70 | 71 | override func setupConstraints() { 72 | super.setupConstraints() 73 | 74 | tableView.snp.makeConstraints { make in 75 | make.edges.equalToSuperview() 76 | } 77 | } 78 | 79 | // MARK: Binding 80 | 81 | func bind(reactor: SettingViewReactor) { 82 | // Action 83 | rx.viewDidLoad 84 | .map { Localized.setting } 85 | .bind(to: rx.title) 86 | .disposed(by: disposeBag) 87 | 88 | // State 89 | reactor.state.map { $0.sections } 90 | .bind(to: tableView.rx.items(dataSource: dataSource!)) 91 | .disposed(by: disposeBag) 92 | 93 | tableView.rx.itemSelected 94 | .subscribe(onNext: { [weak tableView] indexPath in 95 | tableView?.deselectRow(at: indexPath, animated: true) 96 | }).disposed(by: disposeBag) 97 | 98 | tableView.rx.setDelegate(self) 99 | .disposed(by: disposeBag) 100 | 101 | tableView.rx.itemSelected(dataSource: dataSource!) 102 | .subscribe(onNext: { [weak self] sectionItem in 103 | guard let self = self else { return } 104 | switch sectionItem { 105 | case .openSource: 106 | let viewController = AcknowListViewController() 107 | self.navigationController?.pushViewController(viewController, animated: true) 108 | 109 | case .githubRepo: 110 | self.pushToWebVC(urlString: Constants.ETC.repoURL) 111 | } 112 | }).disposed(by: disposeBag) 113 | } 114 | 115 | private func dataSourceFactory() -> RxTableViewSectionedReloadDataSource { 116 | return .init(configureCell: { (_, tableView, indexPath, sectionItem) -> UITableViewCell in 117 | switch sectionItem { 118 | case let .openSource(cellReactor): 119 | let cell = tableView.dequeue(Reusable.settingCell, for: indexPath) 120 | cell.reactor = cellReactor 121 | return cell 122 | case let .githubRepo(cellReactor): 123 | let cell = tableView.dequeue(Reusable.settingCell, for: indexPath) 124 | cell.reactor = cellReactor 125 | return cell 126 | } 127 | }) 128 | } 129 | 130 | private func donate() { 131 | guard let firstLanguage = Locale.preferredLanguages.first else { return } 132 | 133 | 134 | let urlString = firstLanguage.hasPrefix("ko") ? WebLinks.kakaoChat : WebLinks.SurveyFormGoogle 135 | pushToWebVC(urlString: urlString) 136 | } 137 | 138 | // MARK: Route 139 | 140 | private func pushToWebVC(urlString: String) { 141 | guard let url = URL(string: urlString) else { return } 142 | let controller = SFSafariViewController(url: url) 143 | present(controller, animated: true, completion: nil) 144 | } 145 | 146 | private func openURL(urlString: String) { 147 | guard let url = URL(string: urlString) else { return } 148 | UIApplication.shared.open(url, options: [:], completionHandler: nil) 149 | } 150 | } 151 | 152 | // MARK: - UITableViewDelegate 153 | 154 | extension SettingViewController: UITableViewDelegate { 155 | func tableView(_: UITableView, viewForFooterInSection section: Int) -> UIView? { 156 | guard let type = SettingFooterType(rawValue: section) else { return nil } 157 | let footerReactor = SettingSectionFooterViewReactor(type: type) 158 | return SettingSectionFooterView(reactor: footerReactor) 159 | } 160 | 161 | func tableView(_: UITableView, heightForFooterInSection section: Int) -> CGFloat { 162 | guard let type = SettingFooterType(rawValue: section) else { return 0.0 } 163 | switch type { 164 | case .dataComment: 165 | return Metric.sectionFooterHeight 166 | } 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /reactorkitKeyboardExample/ViewControllers/SettingsViewReactor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SettingsViewReactor.swift 3 | // reactorkitKeyboardExample 4 | // 5 | // Created by Fernando on 2020/04/21. 6 | // Copyright © 2020 tmsae. All rights reserved. 7 | // 8 | 9 | import ReactorKit 10 | import RxCocoa 11 | import RxSwift 12 | 13 | final class SettingViewReactor: Reactor { 14 | typealias Action = NoAction 15 | 16 | struct State { 17 | 18 | var usageSectionItems: [SettingSectionItem] { 19 | return [ 20 | .openSource(SettingCellReactor(type: .openSource)), 21 | .githubRepo(SettingCellReactor(type: .githubRepo)) 22 | ] 23 | } 24 | var sections: [SettingSection] { 25 | return [.usage(self.usageSectionItems)] 26 | } 27 | } 28 | 29 | let initialState: State 30 | 31 | // MARK: Initializing 32 | 33 | init() { 34 | initialState = State() 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /reactorkitKeyboardExample/ViewControllers/WriteViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WriteViewController.swift 3 | // reactorkitKeyboardExample 4 | // 5 | // Created by Fernando on 2020/04/21. 6 | // Copyright © 2020 tmsae. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import ReactorKit 11 | import RxCocoa 12 | import RxSwift 13 | 14 | final class WriteViewController: BaseViewController, View { 15 | 16 | // MARK: Properties 17 | 18 | private struct Metric { 19 | static let textViewTop: CGFloat = 15.0 20 | static let textSize: CGFloat = 15.0 21 | } 22 | 23 | private struct Font { 24 | static let textViewStyle = UIFont.systemFont(ofSize: Metric.textSize, 25 | weight: .regular) 26 | } 27 | 28 | private struct Localized { 29 | static let writeTitle = NSLocalizedString("write_title", comment: "추가하기") 30 | static let editTitle = NSLocalizedString("edit_title", comment: "편집하기") 31 | static let close = NSLocalizedString("common_close", comment: "닫기") 32 | static let save = NSLocalizedString("common_save", comment: "저장") 33 | } 34 | 35 | // MARK: Initializing 36 | 37 | private let leftBarButtonItem = UIBarButtonItem(title: Localized.close, 38 | style: .plain, 39 | target: nil, 40 | action: nil) 41 | 42 | private let rightBarButtonItem = UIBarButtonItem(title: Localized.save, 43 | style: .plain, 44 | target: nil, 45 | action: nil) 46 | 47 | private let textView = UITextView().then { 48 | $0.backgroundColor = Color.background 49 | $0.textColor = Color.title 50 | $0.font = Font.textViewStyle 51 | } 52 | 53 | init(reactor: WriteViewReactor) { 54 | super.init() 55 | self.reactor = reactor 56 | } 57 | 58 | required init?(coder _: NSCoder) { 59 | fatalError("init(coder:) has not been implemented") 60 | } 61 | 62 | // MARK: View Life Cycle 63 | 64 | override func viewDidLoad() { 65 | super.viewDidLoad() 66 | } 67 | 68 | override func addViews() { 69 | super.addViews() 70 | 71 | self.view.backgroundColor = Color.background 72 | navigationItem.leftBarButtonItem = leftBarButtonItem 73 | navigationItem.rightBarButtonItem = rightBarButtonItem 74 | view.addSubview(textView) 75 | } 76 | 77 | override func setupConstraints() { 78 | super.setupConstraints() 79 | 80 | textView.snp.makeConstraints { make in 81 | make.top.equalTo(Metric.textViewTop) 82 | make.left.right.equalToSuperview() 83 | make.height.equalToSuperview() 84 | } 85 | } 86 | 87 | // MARK: Binding 88 | 89 | func bind(reactor: WriteViewReactor) { 90 | // State 91 | 92 | self.rx.viewDidLoad 93 | .asDriver() 94 | .drive(onNext: { [weak self] _ in 95 | guard let self = self else { return } 96 | self.textView.becomeFirstResponder() 97 | }) 98 | .disposed(by: self.disposeBag) 99 | 100 | rx.viewWillAppear 101 | .map { _ in Reactor.Action.initializeData } 102 | .bind(to: reactor.action) 103 | .disposed(by: self.disposeBag) 104 | 105 | leftBarButtonItem.rx.tap 106 | .subscribe(onNext: { [weak self] _ in 107 | guard let self = self else { return } 108 | self.dismiss(animated: true, completion: nil) 109 | }) 110 | .disposed(by: disposeBag) 111 | 112 | rightBarButtonItem.rx.tap 113 | .subscribe(onNext: { [weak self] _ in 114 | guard let self = self else { return } 115 | let mode = self.reactor?.currentState.mode 116 | switch mode { 117 | case .edit: 118 | logger.verbose("edit") 119 | let text = self.textView.text ?? "" 120 | self.reactor?.action.onNext(.edit(text)) 121 | case .write: 122 | logger.verbose("save") 123 | let text = self.textView.text ?? "" 124 | self.reactor?.action.onNext(.save(text)) 125 | default: 126 | break 127 | } 128 | }) 129 | .disposed(by: disposeBag) 130 | self.textView.rx.text.orEmpty 131 | .subscribe(onNext: { [weak self] string in 132 | guard let self = self else { return } 133 | 134 | if string.isEmpty { 135 | self.rightBarButtonItem.isEnabled = false 136 | } else { 137 | self.rightBarButtonItem.isEnabled = true 138 | } 139 | 140 | logger.debug(string) 141 | }).disposed(by: self.disposeBag) 142 | 143 | reactor.state.map { $0.text } 144 | .distinctUntilChanged() 145 | .bind(to: textView.rx.text) 146 | .disposed(by: disposeBag) 147 | 148 | reactor.state.map { $0.isUpdated } 149 | .filter{ $0 } 150 | .distinctUntilChanged() 151 | .subscribe(onNext: { [weak self] isUpdated in 152 | guard let self = self else { return } 153 | let mode = self.reactor?.currentState.mode 154 | switch mode { 155 | case .edit: 156 | logger.verbose("편집 완료") 157 | self.dismiss(animated: true, completion: nil) 158 | case .write: 159 | logger.verbose("새로 추가 완료") 160 | self.dismiss(animated: true, completion: nil) 161 | default: 162 | break 163 | } 164 | }) 165 | .disposed(by: self.disposeBag) 166 | 167 | reactor.state.map { $0.mode } 168 | .distinctUntilChanged() 169 | .subscribe(onNext: { [weak self] mode in 170 | guard let self = self else { return } 171 | switch mode { 172 | case .edit: 173 | self.title = Localized.editTitle 174 | case .write: 175 | self.title = Localized.writeTitle 176 | } 177 | }).disposed(by: self.disposeBag) 178 | } 179 | 180 | } 181 | -------------------------------------------------------------------------------- /reactorkitKeyboardExample/ViewControllers/WriteViewReactor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WriteViewReactor.swift 3 | // reactorkitKeyboardExample 4 | // 5 | // Created by Fernando on 2020/04/21. 6 | // Copyright © 2020 tmsae. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | import ReactorKit 11 | import RxCocoa 12 | import RxSwift 13 | 14 | 15 | final class WriteViewReactor: Reactor { 16 | 17 | enum ContentMode { 18 | case write 19 | case edit 20 | } 21 | 22 | enum Action { 23 | case initializeData 24 | case getPasteboard 25 | case getIndex 26 | case edit(String) 27 | case save(String) 28 | } 29 | 30 | enum Mutation { 31 | case getIndex 32 | case setText(String) 33 | case edit(String) 34 | case save(String) 35 | } 36 | 37 | struct State { 38 | var text: String? 39 | var isUpdated: Bool = false 40 | var mode: ContentMode 41 | var currentIndex: Int? 42 | } 43 | 44 | let initialState: State 45 | let utility = Utility() 46 | 47 | // MARK: Initializing 48 | 49 | init(mode: ContentMode, text: String?) { 50 | initialState = State(text: text, mode: mode) 51 | } 52 | 53 | // MARK: Mutate 54 | 55 | func mutate(action: Action) -> Observable { 56 | switch action { 57 | case .initializeData: 58 | switch currentState.mode { 59 | case .edit: 60 | return .just(Mutation.getIndex) 61 | case .write: 62 | return .empty() 63 | default: 64 | return .empty() 65 | } 66 | case .getIndex: 67 | return .just(Mutation.getIndex) 68 | case .getPasteboard: 69 | let text = getPasteboard() 70 | return .just(.setText(text)) 71 | case let .edit(text): 72 | return .just(.edit(text)) 73 | case let .save(text): 74 | return .just(.save(text)) 75 | 76 | } 77 | } 78 | 79 | // MARK: Reduce 80 | 81 | func reduce(state: State, mutation: Mutation) -> State { 82 | var state = state 83 | switch mutation { 84 | case .getIndex: 85 | if let text = state.text { 86 | let index = utility.getIndex(text: text) 87 | state.currentIndex = index 88 | } 89 | return state 90 | 91 | case let .setText(string): 92 | state.text = string 93 | return state 94 | 95 | case let .edit(text): 96 | if let index = state.currentIndex { 97 | let isEdited = utility.updateWord(index: index, text: text) 98 | state.isUpdated = isEdited 99 | return state 100 | } 101 | 102 | case let .save(text): 103 | let isSaved = utility.saveWord(text: text) 104 | state.isUpdated = isSaved 105 | return state 106 | } 107 | 108 | return state 109 | } 110 | 111 | func getPasteboard() -> String { 112 | guard let copyText = UIPasteboard.general.string else { return "" } 113 | if copyText.isEmpty == false { 114 | logger.verbose("[+] detected something in clipboard") 115 | return copyText 116 | } 117 | 118 | return "" 119 | } 120 | } 121 | -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Views/Cells/MainTableViewCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MainTableViewCell.swift 3 | // reactorkitKeyboardExample 4 | // 5 | // Created by Fernando on 2020/04/21. 6 | // Copyright © 2020 tmsae. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | import ReactorKit 12 | import RxCocoa 13 | import RxSwift 14 | 15 | final class MainTableViewCell: BaseTableViewCell, View { 16 | 17 | typealias Reactor = MainTableViewCellReactor 18 | 19 | private struct Metric { 20 | static let titleLeft: CGFloat = 10.0 21 | } 22 | 23 | // MARK: Initializing 24 | override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { 25 | super.init(style: style, reuseIdentifier: reuseIdentifier) 26 | self.accessoryType = .disclosureIndicator 27 | self.textLabel?.font = UIFont.systemFont(ofSize: 14.0) 28 | } 29 | 30 | required init?(coder aDecoder: NSCoder) { 31 | fatalError("init(coder:) has not been implemented") 32 | } 33 | 34 | // MARK: Initializing 35 | override func addViews() { 36 | super.addViews() 37 | self.contentView.backgroundColor = Color.background 38 | self.selectionStyle = .none 39 | } 40 | 41 | override func setupConstraints() { 42 | super.setupConstraints() 43 | } 44 | 45 | // MARK: Binding 46 | func bind(reactor: MainTableViewCellReactor) { 47 | 48 | reactor.state.map { $0.list.message } 49 | .distinctUntilChanged() 50 | .bind(to: self.textLabel!.rx.text) 51 | .disposed(by: self.disposeBag) 52 | } 53 | } 54 | 55 | -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Views/Cells/MainTableViewCellReactor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MainTableViewCellReactor.swift 3 | // reactorkitKeyboardExample 4 | // 5 | // Created by Fernando on 2020/04/21. 6 | // Copyright © 2020 tmsae. All rights reserved. 7 | // 8 | 9 | import ReactorKit 10 | import RxCocoa 11 | import RxSwift 12 | 13 | final class MainTableViewCellReactor: Reactor { 14 | typealias Action = NoAction 15 | typealias Mutaiton = NoMutation 16 | 17 | struct State { 18 | var list: MainViewReactor.Item 19 | } 20 | 21 | let initialState: State 22 | 23 | init(list: MainViewReactor.Item) { 24 | initialState = State(list: list) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Views/Cells/SettingCell.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SettingCell.swift 3 | // reactorkitKeyboardExample 4 | // 5 | // Created by Fernando on 2020/04/21. 6 | // Copyright © 2020 tmsae. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | import ReactorKit 12 | import RxCocoa 13 | import RxSwift 14 | 15 | final class SettingCell: BaseTableViewCell, ReactorKit.View { 16 | 17 | typealias Reactor = SettingCellReactor 18 | 19 | // MARK: Properties 20 | 21 | // MARK: Initializing 22 | override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) { 23 | super.init(style: style, reuseIdentifier: reuseIdentifier) 24 | self.accessoryType = .disclosureIndicator 25 | self.textLabel?.font = UIFont.systemFont(ofSize: 14.0) 26 | } 27 | 28 | required init?(coder aDecoder: NSCoder) { 29 | fatalError("init(coder:) has not been implemented") 30 | } 31 | 32 | override func addViews() { 33 | super.addViews() 34 | // self.contentView.backgroundColor = .white 35 | } 36 | 37 | override func setupConstraints() { 38 | super.setupConstraints() 39 | } 40 | 41 | // MARK: Binding 42 | func bind(reactor: Reactor) { 43 | 44 | reactor.state.map { $0.type.title } 45 | .bind(to: self.textLabel!.rx.text) 46 | .disposed(by: self.disposeBag) 47 | } 48 | } 49 | 50 | -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Views/Cells/SettingCellReactor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SettingCellReactor.swift 3 | // reactorkitKeyboardExample 4 | // 5 | // Created by Fernando on 2020/04/21. 6 | // Copyright © 2020 tmsae. All rights reserved. 7 | // 8 | 9 | import ReactorKit 10 | import RxCocoa 11 | import RxSwift 12 | 13 | final class SettingCellReactor: Reactor { 14 | typealias Action = NoAction 15 | 16 | struct State { 17 | var type: SettingMenuTypes 18 | } 19 | 20 | let initialState: State 21 | 22 | init(type: SettingMenuTypes) { 23 | initialState = State(type: type) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Views/Cells/SettingSectionFooterView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SettingSectionFooterView.swift 3 | // reactorkitKeyboardExample 4 | // 5 | // Created by Fernando on 2020/04/21. 6 | // Copyright © 2020 tmsae. All rights reserved. 7 | // 8 | 9 | import UIKit 10 | 11 | import ReactorKit 12 | import RxCocoa 13 | import RxSwift 14 | 15 | final class SettingSectionFooterView: UIView, ReactorKit.View { 16 | 17 | typealias Reactor = SettingSectionFooterViewReactor 18 | 19 | // MARK: Properties 20 | private struct Metric { 21 | static let descriptionLeading: CGFloat = 10.0 22 | static let descriptionTrailing: CGFloat = -10.0 23 | } 24 | 25 | private struct Font { 26 | static let description = UIFont.systemFont(ofSize: 12.0, weight: .regular) 27 | } 28 | 29 | var disposeBag = DisposeBag() 30 | 31 | // MARK: UI Views 32 | private let descriptionLabel = UILabel().then { 33 | $0.font = Font.description 34 | $0.textColor = Color.description 35 | $0.text = "" 36 | $0.textAlignment = .left 37 | } 38 | 39 | // MARK: Initializing 40 | init(reactor: Reactor) { 41 | defer { self.reactor = reactor } 42 | super.init(frame: .zero) 43 | addViews() 44 | setupConstraints() 45 | } 46 | 47 | required init?(coder: NSCoder) { 48 | fatalError("init(coder:) has not been implemented") 49 | } 50 | 51 | private func addViews() { 52 | self.addSubview(descriptionLabel) 53 | } 54 | 55 | private func setupConstraints() { 56 | descriptionLabel.snp.makeConstraints { make in 57 | make.leading.equalTo(Metric.descriptionLeading) 58 | make.trailing.equalTo(Metric.descriptionTrailing) 59 | make.top.bottom.equalToSuperview() 60 | } 61 | } 62 | 63 | // MARK: Binding 64 | 65 | func bind(reactor: Reactor) { 66 | reactor.state.map { $0.type.description } 67 | .bind(to: descriptionLabel.rx.text) 68 | .disposed(by: self.disposeBag) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /reactorkitKeyboardExample/Views/Cells/SettingSectionFooterViewReactor.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SettingSectionFooterViewReactor.swift 3 | // reactorkitKeyboardExample 4 | // 5 | // Created by Fernando on 2020/04/21. 6 | // Copyright © 2020 tmsae. All rights reserved. 7 | // 8 | 9 | 10 | import ReactorKit 11 | import RxCocoa 12 | import RxSwift 13 | 14 | class SettingSectionFooterViewReactor: Reactor { 15 | 16 | typealias Action = NoAction 17 | 18 | struct State { 19 | var type: SettingFooterType 20 | } 21 | 22 | let initialState: State 23 | 24 | init(type: SettingFooterType) { 25 | initialState = State(type: type) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /reactorkitKeyboardExample/ko.lproj/LaunchScreen.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /reactorkitKeyboardExample/ko.lproj/Main.strings: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /reactorkitKeyboardExample/reactorkitKeyboardExample.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.application-groups 6 | 7 | group.com.tmsae.keyboardExample 8 | 9 | 10 | 11 | --------------------------------------------------------------------------------